diff --git a/README.md b/README.md index 046312da5..0648129cb 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ for **custom RISC-V instructions**; caches ([iCACHE](https://stnolting.github.io/neorv32/#_processor_internal_instruction_cache_icache) and [dCACHE](https://stnolting.github.io/neorv32/#_processor_internal_data_cache_dcache)) * pre-installed bootloader ([BOOTLDROM](https://stnolting.github.io/neorv32/#_bootloader_rom_bootrom)) with serial user interface; -allows booting application code via UART or from external SPI flash +allows booting application code via UART, TWI or from external SPI flash **Timers and Counters** diff --git a/docs/datasheet/software_bootloader.adoc b/docs/datasheet/software_bootloader.adoc index c1d1bfe8a..88261221e 100644 --- a/docs/datasheet/software_bootloader.adoc +++ b/docs/datasheet/software_bootloader.adoc @@ -46,6 +46,7 @@ The bootloader requires certain CPU and SoC extensions and modules to be enabled | _RECOMMENDED_ | The machine timer of the <<_core_local_interruptor_clint>> is used to control blinking of the status LED and also to automatically trigger the <<_auto_boot_sequence>>. | OPTIONAL | The SPI controller (<<_serial_peripheral_interface_controller_spi>>) is needed to store/load executable from external flash using the <<_auto_boot_sequence>>. | OPTIONAL | The XIP controller (<<_execute_in_place_module_xip>>) is needed to boot/execute code directly from a pre-programmed SPI flash. +| OPTIONAL | The TWI controller (<<_two_wire_serial_interface_controller_twi>>) is needed to boot/execute code directly from pre-programmed TWI memory. |======================= @@ -70,6 +71,17 @@ Most properties (like chip select line, flash address width, SPI clock frequency without the need to change the source code. Custom configuration can be made using command line switches (defines) when recompiling the bootloader. See the User Guide https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader for more information. +:sectnums: +==== Bootloader TWI memory Requirements + +The bootloader can access an TWI-compatible memory via the processor's top entity TWI port. Single- and dual address memory is supported, and reading is done in the following pattern +`Device Address + Enabled Read | Memory Address Byte 0 | Memory Address 1 (optional) | Read Byte 0 | Read Byte 1 | Read Byte 2 | Read Byte 3`. +The addresses are incremented until the end of the program binary is reached. + +A python upload script for uploading is provided in the `sw/eeprom_upload` folder. Currently only for the https://www.robot-electronics.co.uk/htm/usb_iss_tech.htm[USB-ISS] module. + + +Clock speed information can be read here: <<_two_wire_serial_interface_controller_twi>>. :sectnums: ==== Bootloader Console @@ -154,6 +166,7 @@ Available CMDs: u: Upload s: Store to flash l: Load from flash + t: Load from TWI Device x: Boot from flash (XIP) e: Execute CMD:> @@ -167,6 +180,7 @@ The auto boot countdown is stopped and the bootloader's user console is ready to * `u`: Upload new program executable (`neorv32_exe.bin`) via UART into the instruction memory * `s`: Store executable to SPI flash at `spi_csn_o(0)` (little-endian byte order) * `l`: Load executable from SPI flash at `spi_csn_o(0)` (little-endian byte order) +* `t`: Load executable from TWI memory at `0x50` (little-endian byte order) (disabled by default) * `x`: Boot program directly from flash via XIP (requires a pre-programmed image) * `e`: Start the application, which is currently stored in the instruction memory (IMEM) @@ -206,7 +220,8 @@ https://stnolting.github.io/neorv32/ug/#_programming_an_external_spi_flash_via_t When you reset the NEORV32 processor, the bootloader waits 8 seconds for a UART console input before it starts the automatic boot sequence. This sequence tries to fetch a valid boot image from the external SPI -flash, connected to SPI chip select `spi_csn_o(0)`. If a valid boot image is found that can be successfully +flash, connected to SPI chip select `spi_csn_o(0)` or from external TWI memory. If both are enabled, the bootloader +will select SPI. If a valid boot image is found that can be successfully transferred into the instruction memory, it is automatically started. If no SPI flash is detected or if there is no valid boot image found, and error code will be shown. @@ -228,6 +243,7 @@ In many cases the error source is just _temporary_ (like some HF spike during an | **`ERR_CHKS`** | This indicates a checksum error. Something went wrong during the transfer of the program image (upload via UART or loading from the external SPI flash). If the error was caused by a UART upload, just try it again. When the error was generated during a flash access, the stored image might be corrupted. | **`ERR_FLSH`** | This error occurs if the attached SPI flash cannot be accessed. Make sure you have the right type of flash and that it is properly connected to the NEORV32 SPI port using chip select #0. | **`ERR_EXC`** | The bootloader encountered an unexpected exception during operation. This might be caused when it tries to access peripherals that were not implemented during synthesis. Example: executing commands `l` or `s` (SPI flash operations) without the SPI module being implemented. +| **`ERR_TWI`** | The TWI received an unexpected NACK while reading the external memory. Are the address and speed settings correct? |======================= [TIP] diff --git a/docs/userguide/customizing_the_bootloader.adoc b/docs/userguide/customizing_the_bootloader.adoc index af57e4e47..da8229db0 100644 --- a/docs/userguide/customizing_the_bootloader.adoc +++ b/docs/userguide/customizing_the_bootloader.adoc @@ -39,14 +39,23 @@ minimal base & privileged ISA `rv32e_zicsr_zifencei` only to ensure it can work | `SPI_BOOT_BASE_ADDR` | `0x00400000` | _any_ 32-bit value | Defines the _base_ address of the executable in external flash 4+^| XIP configuration | `XIP_EN` | `0` | `0`, `1` | Set `1` to enable the XIP options +4+^| TWI configuration +| `TWI_EN` | `0` | `0`, `1` | Set `1` to enable the usage of the TWI module (including load executables from TWI device option) +| `TWI_CLK_PRSC` | `CLK_PRSC_64` | `CLK_PRSC_2` `CLK_PRSC_4` `CLK_PRSC_8` `CLK_PRSC_64` `CLK_PRSC_128` `CLK_PRSC_1024` `CLK_PRSC_2024` `CLK_PRSC_4096` | TWI clock pre-scaler (dividing main processor clock) +| `TWI_CLK_DIV` | `3` | `0` ... `15` | TWI clock divider (dividing twi clock) +| `TWI_DEVICE_ID` | `0x50` | `0x00` ... `0x7F` | First TWI device ID to start. Is incremented until the end of the program is reached, when `TWI_ADDR_BYTES` is `1`. +| `TWI_ADDR_BYTES` | `1` | `1`, `2` | TWI memory address size in number of bytes. When `TWI_ADDR_BYTES` is `1`, `TWI_DEVICE_ID` the gets incremented as well. |======================= +[IMPORTANT] +Enabling all features while sticking to the minimal RISC-V ISA will result in a too-large binary! + [NOTE] The XIP options re-use the "SPI configuration" options for configuring the XIP's SPI connection. Each configuration parameter is implemented as C-language `define` that can be manually overridden (_redefined_) when invoking the bootloader's makefile. The according parameter and its new value has to be _appended_ -(using `+=`) to the makefile `USER_FLAGS` variable. Make sure to use the `-D` prefix here. +(using `+=`) to the makefile `USER_FLAGS` variable. Make sure to use the `-D` prefix here. The configuration is also listed in the makefile of the bootloader. For example, to configure a UART Baud rate of 57600 and redirecting the status LED to GPIO output pin 20 use the following command: diff --git a/docs/userguide/executable_upload.adoc b/docs/userguide/executable_upload.adoc index 11a9c3f2d..668756485 100644 --- a/docs/userguide/executable_upload.adoc +++ b/docs/userguide/executable_upload.adoc @@ -123,6 +123,9 @@ See section https://stnolting.github.io/neorv32/#_bootloader[Bootloader] of the See section <<_programming_an_external_spi_flash_via_the_bootloader>> to learn how to use an external SPI flash for nonvolatile program storage. +[TIP] +The bootloader also supports booting from external TWI memory. Enable it in the bootloader makefile, but be careful, enabling all features may result in a too-big binary. + [TIP] Executables can also be uploaded via the **on-chip debugger**. See section <<_debugging_with_gdb>> for more information. diff --git a/sw/bootloader/bootloader.c b/sw/bootloader/bootloader.c index 47825802a..e968e05d2 100644 --- a/sw/bootloader/bootloader.c +++ b/sw/bootloader/bootloader.c @@ -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,33 @@ #define XIP_EN 1 #endif +/* -------- TWI configuration -------- */ + +/** Enable TWI for copying to RAM */ +#ifndef TWI_EN + #define TWI_EN 0 +#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 Device ID */ +#ifndef TWI_DEVICE_ID + #define TWI_DEVICE_ID 0x50 +#endif + +/** TWI Memory address width (in numbers of bytes; 1 or 2) */ +#ifndef TWI_ADDR_BYTES + #define TWI_ADDR_BYTES 1 +#endif + /**@}*/ @@ -111,7 +139,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 TWI device */ }; @@ -122,18 +151,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" + "SPI", + "TWI" }; @@ -237,6 +268,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); + /**********************************************************************//** * Bootloader main. @@ -279,6 +313,11 @@ int main(void) { #endif #endif +#if (TWI_EN != 0) + // setup TWI + neorv32_twi_setup(TWI_CLK_PRSC, TWI_CLK_DIV, 0); +#endif + // Configure CLINT timer interrupt if (neorv32_clint_available()) { NEORV32_CLINT->MTIME.uint32[0] = 0; @@ -314,7 +353,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_clint_available()) { @@ -331,7 +370,11 @@ int main(void) { } if (neorv32_clint_time_get() >= 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); @@ -377,6 +420,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? @@ -419,6 +467,9 @@ void print_help(void) { " s: Store to flash\n" " l: Load from flash\n" #endif +#if (TWI_EN != 0) + " t: Load from TWI Device\n" +#endif #if (XIP_EN != 0) " x: Boot from flash (XIP)\n" #endif @@ -521,25 +572,36 @@ 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<