Line 62... |
Line 62... |
/**@{*/
|
/**@{*/
|
/** UART BAUD rate */
|
/** UART BAUD rate */
|
#define BAUD_RATE (19200)
|
#define BAUD_RATE (19200)
|
/** Time until the auto-boot sequence starts (in seconds) */
|
/** Time until the auto-boot sequence starts (in seconds) */
|
#define AUTOBOOT_TIMEOUT 8
|
#define AUTOBOOT_TIMEOUT 8
|
/** Bootloader status LED at GPIO output port (0..15) */
|
/** Set to 0 to disable bootloader status LED */
|
|
#define STATUS_LED_EN (1)
|
|
/** Bootloader status LED at GPIO output port */
|
#define STATUS_LED (0)
|
#define STATUS_LED (0)
|
/** SPI flash boot image base address */
|
/** SPI flash boot image base address */
|
#define SPI_FLASH_BOOT_ADR (0x00800000)
|
#define SPI_FLASH_BOOT_ADR (0x00800000)
|
/** SPI flash chip select at spi_csn_o */
|
/** SPI flash chip select at spi_csn_o */
|
#define SPI_FLASH_CS (0)
|
#define SPI_FLASH_CS (0)
|
Line 138... |
Line 140... |
/* Internal helper macro */
|
/* Internal helper macro */
|
#define str(a) #a
|
#define str(a) #a
|
/**@}*/
|
/**@}*/
|
|
|
|
|
|
/**********************************************************************//**
|
|
* This global variable keeps the size of the available executable in bytes.
|
|
* If =0 no executable is available (yet).
|
|
**************************************************************************/
|
|
uint32_t exe_available = 0;
|
|
|
|
|
// Function prototypes
|
// Function prototypes
|
void __attribute__((__interrupt__)) mtime_irq_handler(void);
|
void __attribute__((__interrupt__)) bootloader_trap_handler(void);
|
void print_help(void);
|
void print_help(void);
|
void start_app(void);
|
void start_app(void);
|
void get_exe(int src);
|
void get_exe(int src);
|
void save_exe(void);
|
void save_exe(void);
|
uint32_t get_exe_word(int src, uint32_t addr);
|
uint32_t get_exe_word(int src, uint32_t addr);
|
Line 166... |
Line 175... |
**************************************************************************/
|
**************************************************************************/
|
int main(void) {
|
int main(void) {
|
|
|
// ------------------------------------------------
|
// ------------------------------------------------
|
// Processor hardware initialization
|
// Processor hardware initialization
|
// - all IO devices are reset and disbaled by the crt0 code
|
// - all IO devices are reset and disabled by the crt0 code
|
// ------------------------------------------------
|
// ------------------------------------------------
|
|
|
// get clock speed (in Hz)
|
// get clock speed (in Hz)
|
uint32_t clock_speed = SYSINFO_CLK;
|
uint32_t clock_speed = SYSINFO_CLK;
|
|
|
Line 186... |
Line 195... |
neorv32_uart_setup(BAUD_RATE, 0, 0);
|
neorv32_uart_setup(BAUD_RATE, 0, 0);
|
|
|
// Configure machine system timer interrupt for ~2Hz
|
// Configure machine system timer interrupt for ~2Hz
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + (clock_speed/4));
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + (clock_speed/4));
|
|
|
// confiure interrupt vector (bare-metal, no neorv32 rte)
|
// confiure trap handler (bare-metal, no neorv32 rte available)
|
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)(&mtime_irq_handler));
|
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)(&bootloader_trap_handler));
|
neorv32_cpu_csr_write(CSR_MIE, 1 << CPU_MIE_MTIE); // activate MTIME IRQ source
|
|
|
|
|
neorv32_cpu_csr_write(CSR_MIE, 1 << CPU_MIE_MTIE); // activate MTIME IRQ source
|
neorv32_cpu_eint(); // enable global interrupts
|
neorv32_cpu_eint(); // enable global interrupts
|
|
|
// init GPIO
|
if (STATUS_LED_EN == 1) {
|
neorv32_gpio_port_set(1 << STATUS_LED); // activate status LED, clear all others
|
// activate status LED, clear all others
|
|
neorv32_gpio_port_set(1 << STATUS_LED);
|
|
}
|
|
|
// abuse mscratch CSR as global variable to store the size of the last uploaded executable
|
// global variable to executable size; 0 means there is no exe available
|
// this CSR must not be used by the bootloader's crt0.S!
|
exe_available = 0;
|
neorv32_cpu_csr_write(CSR_MSCRATCH, 0);
|
|
|
|
|
|
// ------------------------------------------------
|
// ------------------------------------------------
|
// Show bootloader intro and system info
|
// Show bootloader intro and system info
|
// ------------------------------------------------
|
// ------------------------------------------------
|
Line 231... |
Line 241... |
// ------------------------------------------------
|
// ------------------------------------------------
|
neorv32_uart_print("\n\nAutoboot in "xstr(AUTOBOOT_TIMEOUT)"s. Press key to abort.\n");
|
neorv32_uart_print("\n\nAutoboot in "xstr(AUTOBOOT_TIMEOUT)"s. Press key to abort.\n");
|
|
|
uint64_t timeout_time = neorv32_mtime_get_time() + (uint64_t)(AUTOBOOT_TIMEOUT * clock_speed);
|
uint64_t timeout_time = neorv32_mtime_get_time() + (uint64_t)(AUTOBOOT_TIMEOUT * clock_speed);
|
|
|
while ((UART_DATA & (1 << UART_DATA_AVAIL)) == 0) { // wait for any key to be pressed or timeout
|
while ((UART_DATA & (1 << UART_DATA_AVAIL)) == 0) { // wait for any key to be pressed
|
|
|
if (neorv32_mtime_get_time() >= timeout_time) { // timeout? start auto boot sequence
|
if (neorv32_mtime_get_time() >= timeout_time) { // timeout? start auto boot sequence
|
get_exe(EXE_STREAM_FLASH); // try loading from spi flash
|
get_exe(EXE_STREAM_FLASH); // try loading from spi flash
|
neorv32_uart_print("\n");
|
neorv32_uart_print("\n");
|
start_app();
|
start_app();
|
Line 255... |
Line 265... |
neorv32_uart_putc(c); // echo
|
neorv32_uart_putc(c); // echo
|
neorv32_uart_print("\n");
|
neorv32_uart_print("\n");
|
|
|
if (c == 'r') { // restart bootloader
|
if (c == 'r') { // restart bootloader
|
neorv32_cpu_dint(); // disable global interrupts
|
neorv32_cpu_dint(); // disable global interrupts
|
// jump to beginning of boot ROM
|
asm volatile ("li t0, %[input_i]; jr t0" : : [input_i] "i" (BOOTLOADER_BASE_ADDRESS)); // jump to beginning of boot ROM
|
asm volatile ("li t0, %[input_i]; jr t0" : : [input_i] "i" (BOOTLOADER_BASE_ADDRESS));
|
|
while(1); // just for the compiler
|
while(1); // just for the compiler
|
}
|
}
|
else if (c == 'h') { // help menu
|
else if (c == 'h') { // help menu
|
print_help();
|
print_help();
|
}
|
}
|
Line 274... |
Line 283... |
get_exe(EXE_STREAM_FLASH);
|
get_exe(EXE_STREAM_FLASH);
|
}
|
}
|
else if (c == 'e') { // start application program
|
else if (c == 'e') { // start application program
|
start_app();
|
start_app();
|
}
|
}
|
else if (c == '?') { // credits
|
else if (c == '?') {
|
neorv32_uart_print("by Stephan Nolting");
|
neorv32_uart_print("by Stephan Nolting");
|
}
|
}
|
else { // unknown command
|
else { // unknown command
|
neorv32_uart_print("Invalid CMD");
|
neorv32_uart_print("Invalid CMD");
|
}
|
}
|
Line 307... |
Line 316... |
* Start application program at the beginning of instruction space.
|
* Start application program at the beginning of instruction space.
|
**************************************************************************/
|
**************************************************************************/
|
void start_app(void) {
|
void start_app(void) {
|
|
|
// executable available?
|
// executable available?
|
if (neorv32_cpu_csr_read(CSR_MSCRATCH) == 0) {
|
if (exe_available == 0) {
|
neorv32_uart_print("No executable available.");
|
neorv32_uart_print("No executable available.");
|
return;
|
return;
|
}
|
}
|
|
|
// no need to shutdown or reset the used peripherals
|
// no need to shutdown or reset the used peripherals
|
Line 338... |
Line 347... |
while (1);
|
while (1);
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Machine system timer (MTIME) interrupt handler.
|
* Bootloader trap handler.
|
|
* Primarily used for the MTIME tick.
|
* @warning Since we have no runtime environment, we have to use the interrupt attribute here. Here, and only here!
|
* @warning Since we have no runtime environment, we have to use the interrupt attribute here. Here, and only here!
|
**************************************************************************/
|
**************************************************************************/
|
void __attribute__((__interrupt__)) mtime_irq_handler(void) {
|
void __attribute__((__interrupt__)) bootloader_trap_handler(void) {
|
|
|
// make sure this was caused by MTIME IRQ
|
// make sure this was caused by MTIME IRQ
|
uint32_t cause = neorv32_cpu_csr_read(CSR_MCAUSE);
|
uint32_t cause = neorv32_cpu_csr_read(CSR_MCAUSE);
|
if (cause != TRAP_CODE_MTI) { // raw exception code for MTI
|
if (cause != TRAP_CODE_MTI) { // raw exception code for MTI
|
neorv32_uart_print("\n\nEXCEPTION: ");
|
neorv32_uart_print("\n\nEXCEPTION (");
|
print_hex_word(cause);
|
print_hex_word(cause);
|
neorv32_uart_print(" @ 0x");
|
neorv32_uart_print(") @ 0x");
|
print_hex_word(neorv32_cpu_csr_read(CSR_MEPC));
|
print_hex_word(neorv32_cpu_csr_read(CSR_MEPC));
|
system_error(ERROR_SYSTEM);
|
system_error(ERROR_SYSTEM);
|
while(1); // freeze
|
while(1); // freeze
|
}
|
}
|
else {
|
else {
|
|
if (STATUS_LED_EN == 1) {
|
// toggle status LED
|
// toggle status LED
|
neorv32_gpio_pin_toggle(STATUS_LED);
|
neorv32_gpio_pin_toggle(STATUS_LED);
|
|
}
|
// set time for next IRQ
|
// set time for next IRQ
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + (SYSINFO_CLK/4));
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + (SYSINFO_CLK/4));
|
}
|
}
|
}
|
}
|
|
|
Line 369... |
Line 381... |
*
|
*
|
* @param src Source of executable stream data. See #EXE_STREAM_SOURCE.
|
* @param src Source of executable stream data. See #EXE_STREAM_SOURCE.
|
**************************************************************************/
|
**************************************************************************/
|
void get_exe(int src) {
|
void get_exe(int src) {
|
|
|
// is instruction memory (actually, the IMEM) read-only?
|
// is instruction memory (IMEM) read-only?
|
if (SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_INT_IMEM_ROM)) {
|
if (SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_INT_IMEM_ROM)) {
|
system_error(ERROR_ROM);
|
system_error(ERROR_ROM);
|
}
|
}
|
|
|
// flash image base address
|
// flash image base address
|
Line 418... |
Line 430... |
checksum += d;
|
checksum += d;
|
pnt[i++] = d;
|
pnt[i++] = d;
|
addr += 4;
|
addr += 4;
|
}
|
}
|
|
|
/*
|
|
// Debugging stuff
|
|
neorv32_uart_putc('.');
|
|
print_hex_word(signature);
|
|
neorv32_uart_putc('.');
|
|
print_hex_word(imem_size);
|
|
neorv32_uart_putc('.');
|
|
print_hex_word(check);
|
|
neorv32_uart_putc('.');
|
|
print_hex_word(checksum);
|
|
neorv32_uart_putc('.');
|
|
*/
|
|
|
|
// error during transfer?
|
// error during transfer?
|
if ((checksum + check) != 0) {
|
if ((checksum + check) != 0) {
|
system_error(ERROR_CHECKSUM);
|
system_error(ERROR_CHECKSUM);
|
}
|
}
|
else {
|
else {
|
neorv32_uart_print("OK");
|
neorv32_uart_print("OK");
|
neorv32_cpu_csr_write(CSR_MSCRATCH, size); // store exe size in "global variable"
|
exe_available = size; // store exe size
|
}
|
}
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Store content of instruction memory to SPI flash.
|
* Store content of instruction memory to SPI flash.
|
**************************************************************************/
|
**************************************************************************/
|
void save_exe(void) {
|
void save_exe(void) {
|
|
|
// size of last uploaded executable
|
// size of last uploaded executable
|
uint32_t size = neorv32_cpu_csr_read(CSR_MSCRATCH);
|
uint32_t size = exe_available;
|
|
|
if (size == 0) {
|
if (size == 0) {
|
neorv32_uart_print("No executable available.");
|
neorv32_uart_print("No executable available.");
|
return;
|
return;
|
}
|
}
|
Line 502... |
Line 501... |
uint32_t d = (uint32_t)*pnt++;
|
uint32_t d = (uint32_t)*pnt++;
|
checksum += d;
|
checksum += d;
|
spi_flash_write_word(addr, d);
|
spi_flash_write_word(addr, d);
|
addr += 4;
|
addr += 4;
|
i++;
|
i++;
|
// if ((i & 0x000000FF) == 0) {
|
|
// neorv32_uart_putc('.');
|
|
// }
|
|
}
|
}
|
|
|
// write checksum (sum complement)
|
// write checksum (sum complement)
|
checksum = (~checksum) + 1;
|
checksum = (~checksum) + 1;
|
spi_flash_write_word(SPI_FLASH_BOOT_ADR + EXE_OFFSET_CHECKSUM, checksum);
|
spi_flash_write_word(SPI_FLASH_BOOT_ADR + EXE_OFFSET_CHECKSUM, checksum);
|
Line 550... |
Line 546... |
*
|
*
|
* @param[in] err_code Error code. See #ERROR_CODES.
|
* @param[in] err_code Error code. See #ERROR_CODES.
|
**************************************************************************/
|
**************************************************************************/
|
void system_error(uint8_t err_code) {
|
void system_error(uint8_t err_code) {
|
|
|
neorv32_uart_print("\a\nERR_"); // output error code with annoying bell sound
|
neorv32_uart_print("\a\nBootloader ERR_"); // output error code with annoying bell sound
|
if (err_code <= ERROR_SYSTEM) {
|
neorv32_uart_putc('0' + ((char)err_code)); // FIXME err_code should/must be below 10
|
neorv32_uart_putc('0' + ((char)err_code));
|
|
}
|
|
else {
|
|
neorv32_uart_print("unknown");
|
|
}
|
|
|
|
neorv32_cpu_dint(); // deactivate IRQs
|
neorv32_cpu_dint(); // deactivate IRQs
|
|
if (STATUS_LED_EN == 1) {
|
neorv32_gpio_port_set(1 << STATUS_LED); // permanently light up status LED
|
neorv32_gpio_port_set(1 << STATUS_LED); // permanently light up status LED
|
|
}
|
|
|
asm volatile ("wfi"); // power-down
|
asm volatile ("wfi"); // power-down
|
while(1); // freeze
|
while(1); // freeze
|
}
|
}
|
|
|