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
12
#include "
sw/device/lib/arch/device.h
"
13
#include "
sw/device/lib/base/macros.h
"
14
#include "
sw/device/lib/base/math.h
"
15
#include "
sw/device/lib/base/stdasm.h
"
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
51
}
ibex_internal_irq_t
;
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
;
65
}
ibex_timeout_t
;
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
*/
77
OT_WARN_UNUSED_RESULT
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
*/
104
OT_WARN_UNUSED_RESULT
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
*/
127
OT_WARN_UNUSED_RESULT
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
*/
147
OT_WARN_UNUSED_RESULT
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
*/
175
OT_WARN_UNUSED_RESULT
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
*/
189
OT_WARN_UNUSED_RESULT
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
*/
201
OT_WARN_UNUSED_RESULT
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_
sw
device
lib
runtime
ibex.h
Return to
OpenTitan Documentation