Software APIs
ibex.h
Go to the documentation of this file.
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_
6 #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_
7 
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
16 
17 // IBEX_SPIN_FOR needs a dependency on check.h, but the build fails if a
18 // dependency on sw_lib_testing_check is added.
19 
20 /**
21  * @file
22  * @brief This header provides Ibex-specific functions and enums, such as
23  * cycle-accurate busy loops.
24  */
25 
26 /**
27  * An Ibex exception type.
28  *
29  * This enum is used to decode RISC-V exception causes generated by Ibex.
30  */
31 typedef enum ibex_exc {
32  kIbexExcInstrMisaligned = 0,
33  kIbexExcInstrAccessFault = 1,
34  kIbexExcIllegalInstrFault = 2,
35  kIbexExcBreakpoint = 3,
36  kIbexExcLoadAccessFault = 5,
37  kIbexExcStoreAccessFault = 7,
38  kIbexExcUserECall = 8,
39  kIbexExcMachineECall = 11,
40  kIbexExcMax = 31
41 } ibex_exc_t;
42 
43 /**
44  * An Ibex internal IRQ type.
45  *
46  * This enum is used to decode RISC-V internal IRQs generated by Ibex.
47  */
48 typedef enum ibex_internal_irq {
49  kIbexInternalIrqLoadInteg = 0xffffffe0,
50  kIbexInternalIrqNmi = 0x8000001f
52 
53 /**
54  * A spinwait timeout type.
55  */
56 typedef struct ibex_timeout {
57  /**
58  * The number of cycles to timeout.
59  */
60  uint64_t cycles;
61  /**
62  * The initial cycle count.
63  */
64  uint64_t start;
66 
67 /**
68  * Read the cycle counter.
69  *
70  * The value of the counter is stored across two 32-bit registers: `mcycle` and
71  * `mcycleh`. This function is guaranteed to return a valid 64-bit cycle
72  * counter value, even if `mcycle` overflows before reading `mcycleh`.
73  *
74  * Adapted from: The RISC-V Instruction Set Manual, Volume I: Unprivileged ISA
75  * V20191213, pp. 61.
76  */
78 inline uint64_t ibex_mcycle_read(void) {
79  uint32_t cycle_low = 0;
80  uint32_t cycle_high = 0;
81  uint32_t cycle_high_2 = 0;
82  asm volatile(
83  "1:"
84  " csrr %0, mcycleh;" // Read `mcycleh`.
85  " csrr %1, mcycle;" // Read `mcycle`.
86  " csrr %2, mcycleh;" // Read `mcycleh` again.
87  " bne %0, %2, 1b;" // Try again if `mcycle` overflowed before
88  // reading `mcycleh`.
89  : "=r"(cycle_high), "=r"(cycle_low), "=r"(cycle_high_2)
90  :);
91  return (uint64_t)cycle_high << 32 | cycle_low;
92 }
93 
94 /**
95  * Reads the mcause register.
96  *
97  * When an exception is encountered, the corresponding exception code is stored
98  * in mcause register.
99  *
100  * A list of the exception codes can be found at:
101  * https://ibex-core.readthedocs.io/en/latest/03_reference/
102  * exception_interrupts.html#exceptions
103  */
105 uint32_t ibex_mcause_read(void);
106 
107 /**
108  * Reads the mtval register.
109  *
110  * When an exception is encountered, the Machine Trap Value (mtval) register
111  * can holds exception-specific information to assist software in handling the
112  * trap.
113  *
114  * From the Ibex documentation (found at
115  * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html)
116  * - In the case of errors in the load-store unit mtval holds the address of
117  * the transaction causing the error.
118  *
119  * - If a transaction is misaligned, mtval holds the address of the missing
120  * transaction part.
121  *
122  * - In the case of illegal instruction exceptions, mtval holds the actual
123  * faulting instruction.
124  *
125  * - For all other exceptions, mtval is 0.
126  */
128 uint32_t ibex_mtval_read(void);
129 
130 /**
131  * Reads the mepc register.
132  *
133  * When an exception is encountered, the current program counter is saved in
134  * mepc, and the core jumps to the exception address. When an MRET instruction
135  * is executed, the value from mepc replaces the current program counter.
136  *
137  * From the Ibex documentation (found at
138  * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html)
139  *
140  * Please note that in case of a fault, mepc must be modified to hold the
141  * address of the next instruction, which can be at the 2byte (16bit) or 4byte
142  * (32bit) offset, dependent on the fault cause instruction type (standard or
143  * compressed).
144  *
145  * @return The mepc register value.
146  */
148 uint32_t ibex_mepc_read(void);
149 
150 /**
151  * Writes the mepc register.
152  *
153  * When an exception is encountered, the current program counter is saved in
154  * mepc, and the core jumps to the exception address. When an MRET instruction
155  * is executed, the value from mepc replaces the current program counter.
156  *
157  * From the Ibex documentation (found at
158  * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html)
159  *
160  * Please note that in case of a fault, mepc must be modified to hold the
161  * address of the next instruction, which can be at the 2byte (16bit) or 4byte
162  * (32bit) offset, dependent on the fault cause instruction type (standard or
163  * compressed).
164  *
165  * @param The new value to be written to the mepc register.
166  */
167 void ibex_mepc_write(uint32_t mepc);
168 
169 /**
170  * Initializes the ibex timeout based on current mcycle count.
171  *
172  * @param timeout_usec Timeout in microseconds.
173  * @return The initialized timeout value.
174  */
176 inline ibex_timeout_t ibex_timeout_init(uint32_t timeout_usec) {
177  return (ibex_timeout_t){
178  .cycles = udiv64_slow(kClockFreqCpuHz * timeout_usec, 1000000, NULL),
179  .start = ibex_mcycle_read(),
180  };
181 }
182 
183 /**
184  * Check whether the timeout has expired.
185  *
186  * @param timeout Holds the counter start value.
187  * @return True if the timeout has expired and false otherwise.
188  */
190 inline bool ibex_timeout_check(const ibex_timeout_t *timeout) {
191  return ibex_mcycle_read() - timeout->start > timeout->cycles;
192 }
193 
194 /**
195  * Returns the time elapsed in microseconds since `ibex_timeout_init` was
196  * called.
197  *
198  * @param timeout Holds the counter start value..
199  * @return Time elapsed in microseconds.
200  */
202 inline uint64_t ibex_timeout_elapsed(const ibex_timeout_t *timeout) {
203  return udiv64_slow((ibex_mcycle_read() - timeout->start) * 1000000,
204  kClockFreqCpuHz, NULL);
205 }
206 
207 /**
208  * Convenience macro to spin with timeout in microseconds.
209  *
210  * @param expr An expression that is evaluated multiple times until true.
211  * @param timeout_usec Timeout in microseconds.
212  */
213 // TODO(#17495) Remove this macro and rename `IBEX_TRY_SPIN_FOR` when all the
214 // test are refactored.
215 #define IBEX_SPIN_FOR(expr, timeout_usec) \
216  do { \
217  const ibex_timeout_t timeout_ = ibex_timeout_init(timeout_usec); \
218  while (!(expr)) { \
219  CHECK(!ibex_timeout_check(&timeout_), \
220  "Timed out after %d usec (%d CPU cycles) waiting for " #expr, \
221  (uint32_t)timeout_usec, (uint32_t)timeout_.cycles); \
222  } \
223  } while (0)
224 
225 /**
226  * Convenience macro to spin with timeout in microseconds.
227  *
228  * @param expr An expression that is evaluated multiple times until true.
229  * @param timeout_usec Timeout in microseconds.
230  * @return `kDeadlineExceeded` in case of timeout.
231  */
232 #define IBEX_TRY_SPIN_FOR(expr, timeout_usec) \
233  do { \
234  const ibex_timeout_t timeout_ = ibex_timeout_init(timeout_usec); \
235  while (!(expr)) { \
236  if (ibex_timeout_check(&timeout_)) { \
237  return DEADLINE_EXCEEDED(); \
238  } \
239  } \
240  } while (0)
241 
242 #endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_