diff --git a/docs/datasheet/cpu.adoc b/docs/datasheet/cpu.adoc index 52611a7ea..655775864 100644 --- a/docs/datasheet/cpu.adoc +++ b/docs/datasheet/cpu.adoc @@ -472,7 +472,7 @@ To benchmark a certain processor configuration for its setup-specific CPI value The `A` ISA extension adds instructions and mechanisms for atomic memory access operations. Note that the NEORV32 `A` only includes the _load-reservate_ (`lr.w`) and _store-conditional_ (`sc.w`) instructions - the remaining read-modify-write instructions (like `amoswap`) are **not supported**. However, these missing instructions can be emulated using the -LR and SC operations. +LR and SC operations (quote from the RISC-V spec.: "_Any AMO can be emulated by an LR/SC pair._"). .AMO Emulation [NOTE] diff --git a/docs/datasheet/soc_gpio.adoc b/docs/datasheet/soc_gpio.adoc index e1e599e52..bf1b29980 100644 --- a/docs/datasheet/soc_gpio.adoc +++ b/docs/datasheet/soc_gpio.adoc @@ -5,7 +5,7 @@ [cols="<3,<3,<4"] [frame="topbot",grid="none"] |======================= -| Hardware source files: | neorv32_gpio.vhd | +| Hardware source files: | neorv32_gpio.vhd | | Software driver files: | neorv32_gpio.c | | | neorv32_gpio.h | | Top entity ports: | `gpio_o` | 64-bit parallel output port @@ -37,8 +37,8 @@ be performed within a single clock cycle. [options="header",grid="rows"] |======================= | Address | Name [C] | Bit(s) | R/W | Function -| `0xfffffc00` | `INPUT_LO` | 31:0 | r/- | parallel input port pins 31:0 -| `0xfffffc04` | `INPUT_HI` | 31:0 | r/- | parallel input port pins 63:32 -| `0xfffffc08` | `OUTPUT_LO` | 31:0 | r/w | parallel output port pins 31:0 -| `0xfffffc0c` | `OUTPUT_HI` | 31:0 | r/w | parallel output port pins 63:32 +| `0xfffffc00` | `INPUT[0]` | 31:0 | r/- | parallel input port pins 31:0 +| `0xfffffc04` | `INPUT[1]` | 31:0 | r/- | parallel input port pins 63:32 +| `0xfffffc08` | `OUTPUT[0]` | 31:0 | r/w | parallel output port pins 31:0 +| `0xfffffc0c` | `OUTPUT[1]` | 31:0 | r/w | parallel output port pins 63:32 |======================= diff --git a/sw/bootloader/bootloader.c b/sw/bootloader/bootloader.c index 49f09cd19..91017a3c3 100644 --- a/sw/bootloader/bootloader.c +++ b/sw/bootloader/bootloader.c @@ -8,7 +8,6 @@ /** * @file bootloader.c - * @author Stephan Nolting * @brief Default NEORV32 bootloader. */ diff --git a/sw/common/crt0.S b/sw/common/crt0.S index 68cd76964..98818d42a 100644 --- a/sw/common/crt0.S +++ b/sw/common/crt0.S @@ -143,11 +143,12 @@ __crt0_call_constructors_loop_end: __crt0_main_entry: addi x10, zero, 0 // x10 = a0 = argc = 0 addi x11, zero, 0 // x11 = a1 = argv = 0 - jal x1, main // call actual app's main function + jal x1, main // call actual main function __crt0_main_exit: // main's "return" and "exit" will arrive here csrw mie, zero // disable all interrupt sources - csrw mscratch, a0 // backup main's return code to mscratch (for debugger) + csrw mscratch, a0 // backup main's return code to mscratch (for debugger or destructors) + // ************************************************************************************************ @@ -155,8 +156,10 @@ __crt0_main_exit: // main's "return" and "exit" will arrive here // ************************************************************************************************ #ifndef MAKE_BOOTLOADER // destructors are not supported for bootloader __crt0_call_destructors: - la x8, __fini_array_start - la x9, __fini_array_end + la x8, __crt0_trap_handler // use the crt0 trap handler if there are exceptions in the destructors + csrw mtvec, x8 + la x8, __fini_array_start + la x9, __fini_array_end __crt0_call_destructors_loop: bge x8, x9, __crt0_call_destructors_loop_end diff --git a/sw/example/atomic_test/main.c b/sw/example/atomic_test/main.c index e47b71b1e..90868b713 100644 --- a/sw/example/atomic_test/main.c +++ b/sw/example/atomic_test/main.c @@ -250,8 +250,8 @@ int main() { #else - #warning Program HAS NOT BEEN COMPILED as RISC-V A ISA extensions is not enabled! - neorv32_uart0_printf("\nProgram HAS NOT BEEN COMPILED as RISC-V A ISA extensions is not enabled!\n"); + #warning Program HAS NOT BEEN COMPILED since RISC-V 'A' ISA extension is not enabled! + neorv32_uart0_printf("\nProgram HAS NOT BEEN COMPILED since RISC-V 'A' ISA extension is not enabled!\n"); #endif diff --git a/sw/example/demo_newlib/main.c b/sw/example/demo_newlib/main.c index 37eb9236c..4b6ed54da 100644 --- a/sw/example/demo_newlib/main.c +++ b/sw/example/demo_newlib/main.c @@ -26,6 +26,16 @@ /**@}*/ +/**********************************************************************//** + * @name Print main's return code using a destructor + **************************************************************************/ +void __attribute__((destructor)) main_destructor_test(void) { + + int32_t main_ret = (int32_t)neorv32_cpu_csr_read(CSR_MSCRATCH); + neorv32_uart0_printf("\nDestructor: main terminated with return/exit code %i.\n", main_ret); +} + + /**********************************************************************//** * @name Max heap size (from linker script's "__neorv32_heap_size") **************************************************************************/ @@ -111,10 +121,11 @@ int main() { // NOTE: exit is highly over-sized as it also includes clean-up functions (destructors), which // are not required for bare-metal or RTOS applications... better use the simple 'return' or even better // make sure main never returns. Anyway, let's check if 'exit' works. - neorv32_uart0_printf("terminating via "); - exit(0); + int exit_code = 7; + neorv32_uart0_printf(" terminating by exit(%i)...\n", exit_code); + exit(exit_code); // should never be reached - neorv32_uart0_printf("failed!n"); + neorv32_uart0_printf("exit failed!\n"); return 0; } diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index da139526d..554e03454 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1898,12 +1898,12 @@ int main() { amo_var = 0x00cafe00; // initialize asm volatile ("fence"); // flush/reload d-cache - tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&amo_var); + tmp_a = neorv32_cpu_amolr((uint32_t)&amo_var); amo_var = 0x10cafe00; // break reservation asm volatile ("fence"); // flush/reload d-cache - tmp_b = neorv32_cpu_store_conditional_word((uint32_t)&amo_var, 0xaaaaaaaa); - tmp_b = (tmp_b << 1) | neorv32_cpu_store_conditional_word((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail - tmp_b = (tmp_b << 1) | neorv32_cpu_store_conditional_word((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception! + tmp_b = neorv32_cpu_amosc((uint32_t)&amo_var, 0xaaaaaaaa); + tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail + tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception! asm volatile ("fence"); // flush/reload d-cache if ((tmp_a == 0x00cafe00) && // correct LR.W result @@ -1939,12 +1939,12 @@ int main() { amo_var = 0x00abba00; // initialize asm volatile ("fence"); // flush/reload d-cache - tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&amo_var); + tmp_a = neorv32_cpu_amolr((uint32_t)&amo_var); asm volatile ("fence"); // flush/reload d-cache neorv32_cpu_load_unsigned_word((uint32_t)&amo_var); // dummy read, must not alter reservation set state - tmp_b = neorv32_cpu_store_conditional_word((uint32_t)&amo_var, 0xcccccccc); - tmp_b = (tmp_b << 1) | neorv32_cpu_store_conditional_word((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail - tmp_b = (tmp_b << 1) | neorv32_cpu_store_conditional_word((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception! + tmp_b = neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc); + tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail + tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception! asm volatile ("fence"); // flush/reload d-cache if ((tmp_a == 0x00abba00) && // correct LR.W result diff --git a/sw/lib/include/neorv32_aux.h b/sw/lib/include/neorv32_aux.h index 17d5786c3..a6600861f 100644 --- a/sw/lib/include/neorv32_aux.h +++ b/sw/lib/include/neorv32_aux.h @@ -28,26 +28,50 @@ /**********************************************************************//** - * Processor clock prescaler select + * Processor clock prescaler select (relative to processor's main clock) **************************************************************************/ /**@{*/ enum NEORV32_CLOCK_PRSC_enum { - CLK_PRSC_2 = 0, /**< CPU_CLK (from clk_i top signal) / 2 */ - CLK_PRSC_4 = 1, /**< CPU_CLK (from clk_i top signal) / 4 */ - CLK_PRSC_8 = 2, /**< CPU_CLK (from clk_i top signal) / 8 */ - CLK_PRSC_64 = 3, /**< CPU_CLK (from clk_i top signal) / 64 */ - CLK_PRSC_128 = 4, /**< CPU_CLK (from clk_i top signal) / 128 */ - CLK_PRSC_1024 = 5, /**< CPU_CLK (from clk_i top signal) / 1024 */ - CLK_PRSC_2048 = 6, /**< CPU_CLK (from clk_i top signal) / 2048 */ - CLK_PRSC_4096 = 7 /**< CPU_CLK (from clk_i top signal) / 4096 */ + CLK_PRSC_2 = 0, /**< CPU_CLK / 2 */ + CLK_PRSC_4 = 1, /**< CPU_CLK / 4 */ + CLK_PRSC_8 = 2, /**< CPU_CLK / 8 */ + CLK_PRSC_64 = 3, /**< CPU_CLK / 64 */ + CLK_PRSC_128 = 4, /**< CPU_CLK / 128 */ + CLK_PRSC_1024 = 5, /**< CPU_CLK / 1024 */ + CLK_PRSC_2048 = 6, /**< CPU_CLK / 2048 */ + CLK_PRSC_4096 = 7 /**< CPU_CLK / 4096 */ }; /**@}*/ /**********************************************************************//** - * @name Date and time struct + * @name Subword-access helper **************************************************************************/ /**@{*/ +/** @name 64-bit */ +typedef union { + uint64_t uint64; + uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; + uint16_t uint16[sizeof(uint64_t)/sizeof(uint16_t)]; + uint8_t uint8[ sizeof(uint64_t)/sizeof(uint8_t)]; +} subwords64_t; +/** @name 32-bit */ +typedef union { + uint32_t uint32[sizeof(uint32_t)/sizeof(uint32_t)]; + uint16_t uint16[sizeof(uint32_t)/sizeof(uint16_t)]; + uint8_t uint8[ sizeof(uint32_t)/sizeof(uint8_t)]; +} subwords32_t; +/** @name 16-bit */ +typedef union { + uint16_t uint16[sizeof(uint16_t)/sizeof(uint16_t)]; + uint8_t uint8[ sizeof(uint16_t)/sizeof(uint8_t)]; +} subwords16_t; +/**@}*/ + + +/**********************************************************************//** + * @name Date and time struct + **************************************************************************/ typedef struct { uint16_t year; /**< current year (absolute) */ uint8_t month; /**< 1..12 */ @@ -57,11 +81,10 @@ typedef struct { uint8_t minutes; /**< 0..59 */ uint8_t seconds; /**< 0..59 */ } date_t; -/**@}*/ /**********************************************************************//** - * @name Prototypes + * @name AUX prototypes **************************************************************************/ /**@{*/ uint64_t neorv32_aux_date2unixtime(date_t* date); diff --git a/sw/lib/include/neorv32_cpu.h b/sw/lib/include/neorv32_cpu.h index 8e6d56220..cc65e2312 100644 --- a/sw/lib/include/neorv32_cpu.h +++ b/sw/lib/include/neorv32_cpu.h @@ -38,119 +38,6 @@ void neorv32_cpu_goto_user_mode(void); /**@}*/ -// ################################################################################################# -// Context save/restore helpers -// ################################################################################################# - - -/**********************************************************************//** - * Save all integer registers to the stack. - * - * @note This inlined function automatically constrains the number - * of registers when compiling for rv32e (only 16 registers). - **************************************************************************/ -inline void __attribute__ ((always_inline)) neorv32_context_save(void) { - - // do not backup x0 and sp - asm volatile ( -#ifndef __riscv_32e - "addi sp, sp, -30*4 \n" -#else - "addi sp, sp, -14*4 \n" -#endif - "sw x1, 0*4(sp) \n" - "sw x3, 1*4(sp) \n" - "sw x4, 2*4(sp) \n" - "sw x5, 3*4(sp) \n" - "sw x6, 4*4(sp) \n" - "sw x7, 5*4(sp) \n" - "sw x8, 6*4(sp) \n" - "sw x9, 7*4(sp) \n" - "sw x10, 8*4(sp) \n" - "sw x11, 9*4(sp) \n" - "sw x12, 10*4(sp) \n" - "sw x13, 11*4(sp) \n" - "sw x14, 12*4(sp) \n" - "sw x15, 13*4(sp) \n" -#ifndef __riscv_32e - "sw x16, 14*4(sp) \n" - "sw x17, 15*4(sp) \n" - "sw x18, 16*4(sp) \n" - "sw x19, 17*4(sp) \n" - "sw x20, 18*4(sp) \n" - "sw x21, 19*4(sp) \n" - "sw x22, 20*4(sp) \n" - "sw x23, 21*4(sp) \n" - "sw x24, 22*4(sp) \n" - "sw x25, 23*4(sp) \n" - "sw x26, 24*4(sp) \n" - "sw x27, 25*4(sp) \n" - "sw x28, 26*4(sp) \n" - "sw x29, 27*4(sp) \n" - "sw x30, 28*4(sp) \n" - "sw x31, 29*4(sp) \n" -#endif - ); -} - - -/**********************************************************************//** - * Restore all integer registers from the stack. - * - * @note This inlined function automatically constrains the number - * of registers when compiling for rv32e (only 16 registers). - **************************************************************************/ -inline void __attribute__ ((always_inline)) neorv32_context_restore(void) { - - // do not restore x0 and sp - asm volatile ( - "lw x1, 0*4(sp) \n" - "lw x3, 1*4(sp) \n" - "lw x4, 2*4(sp) \n" - "lw x5, 3*4(sp) \n" - "lw x6, 4*4(sp) \n" - "lw x7, 5*4(sp) \n" - "lw x8, 6*4(sp) \n" - "lw x9, 7*4(sp) \n" - "lw x10, 8*4(sp) \n" - "lw x11, 9*4(sp) \n" - "lw x12, 10*4(sp) \n" - "lw x13, 11*4(sp) \n" - "lw x14, 12*4(sp) \n" - "lw x15, 13*4(sp) \n" -#ifndef __riscv_32e - "lw x16, 14*4(sp) \n" - "lw x17, 15*4(sp) \n" - "lw x18, 16*4(sp) \n" - "lw x19, 17*4(sp) \n" - "lw x20, 18*4(sp) \n" - "lw x21, 19*4(sp) \n" - "lw x22, 20*4(sp) \n" - "lw x23, 21*4(sp) \n" - "lw x24, 22*4(sp) \n" - "lw x25, 23*4(sp) \n" - "lw x26, 24*4(sp) \n" - "lw x27, 25*4(sp) \n" - "lw x28, 26*4(sp) \n" - "lw x29, 27*4(sp) \n" - "lw x30, 28*4(sp) \n" - "lw x31, 29*4(sp) \n" -#endif -#ifndef __riscv_32e - "addi sp, sp, +30*4 \n" -#else - "addi sp, sp, +14*4 \n" -#endif - "ret \n" - ); -} - - -// ################################################################################################# -// Load/store helpers -// ################################################################################################# - - /**********************************************************************//** * Store unsigned word to address space. * @@ -163,7 +50,6 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_store_unsigned_word(uint uint32_t reg_addr = addr; uint32_t reg_data = wdata; - asm volatile ("sw %[da], 0(%[ad])" : : [da] "r" (reg_data), [ad] "r" (reg_addr)); } @@ -180,7 +66,6 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_store_unsigned_half(uint uint32_t reg_addr = addr; uint32_t reg_data = (uint32_t)wdata; - asm volatile ("sh %[da], 0(%[ad])" : : [da] "r" (reg_data), [ad] "r" (reg_addr)); } @@ -195,7 +80,6 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_store_unsigned_byte(uint uint32_t reg_addr = addr; uint32_t reg_data = (uint32_t)wdata; - asm volatile ("sb %[da], 0(%[ad])" : : [da] "r" (reg_data), [ad] "r" (reg_addr)); } @@ -212,9 +96,7 @@ inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_load_unsigned_word(u uint32_t reg_addr = addr; uint32_t reg_data; - asm volatile ("lw %[da], 0(%[ad])" : [da] "=r" (reg_data) : [ad] "r" (reg_addr)); - return reg_data; } @@ -231,9 +113,7 @@ inline uint16_t __attribute__ ((always_inline)) neorv32_cpu_load_unsigned_half(u uint32_t reg_addr = addr; uint16_t reg_data; - asm volatile ("lhu %[da], 0(%[ad])" : [da] "=r" (reg_data) : [ad] "r" (reg_addr)); - return reg_data; } @@ -250,9 +130,7 @@ inline int16_t __attribute__ ((always_inline)) neorv32_cpu_load_signed_half(uint uint32_t reg_addr = addr; int16_t reg_data; - asm volatile ("lh %[da], 0(%[ad])" : [da] "=r" (reg_data) : [ad] "r" (reg_addr)); - return reg_data; } @@ -267,9 +145,7 @@ inline uint8_t __attribute__ ((always_inline)) neorv32_cpu_load_unsigned_byte(ui uint32_t reg_addr = addr; uint8_t reg_data; - asm volatile ("lbu %[da], 0(%[ad])" : [da] "=r" (reg_data) : [ad] "r" (reg_addr)); - return reg_data; } @@ -284,78 +160,11 @@ inline int8_t __attribute__ ((always_inline)) neorv32_cpu_load_signed_byte(uint3 uint32_t reg_addr = addr; int8_t reg_data; - asm volatile ("lb %[da], 0(%[ad])" : [da] "=r" (reg_data) : [ad] "r" (reg_addr)); - return reg_data; } -// ################################################################################################# -// Atomic memory access / load-reservate/store-conditional helpers -// ################################################################################################# - - -/**********************************************************************//** - * Atomic memory access: load-reservate word. - * - * @note The address has to be word-aligned - otherwise an alignment exception will be raised. - * @warning This function requires the A ISA extension. - * - * @param[in] addr Address (32-bit). - * @return Read data word (32-bit). - **************************************************************************/ -inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_load_reservate_word(uint32_t addr) { - -#if defined __riscv_atomic - uint32_t amo_addr = addr; - uint32_t amo_rdata; - - asm volatile ("lr.w %[dst], 0(%[addr])" : [dst] "=r" (amo_rdata) : [addr] "r" (amo_addr)); - - return amo_rdata; -#else - (void)addr; - - return 0; -#endif -} - - -/**********************************************************************//** - * Atomic memory access: store-conditional word. - * - * @note The address has to be word-aligned - otherwise an alignment exception will be raised. - * @warning This function requires the A ISA extension. - * - * @param[in] addr Address (32-bit). - * @param[in] wdata Data word to-be-written conditionally (32-bit). - * @return Status: 0 = ok, 1 = failed (32-bit). - **************************************************************************/ -inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_store_conditional_word(uint32_t addr, uint32_t wdata) { - -#if defined __riscv_atomic - uint32_t amo_addr = addr; - uint32_t amo_wdata = wdata; - uint32_t amo_status; - - asm volatile ("sc.w %[dst], %[src], (%[addr])" : [dst] "=r" (amo_status) : [src] "r" (amo_wdata), [addr] "r" (amo_addr)); - - return amo_status; -#else - (void)addr; - (void)wdata; - - return 1; // always fail -#endif -} - - -// ################################################################################################# -// CSR access helpers -// ################################################################################################# - - /**********************************************************************//** * Read data from CPU control and status register (CSR). * @@ -365,9 +174,7 @@ inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_store_conditional_wo inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_csr_read(const int csr_id) { uint32_t csr_data; - asm volatile ("csrr %[result], %[input_i]" : [result] "=r" (csr_data) : [input_i] "i" (csr_id)); - return csr_data; } @@ -381,7 +188,6 @@ inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_csr_read(const int c inline void __attribute__ ((always_inline)) neorv32_cpu_csr_write(const int csr_id, uint32_t data) { uint32_t csr_data = data; - asm volatile ("csrw %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data)); } @@ -395,7 +201,6 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_write(const int csr_ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_set(const int csr_id, uint32_t mask) { uint32_t csr_data = mask; - asm volatile ("csrs %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data)); } @@ -409,16 +214,10 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_set(const int csr_id inline void __attribute__ ((always_inline)) neorv32_cpu_csr_clr(const int csr_id, uint32_t mask) { uint32_t csr_data = mask; - asm volatile ("csrc %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data)); } -// ################################################################################################# -// Misc helpers -// ################################################################################################# - - /**********************************************************************//** * Put CPU into sleep / power-down mode. * diff --git a/sw/lib/include/neorv32_cpu_amo.h b/sw/lib/include/neorv32_cpu_amo.h index ff111457e..f0419e73c 100644 --- a/sw/lib/include/neorv32_cpu_amo.h +++ b/sw/lib/include/neorv32_cpu_amo.h @@ -19,6 +19,61 @@ #include +/**********************************************************************//** + * Atomic memory access: load-reservate word. + * + * @note The address has to be word-aligned - otherwise an alignment exception will be raised. + * @warning This function requires the A ISA extension. + * + * @param[in] addr Address (32-bit). + * @return Read data word (32-bit). + **************************************************************************/ +inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_amolr(uint32_t addr) { + +#if defined __riscv_atomic + uint32_t amo_addr = addr; + uint32_t amo_rdata; + + asm volatile ("lr.w %[dst], 0(%[addr])" : [dst] "=r" (amo_rdata) : [addr] "r" (amo_addr)); + + return amo_rdata; +#else + (void)addr; + + return 0; +#endif +} + + +/**********************************************************************//** + * Atomic memory access: store-conditional word. + * + * @note The address has to be word-aligned - otherwise an alignment exception will be raised. + * @warning This function requires the A ISA extension. + * + * @param[in] addr Address (32-bit). + * @param[in] wdata Data word to-be-written conditionally (32-bit). + * @return Status: 0 = ok, 1 = failed (32-bit). + **************************************************************************/ +inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_amosc(uint32_t addr, uint32_t wdata) { + +#if defined __riscv_atomic + uint32_t amo_addr = addr; + uint32_t amo_wdata = wdata; + uint32_t amo_status; + + asm volatile ("sc.w %[dst], %[src], (%[addr])" : [dst] "=r" (amo_status) : [src] "r" (amo_wdata), [addr] "r" (amo_addr)); + + return amo_status; +#else + (void)addr; + (void)wdata; + + return 1; // always fail +#endif +} + + // prototypes uint32_t neorv32_cpu_amoswapw(uint32_t addr, uint32_t wdata); uint32_t neorv32_cpu_amoaddw(uint32_t addr, uint32_t wdata); diff --git a/sw/lib/include/neorv32_gpio.h b/sw/lib/include/neorv32_gpio.h index d7a28d5a8..fb8097e71 100644 --- a/sw/lib/include/neorv32_gpio.h +++ b/sw/lib/include/neorv32_gpio.h @@ -27,10 +27,8 @@ /**@{*/ /** GPIO module prototype */ typedef volatile struct __attribute__((packed,aligned(4))) { - const uint32_t INPUT_LO; /**< offset 0: parallel input port lower 32-bit, read-only */ - const uint32_t INPUT_HI; /**< offset 4: parallel input port upper 32-bit, read-only */ - uint32_t OUTPUT_LO; /**< offset 8: parallel output port lower 32-bit */ - uint32_t OUTPUT_HI; /**< offset 12: parallel output port upper 32-bit */ + const uint32_t INPUT[2]; /**< offset 0: parallel input port, read-only */ + uint32_t OUTPUT[2]; /**< offset 8: parallel output port */ } neorv32_gpio_t; /** GPIO module hardware access (#neorv32_gpio_t) */ diff --git a/sw/lib/include/neorv32_legacy.h b/sw/lib/include/neorv32_legacy.h index e8919a94a..8bf98bf9b 100644 --- a/sw/lib/include/neorv32_legacy.h +++ b/sw/lib/include/neorv32_legacy.h @@ -67,4 +67,23 @@ #define neorv32_uart1_scan(buffer, max_size, echo) neorv32_uart_scan(NEORV32_UART1, buffer, max_size, echo) /**@}*/ +/**********************************************************************//** + * @name GPIO aliases + **************************************************************************/ +/**@{*/ +#define INPUT_LO INPUT[0] +#define INPUT_HI INPUT[1] +#define OUTPUT_LO OUTPUT[0] +#define OUTPUT_HI OUTPUT[1] +/**@}*/ + +/**********************************************************************//** + * @name Atomic LR/SC aliases + **************************************************************************/ +/**@{*/ +#define neorv32_cpu_load_reservate_word(addr, wdata) neorv32_cpu_amolr(addr, wdata) +#define neorv32_cpu_store_conditional_word(addr, wdata) neorv32_cpu_amosc(addr, wdata) +/**@}*/ + + #endif // neorv32_legacy_h diff --git a/sw/lib/source/neorv32_cpu.c b/sw/lib/source/neorv32_cpu.c index 574879b39..d1e2eed46 100644 --- a/sw/lib/source/neorv32_cpu.c +++ b/sw/lib/source/neorv32_cpu.c @@ -20,21 +20,23 @@ /**********************************************************************//** * Unavailable extensions warnings. **************************************************************************/ +/**@{*/ #if defined __riscv_d || (__riscv_flen == 64) - #error Double-precision floating-point extension is NOT supported! + #error Double-precision floating-point extension D/Zdinx is NOT supported! #endif #if (__riscv_xlen > 32) - #error Only 32-bit is supported! + #error Only XLEN=32 (rv32) is supported! #endif #ifdef __riscv_fdiv - #warning Floating-point division instruction is NOT supported yet! + #warning Floating-point division instruction FDIV is NOT supported! #endif #ifdef __riscv_fsqrt - #warning Floating-point square root instruction is NOT supported yet! + #warning Floating-point square root instruction FSQRT is NOT supported! #endif +/**@}*/ /**********************************************************************//** @@ -44,10 +46,7 @@ **************************************************************************/ uint64_t neorv32_cpu_get_cycle(void) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; uint32_t tmp1, tmp2, tmp3; while(1) { @@ -73,14 +72,11 @@ uint64_t neorv32_cpu_get_cycle(void) { **************************************************************************/ void neorv32_cpu_set_mcycle(uint64_t value) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; cycles.uint64 = value; - // prevent low-to-high word overflow while writing + // prevent low-to-high carry while writing neorv32_cpu_csr_write(CSR_MCYCLE, 0); neorv32_cpu_csr_write(CSR_MCYCLEH, cycles.uint32[1]); neorv32_cpu_csr_write(CSR_MCYCLE, cycles.uint32[0]); @@ -94,10 +90,7 @@ void neorv32_cpu_set_mcycle(uint64_t value) { **************************************************************************/ uint64_t neorv32_cpu_get_instret(void) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; uint32_t tmp1, tmp2, tmp3; while(1) { @@ -123,14 +116,11 @@ uint64_t neorv32_cpu_get_instret(void) { **************************************************************************/ void neorv32_cpu_set_minstret(uint64_t value) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; cycles.uint64 = value; - // prevent low-to-high word overflow while writing + // prevent low-to-high carry while writing neorv32_cpu_csr_write(CSR_MINSTRET, 0); neorv32_cpu_csr_write(CSR_MINSTRETH, cycles.uint32[1]); neorv32_cpu_csr_write(CSR_MINSTRET, cycles.uint32[0]); @@ -458,7 +448,7 @@ uint32_t neorv32_cpu_hpm_get_size(void) { void __attribute__((naked,noinline)) neorv32_cpu_goto_user_mode(void) { asm volatile ( - "csrw mepc, ra \n" // move return address to mepc so we can return using "mret". also, we can now use ra as temp register + "csrw mepc, ra \n" // move return address to mepc so we can return using mret; we can now use ra as temp register "li ra, 3<<11 \n" // bit mask to clear the two MPP bits "csrc mstatus, ra \n" // clear MPP bits -> MPP = u-mode "csrr ra, mstatus \n" // get mstatus diff --git a/sw/lib/source/neorv32_cpu_amo.c b/sw/lib/source/neorv32_cpu_amo.c index 2f5e818cb..941bcf527 100644 --- a/sw/lib/source/neorv32_cpu_amo.c +++ b/sw/lib/source/neorv32_cpu_amo.c @@ -17,17 +17,6 @@ #include "neorv32_cpu_amo.h" -/**********************************************************************//** - * MIN/MAX helpers. - **************************************************************************/ -/**@{*/ -static inline int32_t MAX(int32_t a, int32_t b) { return((a) > (b) ? a : b); } -static inline int32_t MIN(int32_t a, int32_t b) { return((a) < (b) ? a : b); } -static inline int32_t MAXU(uint32_t a, uint32_t b) { return((a) > (b) ? a : b); } -static inline int32_t MINU(uint32_t a, uint32_t b) { return((a) < (b) ? a : b); } -/**@}*/ - - /**********************************************************************//** * Atomic SWAP (AMOSWAP.W). * return <= MEM[addr]; MEM[addr] <= wdata @@ -40,18 +29,25 @@ static inline int32_t MINU(uint32_t a, uint32_t b) { return((a) < (b) ? a : b); **************************************************************************/ uint32_t neorv32_cpu_amoswapw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t status; while(1) { - rdata = neorv32_cpu_load_reservate_word(addr); - status = neorv32_cpu_store_conditional_word(addr, wdata); + rdata = neorv32_cpu_amolr(addr); + status = neorv32_cpu_amosc(addr, wdata); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -67,20 +63,27 @@ uint32_t neorv32_cpu_amoswapw(uint32_t addr, uint32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amoaddw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = neorv32_cpu_load_reservate_word(addr); + rdata = neorv32_cpu_amolr(addr); tmp = rdata + wdata; - status = neorv32_cpu_store_conditional_word(addr, tmp); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -96,20 +99,27 @@ uint32_t neorv32_cpu_amoaddw(uint32_t addr, uint32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amoandw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = neorv32_cpu_load_reservate_word(addr); + rdata = neorv32_cpu_amolr(addr); tmp = rdata & wdata; - status = neorv32_cpu_store_conditional_word(addr, tmp); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -125,20 +135,27 @@ uint32_t neorv32_cpu_amoandw(uint32_t addr, uint32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amoorw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = neorv32_cpu_load_reservate_word(addr); + rdata = neorv32_cpu_amolr(addr); tmp = rdata | wdata; - status = neorv32_cpu_store_conditional_word(addr, tmp); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -154,20 +171,27 @@ uint32_t neorv32_cpu_amoorw(uint32_t addr, uint32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amoxorw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = neorv32_cpu_load_reservate_word(addr); + rdata = neorv32_cpu_amolr(addr); tmp = rdata ^ wdata; - status = neorv32_cpu_store_conditional_word(addr, tmp); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -183,20 +207,27 @@ uint32_t neorv32_cpu_amoxorw(uint32_t addr, uint32_t wdata) { **************************************************************************/ int32_t neorv32_cpu_amomaxw(uint32_t addr, int32_t wdata) { +#if defined __riscv_atomic int32_t rdata; int32_t tmp; uint32_t status; while(1) { - rdata = (int32_t)neorv32_cpu_load_reservate_word(addr); - tmp = MAX(rdata, wdata); - status = neorv32_cpu_store_conditional_word(addr, tmp); + rdata = (int32_t)neorv32_cpu_amolr(addr); + tmp = neorv32_aux_max(rdata, wdata); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -212,20 +243,27 @@ int32_t neorv32_cpu_amomaxw(uint32_t addr, int32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amomaxuw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = (uint32_t)neorv32_cpu_load_reservate_word(addr); - tmp = MAXU(rdata, wdata); - status = neorv32_cpu_store_conditional_word(addr, tmp); + rdata = (uint32_t)neorv32_cpu_amolr(addr); + tmp = neorv32_aux_max(rdata, wdata); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -241,20 +279,27 @@ uint32_t neorv32_cpu_amomaxuw(uint32_t addr, uint32_t wdata) { **************************************************************************/ int32_t neorv32_cpu_amominw(uint32_t addr, int32_t wdata) { +#if defined __riscv_atomic int32_t rdata; int32_t tmp; uint32_t status; while(1) { - rdata = (int32_t)neorv32_cpu_load_reservate_word(addr); - tmp = MIN(rdata, wdata); - status = neorv32_cpu_store_conditional_word(addr, tmp); + rdata = (int32_t)neorv32_cpu_amolr(addr); + tmp = neorv32_aux_min(rdata, wdata); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } @@ -270,18 +315,25 @@ int32_t neorv32_cpu_amominw(uint32_t addr, int32_t wdata) { **************************************************************************/ uint32_t neorv32_cpu_amominuw(uint32_t addr, uint32_t wdata) { +#if defined __riscv_atomic uint32_t rdata; uint32_t tmp; uint32_t status; while(1) { - rdata = (uint32_t)neorv32_cpu_load_reservate_word(addr); - tmp = MINU(rdata, wdata); - status = neorv32_cpu_store_conditional_word(addr, tmp); + rdata = (uint32_t)neorv32_cpu_amolr(addr); + tmp = neorv32_aux_min(rdata, wdata); + status = neorv32_cpu_amosc(addr, tmp); if (status == 0) { break; } } return rdata; +#else + (void)addr; + (void)wdata; + + return 0; +#endif } diff --git a/sw/lib/source/neorv32_gpio.c b/sw/lib/source/neorv32_gpio.c index 8f59632f8..6ba539912 100644 --- a/sw/lib/source/neorv32_gpio.c +++ b/sw/lib/source/neorv32_gpio.c @@ -44,22 +44,13 @@ int neorv32_gpio_available(void) { void neorv32_gpio_pin_set(int pin, int value) { uint32_t mask = (uint32_t)(1 << (pin & 0x1f)); + int lohi = (pin < 32) ? 0 : 1; - if (pin < 32) { - if (value) { - NEORV32_GPIO->OUTPUT_LO |= mask; - } - else { - NEORV32_GPIO->OUTPUT_LO &= ~mask; - } + if (value) { + NEORV32_GPIO->OUTPUT[lohi] |= mask; } else { - if (value) { - NEORV32_GPIO->OUTPUT_HI |= mask; - } - else { - NEORV32_GPIO->OUTPUT_HI &= ~mask; - } + NEORV32_GPIO->OUTPUT[lohi] &= ~mask; } } @@ -72,13 +63,8 @@ void neorv32_gpio_pin_set(int pin, int value) { void neorv32_gpio_pin_toggle(int pin) { uint32_t mask = (uint32_t)(1 << (pin & 0x1f)); - - if (pin < 32) { - NEORV32_GPIO->OUTPUT_LO ^= mask; - } - else { - NEORV32_GPIO->OUTPUT_HI ^= mask; - } + int lohi = (pin < 32) ? 0 : 1; + NEORV32_GPIO->OUTPUT[lohi] ^= mask; } @@ -91,13 +77,8 @@ void neorv32_gpio_pin_toggle(int pin) { uint32_t neorv32_gpio_pin_get(int pin) { uint32_t mask = (uint32_t)(1 << (pin & 0x1f)); - - if (pin < 32) { - return NEORV32_GPIO->INPUT_LO & mask; - } - else { - return NEORV32_GPIO->INPUT_HI & mask; - } + int lohi = (pin < 32) ? 0 : 1; + return NEORV32_GPIO->INPUT[lohi] & mask; } @@ -108,14 +89,11 @@ uint32_t neorv32_gpio_pin_get(int pin) { **************************************************************************/ void neorv32_gpio_port_set(uint64_t port_data) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } data; + subwords64_t data; data.uint64 = port_data; - NEORV32_GPIO->OUTPUT_LO = data.uint32[0]; - NEORV32_GPIO->OUTPUT_HI = data.uint32[1]; + NEORV32_GPIO->OUTPUT[0] = data.uint32[0]; + NEORV32_GPIO->OUTPUT[1] = data.uint32[1]; } @@ -126,14 +104,11 @@ void neorv32_gpio_port_set(uint64_t port_data) { **************************************************************************/ void neorv32_gpio_port_toggle(uint64_t toggle) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } data; + subwords64_t data; data.uint64 = toggle; - NEORV32_GPIO->OUTPUT_LO ^= data.uint32[0]; - NEORV32_GPIO->OUTPUT_HI ^= data.uint32[1]; + NEORV32_GPIO->OUTPUT[0] ^= data.uint32[0]; + NEORV32_GPIO->OUTPUT[1] ^= data.uint32[1]; } @@ -144,13 +119,10 @@ void neorv32_gpio_port_toggle(uint64_t toggle) { **************************************************************************/ uint64_t neorv32_gpio_port_get(void) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } data; + subwords64_t data; - data.uint32[0] = NEORV32_GPIO->INPUT_LO; - data.uint32[1] = NEORV32_GPIO->INPUT_HI; + data.uint32[0] = NEORV32_GPIO->INPUT[0]; + data.uint32[1] = NEORV32_GPIO->INPUT[1]; return data.uint64; } diff --git a/sw/lib/source/neorv32_mtime.c b/sw/lib/source/neorv32_mtime.c index cecca7dd5..a65a1a2b2 100644 --- a/sw/lib/source/neorv32_mtime.c +++ b/sw/lib/source/neorv32_mtime.c @@ -44,18 +44,14 @@ int neorv32_mtime_available(void) { **************************************************************************/ void neorv32_mtime_set_time(uint64_t time) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; cycles.uint64 = time; + // prevent low-to-high carry while writing NEORV32_MTIME->TIME_LO = 0; NEORV32_MTIME->TIME_HI = cycles.uint32[1]; NEORV32_MTIME->TIME_LO = cycles.uint32[0]; - - asm volatile("nop"); // delay due to write buffer } @@ -68,10 +64,7 @@ void neorv32_mtime_set_time(uint64_t time) { **************************************************************************/ uint64_t neorv32_mtime_get_time(void) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; uint32_t tmp1, tmp2, tmp3; while(1) { @@ -100,14 +93,12 @@ uint64_t neorv32_mtime_get_time(void) { **************************************************************************/ void neorv32_mtime_set_timecmp(uint64_t timecmp) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; cycles.uint64 = timecmp; - NEORV32_MTIME->TIMECMP_LO = -1; // prevent MTIMECMP from temporarily becoming smaller than the lesser of the old and new values + // prevent MTIMECMP from temporarily becoming smaller than the lesser of the old and new values + NEORV32_MTIME->TIMECMP_LO = -1; NEORV32_MTIME->TIMECMP_HI = cycles.uint32[1]; NEORV32_MTIME->TIMECMP_LO = cycles.uint32[0]; } @@ -120,10 +111,7 @@ void neorv32_mtime_set_timecmp(uint64_t timecmp) { **************************************************************************/ uint64_t neorv32_mtime_get_timecmp(void) { - union { - uint64_t uint64; - uint32_t uint32[sizeof(uint64_t)/sizeof(uint32_t)]; - } cycles; + subwords64_t cycles; cycles.uint32[0] = NEORV32_MTIME->TIMECMP_LO; cycles.uint32[1] = NEORV32_MTIME->TIMECMP_HI; diff --git a/sw/lib/source/neorv32_rte.c b/sw/lib/source/neorv32_rte.c index 503669e34..b9eb8b0f6 100644 --- a/sw/lib/source/neorv32_rte.c +++ b/sw/lib/source/neorv32_rte.c @@ -185,10 +185,8 @@ static void __attribute__((__naked__,aligned(4))) __neorv32_rte_core(void) { default: handler_base = (uint32_t)(&neorv32_rte_debug_handler); break; } - // execute handler - void (*handler_pnt)(void); - handler_pnt = (void*)handler_base; - (*handler_pnt)(); + // call handler + asm volatile ("jalr ra, 0(%[dst])" : : [dst] "r" (handler_base)); // compute return address (for exceptions only) // do not alter return address if instruction access exception (fatal?) diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index cf61934fc..fea6c3ab8 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -1437,24 +1437,24 @@ - INPUT_LO + INPUT[0] Parallel input register - low 0x00 read-only - INPUT_HI + INPUT[1] Parallel input register - high 0x04 read-only - OUTPUT_LO + OUTPUT[0] Parallel output register - low 0x08 - OUTPUT_HI + OUTPUT[1] Parallel output register - high 0x0C