From 978fdb8ff851b03eed41b30aa69717c2ec3991ec Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 06:46:27 +0100 Subject: [PATCH 1/6] [package] update version ID --- rtl/core/neorv32_package.vhd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 97c93f21f..3823da556 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -29,7 +29,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100903"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100904"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width From b3e2ce4364b6563e0e815c33ca332b3f46f9a8a8 Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 06:47:10 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20[rte]=20use=20a=20sing?= =?UTF-8?q?le=20global=20trap=20vector=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trap handler installation are global and apply to both cores --- sw/lib/include/neorv32_rte.h | 1 - sw/lib/source/neorv32_rte.c | 126 ++++++++++++++--------------------- 2 files changed, 51 insertions(+), 76 deletions(-) diff --git a/sw/lib/include/neorv32_rte.h b/sw/lib/include/neorv32_rte.h index ff4c54afa..75adc8e85 100644 --- a/sw/lib/include/neorv32_rte.h +++ b/sw/lib/include/neorv32_rte.h @@ -65,7 +65,6 @@ enum NEORV32_RTE_TRAP_enum { void neorv32_rte_setup(void); void neorv32_rte_core(void); int neorv32_rte_handler_install(int id, void (*handler)(void)); -int neorv32_rte_handler_uninstall(int id); void neorv32_rte_debug_handler(void); uint32_t neorv32_rte_context_get(int x); void neorv32_rte_context_put(int x, uint32_t data); diff --git a/sw/lib/source/neorv32_rte.c b/sw/lib/source/neorv32_rte.c index 338f1a86f..f7cc0f704 100644 --- a/sw/lib/source/neorv32_rte.c +++ b/sw/lib/source/neorv32_rte.c @@ -19,8 +19,8 @@ // RTE private variables and functions // ------------------------------------------------------------------------------------------------ -// private trap vector look-up table (one table per core) -static volatile uint32_t __neorv32_rte_vector_lut[2][NEORV32_RTE_NUM_TRAPS]; +// private trap vector look-up table (for all cores!) +static volatile uint32_t __neorv32_rte_vector_lut[NEORV32_RTE_NUM_TRAPS]; // private helper functions static void __neorv32_rte_print_hex(uint32_t num, int digits); @@ -34,7 +34,7 @@ static void __neorv32_rte_print_hex(uint32_t num, int digits); * NEORV32 runtime environment (RTE): * Setup RTE. * - * @warning This function must be called on all cores that wish to use the RTE. + * @note This function must be called on all cores that wish to use the RTE. * * @note This function installs a debug handler for ALL trap sources, which * gives detailed information about the trap via UART0 (if available). Actual @@ -51,14 +51,15 @@ void neorv32_rte_setup(void) { // disable all IRQ channels neorv32_cpu_csr_write(CSR_MIE, 0); - // install debug handler for all trap sources - int id; - for (id = 0; id < ((int)NEORV32_RTE_NUM_TRAPS); id++) { - neorv32_rte_handler_uninstall(id); + // install debug handler for all trap sources (only on core 0) + if (neorv32_cpu_csr_read(CSR_MHARTID) == 0) { + int index; + for (index = 0; index < ((int)NEORV32_RTE_NUM_TRAPS); index++) { + __neorv32_rte_vector_lut[index] = (uint32_t)(&neorv32_rte_debug_handler); + } + asm volatile ("fence"); // flush handler table to main memory } - // flush table to main memory - asm volatile ("fence"); } @@ -66,8 +67,8 @@ void neorv32_rte_setup(void) { * NEORV32 runtime environment (RTE): * Install trap handler function (second-level trap handler). * - * @note This function operates on the RTE instance of the - * core on which this function is executed. + * @note Trap handler installation applies to both cores. Hence, both + * cores will execute the same handler for the same trap. * * @param[in] id Identifier (type) of the targeted trap * See #NEORV32_RTE_TRAP_enum. @@ -86,36 +87,9 @@ int neorv32_rte_handler_install(int id, void (*handler)(void)) { } // install handler - uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1; - __neorv32_rte_vector_lut[hart_id][index] = (uint32_t)handler; - return 0; -} - - -/**********************************************************************//** - * NEORV32 runtime environment (RTE): - * Uninstall trap handler function from NEORV32 runtime environment, - * which was previously installed via #neorv32_rte_handler_install(). - * - * @note This function operates on the RTE instance of the - * core on which this function is executed. - * - * @param[in] id Identifier (type) of the targeted trap. - * See #NEORV32_RTE_TRAP_enum. - * - * @return 0 if success, -1 if invalid trap ID. - **************************************************************************/ -int neorv32_rte_handler_uninstall(int id) { + __neorv32_rte_vector_lut[index] = (uint32_t)handler; + asm volatile ("fence"); // flush updated handler table to main memory - // check if invalid trap ID - uint32_t index = (uint32_t)id; - if (index >= NEORV32_RTE_NUM_TRAPS) { - return -1; - } - - // use dummy handler in case the trap is accidentally triggered - uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1; - __neorv32_rte_vector_lut[hart_id][index] = (uint32_t)(&neorv32_rte_debug_handler); return 0; } @@ -176,39 +150,42 @@ void __attribute__((__naked__,aligned(4))) neorv32_rte_core(void) { #endif ); + // flush context (stack frame) to main memory + // reload trap table from main memory + asm volatile ("fence"); + // find according trap handler base address - uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1; uint32_t handler_base; switch (neorv32_cpu_csr_read(CSR_MCAUSE)) { - case TRAP_CODE_I_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_ACCESS]; break; - case TRAP_CODE_I_ILLEGAL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_ILLEGAL]; break; - case TRAP_CODE_I_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_MISALIGNED]; break; - case TRAP_CODE_BREAKPOINT: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_BREAKPOINT]; break; - case TRAP_CODE_L_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_L_MISALIGNED]; break; - case TRAP_CODE_L_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_L_ACCESS]; break; - case TRAP_CODE_S_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_S_MISALIGNED]; break; - case TRAP_CODE_S_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_S_ACCESS]; break; - case TRAP_CODE_UENV_CALL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_UENV_CALL]; break; - case TRAP_CODE_MENV_CALL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MENV_CALL]; break; - case TRAP_CODE_MSI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MSI]; break; - case TRAP_CODE_MTI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MTI]; break; - case TRAP_CODE_MEI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MEI]; break; - case TRAP_CODE_FIRQ_0: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_0]; break; - case TRAP_CODE_FIRQ_1: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_1]; break; - case TRAP_CODE_FIRQ_2: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_2]; break; - case TRAP_CODE_FIRQ_3: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_3]; break; - case TRAP_CODE_FIRQ_4: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_4]; break; - case TRAP_CODE_FIRQ_5: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_5]; break; - case TRAP_CODE_FIRQ_6: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_6]; break; - case TRAP_CODE_FIRQ_7: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_7]; break; - case TRAP_CODE_FIRQ_8: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_8]; break; - case TRAP_CODE_FIRQ_9: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_9]; break; - case TRAP_CODE_FIRQ_10: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_10]; break; - case TRAP_CODE_FIRQ_11: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_11]; break; - case TRAP_CODE_FIRQ_12: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_12]; break; - case TRAP_CODE_FIRQ_13: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_13]; break; - case TRAP_CODE_FIRQ_14: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_14]; break; - case TRAP_CODE_FIRQ_15: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_15]; break; + case TRAP_CODE_I_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_ACCESS]; break; + case TRAP_CODE_I_ILLEGAL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_ILLEGAL]; break; + case TRAP_CODE_I_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_MISALIGNED]; break; + case TRAP_CODE_BREAKPOINT: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_BREAKPOINT]; break; + case TRAP_CODE_L_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_L_MISALIGNED]; break; + case TRAP_CODE_L_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_L_ACCESS]; break; + case TRAP_CODE_S_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_S_MISALIGNED]; break; + case TRAP_CODE_S_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_S_ACCESS]; break; + case TRAP_CODE_UENV_CALL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_UENV_CALL]; break; + case TRAP_CODE_MENV_CALL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MENV_CALL]; break; + case TRAP_CODE_MSI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MSI]; break; + case TRAP_CODE_MTI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MTI]; break; + case TRAP_CODE_MEI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MEI]; break; + case TRAP_CODE_FIRQ_0: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_0]; break; + case TRAP_CODE_FIRQ_1: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_1]; break; + case TRAP_CODE_FIRQ_2: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_2]; break; + case TRAP_CODE_FIRQ_3: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_3]; break; + case TRAP_CODE_FIRQ_4: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_4]; break; + case TRAP_CODE_FIRQ_5: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_5]; break; + case TRAP_CODE_FIRQ_6: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_6]; break; + case TRAP_CODE_FIRQ_7: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_7]; break; + case TRAP_CODE_FIRQ_8: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_8]; break; + case TRAP_CODE_FIRQ_9: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_9]; break; + case TRAP_CODE_FIRQ_10: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_10]; break; + case TRAP_CODE_FIRQ_11: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_11]; break; + case TRAP_CODE_FIRQ_12: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_12]; break; + case TRAP_CODE_FIRQ_13: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_13]; break; + case TRAP_CODE_FIRQ_14: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_14]; break; + case TRAP_CODE_FIRQ_15: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_15]; break; default: handler_base = (uint32_t)(&neorv32_rte_debug_handler); break; } @@ -343,12 +320,11 @@ void neorv32_rte_debug_handler(void) { neorv32_uart0_puts(" "); // core ID - uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1; - if (hart_id) { - neorv32_uart0_puts("core1: "); + if (neorv32_cpu_csr_read(CSR_MHARTID) & 1) { + neorv32_uart0_puts("[cpu1] "); } else { - neorv32_uart0_puts("core0: "); + neorv32_uart0_puts("[cpu0] "); } // privilege level of the CPU when the trap occurred @@ -408,7 +384,7 @@ void neorv32_rte_debug_handler(void) { // unhandled IRQ - disable interrupt channel if (((int32_t)trap_cause) < 0) { // is interrupt - neorv32_uart0_puts(" Disabling IRQ source\n"); + neorv32_uart0_puts(" Disabling IRQ source"); neorv32_cpu_csr_clr(CSR_MIE, 1 << (trap_cause & 0x1f)); } From 04c8add9e1412453e076e064f46a8860173bec81 Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 06:50:59 +0100 Subject: [PATCH 3/6] [changelog] add v1.10.9.4 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49118ecb3..69c554333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date | Version | Comment | Ticket | |:----:|:-------:|:--------|:------:| +| 11.01.2025 | 1.10.9.4 | :warning: RTE: use a single, global trap handler table that applies to _both_ cores | [#1150](https://github.com/stnolting/neorv32/pull/1150) | | 10.01.2025 | 1.10.9.3 | split functional behavior of `fence` and `fence.i` instructions | [#1149](https://github.com/stnolting/neorv32/pull/1149) | | 10.01.2025 | 1.10.9.2 | clean-up SMP dual-core configuration (HW and SW optimizations) | [#1146](https://github.com/stnolting/neorv32/pull/1146) | | 09.01.2025 | 1.10.9.1 | fix side-effects of CSR read instructions | [#1145](https://github.com/stnolting/neorv32/pull/1145) | From 85148e7667132de4092e0d7909c45e366fcb9e6c Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 06:51:18 +0100 Subject: [PATCH 4/6] [docs] rework section "RTE" --- docs/datasheet/software_rte.adoc | 269 ++++++++++--------------------- 1 file changed, 87 insertions(+), 182 deletions(-) diff --git a/docs/datasheet/software_rte.adoc b/docs/datasheet/software_rte.adoc index 31de9e8d2..6b32fa6ee 100644 --- a/docs/datasheet/software_rte.adoc +++ b/docs/datasheet/software_rte.adoc @@ -1,71 +1,70 @@ :sectnums: === NEORV32 Runtime Environment -The NEORV32 software framework provides a minimal **runtime environment** (abbreviated "RTE") that takes care of a stable -and _safe_ execution environment by handling _all_ traps (exceptions & interrupts). The RTE simplifies trap handling -by wrapping the CPU's privileged architecture (i.e. trap-related CSRs and the actual execution of trap handlers) -into a unified software API. +The NEORV32 software framework provides a minimal runtime environment ("RTE") that takes care of a stable +and _safe_ execution environment by providing a unified interface for handling of _all_ traps (exceptions and +interrupts). Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible traps. +These default handlers just output a message via UART when a certain trap has been triggered. The default +handlers can be overridden by the application code to install application-specific handler functions for each trap. -Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible traps. These -default handlers just output a message via UART to inform the user when a certain trap has been triggered. The -default handlers can be overridden by the application code to install application-specific handler functions for each trap. - -[IMPORTANT] -Using the RTE is **optional but highly recommended**. The RTE provides a simple and comfortable way of delegating -traps to application-specific handlers while making sure that all traps (even though they are not explicitly used -by the application) are handled correctly. Performance-optimized applications or embedded operating systems may -not use the RTE at all in order to increase response time. +Using the RTE is **optional but highly recommended** for bare-metal / non-OS applications. The RTE provides a +simple and comfortable way of delegating traps to application-specific handlers while making sure that all traps +(even though they are not explicitly used by the application) are handled correctly. Performance-optimized +applications or embedded operating systems may not use the RTE at all in order to increase response time. ==== RTE Operation -The RTE manages the trap-related CSRs of the CPU's privileged architecture (<<_machine_trap_handling_csrs>>). -It initializes the <<_mtvec>> CSR in DIRECT mode, which then provides the base entry point for _all_ traps. The address -stored to this register defines the address of the **first-level trap handler**, which is provided by the -NEORV32 RTE. Whenever an exception or interrupt is triggered this first-level trap handler is executed. +The RTE manages the trap-related CSRs of the CPU's privileged architecture (see <<_machine_trap_handling_csrs>>). +It initializes the <<_mtvec>> CSR in DIRECT mode, which provides the base entry point for _all_ traps. The address +stored to this register defines the address of the **first-level trap handler**, which is provided by the NEORV32 +RTE. Whenever an exception or interrupt is triggered this first-level trap handler is executed. -The first-level handler performs a complete context save, analyzes the source of the trap and -calls the according **second-level trap handler**, which takes care of the actual exception/interrupt -handling. The RTE manages a private look-up table to store the addresses of the according second-level trap handlers. +The first-level handler performs a complete context save, analyzes the source of the trap and calls the according +**second-level trap handler**, which takes care of the actual exception/interrupt handling. The RTE manages an +internal look-up table to track the addresses of the according second-level trap handlers. After the initial RTE setup, each entry in the RTE's trap handler look-up table is initialized with a -<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they -just output a message via the *primary UART (UART0)* to inform the user that a trap has occurred, which is not (yet) -handled by the actual application. After sending this message, the RTE tries to continue executing the actual program -by resolving the trap cause. +<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they just output +a debugging message via the primary UART (UART0) (if enabled) to inform the user that a trap has occurred that is +not (yet) handled by a proper application-specific trap handler. After sending this message, the RTE tries to resume +normal execution by moving on to the next linear instruction. .Dual-Core Configuration [NOTE] -The RTE also supports the SMP <<_dual_core_configuration>> as it provides core-individual internal trap management. +The RTE's internal trap handler look-up table is used globally for **both** cores. If a core-specific handling is +required, the according user-defined trap handler need to retrieve the core's ID from <<_mhartid>> and branch +accordingly. ==== Using the RTE -.Machine-Mode Only -[IMPORTANT] -All provided RTE functions can be called only from machine-mode code. +The NEORV32 runtime environment is part of the default NEORV32 software framework. The links to the according +software references are listed below. + +[cols="<1,<8"] +[grid="none"] +|======================= +| neorv32_rte.c | link:https://stnolting.github.io/neorv32/sw/neorv32__rte_8c.html[Online software reference (Doxygen)] +| neorv32_rte.h | link:https://stnolting.github.io/neorv32/sw/neorv32__rte_8h.html[Online software reference (Doxygen)] +|======================= -The NEORV32 is part of the default NEORV32 software framework. However, it has to explicitly enabled by calling -the RTE's setup function: +The RTE has to be explicitly enabled by calling the according setup function. It is recommended to do this right at the +beginning of the application's `main` function. For the SMP <<_dual_core_configuration>> the RTE setup functions has to +be called on each core that wants to use the RTE. -.RTE Setup (Function Prototype) +.RTE Setup Right at the Beginning of "main" [source,c] ---- -void neorv32_rte_setup(void); ----- +int main() { -.RTE Setup -[NOTE] -The RTE should be enabled right at the beginning of the application's `main` function. For the SMP -<<_dual_core_configuration>> the RTE setup functions has to be called on each core individually. + neorv32_rte_setup(); // setup NEORV32 runtime environment -[IMPORTANT] -It is recommended to not use the <<_mscratch>> CSR when using the RTE as this register is used to provide services -for <<_application_context_handling>> (i.e. modifying the registers of application code that caused a trap). + ... +---- -As mentioned above, all traps will just trigger execution of the RTE's <<_default_rte_trap_handlers>> at first. -To use application-specific handlers, which actually "handle" a trap, the default handlers can be overridden -by installing user-defined ones: +After setup, all traps will trigger execution of the RTE's <<_default_rte_trap_handlers>> at first. In order to use +application-specific trap handlers the default debug handlers can be overridden by installing user-defined ones: .Installing an Application-Specific Trap Handler (Function Prototype) [source,c] @@ -73,46 +72,11 @@ by installing user-defined ones: int neorv32_rte_handler_install(uint8_t id, void (*handler)(void)); ---- -The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled -by the user-defined handler. These IDs are defined in `sw/lib/include/neorv32_rte.h`: - -.RTE Trap Identifiers (cut-out) -[source,c] ----- -enum NEORV32_RTE_TRAP_enum { - RTE_TRAP_I_MISALIGNED = 0, /**< Instruction address misaligned */ - RTE_TRAP_I_ACCESS = 1, /**< Instruction (bus) access fault */ - RTE_TRAP_I_ILLEGAL = 2, /**< Illegal instruction */ - RTE_TRAP_BREAKPOINT = 3, /**< Breakpoint (EBREAK instruction) */ - RTE_TRAP_L_MISALIGNED = 4, /**< Load address misaligned */ - RTE_TRAP_L_ACCESS = 5, /**< Load (bus) access fault */ - RTE_TRAP_S_MISALIGNED = 6, /**< Store address misaligned */ - RTE_TRAP_S_ACCESS = 7, /**< Store (bus) access fault */ - RTE_TRAP_UENV_CALL = 8, /**< Environment call from user mode (ECALL instruction) */ - RTE_TRAP_MENV_CALL = 9, /**< Environment call from machine mode (ECALL instruction) */ - RTE_TRAP_MSI = 10, /**< Machine software interrupt */ - RTE_TRAP_MTI = 11, /**< Machine timer interrupt */ - RTE_TRAP_MEI = 12, /**< Machine external interrupt */ - RTE_TRAP_FIRQ_0 = 13, /**< Fast interrupt channel 0 */ - RTE_TRAP_FIRQ_1 = 14, /**< Fast interrupt channel 1 */ - RTE_TRAP_FIRQ_2 = 15, /**< Fast interrupt channel 2 */ - RTE_TRAP_FIRQ_3 = 16, /**< Fast interrupt channel 3 */ - RTE_TRAP_FIRQ_4 = 17, /**< Fast interrupt channel 4 */ - RTE_TRAP_FIRQ_5 = 18, /**< Fast interrupt channel 5 */ - RTE_TRAP_FIRQ_6 = 19, /**< Fast interrupt channel 6 */ - RTE_TRAP_FIRQ_7 = 20, /**< Fast interrupt channel 7 */ - RTE_TRAP_FIRQ_8 = 21, /**< Fast interrupt channel 8 */ - RTE_TRAP_FIRQ_9 = 22, /**< Fast interrupt channel 9 */ - RTE_TRAP_FIRQ_10 = 23, /**< Fast interrupt channel 10 */ - RTE_TRAP_FIRQ_11 = 24, /**< Fast interrupt channel 11 */ - RTE_TRAP_FIRQ_12 = 25, /**< Fast interrupt channel 12 */ - RTE_TRAP_FIRQ_13 = 26, /**< Fast interrupt channel 13 */ - RTE_TRAP_FIRQ_14 = 27, /**< Fast interrupt channel 14 */ - RTE_TRAP_FIRQ_15 = 28 /**< Fast interrupt channel 15 */ ----- - -The second argument `*handler` is the actual function that implements the user-defined trap handler. -The custom handler functions need to have a specific format without any arguments and with no return value: +The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled by the +user-defined handler. These IDs are defined in `sw/lib/include/neorv32_rte.h`. However, more convenient device-specific +aliases are also defined in `sw/lib/include/neorv32.h`. The second argument `handler` is the actual function that +implements the user-defined trap handler. The custom handler functions must have a specific type without any arguments +and with no return value: .Custom Trap Handler (Function Prototype) [source,c] @@ -124,140 +88,81 @@ void custom_trap_handler_xyz(void) { ---- .Custom Trap Handler Attributes -[WARNING] -Do **NOT** use the `((interrupt))` attribute for the application trap handler functions! This -will place a `mret` instruction to the end of it making it impossible to return to the first-level -trap handler of the RTE core, which will cause stack corruption. - -The following example shows how to install a custom handler (`custom_timer_irq_handler`) for handling -the RISC-V CLINT timer interrupt: - -.Installing a CLINT Timer IRQ Handler -[source,c] ----- -neorv32_rte_handler_install(RTE_TRAP_MTI, custom_timer_irq_handler); ----- - -User-defined trap handlers can also be un-installed. This will remove the users trap handler from the RTE core -and will re-install the <<_default_rte_trap_handlers>> for the specific trap. +[IMPORTANT] +Do **NOT** use the `((interrupt))` attribute for the application trap handler functions! This would place an `mret` +instruction at the end of the handler making it impossible to return to the first-level trap handler of the RTE core. -.Function Prototype: Installing an Application-Specific Trap Handler -[source,c] ----- -int neorv32_rte_handler_uninstall(uint8_t id); ----- +.`mscratch` CSR +[IMPORTANT] +The <<_mscratch>> CSR should not be used inside an application trap handler as this register is used by the RTE to +provide the base address of the application's stack frame <<_application_context_handling>> (i.e. modifying the +registers of application code that caused a trap). -The argument `id` defines the identifier of the according trap that shall be un-installed. -The following example shows how to un-install the custom handler `custom_timer_irq_handler` from the -RISC-V CLINT timer interrupt: +The following example shows how to install trap handlers for exemplary traps. -.Example: Removing the Custom CLINT Timer IRQ Handler +.Installing Custom Trap Handlers Examples [source,c] ---- -neorv32_rte_handler_uninstall(RTE_TRAP_MTI); +neorv32_rte_handler_install(RTE_TRAP_MTI, machine_timer_irq_handler); // handler for machine timer interrupt +neorv32_rte_handler_install(RTE_TRAP_MENV_CALL, environment_call_handler); // handler for machine environment call exception +neorv32_rte_handler_install(SLINK_RX_RTE_ID, slink_rx_handler); // handler for SLINK receive interrupt ---- -.Dual-Core Configuration -[NOTE] -The RTE handler install/uninstall functions can be called on any core in the SMP <<_dual_core_configuration>>. -Internally, the functions will only access the core-specific management entries. - ==== Default RTE Trap Handlers The default RTE trap handlers are executed when a certain trap is triggered that is not (yet) handled by an application-defined trap handler. The default handler will output a message giving additional debug information via the <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>> to inform the user and it will also -try to resume normal program execution. Some exemplary RTE outputs are shown below. +try to resume normal program execution (exemplary RTE outputs are shown below). The specific message right at +the beginning of the debug trap handler message corresponds to the trap code obtained from the <<_mcause>> CSR +(see <<_neorv32_trap_listing>>). -.Continuing Execution -[WARNING] In most cases the RTE can successfully continue operation - for example if it catches an **interrupt** request that is not handled by the actual application program. However, if the RTE catches an un-handled **trap** like -a bus access fault exception continuing execution will most likely fail making the CPU crash. Some exceptions -cannot be resolved by the default debug trap handlers and will halt the CPU (see example below). +a bus access fault exception, continuing execution will most likely fail making the CPU crash. -.RTE Default Trap Handler Output Examples +.RTE Default Trap Handler UART0 Output Examples [source] ---- - [M] Illegal instruction @ PC=0x000002d6, MTINST=0x000000FF, MTVAL=0x00000000 <1> - [U] Illegal instruction @ PC=0x00000302, MTINST=0x00000000, MTVAL=0x00000000 <2> - [U] Load address misaligned @ PC=0x00000440, MTINST=0x01052603, MTVAL=0x80000101 <3> - [M] Fast IRQ 0x00000003 @ PC=0x00000820, MTINST=0x00000000, MTVAL=0x00000000 <4> - [M] Instruction access fault @ PC=0x90000000, MTINST=0x42078b63, MTVAL=0x00000000 !!FATAL EXCEPTION!! Halting CPU. \n <5> + [cpu0] [M] Illegal instruction @ PC=0x000002d6, MTINST=0x000000FF, MTVAL=0x00000000 <1> + [cpu0] [U] Illegal instruction @ PC=0x00000302, MTINST=0x00000000, MTVAL=0x00000000 <2> + [cpu0] [U] Load address misaligned @ PC=0x00000440, MTINST=0x01052603, MTVAL=0x80000101 <3> + [cpu1] [M] Fast IRQ 0x3 @ PC=0x00000820, MTINST=0x00000000, MTVAL=0x00000000 <4> + [cpu1] [M] Instruction access fault @ PC=0x90000000, MTINST=0x42078b63, MTVAL=0x00000000 !!FATAL EXCEPTION!! Halting CPU. \n <5> ---- -<1> Illegal 32-bit instruction `MTINST=0x000000FF` at address `PC=0x000002d6` while the CPU was in machine-mode (`[M]`). -<2> Illegal 16-bit instruction `MTINST=0x00000000` at address `PC=0x00000302` while the CPU was in user-mode (`[U]`). -<3> Misaligned load access at address `PC=0x00000440` caused by instruction `MTINST=0x01052603` (trying to load a full 32-bit word from address `MTVAL=0x80000101`) while the CPU was in machine-mode (`[U]`). -<4> Fast interrupt request from channel 3 before executing instruction at address `PC=0x00000820` while the CPU was in machine-mode (`[M]`). -<5> Instruction bus access fault at address `PC=0x90000000` while executing instruction `MTINST=0x42078b63` - this is fatal for the default debug trap handler while the CPU was in machine-mode (`[M]`). - -The specific message right at the beginning of the debug trap handler message corresponds to the trap code -obtained from the <<_mcause>> CSR (see <<_neorv32_trap_listing>>). A full list of all messages and the according -`mcause` trap codes is shown below. - -.RTE Default Trap Handler Messages and According `mcause` Values -[cols="<5,^5"] -[options="header",grid="rows"] -|======================= -| Trap identifier | According `mcause` CSR value -| "Instruction address misaligned" | `0x00000000` -| "Instruction access fault" | `0x00000001` -| "Illegal instruction" | `0x00000002` -| "Breakpoint" | `0x00000003` -| "Load address misaligned" | `0x00000004` -| "Load access fault" | `0x00000005` -| "Store address misaligned" | `0x00000006` -| "Store access fault" | `0x00000007` -| "Environment call from U-mode" | `0x00000008` -| "Environment call from M-mode" | `0x0000000b` -| "Machine software IRQ" | `0x80000003` -| "Machine timer IRQ" | `0x80000007` -| "Machine external IRQ" | `0x8000000b` -| "Fast IRQ 0x00000000" | `0x80000010` -| "Fast IRQ 0x00000001" | `0x80000011` -| "Fast IRQ 0x00000002" | `0x80000012` -| "Fast IRQ 0x00000003" | `0x80000013` -| "Fast IRQ 0x00000004" | `0x80000014` -| "Fast IRQ 0x00000005" | `0x80000015` -| "Fast IRQ 0x00000006" | `0x80000016` -| "Fast IRQ 0x00000007" | `0x80000017` -| "Fast IRQ 0x00000008" | `0x80000018` -| "Fast IRQ 0x00000009" | `0x80000019` -| "Fast IRQ 0x0000000a" | `0x8000001a` -| "Fast IRQ 0x0000000b" | `0x8000001b` -| "Fast IRQ 0x0000000c" | `0x8000001c` -| "Fast IRQ 0x0000000d" | `0x8000001d` -| "Fast IRQ 0x0000000e" | `0x8000001e` -| "Fast IRQ 0x0000000f" | `0x8000001f` -| "Unknown trap cause" | undefined -|======================= +<1> Illegal 32-bit instruction `MTINST=0x000000FF` at address `PC=0x000002d6` while the CPU 0 was in machine-mode (`[M]`). +<2> Illegal 16-bit instruction `MTINST=0x00000000` at address `PC=0x00000302` while the CPU 0 was in user-mode (`[U]`). +<3> Misaligned load access at address `PC=0x00000440` caused by instruction `MTINST=0x01052603` (trying to load a full 32-bit word from address `MTVAL=0x80000101`) while the CPU 0 was in user-mode (`[U]`). +<4> Fast interrupt request from channel 3 before executing instruction at address `PC=0x00000820` while the CPU 1 was in machine-mode (`[M]`). +<5> Instruction bus access fault at address `PC=0x90000000` while executing instruction `MTINST=0x42078b63` while the CPU 1 was in machine-mode (`[M]`). ==== Application Context Handling -Upon trap entry the RTE backups the _entire_ application context (i.e. all `x` general purpose registers) -to the stack. The context is restored automatically after trap completion. The base address of the according -stack frame is copied to the <<_mscratch>> CSR. By having this information available, the RTE provides dedicated -functions for accessing and _altering_ the application context: +Upon trap entry the RTE backups the entire application context (i.e. all `x` general purpose registers) to the +stack. The context is restored automatically after trap completion. The base address of the according stack frame +is copied to the <<_mscratch>> CSR. By having this information available, the RTE provides dedicated functions +for accessing and altering the application context: -.Context Access Functions +.RTE Context Access Functions [source,c] ---- // Prototypes -uint32_t neorv32_rte_context_get(int x); // read register x -void neorv32_rte_context_put(int x, uint32_t data); write data to register x +uint32_t neorv32_rte_context_get(int x); // read register +void neorv32_rte_context_put(int x, uint32_t data); // write data to register // Examples uint32_t tmp = neorv32_rte_context_get(9); // read register 'x9' neorv32_rte_context_put(28, tmp); // write 'tmp' to register 'x28' ---- -.RISC-V `E` Extension -[NOTE] -Registers `x16..x31` are not available if the RISC-V <<_e_isa_extension>> is enabled. +The `x` argument is used to specify one of the RISC-V general purpose register `x0` to `x31`. Note that registers +`x16` to `x31` are not available if the RISC-V <<_e_isa_extension>> is enabled. For he SMP <<_dual_core_configuration>> +the provided context functions will access the stack frame of the interrupted application code that was running +on the specific CPU core that caused the trap entry. -The context access functions can be used by application-specific trap handlers to emulate unsupported +The context access functions can be used by application-specific trap handlers to _emulate_ unsupported CPU / SoC features like unimplemented IO modules, unsupported instructions and even unaligned memory accesses. .Demo Program: Emulate Unaligned Memory Access From c0336770479dac294e296ede8cdf702f1bbe594b Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 06:51:30 +0100 Subject: [PATCH 5/6] [docs] update section "dual-core" --- docs/datasheet/cpu_dual_core.adoc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/datasheet/cpu_dual_core.adoc b/docs/datasheet/cpu_dual_core.adoc index fbc8430a2..cea825462 100644 --- a/docs/datasheet/cpu_dual_core.adoc +++ b/docs/datasheet/cpu_dual_core.adoc @@ -26,7 +26,7 @@ The following table summarizes the most important aspects when using the dual-co |======================= | **CPU configuration** | Both cores use the same cache, CPU and ISA configuration provided by the according top generics. | **Debugging** | A special SMP openOCD script (`sw/openocd/openocd_neorv32.dual_core.cfg`) is required to -debug both cores at one. SMP-debugging is fully supported by the RISC-V gdb port. +debug both cores at once. SMP-debugging is fully supported by the RISC-V gdb port. | **Clock and reset** | Both cores use the same global processor clock and reset. If <<_cpu_clock_gating>> is enabled, the clock of each core can be individually halted by putting the core into <<_sleep_mode>>. | **Address space** | Both cores have full access to the same physical <<_address_space>>. @@ -34,9 +34,10 @@ is enabled, the clock of each core can be individually halted by putting the cor all <<_neorv32_specific_fast_interrupt_requests>> (FIRQs). Additionally, the RISC-V machine-level _external interrupt_ (via the top `mext_irq_i` port) is also send to both cores. In contrast, the RISC-V machine level _software_ and _timer_ interrupts are core-exclusive (provided by the <<_core_local_interruptor_clint>>). -| **RTE** | The <<_neorv32_runtime_environment>> fully supports the dual-core configuration and provides -core-individual trap handler tables. However, the RTE needs to be explicitly initialized on each core -(executing `neorv32_rte_setup()`). +| **RTE** | The <<_neorv32_runtime_environment>> can be used for both cores. However, the RTE needs to be +explicitly initialized on each core (executing `neorv32_rte_setup()`). Note that the installed trap handlers +apply to both cores. The installed user-defined trap handlers can determine the core's ID to perform +core-specific trap handling. | **Memory** | Each core has its own stack. The top of stack of core 0 is defined by the <<_linker_script>> while the top of stack of core 1 has to be explicitly defined by core 0 (see <<_dual_core_boot>>). Both cores share the same heap, `.data` and `.bss` sections. Hence, only core 0 setups the `.data` and `.bss` @@ -53,6 +54,19 @@ instructions) or by using <<_atomic_memory_access>>. |======================= +==== SMP Software Library + +An SMP library provides basic functions for launching the secondary core and for performing direct +core-to-core communication: + +[cols="<1,<8"] +[grid="none"] +|======================= +| neorv32_smp.c | link:https://stnolting.github.io/neorv32/sw/neorv32__smp_8c.html[Online software reference (Doxygen)] +| neorv32_smp.h | link:https://stnolting.github.io/neorv32/sw/neorv32__smp_8h.html[Online software reference (Doxygen)] +|======================= + + ==== Inter-Core Communication (ICC) Both cores can communicate with each other via a direct point-to-point connection based on FIFO-like message @@ -78,11 +92,6 @@ The ICC FIFOs do not provide any interrupt capabilities. Software is expected to interrupt of the receiving core (provided by the <<_core_local_interruptor_clint>>) to inform it about available messages. -.ICC Software API -[TIP] -The NEORV32 software framework provides API wrappers to abstract inter-core communication: -`sw/lib/include/noevr32_smp.h` - ==== Dual-Core Boot From 547d87ecffee625237057fd9fbcfe7951fcba487 Mon Sep 17 00:00:00 2001 From: stnolting Date: Sat, 11 Jan 2025 07:00:48 +0100 Subject: [PATCH 6/6] [sw/examples] add dual-core RTE demo program --- sw/example/demo_dual_core_rte/Makefile | 33 ++++ sw/example/demo_dual_core_rte/main.c | 203 +++++++++++++++++++++++ sw/example/demo_dual_core_rte/spinlock.c | 31 ++++ sw/example/demo_dual_core_rte/spinlock.h | 12 ++ 4 files changed, 279 insertions(+) create mode 100644 sw/example/demo_dual_core_rte/Makefile create mode 100644 sw/example/demo_dual_core_rte/main.c create mode 100644 sw/example/demo_dual_core_rte/spinlock.c create mode 100644 sw/example/demo_dual_core_rte/spinlock.h diff --git a/sw/example/demo_dual_core_rte/Makefile b/sw/example/demo_dual_core_rte/Makefile new file mode 100644 index 000000000..7715e365b --- /dev/null +++ b/sw/example/demo_dual_core_rte/Makefile @@ -0,0 +1,33 @@ +# Application makefile. +# Use this makefile to configure all relevant CPU / compiler options. + +# Override the default CPU ISA +MARCH = rv32ia_zicsr_zifencei + +# Override the default RISC-V GCC prefix +#RISCV_PREFIX ?= riscv-none-elf- + +# Override default optimization goal +EFFORT = -Os + +# Add extended debug symbols +USER_FLAGS += -ggdb -gdwarf-3 + +# Adjust processor IMEM size +USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=16k + +# Adjust processor DMEM size +USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k + +# Adjust maximum heap size +#USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=3k + +# Additional sources +#APP_SRC += $(wildcard ./*.c) +#APP_INC += -I . + +# Set path to NEORV32 root directory +NEORV32_HOME ?= ../../.. + +# Include the main NEORV32 makefile +include $(NEORV32_HOME)/sw/common/common.mk diff --git a/sw/example/demo_dual_core_rte/main.c b/sw/example/demo_dual_core_rte/main.c new file mode 100644 index 000000000..8ffec5419 --- /dev/null +++ b/sw/example/demo_dual_core_rte/main.c @@ -0,0 +1,203 @@ +// ================================================================================ // +// 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 demo_dual_core_rte/main.c + * @brief SMP dual-core program to show how to use the RTE on two cores. + * This example runs the same code on both cores and triggers the timer + * and software interrupts to showcase dual-core trap handling using the + * NEORV32 runtime environment (RTE). + **************************************************************************/ +#include +#include "spinlock.h" + +/** User configuration */ +#define BAUD_RATE 19200 + +/** Global variables */ +volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memory for core1 + + +/**********************************************************************//** + * Machine timer (CLINT) interrupt handler for BOTH cores. + **************************************************************************/ +void trap_handler_mtmi(void) { + + // find out which core is currently executing this + uint32_t core_id = neorv32_smp_whoami(); + + spin_lock(); + neorv32_uart0_printf("[core %u] MTIMER interrupt.\n", core_id); + spin_unlock(); + + // compute next interrupt time + uint64_t next_irq_time = neorv32_clint_time_get(); // current system time from CLINT.MTIME + if (core_id == 0) { + next_irq_time += 1 * neorv32_sysinfo_get_clk(); // 1 second for core 0 + } + else { + next_irq_time += 2 * neorv32_sysinfo_get_clk(); // 2 seconds for core 0 + } + + // this is automatically mapped to the current core's MTIMECMP register + neorv32_clint_mtimecmp_set(next_irq_time); + + // trigger software interrupt of the other core + if (core_id == 0) { + neorv32_clint_msi_set(1); // trigger core 1 + } + else { + neorv32_clint_msi_set(0); // trigger core 0 + } +} + + +/**********************************************************************//** + * Machine software (CLINT) interrupt handler for BOTH cores. + **************************************************************************/ +void trap_handler_mswi(void) { + + // find out which core is currently executing this + uint32_t core_id = neorv32_smp_whoami(); + + spin_lock(); + neorv32_uart0_printf("[core %u] Software interrupt.\n", core_id); + spin_unlock(); + + // clear software interrupt of current core + neorv32_clint_msi_clr(core_id); +} + + +/**********************************************************************//** + * Machine environment call trap handler for BOTH cores. + **************************************************************************/ +void trap_handler_ecall(void) { + + // find out which core is currently executing this + uint32_t core_id = neorv32_smp_whoami(); + + spin_lock(); + neorv32_uart0_printf("[core %u] Environment call.\n", core_id); + spin_unlock(); +} + + +/**********************************************************************//** + * "Application code" executed by BOTH cores. + * + * @return Irrelevant (but can be inspected by the debugger). + **************************************************************************/ +int app_main(void) { + + // (re-)setup NEORV32 runtime-environment (RTE) for the core that is executing this code + neorv32_rte_setup(); + + + // print message; use spinlock to have exclusive access to UART0 + uint32_t core_id = neorv32_smp_whoami(); // find out which core is currently executing this + spin_lock(); + neorv32_uart0_printf("[core %u] Hello world! This is core %u starting 'app_main()'.\n", core_id, core_id); + spin_unlock(); + + + // The NEORV32 Runtime Environment (RTE) provides an internal trap vector table. Each entry + // corresponds to a specific trap (exception or interrupt). Application software can install + // specific trap handler function to take care of each type of trap. + + // However, there is only a single trap vector table. Hence, both cores will execute the SAME + // handler function if they encounter the same trap. + + // setup machine timer interrupt for ALL cores + neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP + neorv32_rte_handler_install(RTE_TRAP_MTI, trap_handler_mtmi); // install trap handler + neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable interrupt source + + // setup machine software interrupt for ALL cores + neorv32_rte_handler_install(RTE_TRAP_MSI, trap_handler_mswi); // install trap handler + neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MSIE); // enable interrupt source + + // setup machine environment call trap for ALL cores + neorv32_rte_handler_install(RTE_TRAP_MENV_CALL, trap_handler_ecall); // install trap handler + + + // trigger environment call exception (just to test the according handler) + asm volatile ("ecall"); + + // enable machine-level interrupts and wait in sleep mode + neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE); + while (1) { + neorv32_cpu_sleep(); + } + + return 0; +} + + +/**********************************************************************//** + * Main function for core 0 (primary core). + * + * @warning This program requires the dual-core configuration, the CLINT, UART0 + * and the A/Zaamo ISA extension. + * + * @return Irrelevant (but can be inspected by the debugger). + **************************************************************************/ +int main(void) { + + // setup NEORV32 runtime-environment (RTE) for _this_ core (core 0) + // this is not required but keeps us safe + neorv32_rte_setup(); + + + // setup UART0 at default baud rate, no interrupts + if (neorv32_uart0_available() == 0) { // UART0 available? + return -1; + } + neorv32_uart0_setup(BAUD_RATE, 0); + neorv32_uart0_printf("\n<< NEORV32 SMP Dual-Core RTE Demo >>\n\n"); + + + // check hardware/software configuration + if (neorv32_sysinfo_get_numcores() < 2) { // two cores available? + neorv32_uart0_printf("[ERROR] dual-core option not enabled!\n"); + return -1; + } + if (neorv32_clint_available() == 0) { // CLINT available? + neorv32_uart0_printf("[ERROR] CLINT module not available!\n"); + return -1; + } + if ((neorv32_cpu_csr_read(CSR_MXISA) & (1< + +/**********************************************************************//** + * Private spinlock locked variable. + **************************************************************************/ +static volatile uint32_t __spin_locked = 0; + + +/**********************************************************************//** + * Spinlock: set lock. + * + * @warning This function is blocking until the lock is acquired and set. + **************************************************************************/ +void spin_lock(void) { + + while(__sync_lock_test_and_set(&__spin_locked, -1)); // -> amoswap.w +} + + +/**********************************************************************//** + * Spinlock: remove lock. + **************************************************************************/ +void spin_unlock(void) { + + //__sync_lock_release(&__spin_locked); // uses fence that is not required here + __sync_lock_test_and_set(&__spin_locked, 0); // -> amoswap.w +} diff --git a/sw/example/demo_dual_core_rte/spinlock.h b/sw/example/demo_dual_core_rte/spinlock.h new file mode 100644 index 000000000..fb07b1841 --- /dev/null +++ b/sw/example/demo_dual_core_rte/spinlock.h @@ -0,0 +1,12 @@ +/** + * @file spinlock.h + * @brief Single simple spin-lock based on atomic memory operations. + */ + +#ifndef spinlock_h +#define spinlock_h + +void spin_lock(void); +void spin_unlock(void); + +#endif // spinlock_h