riscv_pac/lib.rs
1#![no_std]
2
3pub mod result;
4
5use result::Result;
6
7/// Trait for enums of target-specific exception numbers.
8///
9/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
10/// exceptions for a specific device. Alternatively, the `riscv` crate provides a default
11/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its exception number.
12///
13/// # Safety
14///
15/// * This trait must only be implemented on the `riscv` crate or on a PAC of a RISC-V target.
16/// * This trait must only be implemented on enums of exceptions.
17/// * Each enum variant must represent a distinct value (no duplicates are permitted),
18/// * Each enum variant must always return the same value (do not change at runtime).
19/// * All the exception numbers must be less than or equal to `MAX_EXCEPTION_NUMBER`.
20/// * `MAX_EXCEPTION_NUMBER` must coincide with the highest allowed exception number.
21pub unsafe trait ExceptionNumber: Copy {
22 /// Highest number assigned to an exception.
23 const MAX_EXCEPTION_NUMBER: usize;
24
25 /// Converts an exception to its corresponding number.
26 fn number(self) -> usize;
27
28 /// Tries to convert a number to a valid exception.
29 fn from_number(value: usize) -> Result<Self>;
30}
31
32/// Trait for enums of target-specific interrupt numbers.
33///
34/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
35/// interrupts for a specific device. Alternatively, the `riscv` crate provides a default
36/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its interrupt number.
37///
38/// # Safety
39///
40/// * This trait must only be implemented on the `riscv` crate or on a PAC of a RISC-V target.
41/// * This trait must only be implemented on enums of interrupts.
42/// * Each enum variant must represent a distinct value (no duplicates are permitted),
43/// * Each enum variant must always return the same value (do not change at runtime).
44/// * All the interrupt numbers must be less than or equal to `MAX_INTERRUPT_NUMBER`.
45/// * `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number.
46pub unsafe trait InterruptNumber: Copy {
47 /// Highest number assigned to an interrupt source.
48 const MAX_INTERRUPT_NUMBER: usize;
49
50 /// Converts an interrupt source to its corresponding number.
51 fn number(self) -> usize;
52
53 /// Tries to convert a number to a valid interrupt.
54 fn from_number(value: usize) -> Result<Self>;
55}
56
57/// Marker trait for enums of target-specific core interrupt numbers.
58///
59/// Core interrupts are interrupts are retrieved from the `mcause` CSR. Usually, vectored mode is
60/// only available for core interrupts. The `riscv` crate provides a default implementation for
61/// the RISC-V ISA. However, a PAC may override the default implementation if the target has a
62/// different interrupt numbering scheme (e.g., ESP32C3).
63///
64/// # Safety
65///
66/// Each enum variant must represent a valid core interrupt number read from the `mcause` CSR.
67pub unsafe trait CoreInterruptNumber: InterruptNumber {}
68
69/// Marker trait for enums of target-specific external interrupt numbers.
70///
71/// External interrupts are interrupts caused by external sources (e.g., GPIO, UART, SPI).
72/// External interrupts are **not** retrieved from the `mcause` CSR.
73/// Instead, RISC-V processors have a single core interrupt for all external interrupts.
74/// An additional peripheral (e.g., PLIC) is used to multiplex the external interrupts.
75///
76/// # Safety
77///
78/// Each enum variant must represent a valid external interrupt number.
79pub unsafe trait ExternalInterruptNumber: InterruptNumber {}
80
81/// Trait for enums of priority levels.
82///
83/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
84/// priority numbers for a specific device. Each variant must convert to a `usize` of its priority level.
85///
86/// # Safety
87///
88/// * This trait must only be implemented on a PAC of a RISC-V target.
89/// * This trait must only be implemented on enums of priority levels.
90/// * Each enum variant must represent a distinct value (no duplicates are permitted).
91/// * Each enum variant must always return the same value (do not change at runtime).
92/// * All the priority level numbers must be less than or equal to `MAX_PRIORITY_NUMBER`.
93/// * `MAX_PRIORITY_NUMBER` must coincide with the highest allowed priority number.
94pub unsafe trait PriorityNumber: Copy {
95 /// Number assigned to the highest priority level.
96 const MAX_PRIORITY_NUMBER: usize;
97
98 /// Converts a priority level to its corresponding number.
99 fn number(self) -> usize;
100
101 /// Tries to convert a number to a valid priority level.
102 fn from_number(value: usize) -> Result<Self>;
103}
104
105/// Trait for enums of HART identifiers.
106///
107/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
108/// HARTs for a specific device. Each variant must convert to a `usize` of its HART ID number.
109///
110/// # Safety
111///
112/// * This trait must only be implemented on a PAC of a RISC-V target.
113/// * This trait must only be implemented on enums of HART IDs.
114/// * Each enum variant must represent a distinct value (no duplicates are permitted),
115/// * Each anum variant must always return the same value (do not change at runtime).
116/// * All the HART ID numbers must be less than or equal to `MAX_HART_ID_NUMBER`.
117/// * `MAX_HART_ID_NUMBER` must coincide with the highest allowed HART ID number.
118pub unsafe trait HartIdNumber: Copy {
119 /// Highest number assigned to a context.
120 const MAX_HART_ID_NUMBER: usize;
121
122 /// Converts a HART ID to its corresponding number.
123 fn number(self) -> usize;
124
125 /// Tries to convert a number to a valid HART ID.
126 fn from_number(value: usize) -> Result<Self>;
127}
128
129#[cfg(test)]
130mod test {
131 use super::*;
132 use crate::result::Error;
133
134 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
135 enum Exception {
136 E1 = 1,
137 E3 = 3,
138 }
139
140 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
141 enum Interrupt {
142 I1 = 1,
143 I2 = 2,
144 I4 = 4,
145 }
146
147 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
148 enum Priority {
149 P0 = 0,
150 P1 = 1,
151 P2 = 2,
152 P3 = 3,
153 }
154
155 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
156 enum HartId {
157 H0 = 0,
158 H1 = 1,
159 H2 = 2,
160 }
161
162 unsafe impl ExceptionNumber for Exception {
163 const MAX_EXCEPTION_NUMBER: usize = Self::E3 as usize;
164
165 #[inline]
166 fn number(self) -> usize {
167 self as _
168 }
169
170 #[inline]
171 fn from_number(number: usize) -> Result<Self> {
172 match number {
173 1 => Ok(Exception::E1),
174 3 => Ok(Exception::E3),
175 _ => Err(Error::InvalidVariant(number)),
176 }
177 }
178 }
179
180 unsafe impl InterruptNumber for Interrupt {
181 const MAX_INTERRUPT_NUMBER: usize = Self::I4 as usize;
182
183 #[inline]
184 fn number(self) -> usize {
185 self as _
186 }
187
188 #[inline]
189 fn from_number(number: usize) -> Result<Self> {
190 match number {
191 1 => Ok(Interrupt::I1),
192 2 => Ok(Interrupt::I2),
193 4 => Ok(Interrupt::I4),
194 _ => Err(Error::InvalidVariant(number)),
195 }
196 }
197 }
198
199 unsafe impl PriorityNumber for Priority {
200 const MAX_PRIORITY_NUMBER: usize = Self::P3 as usize;
201
202 #[inline]
203 fn number(self) -> usize {
204 self as _
205 }
206
207 #[inline]
208 fn from_number(number: usize) -> Result<Self> {
209 match number {
210 0 => Ok(Priority::P0),
211 1 => Ok(Priority::P1),
212 2 => Ok(Priority::P2),
213 3 => Ok(Priority::P3),
214 _ => Err(Error::InvalidVariant(number)),
215 }
216 }
217 }
218
219 unsafe impl HartIdNumber for HartId {
220 const MAX_HART_ID_NUMBER: usize = Self::H2 as usize;
221
222 #[inline]
223 fn number(self) -> usize {
224 self as _
225 }
226
227 #[inline]
228 fn from_number(number: usize) -> Result<Self> {
229 match number {
230 0 => Ok(HartId::H0),
231 1 => Ok(HartId::H1),
232 2 => Ok(HartId::H2),
233 _ => Err(Error::InvalidVariant(number)),
234 }
235 }
236 }
237
238 #[test]
239 fn check_exception_enum() {
240 assert_eq!(Exception::E1.number(), 1);
241 assert_eq!(Exception::E3.number(), 3);
242
243 assert_eq!(Exception::from_number(0), Err(Error::InvalidVariant(0)));
244 assert_eq!(Exception::from_number(1), Ok(Exception::E1));
245 assert_eq!(Exception::from_number(2), Err(Error::InvalidVariant(2)));
246 assert_eq!(Exception::from_number(3), Ok(Exception::E3));
247 assert_eq!(Exception::from_number(4), Err(Error::InvalidVariant(4)));
248 }
249
250 #[test]
251 fn check_interrupt_enum() {
252 assert_eq!(Interrupt::I1.number(), 1);
253 assert_eq!(Interrupt::I2.number(), 2);
254 assert_eq!(Interrupt::I4.number(), 4);
255
256 assert_eq!(Interrupt::from_number(0), Err(Error::InvalidVariant(0)));
257 assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
258 assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
259 assert_eq!(Interrupt::from_number(3), Err(Error::InvalidVariant(3)));
260 assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
261 assert_eq!(Interrupt::from_number(5), Err(Error::InvalidVariant(5)));
262 }
263
264 #[test]
265 fn check_priority_enum() {
266 assert_eq!(Priority::P0.number(), 0);
267 assert_eq!(Priority::P1.number(), 1);
268 assert_eq!(Priority::P2.number(), 2);
269 assert_eq!(Priority::P3.number(), 3);
270
271 assert_eq!(Priority::from_number(0), Ok(Priority::P0));
272 assert_eq!(Priority::from_number(1), Ok(Priority::P1));
273 assert_eq!(Priority::from_number(2), Ok(Priority::P2));
274 assert_eq!(Priority::from_number(3), Ok(Priority::P3));
275 assert_eq!(Priority::from_number(4), Err(Error::InvalidVariant(4)));
276 }
277
278 #[test]
279 fn check_hart_id_enum() {
280 assert_eq!(HartId::H0.number(), 0);
281 assert_eq!(HartId::H1.number(), 1);
282 assert_eq!(HartId::H2.number(), 2);
283
284 assert_eq!(HartId::from_number(0), Ok(HartId::H0));
285 assert_eq!(HartId::from_number(1), Ok(HartId::H1));
286 assert_eq!(HartId::from_number(2), Ok(HartId::H2));
287 assert_eq!(HartId::from_number(3), Err(Error::InvalidVariant(3)));
288 }
289}