mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-23 21:57:33 -04:00
[bootloer] add minimal drivers
UART0, SPI, TWI
This commit is contained in:
parent
74636ec991
commit
da53cea8fc
6 changed files with 528 additions and 0 deletions
244
sw/bootloader/spi_flash.c
Normal file
244
sw/bootloader/spi_flash.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file spi_flash.c
|
||||
* @brief SPI flash driver.
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
#include "config.h"
|
||||
#include "spi_flash.h"
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* SPI flash commands
|
||||
**************************************************************************/
|
||||
enum SPI_FLASH_CMD_enum {
|
||||
SPI_FLASH_CMD_PAGE_PROGRAM = 0x02, /**< Program page */
|
||||
SPI_FLASH_CMD_READ = 0x03, /**< Read data */
|
||||
SPI_FLASH_CMD_WRITE_DISABLE = 0x04, /**< Disallow write access */
|
||||
SPI_FLASH_CMD_READ_STATUS = 0x05, /**< Get status register */
|
||||
SPI_FLASH_CMD_WRITE_ENABLE = 0x06, /**< Allow write access */
|
||||
SPI_FLASH_CMD_WAKE = 0xAB, /**< Wake up from sleep mode */
|
||||
SPI_FLASH_CMD_SECTOR_ERASE = 0xD8 /**< Erase complete sector */
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* SPI flash status register bits
|
||||
**************************************************************************/
|
||||
enum SPI_FLASH_SREG_enum {
|
||||
FLASH_SREG_BUSY = 0, /**< Busy, write/erase in progress when set, read-only */
|
||||
FLASH_SREG_WEL = 1 /**< Write access enabled when set, read-only */
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Send single command to SPI flash.
|
||||
*
|
||||
* @param[in] cmd Command byte.
|
||||
**************************************************************************/
|
||||
void spi_flash_cmd(uint8_t cmd) {
|
||||
|
||||
neorv32_spi_cs_en(SPI_FLASH_CS);
|
||||
neorv32_spi_transfer(cmd);
|
||||
neorv32_spi_cs_dis();
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Read flash status register.
|
||||
*
|
||||
* @return SPI flash status register.
|
||||
**************************************************************************/
|
||||
uint8_t spi_flash_read_status(void) {
|
||||
|
||||
neorv32_spi_cs_en(SPI_FLASH_CS);
|
||||
|
||||
neorv32_spi_transfer(SPI_FLASH_CMD_READ_STATUS);
|
||||
uint8_t res = neorv32_spi_transfer(0);
|
||||
|
||||
neorv32_spi_cs_dis();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Send address to flash.
|
||||
*
|
||||
* @param[in] addr Byte address.
|
||||
**************************************************************************/
|
||||
void spi_flash_send_addr(uint32_t addr) {
|
||||
|
||||
subwords32_t address;
|
||||
address.uint32 = addr;
|
||||
|
||||
#if (FLASH_ADDR_BYTES == 2)
|
||||
neorv32_spi_transfer(address.uint8[1]);
|
||||
neorv32_spi_transfer(address.uint8[0]);
|
||||
#elif (FLASH_ADDR_BYTES == 3)
|
||||
neorv32_spi_transfer(address.uint8[2]);
|
||||
neorv32_spi_transfer(address.uint8[1]);
|
||||
neorv32_spi_transfer(address.uint8[0]);
|
||||
#elif (FLASH_ADDR_BYTES == 4)
|
||||
neorv32_spi_transfer(address.uint8[3]);
|
||||
neorv32_spi_transfer(address.uint8[2]);
|
||||
neorv32_spi_transfer(address.uint8[1]);
|
||||
neorv32_spi_transfer(address.uint8[0]);
|
||||
#else
|
||||
#error "Invalid FLASH_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Write byte to SPI flash.
|
||||
*
|
||||
* @param[in] addr SPI flash read address.
|
||||
* @param[in] wdata SPI flash read data.
|
||||
**************************************************************************/
|
||||
void spi_flash_write_byte(uint32_t addr, uint8_t wdata) {
|
||||
|
||||
spi_flash_cmd(SPI_FLASH_CMD_WRITE_ENABLE); // allow write-access
|
||||
|
||||
neorv32_spi_cs_en(SPI_FLASH_CS);
|
||||
|
||||
neorv32_spi_transfer(SPI_FLASH_CMD_PAGE_PROGRAM);
|
||||
spi_flash_send_addr(addr);
|
||||
neorv32_spi_transfer(wdata);
|
||||
|
||||
neorv32_spi_cs_dis();
|
||||
|
||||
while(1) {
|
||||
if ((spi_flash_read_status() & (1 << FLASH_SREG_BUSY)) == 0) { // write in progress flag cleared?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Check if SPI and flash are available/working by making sure the WEL
|
||||
* flag of the flash status register can be set and cleared again.
|
||||
*
|
||||
* @return 0 if success, !=0 if error
|
||||
**************************************************************************/
|
||||
int spi_flash_check(void) {
|
||||
|
||||
#if (SPI_EN != 0)
|
||||
if (neorv32_spi_available()) {
|
||||
// the flash may have been set to sleep prior to reaching this point. Make sure it's alive
|
||||
spi_flash_cmd(SPI_FLASH_CMD_WAKE);
|
||||
|
||||
// set WEL
|
||||
spi_flash_cmd(SPI_FLASH_CMD_WRITE_ENABLE);
|
||||
if ((spi_flash_read_status() & (1 << FLASH_SREG_WEL)) == 0) { // fail if WEL is cleared
|
||||
return -1;
|
||||
}
|
||||
|
||||
// clear WEL
|
||||
spi_flash_cmd(SPI_FLASH_CMD_WRITE_DISABLE);
|
||||
if ((spi_flash_read_status() & (1 << FLASH_SREG_WEL)) != 0) { // fail if WEL is set
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Read byte from SPI flash.
|
||||
*
|
||||
* @param[in] addr Word-aligned address.
|
||||
* @param[in,out] rdata Pointer for returned data (uint32_t).
|
||||
* @return 0 if success, !=0 if error
|
||||
**************************************************************************/
|
||||
int spi_flash_read_word(uint32_t addr, uint32_t* rdata) {
|
||||
|
||||
#if (SPI_EN != 0)
|
||||
neorv32_spi_cs_en(SPI_FLASH_CS);
|
||||
|
||||
neorv32_spi_transfer(SPI_FLASH_CMD_READ);
|
||||
spi_flash_send_addr(addr);
|
||||
|
||||
subwords32_t tmp;
|
||||
tmp.uint8[0] = neorv32_spi_transfer(0);
|
||||
tmp.uint8[1] = neorv32_spi_transfer(1);
|
||||
tmp.uint8[2] = neorv32_spi_transfer(2);
|
||||
tmp.uint8[3] = neorv32_spi_transfer(3);
|
||||
|
||||
neorv32_spi_cs_dis();
|
||||
|
||||
*rdata = tmp.uint32;
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Write word to SPI flash.
|
||||
*
|
||||
* @param addr SPI flash write address.
|
||||
* @param wdata SPI flash write data.
|
||||
* @return 0 if success, !=0 if error
|
||||
**************************************************************************/
|
||||
int spi_flash_write_word(uint32_t addr, uint32_t wdata) {
|
||||
|
||||
#if (SPI_EN != 0)
|
||||
subwords32_t data;
|
||||
data.uint32 = wdata;
|
||||
|
||||
// little-endian byte order
|
||||
int i;
|
||||
for (i=0; i<4; i++) {
|
||||
spi_flash_write_byte(addr + i, data.uint8[i]);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Erase sector (64kB) at base address.
|
||||
*
|
||||
* @param[in] addr Base address of sector to erase.
|
||||
* @return 0 if success, !=0 if error
|
||||
**************************************************************************/
|
||||
int spi_flash_erase_sector(uint32_t addr) {
|
||||
|
||||
#if (SPI_EN != 0)
|
||||
spi_flash_cmd(SPI_FLASH_CMD_WRITE_ENABLE); // allow write-access
|
||||
|
||||
neorv32_spi_cs_en(SPI_FLASH_CS);
|
||||
|
||||
neorv32_spi_transfer(SPI_FLASH_CMD_SECTOR_ERASE);
|
||||
spi_flash_send_addr(addr);
|
||||
|
||||
neorv32_spi_cs_dis();
|
||||
|
||||
while(1) {
|
||||
if ((spi_flash_read_status() & (1 << FLASH_SREG_BUSY)) == 0) { // write-in-progress flag cleared?
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
24
sw/bootloader/spi_flash.h
Normal file
24
sw/bootloader/spi_flash.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file spi_flash.h
|
||||
* @brief SPI flash driver.
|
||||
*/
|
||||
|
||||
#ifndef SPI_FLASH_H
|
||||
#define SPI_FLASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int spi_flash_check(void) ;
|
||||
int spi_flash_read_word(uint32_t addr, uint32_t* rdata) ;
|
||||
int spi_flash_write_word(uint32_t addr, uint32_t wdata);
|
||||
int spi_flash_erase_sector(uint32_t addr);
|
||||
|
||||
#endif // SPI_FLASH_H
|
103
sw/bootloader/twi_flash.c
Normal file
103
sw/bootloader/twi_flash.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file twi_flash.c
|
||||
* @brief TWI flash driver.
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
#include "config.h"
|
||||
#include "twi_flash.h"
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Read 32-bit word from word-aligned TWI flash address.
|
||||
*
|
||||
* @param[in] addr Word-aligned address.
|
||||
* @param[in,out] rdata Pointer for returned data (uint32_t).
|
||||
* @return 0 if success, != 0 if error
|
||||
**************************************************************************/
|
||||
int twi_flash_read_word(uint32_t addr, uint32_t* rdata) {
|
||||
|
||||
#if (TWI_EN != 0)
|
||||
int i;
|
||||
int device_nack = 0;
|
||||
uint8_t transfer;
|
||||
subwords32_t data, address;
|
||||
|
||||
address.uint32 = addr;
|
||||
|
||||
/***********************
|
||||
* set address to read
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// send device address
|
||||
uint8_t device_id = address.uint8[FLASH_ADDR_BYTES] + TWI_DEVICE_ID;
|
||||
transfer = device_id << 1;
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
|
||||
// send read access address
|
||||
#if (FLASH_ADDR_BYTES == 1)
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
#elif (FLASH_ADDR_BYTES == 2)
|
||||
transfer = address.uint8[1];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
#elif (FLASH_ADDR_BYTES == 3)
|
||||
transfer = address.uint8[2];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
transfer = address.uint8[1];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
#else
|
||||
#error "Invalid FLASH_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
|
||||
/***********************
|
||||
* read data
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// send device address with read flag
|
||||
transfer = device_id << 1;
|
||||
transfer |= 0x01;
|
||||
device_nack |= neorv32_twi_transfer(&transfer, 0);
|
||||
|
||||
if (device_nack) {
|
||||
neorv32_twi_generate_stop();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read
|
||||
for (i = 0; i < 3; i++) {
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_transfer(&transfer, 1); // ACK by host
|
||||
data.uint8[i] = transfer;
|
||||
}
|
||||
|
||||
// last read with NACK by host
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_transfer(&transfer, 0); // NACK by host
|
||||
data.uint8[3] = transfer;
|
||||
|
||||
*rdata = data.uint32;
|
||||
|
||||
neorv32_twi_generate_stop();
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
21
sw/bootloader/twi_flash.h
Normal file
21
sw/bootloader/twi_flash.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file twi_flash.h
|
||||
* @brief TWI flash driver.
|
||||
*/
|
||||
|
||||
#ifndef TWI_FLASH_H
|
||||
#define TWI_FLASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int twi_flash_read_word(uint32_t addr, uint32_t* rdata);
|
||||
|
||||
#endif // TWI_FLASH_H
|
111
sw/bootloader/uart.c
Normal file
111
sw/bootloader/uart.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file uart.c
|
||||
* @brief Minimal UART0 driver.
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
#include "config.h"
|
||||
#include "uart.h"
|
||||
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Read single char from UART0.
|
||||
*
|
||||
* @return Received char.
|
||||
**************************************************************************/
|
||||
char uart_getc(void) {
|
||||
|
||||
#if (UART_EN != 0)
|
||||
if (neorv32_uart_available(NEORV32_UART0)) {
|
||||
return neorv32_uart_getc(NEORV32_UART0);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Print single char via UART0.
|
||||
*
|
||||
* @note Converts LF ("\n") to CR+LF ("\r\n").
|
||||
*
|
||||
* @param[in] c Character to print.
|
||||
**************************************************************************/
|
||||
void uart_putc(char c) {
|
||||
|
||||
#if (UART_EN != 0)
|
||||
if (neorv32_uart_available(NEORV32_UART0)) {
|
||||
if (c == '\n') {
|
||||
neorv32_uart_putc(NEORV32_UART0, '\r');
|
||||
}
|
||||
neorv32_uart_putc(NEORV32_UART0, c);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Print zero-terminated string via UART0.
|
||||
*
|
||||
* @param[in] s Pointer to string.
|
||||
**************************************************************************/
|
||||
void uart_puts(const char *s) {
|
||||
|
||||
#if (UART_EN != 0)
|
||||
char c = 0;
|
||||
while ((c = *s++)) {
|
||||
uart_putc(c);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Print 32-bit number as 8-digit hexadecimal value with "0x" suffix via UART0.
|
||||
*
|
||||
* @param[in] num Number to print as hexadecimal.
|
||||
**************************************************************************/
|
||||
void uart_puth(uint32_t num) {
|
||||
|
||||
#if (UART_EN != 0)
|
||||
static const char hex_symbols[16] = "0123456789abcdef";
|
||||
uart_putc('0');
|
||||
uart_putc('x');
|
||||
|
||||
int i;
|
||||
for (i=28; i>=0; i-=4) {
|
||||
uart_putc(hex_symbols[(num >> i) & 0xf]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Read 32-bit binary word from UART0.
|
||||
*
|
||||
* @param[in,out] rdata Pointer for returned data (uint32_t).
|
||||
* @return 0 if success, != 0 if error
|
||||
**************************************************************************/
|
||||
int uart_getw(uint32_t* rdata) {
|
||||
|
||||
#if (UART_EN != 0)
|
||||
int i;
|
||||
subwords32_t tmp;
|
||||
for (i=0; i<4; i++) {
|
||||
tmp.uint8[i] = (uint8_t)uart_getc();
|
||||
}
|
||||
*rdata = tmp.uint32;
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
25
sw/bootloader/uart.h
Normal file
25
sw/bootloader/uart.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file uart.h
|
||||
* @brief Minimal UART0 driver.
|
||||
*/
|
||||
|
||||
#ifndef UART_H
|
||||
#define UART_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
char uart_getc(void);
|
||||
void uart_putc(char c);
|
||||
void uart_puts(const char *s);
|
||||
void uart_puth(uint32_t num);
|
||||
int uart_getw(uint32_t* rdata);
|
||||
|
||||
#endif // UART_H
|
Loading…
Add table
Add a link
Reference in a new issue