Skip to content

Commit

Permalink
Macros work
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Feb 22, 2024
1 parent f5808cc commit 3608f39
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 105 deletions.
3 changes: 3 additions & 0 deletions riscv-pac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ targets = [
"riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf",
"riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf",
]

[dependencies]
riscv-pac-macros = { path = "macros", version = "0.1.0" }
20 changes: 20 additions & 0 deletions riscv-pac/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
authors = [
"The RISC-V Team <[email protected]>",
]
categories = ["embedded", "no-std"]
description = "Derive macros re-exported in `riscv-pac`"
documentation = "https://docs.rs/riscv-rt"
keywords = ["riscv", "register", "peripheral"]
license = "MIT OR Apache-2.0"
name = "riscv-pac-macros"
repository = "https://github.com/rust-embedded/riscv"
version = "0.1.0"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", default-features = false, features = ["derive", "parsing", "proc-macro"]}
159 changes: 159 additions & 0 deletions riscv-pac/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::{collections::HashMap, convert::TryFrom, ops::Range, str::FromStr};
use syn::{parse_macro_input, Data, DeriveInput, Error, Ident};

struct PacNumberEnum {
name: Ident,
valid_ranges: Vec<Range<usize>>,
}

impl PacNumberEnum {
fn valid_condition(&self) -> TokenStream2 {
let mut arms = Vec::new();
for range in &self.valid_ranges {
let (start, end) = (range.start, range.end);
if end - start == 1 {
arms.push(TokenStream2::from_str(&format!("number == {start}")).unwrap());
} else {
arms.push(
TokenStream2::from_str(&format!("({start}..{end}).contains(&number)")).unwrap(),
);
}
}
quote! { #(#arms) || * }
}

fn max_discriminant(&self) -> TokenStream2 {
let max_discriminant = self.valid_ranges.last().expect("invalid range").end - 1;
TokenStream2::from_str(&format!("{max_discriminant}")).unwrap()
}

fn quote(&self, trait_name: &str, num_type: &str, const_name: &str) -> TokenStream2 {
let name = &self.name;
let max_discriminant = self.max_discriminant();
let valid_condition = self.valid_condition();

let trait_name = TokenStream2::from_str(trait_name).unwrap();
let num_type = TokenStream2::from_str(num_type).unwrap();
let const_name = TokenStream2::from_str(const_name).unwrap();

quote! {
unsafe impl #trait_name for #name {
const #const_name: #num_type = #max_discriminant;

#[inline]
fn number(self) -> #num_type {
self as _
}

#[inline]
fn from_number(number: #num_type) -> Result<Self, #num_type> {
if #valid_condition {
// SAFETY: The number is valid for this enum
Ok(unsafe { core::mem::transmute(number) })
} else {
Err(number)
}
}
}
}
}
}

impl TryFrom<DeriveInput> for PacNumberEnum {
type Error = Error;

fn try_from(input: DeriveInput) -> Result<Self, Self::Error> {
let variants = match &input.data {
Data::Enum(data) => &data.variants,
_ => panic!("Input is not an enum"),
};

// Collect the variants and their associated number discriminants
let mut var_map = HashMap::new();
let mut numbers = Vec::new();
for variant in variants {
let ident = &variant.ident;
let value = match &variant.discriminant {
Some(d) => match &d.1 {
syn::Expr::Lit(expr_lit) => match &expr_lit.lit {
syn::Lit::Int(lit_int) => match lit_int.base10_parse::<usize>() {
Ok(num) => num,
Err(_) => panic!("All variant discriminants must be unsigned integers"),
},
_ => panic!("All variant discriminants must be unsigned integers"),
},
_ => panic!("All variant discriminants must be unsigned integers"),
},
_ => panic!("Variant must have a discriminant"),
};
// check for duplicate discriminant values
var_map.insert(value, ident);
numbers.push(value);
}

// sort the number discriminants and generate a list of valid ranges
numbers.sort_unstable();
let mut valid_ranges = Vec::new();
let mut start = numbers[0];
let mut end = start;
for &number in &numbers[1..] {
if number == end + 1 {
end = number;
} else {
valid_ranges.push(start..end + 1);
start = number;
end = start;
}
}
valid_ranges.push(start..end + 1);

Ok(PacNumberEnum {
name: input.ident.clone(),
valid_ranges,
})
}
}

#[proc_macro_derive(ExceptionNumber)]
pub fn exception_number_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let pac_enum = PacNumberEnum::try_from(input).unwrap();
pac_enum
.quote("ExceptionNumber", "u16", "MAX_EXCEPTION_NUMBER")
.into()
}

#[proc_macro_derive(InterruptNumber)]
pub fn interrupt_number_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let pac_enum = PacNumberEnum::try_from(input).unwrap();
pac_enum
.quote("InterruptNumber", "u16", "MAX_INTERRUPT_NUMBER")
.into()
}

#[proc_macro_derive(PriorityNumber)]
pub fn priority_number_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let pac_enum = PacNumberEnum::try_from(input).unwrap();
pac_enum
.quote("PriorityNumber", "u8", "MAX_PRIORITY_NUMBER")
.into()
}

#[proc_macro_derive(HartIdNumber)]
pub fn hart_id_number_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let pac_enum = PacNumberEnum::try_from(input).unwrap();
pac_enum
.quote("HartIdNumber", "u16", "MAX_HART_ID_NUMBER")
.into()
}
128 changes: 23 additions & 105 deletions riscv-pac/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![no_std]

pub use riscv_pac_macros::*;

/// Trait for enums of target-specific exception numbers.
///
/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available
Expand Down Expand Up @@ -133,23 +135,22 @@ pub unsafe trait HartIdNumber: Copy {
mod test {
use super::*;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, ExceptionNumber)]
#[repr(u16)]
enum Exception {
E1 = 1,
E3 = 3,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, InterruptNumber)]
#[repr(u16)]
enum Interrupt {
I1 = 1,
I2 = 2,
I3 = 3,
I4 = 4,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, PriorityNumber)]
#[repr(u8)]
enum Priority {
P0 = 0,
Expand All @@ -158,118 +159,37 @@ mod test {
P3 = 3,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, HartIdNumber)]
#[repr(u16)]
enum Context {
C0 = 0,
C1 = 1,
C2 = 2,
}

unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: u16 = Self::E3 as u16;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number > Self::MAX_EXCEPTION_NUMBER || number == 0 {
Err(number)
} else if number == 1 || number == 3 {
// SAFETY: valid exception number
Ok(unsafe { core::mem::transmute(number) })
} else {
Err(number)
}
}
}

unsafe impl InterruptNumber for Interrupt {
const MAX_INTERRUPT_NUMBER: u16 = Self::I4 as u16;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number > Self::MAX_INTERRUPT_NUMBER || number == 0 {
Err(number)
} else {
// SAFETY: valid interrupt number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

unsafe impl PriorityNumber for Priority {
const MAX_PRIORITY_NUMBER: u8 = Self::P3 as u8;

#[inline]
fn number(self) -> u8 {
self as _
}

#[inline]
fn from_number(number: u8) -> Result<Self, u8> {
if number > Self::MAX_PRIORITY_NUMBER {
Err(number)
} else {
// SAFETY: valid priority number
Ok(unsafe { core::mem::transmute(number) })
}
}
}

unsafe impl HartIdNumber for Context {
const MAX_HART_ID_NUMBER: u16 = Self::C2 as u16;

#[inline]
fn number(self) -> u16 {
self as _
}

#[inline]
fn from_number(number: u16) -> Result<Self, u16> {
if number > Self::MAX_HART_ID_NUMBER {
Err(number)
} else {
// SAFETY: valid context number
Ok(unsafe { core::mem::transmute(number) })
}
}
enum HartId {
H0 = 0,
H1 = 1,
H2 = 2,
}

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

assert_eq!(Exception::from_number(1), Ok(Exception::E1));
assert_eq!(Exception::from_number(3), Ok(Exception::E3));

assert_eq!(Exception::from_number(0), Err(0));
assert_eq!(Exception::from_number(1), Ok(Exception::E1));
assert_eq!(Exception::from_number(2), Err(2));
assert_eq!(Exception::from_number(3), Ok(Exception::E3));
assert_eq!(Exception::from_number(4), Err(4));
}

#[test]
fn check_interrupt_enum() {
assert_eq!(Interrupt::I1.number(), 1);
assert_eq!(Interrupt::I2.number(), 2);
assert_eq!(Interrupt::I3.number(), 3);
assert_eq!(Interrupt::I4.number(), 4);

assert_eq!(Interrupt::from_number(0), Err(0));
assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
assert_eq!(Interrupt::from_number(3), Err(3));
assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));

assert_eq!(Interrupt::from_number(0), Err(0));
assert_eq!(Interrupt::from_number(5), Err(5));
}

Expand All @@ -284,20 +204,18 @@ mod test {
assert_eq!(Priority::from_number(1), Ok(Priority::P1));
assert_eq!(Priority::from_number(2), Ok(Priority::P2));
assert_eq!(Priority::from_number(3), Ok(Priority::P3));

assert_eq!(Priority::from_number(4), Err(4));
}

#[test]
fn check_context_enum() {
assert_eq!(Context::C0.number(), 0);
assert_eq!(Context::C1.number(), 1);
assert_eq!(Context::C2.number(), 2);

assert_eq!(Context::from_number(0), Ok(Context::C0));
assert_eq!(Context::from_number(1), Ok(Context::C1));
assert_eq!(Context::from_number(2), Ok(Context::C2));

assert_eq!(Context::from_number(3), Err(3));
fn check_hart_id_enum() {
assert_eq!(HartId::H0.number(), 0);
assert_eq!(HartId::H1.number(), 1);
assert_eq!(HartId::H2.number(), 2);

assert_eq!(HartId::from_number(0), Ok(HartId::H0));
assert_eq!(HartId::from_number(1), Ok(HartId::H1));
assert_eq!(HartId::from_number(2), Ok(HartId::H2));
assert_eq!(HartId::from_number(3), Err(3));
}
}

0 comments on commit 3608f39

Please sign in to comment.