mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-23 13:47:33 -04:00
[sw/bootloader] add TWI boot option
This commit is contained in:
parent
3fe0b11cf5
commit
9a5bdfba6c
1 changed files with 155 additions and 25 deletions
|
@ -58,6 +58,7 @@
|
|||
#endif
|
||||
|
||||
/* -------- Auto-boot configuration -------- */
|
||||
/* Priority SPI > TWI */
|
||||
|
||||
/** Time until the auto-boot sequence starts (in seconds); 0 = disabled */
|
||||
#ifndef AUTO_BOOT_TIMEOUT
|
||||
|
@ -103,6 +104,29 @@
|
|||
#define XIP_EN 1
|
||||
#endif
|
||||
|
||||
/* -------- TWI configuration -------- */
|
||||
/* assumes single byte address */
|
||||
|
||||
/** Enable TWI for cooping to RAM */
|
||||
#ifndef TWI_EN
|
||||
#define TWI_EN 1
|
||||
#endif
|
||||
|
||||
/** TWI Clock pre-scaler */
|
||||
#ifndef TWI_CLK_PRSC
|
||||
#define TWI_CLK_PRSC CLK_PRSC_64
|
||||
#endif
|
||||
|
||||
/** TWI Clock divider */
|
||||
#ifndef TWI_CLK_DIV
|
||||
#define TWI_CLK_DIV 3
|
||||
#endif
|
||||
|
||||
/** TWI First Slave ID*/
|
||||
#ifndef TWI_SLAVE_ID
|
||||
#define TWI_SLAVE_ID 0x50
|
||||
#endif
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
|
@ -111,7 +135,8 @@
|
|||
**************************************************************************/
|
||||
enum EXE_STREAM_SOURCE_enum {
|
||||
EXE_STREAM_UART = 0, /**< Get executable via UART */
|
||||
EXE_STREAM_FLASH = 1 /**< Get executable via SPI flash */
|
||||
EXE_STREAM_FLASH = 1, /**< Get executable via SPI flash */
|
||||
EXE_STREAM_TWI = 2 /**< Get executable via I2c Slave */
|
||||
};
|
||||
|
||||
|
||||
|
@ -122,18 +147,20 @@ enum ERROR_CODES_enum {
|
|||
ERROR_SIGNATURE = 0, /**< 0: Wrong signature in executable */
|
||||
ERROR_SIZE = 1, /**< 1: Insufficient instruction memory capacity */
|
||||
ERROR_CHECKSUM = 2, /**< 2: Checksum error in executable */
|
||||
ERROR_FLASH = 3 /**< 3: SPI flash access error */
|
||||
ERROR_FLASH = 3, /**< 3: SPI flash access error */
|
||||
ERROR_TWI = 4 /**< 3: TWI access error (missing ACK) */
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Error messages
|
||||
**************************************************************************/
|
||||
const char error_message[4][5] = {
|
||||
const char error_message[5][5] = {
|
||||
"EXE",
|
||||
"SIZE",
|
||||
"CHKS",
|
||||
"FLSH"
|
||||
"FLSH",
|
||||
"TWI"
|
||||
};
|
||||
|
||||
|
||||
|
@ -237,6 +264,9 @@ void spi_flash_write_disable(void);
|
|||
uint8_t spi_flash_read_status(void);
|
||||
void spi_flash_write_addr(uint32_t addr);
|
||||
|
||||
// TWI driver functions
|
||||
uint32_t twi_read_addr(uint32_t addr);
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Sanity check: Base RV32I ISA only!
|
||||
|
@ -287,6 +317,11 @@ int main(void) {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if (TWI_EN != 0)
|
||||
// setup TWI
|
||||
neorv32_twi_setup(TWI_CLK_PRSC, TWI_CLK_DIV, 0);
|
||||
#endif
|
||||
|
||||
// Configure machine system timer interrupt
|
||||
if (neorv32_mtime_available()) {
|
||||
NEORV32_MTIME->TIME_LO = 0;
|
||||
|
@ -322,7 +357,7 @@ int main(void) {
|
|||
// ------------------------------------------------
|
||||
// Auto boot sequence
|
||||
// ------------------------------------------------
|
||||
#if (SPI_EN != 0)
|
||||
#if (SPI_EN != 0 || TWI_EN != 0)
|
||||
#if (AUTO_BOOT_TIMEOUT != 0)
|
||||
if (neorv32_mtime_available()) {
|
||||
|
||||
|
@ -339,7 +374,11 @@ int main(void) {
|
|||
}
|
||||
|
||||
if (neorv32_mtime_get_time() >= timeout_time) { // timeout? start auto boot sequence
|
||||
get_exe(EXE_STREAM_FLASH); // try booting from flash
|
||||
#if (SPI_EN != 0)
|
||||
get_exe(EXE_STREAM_FLASH); // try booting from flash
|
||||
#elif (TWI_EN != 0)
|
||||
get_exe(EXE_STREAM_TWI); // try booting from twi
|
||||
#endif
|
||||
PRINT_TEXT("\n");
|
||||
start_app(0);
|
||||
while(1);
|
||||
|
@ -385,6 +424,11 @@ int main(void) {
|
|||
else if (c == 'l') { // copy executable from flash
|
||||
get_exe(EXE_STREAM_FLASH);
|
||||
}
|
||||
#endif
|
||||
#if (TWI_EN != 0)
|
||||
else if (c == 't') { // copy executable from TWI
|
||||
get_exe(EXE_STREAM_TWI);
|
||||
}
|
||||
#endif
|
||||
else if (c == 'e') { // start application program from IMEM
|
||||
if (exe_available == 0) { // executable available?
|
||||
|
@ -430,6 +474,9 @@ void print_help(void) {
|
|||
" s: Store to flash\n"
|
||||
" l: Load from flash\n"
|
||||
#endif
|
||||
#if (TWI_EN != 0)
|
||||
" t: Load from TWI Slave\n"
|
||||
#endif
|
||||
#if (XIP_EN != 0)
|
||||
" x: Boot from flash (XIP)\n"
|
||||
#endif
|
||||
|
@ -532,25 +579,35 @@ void get_exe(int src) {
|
|||
getting_exe = 1; // to inform trap handler we were trying to get an executable
|
||||
|
||||
// flash image base address
|
||||
uint32_t addr = (uint32_t)SPI_BOOT_BASE_ADDR;
|
||||
uint32_t addr = 0;
|
||||
if (src == EXE_STREAM_FLASH) {
|
||||
addr = (uint32_t)SPI_BOOT_BASE_ADDR;
|
||||
}
|
||||
|
||||
|
||||
// get image from UART?
|
||||
if (src == EXE_STREAM_UART) {
|
||||
PRINT_TEXT("Awaiting neorv32_exe.bin... ");
|
||||
}
|
||||
#if (SPI_EN != 0)
|
||||
else {
|
||||
#if (SPI_EN != 0)
|
||||
else if(src == EXE_STREAM_FLASH) {
|
||||
PRINT_TEXT("Loading from SPI flash @");
|
||||
PRINT_XNUM(addr);
|
||||
PRINT_TEXT("...\n");
|
||||
|
||||
// flash checks
|
||||
if (((NEORV32_SYSINFO->SOC & (1<<SYSINFO_SOC_IO_SPI)) == 0) || // SPI module not implemented?
|
||||
(spi_flash_check() != 0)) { // check if flash ready (or available at all)
|
||||
(spi_flash_check() != 0)) { // check if flash ready (or available at all)
|
||||
system_error(ERROR_FLASH);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#elif (TWI_EN)
|
||||
else {
|
||||
PRINT_TEXT("Loading from TWI slaves, starting with ");
|
||||
PRINT_XNUM(TWI_SLAVE_ID);
|
||||
PRINT_TEXT("...\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// check if valid image
|
||||
uint32_t signature = get_exe_word(src, addr + EXE_OFFSET_SIGNATURE);
|
||||
|
@ -658,27 +715,30 @@ void save_exe(void) {
|
|||
* Get word from executable stream
|
||||
*
|
||||
* @param src Source of executable stream data. See #EXE_STREAM_SOURCE_enum.
|
||||
* @param addr Address when accessing SPI flash.
|
||||
* @param addr Address when accessing SPI flash or TWI slave.
|
||||
* @return 32-bit data word from stream.
|
||||
**************************************************************************/
|
||||
uint32_t get_exe_word(int src, uint32_t addr) {
|
||||
if (src == EXE_STREAM_TWI) {
|
||||
return twi_read_addr(addr);
|
||||
} else {
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data;
|
||||
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data;
|
||||
uint32_t i;
|
||||
|
||||
uint32_t i;
|
||||
for (i=0; i<4; i++) {
|
||||
if (src == EXE_STREAM_UART) {
|
||||
data.uint8[i] = (uint8_t)PRINT_GETC();
|
||||
for (i=0; i<4; i++) {
|
||||
if (src == EXE_STREAM_UART) {
|
||||
data.uint8[i] = (uint8_t)PRINT_GETC();
|
||||
}
|
||||
else {
|
||||
data.uint8[i] = spi_flash_read_byte(addr + i); // little-endian byte order
|
||||
}
|
||||
}
|
||||
else {
|
||||
data.uint8[i] = spi_flash_read_byte(addr + i); // little-endian byte order
|
||||
}
|
||||
}
|
||||
|
||||
return data.uint32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -963,3 +1023,73 @@ void spi_flash_write_addr(uint32_t addr) {
|
|||
#error "Unsupported SPI_FLASH_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
}
|
||||
|
||||
// ##########################################################################################################
|
||||
// TWI driver functions
|
||||
// ##########################################################################################################
|
||||
uint32_t twi_read_addr(uint32_t addr) {
|
||||
|
||||
#if (TWI_EN != 0)
|
||||
int device_nack = 0;
|
||||
uint8_t transfer;
|
||||
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data, address;
|
||||
|
||||
address.uint32 = addr;
|
||||
|
||||
uint8_t device_id = address.uint8[1] + TWI_SLAVE_ID;
|
||||
|
||||
|
||||
|
||||
/***********************
|
||||
* Set address to read
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// Send device addr
|
||||
transfer = device_id << 1;
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
|
||||
// Send read address
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
|
||||
/***********************
|
||||
* Read data
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// Send device addr with read flag
|
||||
transfer = device_id << 1;
|
||||
transfer |= 0x01;
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
|
||||
if (device_nack)
|
||||
{
|
||||
system_error(ERROR_TWI);
|
||||
}
|
||||
|
||||
// Read
|
||||
for (uint8_t i = 0; i <= 2; i++)
|
||||
{
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_trans(&transfer, 1); // ACK by master
|
||||
data.uint8[i] = transfer;
|
||||
}
|
||||
// Last read with NACK by master
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_trans(&transfer, 0); // NACK by master
|
||||
data.uint8[3] = transfer;
|
||||
|
||||
neorv32_twi_generate_stop();
|
||||
|
||||
return data.uint32;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue