Skip to content

Commit 7da7f63

Browse files
committed
Macros work
1 parent d28dd77 commit 7da7f63

File tree

4 files changed

+205
-105
lines changed

4 files changed

+205
-105
lines changed

riscv-pac/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ targets = [
1717
"riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf",
1818
"riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf",
1919
]
20+
21+
[dependencies]
22+
riscv-pac-macros = { path = "macros", version = "0.1.0" }

riscv-pac/macros/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
authors = [
3+
"The RISC-V Team <risc-v@teams.rust-embedded.org>",
4+
]
5+
categories = ["embedded", "no-std"]
6+
description = "Derive macros re-exported in `riscv-pac`"
7+
documentation = "https://docs.rs/riscv-rt"
8+
keywords = ["riscv", "register", "peripheral"]
9+
license = "MIT OR Apache-2.0"
10+
name = "riscv-pac-macros"
11+
repository = "https://github.com/rust-embedded/riscv"
12+
version = "0.1.0"
13+
14+
[lib]
15+
proc-macro = true
16+
17+
[dependencies]
18+
proc-macro2 = "1.0"
19+
quote = "1.0"
20+
syn = { version = "2.0", default-features = false, features = ["derive", "parsing", "proc-macro"]}

riscv-pac/macros/src/lib.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
extern crate proc_macro;
2+
extern crate proc_macro2;
3+
extern crate quote;
4+
extern crate syn;
5+
6+
use proc_macro::TokenStream;
7+
use proc_macro2::TokenStream as TokenStream2;
8+
use quote::quote;
9+
use std::{collections::HashMap, convert::TryFrom, ops::Range, str::FromStr};
10+
use syn::{parse_macro_input, Data, DeriveInput, Error, Ident};
11+
12+
struct PacNumberEnum {
13+
name: Ident,
14+
valid_ranges: Vec<Range<usize>>,
15+
}
16+
17+
impl PacNumberEnum {
18+
fn valid_condition(&self) -> TokenStream2 {
19+
let mut arms = Vec::new();
20+
for range in &self.valid_ranges {
21+
let (start, end) = (range.start, range.end);
22+
if end - start == 1 {
23+
arms.push(TokenStream2::from_str(&format!("number == {start}")).unwrap());
24+
} else {
25+
arms.push(
26+
TokenStream2::from_str(&format!("({start}..{end}).contains(&number)")).unwrap(),
27+
);
28+
}
29+
}
30+
quote! { #(#arms) || * }
31+
}
32+
33+
fn max_discriminant(&self) -> TokenStream2 {
34+
let max_discriminant = self.valid_ranges.last().expect("invalid range").end - 1;
35+
TokenStream2::from_str(&format!("{max_discriminant}")).unwrap()
36+
}
37+
38+
fn quote(&self, trait_name: &str, num_type: &str, const_name: &str) -> TokenStream2 {
39+
let name = &self.name;
40+
let max_discriminant = self.max_discriminant();
41+
let valid_condition = self.valid_condition();
42+
43+
let trait_name = TokenStream2::from_str(trait_name).unwrap();
44+
let num_type = TokenStream2::from_str(num_type).unwrap();
45+
let const_name = TokenStream2::from_str(const_name).unwrap();
46+
47+
quote! {
48+
unsafe impl #trait_name for #name {
49+
const #const_name: #num_type = #max_discriminant;
50+
51+
#[inline]
52+
fn number(self) -> #num_type {
53+
self as _
54+
}
55+
56+
#[inline]
57+
fn from_number(number: #num_type) -> Result<Self, #num_type> {
58+
if #valid_condition {
59+
// SAFETY: The number is valid for this enum
60+
Ok(unsafe { core::mem::transmute(number) })
61+
} else {
62+
Err(number)
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
impl TryFrom<DeriveInput> for PacNumberEnum {
71+
type Error = Error;
72+
73+
fn try_from(input: DeriveInput) -> Result<Self, Self::Error> {
74+
let variants = match &input.data {
75+
Data::Enum(data) => &data.variants,
76+
_ => panic!("Input is not an enum"),
77+
};
78+
79+
// Collect the variants and their associated number discriminants
80+
let mut var_map = HashMap::new();
81+
let mut numbers = Vec::new();
82+
for variant in variants {
83+
let ident = &variant.ident;
84+
let value = match &variant.discriminant {
85+
Some(d) => match &d.1 {
86+
syn::Expr::Lit(expr_lit) => match &expr_lit.lit {
87+
syn::Lit::Int(lit_int) => match lit_int.base10_parse::<usize>() {
88+
Ok(num) => num,
89+
Err(_) => panic!("All variant discriminants must be unsigned integers"),
90+
},
91+
_ => panic!("All variant discriminants must be unsigned integers"),
92+
},
93+
_ => panic!("All variant discriminants must be unsigned integers"),
94+
},
95+
_ => panic!("Variant must have a discriminant"),
96+
};
97+
// check for duplicate discriminant values
98+
var_map.insert(value, ident);
99+
numbers.push(value);
100+
}
101+
102+
// sort the number discriminants and generate a list of valid ranges
103+
numbers.sort_unstable();
104+
let mut valid_ranges = Vec::new();
105+
let mut start = numbers[0];
106+
let mut end = start;
107+
for &number in &numbers[1..] {
108+
if number == end + 1 {
109+
end = number;
110+
} else {
111+
valid_ranges.push(start..end + 1);
112+
start = number;
113+
end = start;
114+
}
115+
}
116+
valid_ranges.push(start..end + 1);
117+
118+
Ok(PacNumberEnum {
119+
name: input.ident.clone(),
120+
valid_ranges,
121+
})
122+
}
123+
}
124+
125+
#[proc_macro_derive(ExceptionNumber)]
126+
pub fn exception_number_derive(input: TokenStream) -> TokenStream {
127+
let input = parse_macro_input!(input as DeriveInput);
128+
let pac_enum = PacNumberEnum::try_from(input).unwrap();
129+
pac_enum
130+
.quote("ExceptionNumber", "u16", "MAX_EXCEPTION_NUMBER")
131+
.into()
132+
}
133+
134+
#[proc_macro_derive(InterruptNumber)]
135+
pub fn interrupt_number_derive(input: TokenStream) -> TokenStream {
136+
let input = parse_macro_input!(input as DeriveInput);
137+
let pac_enum = PacNumberEnum::try_from(input).unwrap();
138+
pac_enum
139+
.quote("InterruptNumber", "u16", "MAX_INTERRUPT_NUMBER")
140+
.into()
141+
}
142+
143+
#[proc_macro_derive(PriorityNumber)]
144+
pub fn priority_number_derive(input: TokenStream) -> TokenStream {
145+
let input = parse_macro_input!(input as DeriveInput);
146+
let pac_enum = PacNumberEnum::try_from(input).unwrap();
147+
pac_enum
148+
.quote("PriorityNumber", "u8", "MAX_PRIORITY_NUMBER")
149+
.into()
150+
}
151+
152+
#[proc_macro_derive(HartIdNumber)]
153+
pub fn hart_id_number_derive(input: TokenStream) -> TokenStream {
154+
let input = parse_macro_input!(input as DeriveInput);
155+
let pac_enum = PacNumberEnum::try_from(input).unwrap();
156+
pac_enum
157+
.quote("HartIdNumber", "u16", "MAX_HART_ID_NUMBER")
158+
.into()
159+
}

riscv-pac/src/lib.rs

Lines changed: 23 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![no_std]
22

3+
pub use riscv_pac_macros::*;
4+
35
/// Trait for enums of target-specific exception numbers.
46
///
57
/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
@@ -133,23 +135,22 @@ pub unsafe trait HartIdNumber: Copy {
133135
mod test {
134136
use super::*;
135137

136-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
138+
#[derive(Clone, Copy, Debug, Eq, PartialEq, ExceptionNumber)]
137139
#[repr(u16)]
138140
enum Exception {
139141
E1 = 1,
140142
E3 = 3,
141143
}
142144

143-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145+
#[derive(Clone, Copy, Debug, Eq, PartialEq, InterruptNumber)]
144146
#[repr(u16)]
145147
enum Interrupt {
146148
I1 = 1,
147149
I2 = 2,
148-
I3 = 3,
149150
I4 = 4,
150151
}
151152

152-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
153+
#[derive(Clone, Copy, Debug, Eq, PartialEq, PriorityNumber)]
153154
#[repr(u8)]
154155
enum Priority {
155156
P0 = 0,
@@ -158,118 +159,37 @@ mod test {
158159
P3 = 3,
159160
}
160161

161-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
162+
#[derive(Clone, Copy, Debug, Eq, PartialEq, HartIdNumber)]
162163
#[repr(u16)]
163-
enum Context {
164-
C0 = 0,
165-
C1 = 1,
166-
C2 = 2,
167-
}
168-
169-
unsafe impl ExceptionNumber for Exception {
170-
const MAX_EXCEPTION_NUMBER: u16 = Self::E3 as u16;
171-
172-
#[inline]
173-
fn number(self) -> u16 {
174-
self as _
175-
}
176-
177-
#[inline]
178-
fn from_number(number: u16) -> Result<Self, u16> {
179-
if number > Self::MAX_EXCEPTION_NUMBER || number == 0 {
180-
Err(number)
181-
} else if number == 1 || number == 3 {
182-
// SAFETY: valid exception number
183-
Ok(unsafe { core::mem::transmute(number) })
184-
} else {
185-
Err(number)
186-
}
187-
}
188-
}
189-
190-
unsafe impl InterruptNumber for Interrupt {
191-
const MAX_INTERRUPT_NUMBER: u16 = Self::I4 as u16;
192-
193-
#[inline]
194-
fn number(self) -> u16 {
195-
self as _
196-
}
197-
198-
#[inline]
199-
fn from_number(number: u16) -> Result<Self, u16> {
200-
if number > Self::MAX_INTERRUPT_NUMBER || number == 0 {
201-
Err(number)
202-
} else {
203-
// SAFETY: valid interrupt number
204-
Ok(unsafe { core::mem::transmute(number) })
205-
}
206-
}
207-
}
208-
209-
unsafe impl PriorityNumber for Priority {
210-
const MAX_PRIORITY_NUMBER: u8 = Self::P3 as u8;
211-
212-
#[inline]
213-
fn number(self) -> u8 {
214-
self as _
215-
}
216-
217-
#[inline]
218-
fn from_number(number: u8) -> Result<Self, u8> {
219-
if number > Self::MAX_PRIORITY_NUMBER {
220-
Err(number)
221-
} else {
222-
// SAFETY: valid priority number
223-
Ok(unsafe { core::mem::transmute(number) })
224-
}
225-
}
226-
}
227-
228-
unsafe impl HartIdNumber for Context {
229-
const MAX_HART_ID_NUMBER: u16 = Self::C2 as u16;
230-
231-
#[inline]
232-
fn number(self) -> u16 {
233-
self as _
234-
}
235-
236-
#[inline]
237-
fn from_number(number: u16) -> Result<Self, u16> {
238-
if number > Self::MAX_HART_ID_NUMBER {
239-
Err(number)
240-
} else {
241-
// SAFETY: valid context number
242-
Ok(unsafe { core::mem::transmute(number) })
243-
}
244-
}
164+
enum HartId {
165+
H0 = 0,
166+
H1 = 1,
167+
H2 = 2,
245168
}
246169

247170
#[test]
248171
fn check_exception_enum() {
249172
assert_eq!(Exception::E1.number(), 1);
250173
assert_eq!(Exception::E3.number(), 3);
251174

252-
assert_eq!(Exception::from_number(1), Ok(Exception::E1));
253-
assert_eq!(Exception::from_number(3), Ok(Exception::E3));
254-
255175
assert_eq!(Exception::from_number(0), Err(0));
176+
assert_eq!(Exception::from_number(1), Ok(Exception::E1));
256177
assert_eq!(Exception::from_number(2), Err(2));
178+
assert_eq!(Exception::from_number(3), Ok(Exception::E3));
257179
assert_eq!(Exception::from_number(4), Err(4));
258180
}
259181

260182
#[test]
261183
fn check_interrupt_enum() {
262184
assert_eq!(Interrupt::I1.number(), 1);
263185
assert_eq!(Interrupt::I2.number(), 2);
264-
assert_eq!(Interrupt::I3.number(), 3);
265186
assert_eq!(Interrupt::I4.number(), 4);
266187

188+
assert_eq!(Interrupt::from_number(0), Err(0));
267189
assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
268190
assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
269-
assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
191+
assert_eq!(Interrupt::from_number(3), Err(3));
270192
assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
271-
272-
assert_eq!(Interrupt::from_number(0), Err(0));
273193
assert_eq!(Interrupt::from_number(5), Err(5));
274194
}
275195

@@ -284,20 +204,18 @@ mod test {
284204
assert_eq!(Priority::from_number(1), Ok(Priority::P1));
285205
assert_eq!(Priority::from_number(2), Ok(Priority::P2));
286206
assert_eq!(Priority::from_number(3), Ok(Priority::P3));
287-
288207
assert_eq!(Priority::from_number(4), Err(4));
289208
}
290209

291210
#[test]
292-
fn check_context_enum() {
293-
assert_eq!(Context::C0.number(), 0);
294-
assert_eq!(Context::C1.number(), 1);
295-
assert_eq!(Context::C2.number(), 2);
296-
297-
assert_eq!(Context::from_number(0), Ok(Context::C0));
298-
assert_eq!(Context::from_number(1), Ok(Context::C1));
299-
assert_eq!(Context::from_number(2), Ok(Context::C2));
300-
301-
assert_eq!(Context::from_number(3), Err(3));
211+
fn check_hart_id_enum() {
212+
assert_eq!(HartId::H0.number(), 0);
213+
assert_eq!(HartId::H1.number(), 1);
214+
assert_eq!(HartId::H2.number(), 2);
215+
216+
assert_eq!(HartId::from_number(0), Ok(HartId::H0));
217+
assert_eq!(HartId::from_number(1), Ok(HartId::H1));
218+
assert_eq!(HartId::from_number(2), Ok(HartId::H2));
219+
assert_eq!(HartId::from_number(3), Err(3));
302220
}
303221
}

0 commit comments

Comments
 (0)