1 |
72 |
zero_gravi |
:sectnums:
|
2 |
|
|
=== NEORV32 Runtime Environment
|
3 |
|
|
|
4 |
|
|
The NEORV32 software framework provides a minimal runtime environment (**RTE**) that takes care of a stable
|
5 |
|
|
and _safe_ execution environment by handling _all_ traps (= exceptions & interrupts). The RTE simplifies trap handling
|
6 |
|
|
by wrapping the CPU's _privileged architecture_ (i.e. trap-related CSRs) into a unified software API.
|
7 |
|
|
The NEORV32 RTE is a software library (`sw/lib/source/neorv32_rte.c`) that is part of the default processor library set.
|
8 |
|
|
It provides public functions via `sw/lib/include/neorv32_rte.h` for application interaction.
|
9 |
|
|
|
10 |
|
|
Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible exceptions. These
|
11 |
|
|
default handlers just output a message via UART to inform the user when a certain trap has been triggered. The
|
12 |
|
|
default handlers can be overridden by the application code to install application-specific handler functions for each trap.
|
13 |
|
|
|
14 |
|
|
[IMPORTANT]
|
15 |
|
|
Using the RTE is **optional but highly recommended**. The RTE provides a simple and comfortable way of delegating
|
16 |
|
|
traps to application-specific handlers while making sure that all traps (even though they are not explicitly used
|
17 |
|
|
by the application) are handled correctly. Performance-optimized applications or embedded operating systems should
|
18 |
|
|
not use the RTE for delegating traps.
|
19 |
|
|
|
20 |
|
|
[NOTE]
|
21 |
|
|
For the **C standard runtime library** see section <>.
|
22 |
|
|
|
23 |
|
|
|
24 |
|
|
==== RTE Operation
|
25 |
|
|
|
26 |
|
|
The RTE handles the trap-related CSRs of the CPU's privileged architecture (<<_machine_trap_handling_csrs>>).
|
27 |
|
|
It initializes the <<_mtvec>> CSR, which provides the base entry point for all trap
|
28 |
|
|
handlers. The address stored to this register reflects the **first-level exception handler**, which is provided by the
|
29 |
|
|
NEORV32 RTE. Whenever an exception or interrupt is triggered this first-level handler is executed.
|
30 |
|
|
|
31 |
|
|
The first-level handler performs a complete context save, analyzes the source of the exception/interrupt and
|
32 |
|
|
calls the according **second-level exception handler**, which takes care of the actual exception/interrupt
|
33 |
|
|
handling. For this, the RTE manages a private look-up table to store the addresses of the according trap
|
34 |
|
|
handlers.
|
35 |
|
|
|
36 |
|
|
After the initial RTE setup, each entry in the RTE's trap handler's look-up table is initialized with a
|
37 |
|
|
<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they
|
38 |
|
|
just output a message via the *primary UART (UART0)* to inform the user that a trap has occurred, that is not
|
39 |
|
|
handled by the actual application. After sending this message, the RTE tries to continue executing the user program.
|
40 |
|
|
|
41 |
|
|
|
42 |
|
|
==== Using the RTE
|
43 |
|
|
|
44 |
|
|
The NEORV32 is enabled by calling the RTE's setup function:
|
45 |
|
|
|
46 |
|
|
.Function Prototype: RTE Setup
|
47 |
|
|
[source,c]
|
48 |
|
|
----
|
49 |
|
|
void neorv32_rte_setup(void);
|
50 |
|
|
----
|
51 |
|
|
|
52 |
|
|
[NOTE]
|
53 |
|
|
The RTE should be enabled right at the beginning of the application's `main` function.
|
54 |
|
|
|
55 |
|
|
As mentioned above, _all_ traps will only trigger execution of the RTE's <<_default_rte_trap_handlers>>.
|
56 |
|
|
To use application-specific handlers, which actually _handle_ a trap, the default handlers can be overridden
|
57 |
|
|
by installing user-defined ones:
|
58 |
|
|
|
59 |
|
|
.Function Prototype: Installing an Application-Specific Trap Handler
|
60 |
|
|
[source,c]
|
61 |
|
|
----
|
62 |
|
|
int neorv32_rte_exception_install(uint8_t id, void (*handler)(void));
|
63 |
|
|
----
|
64 |
|
|
|
65 |
|
|
The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled
|
66 |
|
|
by the user-defined handler. The second argument `*handler` is the actual function that implements the trap
|
67 |
|
|
handler. The function return zero on success and a non-zero value if an error occurred (invalid `id`). In this
|
68 |
|
|
case no modifications to the RTE's trap look-up-table will be made.
|
69 |
|
|
|
70 |
|
|
The custom handler functions need to have a specific format without any arguments an with no return value:
|
71 |
|
|
|
72 |
|
|
.Function Prototype: Custom Trap Handler
|
73 |
|
|
[source,c]
|
74 |
|
|
----
|
75 |
|
|
void custom_trap_handler_xyz(void) {
|
76 |
|
|
|
77 |
|
|
// handle exception/interrupt...
|
78 |
|
|
}
|
79 |
|
|
----
|
80 |
|
|
|
81 |
|
|
.Custom Trap Handler Attributes
|
82 |
|
|
[WARNING]
|
83 |
|
|
Do NOT use the `((interrupt))` attribute for the application exception handler functions! This
|
84 |
|
|
will place a `mret` instruction to the end of it making it impossible to return to the first-level
|
85 |
|
|
exception handler of the RTE core, which will cause stack corruption.
|
86 |
|
|
|
87 |
|
|
The trap identifier `id` specifies the according trap cause. These can be an _asynchronous trap_ like
|
88 |
|
|
an interrupt from one of the processor modules or a _synchronous trap_ triggered by software-caused events
|
89 |
|
|
like an illegal instruction or an environment call instruction. The `sw/lib/include/neorv32_rte.h` library files
|
90 |
|
|
provides aliases for trap events supported by the CPU (see <<_neorv32_trap_listing>>) that can be used when
|
91 |
|
|
installing custom trap handler functions:
|
92 |
|
|
|
93 |
|
|
.RTE Trap ID List
|
94 |
|
|
[cols="<5,<12"]
|
95 |
|
|
[options="header",grid="rows"]
|
96 |
|
|
|=======================
|
97 |
|
|
| ID alias [C] | Description / trap causing event
|
98 |
|
|
| `RTE_TRAP_I_MISALIGNED` | instruction address misaligned
|
99 |
|
|
| `RTE_TRAP_I_ACCESS` | instruction (bus) access fault
|
100 |
|
|
| `RTE_TRAP_I_ILLEGAL` | illegal instruction
|
101 |
|
|
| `RTE_TRAP_BREAKPOINT` | breakpoint (`ebreak` instruction)
|
102 |
|
|
| `RTE_TRAP_L_MISALIGNED` | load address misaligned
|
103 |
|
|
| `RTE_TRAP_L_ACCESS` | load (bus) access fault
|
104 |
|
|
| `RTE_TRAP_S_MISALIGNED` | store address misaligned
|
105 |
|
|
| `RTE_TRAP_S_ACCESS` | store (bus) access fault
|
106 |
|
|
| `RTE_TRAP_MENV_CALL` | environment call from machine mode (`ecall` instruction)
|
107 |
|
|
| `RTE_TRAP_UENV_CALL` | environment call from user mode (`ecall` instruction)
|
108 |
|
|
| `RTE_TRAP_MTI` | machine timer interrupt
|
109 |
|
|
| `RTE_TRAP_MEI` | machine external interrupt
|
110 |
|
|
| `RTE_TRAP_MSI` | machine software interrupt
|
111 |
|
|
| `RTE_TRAP_FIRQ_0` | fast interrupt channel 0
|
112 |
|
|
| `RTE_TRAP_FIRQ_1` | fast interrupt channel 1
|
113 |
|
|
| `RTE_TRAP_FIRQ_2` | fast interrupt channel 2
|
114 |
|
|
| `RTE_TRAP_FIRQ_3` | fast interrupt channel 3
|
115 |
|
|
| `RTE_TRAP_FIRQ_4` | fast interrupt channel 4
|
116 |
|
|
| `RTE_TRAP_FIRQ_5` | fast interrupt channel 5
|
117 |
|
|
| `RTE_TRAP_FIRQ_6` | fast interrupt channel 6
|
118 |
|
|
| `RTE_TRAP_FIRQ_7` | fast interrupt channel 7
|
119 |
|
|
| `RTE_TRAP_FIRQ_8` | fast interrupt channel 8
|
120 |
|
|
| `RTE_TRAP_FIRQ_9` | fast interrupt channel 9
|
121 |
|
|
| `RTE_TRAP_FIRQ_10` | fast interrupt channel 10
|
122 |
|
|
| `RTE_TRAP_FIRQ_11` | fast interrupt channel 11
|
123 |
|
|
| `RTE_TRAP_FIRQ_12` | fast interrupt channel 12
|
124 |
|
|
| `RTE_TRAP_FIRQ_13` | fast interrupt channel 13
|
125 |
|
|
| `RTE_TRAP_FIRQ_14` | fast interrupt channel 14
|
126 |
|
|
| `RTE_TRAP_FIRQ_15` | fast interrupt channel 15
|
127 |
|
|
|=======================
|
128 |
|
|
|
129 |
|
|
The following example shows how to install a custom handler (`custom_mtime_irq_handler`) for handling
|
130 |
|
|
the RISC-V machine timer (MTIME) interrupt:
|
131 |
|
|
|
132 |
|
|
.Example: Installing the MTIME IRQ Handler
|
133 |
|
|
[source,c]
|
134 |
|
|
----
|
135 |
|
|
neorv32_rte_exception_install(RTE_TRAP_MTI, custom_mtime_irq_handler);
|
136 |
|
|
----
|
137 |
|
|
|
138 |
|
|
User-defined trap handlers can also be un-installed. This will remove the users trap handler from the RTE core
|
139 |
|
|
and will re-install the <<_default_rte_trap_handlers>> for the specific trap.
|
140 |
|
|
|
141 |
|
|
.Function Prototype: Installing an Application-Specific Trap Handler
|
142 |
|
|
[source,c]
|
143 |
|
|
----
|
144 |
|
|
int neorv32_rte_exception_uninstall(uint8_t id);
|
145 |
|
|
----
|
146 |
|
|
|
147 |
|
|
The argument `id` defines the identifier of the according trap that shall be un-installed. The function return zero
|
148 |
|
|
on success and a non-zero value if an error occurred (invalid `id`). In this case no modifications to the RTE's trap
|
149 |
|
|
look-up-table will be made.
|
150 |
|
|
|
151 |
|
|
The following example shows how to un-install the custom handler `custom_mtime_irq_handler` from the
|
152 |
|
|
RISC-V machine timer (MTIME) interrupt:
|
153 |
|
|
|
154 |
|
|
.Example: Removing the Custom MTIME IRQ Handler
|
155 |
|
|
[source,c]
|
156 |
|
|
----
|
157 |
|
|
neorv32_rte_exception_uninstall(RTE_TRAP_MTI);
|
158 |
|
|
----
|
159 |
|
|
|
160 |
|
|
|
161 |
|
|
==== Default RTE Trap Handlers
|
162 |
|
|
|
163 |
|
|
The default RTE trap handlers are executed when a certain trap is triggered that is not handled by a user-defined
|
164 |
|
|
application-specific trap handler. These default handler will just output a message giving additional debug information
|
165 |
|
|
via UART0 to inform the user and will try to resume normal execution of the application.
|
166 |
|
|
|
167 |
|
|
.Continuing Execution
|
168 |
|
|
[IMPORTAN]
|
169 |
|
|
In most cases the RTE can successfully continue operation when it catches an interrupt request, which is not handled
|
170 |
|
|
by the actual application program. However, if the RTE catches an un_handled exception like a bus access fault
|
171 |
|
|
continuing execution will most likely fail and the CPU will crash.
|
172 |
|
|
|
173 |
|
|
.RTE Default Trap Handler Output Example (Illegal Instruction)
|
174 |
|
|
[source]
|
175 |
|
|
----
|
176 |
|
|
Illegal instruction @ PC=0x000002d6, MTVAL=0x00001537
|
177 |
|
|
----
|
178 |
|
|
|
179 |
|
|
In this example the "Illegal instruction" _message_ describes the cause of the trap, which is an illegal instruction
|
180 |
|
|
exception here. `PC` shows the current program counter value when the trap occurred and `MTVAL` shows additional
|
181 |
|
|
debug information from the <<_mtval>> CSR. In this case it shows the encoding of the illegal instruction.
|
182 |
|
|
|
183 |
|
|
The specific _message_ corresponds to the trap code from the <<_mcause>> CSR (see <<_neorv32_trap_listing>>).
|
184 |
|
|
A full list of all messages and the according `mcause` trap codes are shown below.
|
185 |
|
|
|
186 |
|
|
.RTE Default Trap Handler Messages and According `mcause` Values
|
187 |
|
|
[cols="<5,^5"]
|
188 |
|
|
[options="header",grid="rows"]
|
189 |
|
|
|=======================
|
190 |
|
|
| Trap identifier | According `mcause` CSR value
|
191 |
|
|
| "Instruction address misaligned" | `0x00000000`
|
192 |
|
|
| "Instruction access fault" | `0x00000001`
|
193 |
|
|
| "Illegal instruction" | `0x00000002`
|
194 |
|
|
| "Breakpoint" | `0x00000003`
|
195 |
|
|
| "Load address misaligned" | `0x00000004`
|
196 |
|
|
| "Load access fault" | `0x00000005`
|
197 |
|
|
| "Store address misaligned" | `0x00000006`
|
198 |
|
|
| "Store access fault" | `0x00000007`
|
199 |
|
|
| "Environment call from U-mode" | `0x00000008`
|
200 |
|
|
| "Environment call from M-mode" | `0x0000000b`
|
201 |
|
|
| "Machine software interrupt" | `0x80000003`
|
202 |
|
|
| "Machine timer interrupt" | `0x80000007`
|
203 |
|
|
| "Machine external interrupt" | `0x8000000b`
|
204 |
|
|
| "Fast interrupt 0" | `0x80000010`
|
205 |
|
|
| "Fast interrupt 1" | `0x80000011`
|
206 |
|
|
| "Fast interrupt 2" | `0x80000012`
|
207 |
|
|
| "Fast interrupt 3" | `0x80000013`
|
208 |
|
|
| "Fast interrupt 4" | `0x80000014`
|
209 |
|
|
| "Fast interrupt 5" | `0x80000015`
|
210 |
|
|
| "Fast interrupt 6" | `0x80000016`
|
211 |
|
|
| "Fast interrupt 7" | `0x80000017`
|
212 |
|
|
| "Fast interrupt 8" | `0x80000018`
|
213 |
|
|
| "Fast interrupt 9" | `0x80000019`
|
214 |
|
|
| "Fast interrupt a" | `0x8000001a`
|
215 |
|
|
| "Fast interrupt b" | `0x8000001b`
|
216 |
|
|
| "Fast interrupt c" | `0x8000001c`
|
217 |
|
|
| "Fast interrupt d" | `0x8000001d`
|
218 |
|
|
| "Fast interrupt e" | `0x8000001e`
|
219 |
|
|
| "Fast interrupt f" | `0x8000001f`
|
220 |
|
|
| "Unknown trap cause" | _not defined_
|
221 |
|
|
|=======================
|
222 |
|
|
|
223 |
|
|
===== Bus Access Faults
|
224 |
|
|
|
225 |
|
|
For bus access faults the RTE default trap handlers also output the error code from the
|
226 |
|
|
<<_internal_bus_monitor_buskeeper>> to show the cause of the bus fault. One example is shown below.
|
227 |
|
|
|
228 |
|
|
.RTE Default Trap Handler Output Example (Load Access Bus Fault)
|
229 |
|
|
[source]
|
230 |
|
|
----
|
231 |
|
|
Load access fault [TIMEOUT_ERR] @ PC=0x00000150, MTVAL=0xFFFFFF70
|
232 |
|
|
----
|
233 |
|
|
|
234 |
|
|
The additional message encapsulated in `[ ]` shows the actual cause of the bus access fault.
|
235 |
|
|
Three different messages are possible here:
|
236 |
|
|
|
237 |
|
|
* `[TIMEOUT_ERR]`: The accessed memory-mapped module did not respond within the valid access time window.
|
238 |
|
|
In Most cases this is caused by accessing a module that has not been implemented or when accessing
|
239 |
|
|
"address space holes" (unused/unmapped addresses).
|
240 |
|
|
* `[DEVICE_ERR]`: The accesses memory-mapped module asserted it's error signal to indicate an invalid access.
|
241 |
|
|
For example this can be caused by trying to write to read-only registers or by writing data quantities (like a byte)
|
242 |
|
|
to devices that do not support sub-word write accesses.
|
243 |
|
|
* `[PMP_ERR]`: This indicates an access right violation caused by the <<_pmp_physical_memory_protection>>.
|