Skip to content

Commit 4056e2f

Browse files
committed
Macros work
1 parent dd7e35a commit 4056e2f

File tree

4 files changed

+201
-23
lines changed

4 files changed

+201
-23
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: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pub mod result;
44

55
use result::Result;
66

7+
pub use riscv_pac_macros::*;
8+
79
/// Trait for enums of target-specific exception numbers.
810
///
911
/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
@@ -137,23 +139,22 @@ pub unsafe trait HartIdNumber: Copy {
137139
mod test {
138140
use super::*;
139141

140-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142+
#[derive(Clone, Copy, Debug, Eq, PartialEq, ExceptionNumber)]
141143
#[repr(u16)]
142144
enum Exception {
143145
E1 = 1,
144146
E3 = 3,
145147
}
146148

147-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149+
#[derive(Clone, Copy, Debug, Eq, PartialEq, InterruptNumber)]
148150
#[repr(u16)]
149151
enum Interrupt {
150152
I1 = 1,
151153
I2 = 2,
152-
I3 = 3,
153154
I4 = 4,
154155
}
155156

156-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157+
#[derive(Clone, Copy, Debug, Eq, PartialEq, PriorityNumber)]
157158
#[repr(u8)]
158159
enum Priority {
159160
P0 = 0,
@@ -162,7 +163,7 @@ mod test {
162163
P3 = 3,
163164
}
164165

165-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
166+
#[derive(Clone, Copy, Debug, Eq, PartialEq, HartIdNumber)]
166167
#[repr(u16)]
167168
enum Context {
168169
C0 = 0,
@@ -253,27 +254,24 @@ mod test {
253254
assert_eq!(Exception::E1.number(), 1);
254255
assert_eq!(Exception::E3.number(), 3);
255256

256-
assert_eq!(Exception::from_number(1), Ok(Exception::E1));
257-
assert_eq!(Exception::from_number(3), Ok(Exception::E3));
258-
259257
assert_eq!(Exception::from_number(0), Err(0));
258+
assert_eq!(Exception::from_number(1), Ok(Exception::E1));
260259
assert_eq!(Exception::from_number(2), Err(2));
260+
assert_eq!(Exception::from_number(3), Ok(Exception::E3));
261261
assert_eq!(Exception::from_number(4), Err(4));
262262
}
263263

264264
#[test]
265265
fn check_interrupt_enum() {
266266
assert_eq!(Interrupt::I1.number(), 1);
267267
assert_eq!(Interrupt::I2.number(), 2);
268-
assert_eq!(Interrupt::I3.number(), 3);
269268
assert_eq!(Interrupt::I4.number(), 4);
270269

270+
assert_eq!(Interrupt::from_number(0), Err(0));
271271
assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
272272
assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
273-
assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
273+
assert_eq!(Interrupt::from_number(3), Err(3));
274274
assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
275-
276-
assert_eq!(Interrupt::from_number(0), Err(0));
277275
assert_eq!(Interrupt::from_number(5), Err(5));
278276
}
279277

@@ -288,20 +286,18 @@ mod test {
288286
assert_eq!(Priority::from_number(1), Ok(Priority::P1));
289287
assert_eq!(Priority::from_number(2), Ok(Priority::P2));
290288
assert_eq!(Priority::from_number(3), Ok(Priority::P3));
291-
292289
assert_eq!(Priority::from_number(4), Err(4));
293290
}
294291

295292
#[test]
296-
fn check_context_enum() {
297-
assert_eq!(Context::C0.number(), 0);
298-
assert_eq!(Context::C1.number(), 1);
299-
assert_eq!(Context::C2.number(), 2);
300-
301-
assert_eq!(Context::from_number(0), Ok(Context::C0));
302-
assert_eq!(Context::from_number(1), Ok(Context::C1));
303-
assert_eq!(Context::from_number(2), Ok(Context::C2));
304-
305-
assert_eq!(Context::from_number(3), Err(3));
293+
fn check_hart_id_enum() {
294+
assert_eq!(HartId::H0.number(), 0);
295+
assert_eq!(HartId::H1.number(), 1);
296+
assert_eq!(HartId::H2.number(), 2);
297+
298+
assert_eq!(HartId::from_number(0), Ok(HartId::H0));
299+
assert_eq!(HartId::from_number(1), Ok(HartId::H1));
300+
assert_eq!(HartId::from_number(2), Ok(HartId::H2));
301+
assert_eq!(HartId::from_number(3), Err(3));
306302
}
307303
}

0 commit comments

Comments
 (0)