From 1263126fb90f66de56565facf43b6426d7b3b74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 22 Feb 2024 16:56:32 +0100 Subject: [PATCH] Macro to implement riscv-pac traits --- riscv-pac/Cargo.toml | 8 +- riscv-pac/macros/Cargo.toml | 2 +- riscv-pac/macros/src/lib.rs | 226 +++++++++++------- riscv-pac/src/lib.rs | 115 +-------- riscv-pac/tests/test.rs | 6 + riscv-pac/tests/ui/fail_empty_macro.rs | 9 + riscv-pac/tests/ui/fail_empty_macro.stderr | 7 + .../tests/ui/fail_more_than_one_trait.rs | 9 + .../tests/ui/fail_more_than_one_trait.stderr | 7 + riscv-pac/tests/ui/fail_no_unsafe.rs | 9 + riscv-pac/tests/ui/fail_no_unsafe.stderr | 7 + riscv-pac/tests/ui/fail_unknown_trait.rs | 9 + riscv-pac/tests/ui/fail_unknown_trait.stderr | 7 + riscv-pac/tests/ui/fail_wrong_repr.rs | 9 + riscv-pac/tests/ui/fail_wrong_repr.stderr | 9 + riscv-pac/tests/ui/pass_test.rs | 83 +++++++ riscv-peripheral/Cargo.toml | 3 +- riscv-peripheral/src/plic.rs | 115 ++++----- riscv/Cargo.toml | 2 +- 19 files changed, 389 insertions(+), 253 deletions(-) create mode 100644 riscv-pac/tests/test.rs create mode 100644 riscv-pac/tests/ui/fail_empty_macro.rs create mode 100644 riscv-pac/tests/ui/fail_empty_macro.stderr create mode 100644 riscv-pac/tests/ui/fail_more_than_one_trait.rs create mode 100644 riscv-pac/tests/ui/fail_more_than_one_trait.stderr create mode 100644 riscv-pac/tests/ui/fail_no_unsafe.rs create mode 100644 riscv-pac/tests/ui/fail_no_unsafe.stderr create mode 100644 riscv-pac/tests/ui/fail_unknown_trait.rs create mode 100644 riscv-pac/tests/ui/fail_unknown_trait.stderr create mode 100644 riscv-pac/tests/ui/fail_wrong_repr.rs create mode 100644 riscv-pac/tests/ui/fail_wrong_repr.stderr create mode 100644 riscv-pac/tests/ui/pass_test.rs diff --git a/riscv-pac/Cargo.toml b/riscv-pac/Cargo.toml index 44fdeb37..22d247ef 100644 --- a/riscv-pac/Cargo.toml +++ b/riscv-pac/Cargo.toml @@ -19,4 +19,10 @@ targets = [ ] [dependencies] -riscv-pac-macros = { path = "macros", version = "0.1.0" } +riscv-pac-macros = { path = "macros", version = "0.1.0", optional = true } + +[features] +default = ["riscv-pac-macros"] + +[dev-dependencies] +trybuild = "1.0" diff --git a/riscv-pac/macros/Cargo.toml b/riscv-pac/macros/Cargo.toml index f3228fb6..233a6616 100644 --- a/riscv-pac/macros/Cargo.toml +++ b/riscv-pac/macros/Cargo.toml @@ -17,4 +17,4 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "2.0", default-features = false, features = ["derive", "parsing", "proc-macro"]} +syn = { version = "2.0" } diff --git a/riscv-pac/macros/src/lib.rs b/riscv-pac/macros/src/lib.rs index 524e6ad9..584656e5 100644 --- a/riscv-pac/macros/src/lib.rs +++ b/riscv-pac/macros/src/lib.rs @@ -6,8 +6,8 @@ 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}; +use std::{collections::HashMap, ops::Range, str::FromStr}; +use syn::{parse_macro_input, Data, DeriveInput, Ident}; struct PacNumberEnum { name: Ident, @@ -15,6 +15,57 @@ struct PacNumberEnum { } impl PacNumberEnum { + fn new(input: &DeriveInput) -> Self { + 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::() { + 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); + + Self { + name: input.ident.clone(), + valid_ranges, + } + } + fn valid_condition(&self) -> TokenStream2 { let mut arms = Vec::new(); for range in &self.valid_ranges { @@ -45,7 +96,7 @@ impl PacNumberEnum { let const_name = TokenStream2::from_str(const_name).unwrap(); quote! { - unsafe impl #trait_name for #name { + unsafe impl riscv_pac::#trait_name for #name { const #const_name: #num_type = #max_discriminant; #[inline] @@ -67,93 +118,88 @@ impl PacNumberEnum { } } -impl TryFrom for PacNumberEnum { - type Error = Error; - - fn try_from(input: DeriveInput) -> Result { - 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::() { - 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, - }) +/// Attribute-like macro that implements the traits of the `riscv-pac` crate for a given enum. +/// +/// As these traits are unsafe, the macro must be called with the `unsafe` keyword followed by the trait name. +/// In this way, we warn callers that they must comply with the requirements of the trait. +/// +/// The trait name must be one of `ExceptionNumber`, `InterruptNumber`, `PriorityNumber`, or `HartIdNumber`. +/// Marker traits `CoreInterruptNumber` and `ExternalInterruptNumber` cannot be implemented using this macro. +/// +/// # Note +/// +/// To implement number-to-enum operation, the macro works with ranges of valid discriminant numbers. +/// If the number is within any of the valid ranges, the number is transmuted to the enum variant. +/// In this way, the macro achieves better performance for enums with a large number of consecutive variants. +/// Thus, the enum must comply with the following requirements: +/// +/// - All the enum variants must have a valid discriminant number (i.e., a number that is within the valid range of the enum). +/// - For the `ExceptionNumber`, `InterruptNumber`, and `HartIdNumber` traits, the enum must be annotated as `#[repr(u16)]` +/// - For the `PriorityNumber` trait, the enum must be annotated as `#[repr(u8)]` +/// +/// If the enum does not meet these requirements, you will have to implement the traits manually (e.g., `riscv::mcause::Interrupt`). +/// For enums with a small number of consecutive variants, it might be better to implement the traits manually. +/// +/// # Safety +/// +/// The struct to be implemented must comply with the requirements of the specified trait. +/// +/// # Example +/// +/// ```rust +/// use riscv_pac::*; +/// +/// #[repr(u16)] +/// #[pac_enum(unsafe ExceptionNumber)] +/// #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// enum Exception { +/// E1 = 1, +/// E3 = 3, +/// } +/// +/// fn main() { +/// 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(2), Err(2)); +/// assert_eq!(Exception::from_number(3), Ok(Exception::E3)); +/// +/// assert_eq!(Exception::MAX_EXCEPTION_NUMBER, 3); +/// } +///``` +#[proc_macro_attribute] +pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as DeriveInput); + let pac_enum = PacNumberEnum::new(&input); + + // attr should be unsafe ExceptionNumber, unsafe InterruptNumber, unsafe PriorityNumber, or unsafe HartIdNumber + // assert that attribute starts with the unsafe token. If not, raise a panic error + let attr = attr.to_string(); + // split string into words and check if the first word is "unsafe" + let attrs = attr.split_whitespace().collect::>(); + if attrs.is_empty() { + panic!("Attribute is empty. Expected: 'riscv_pac::pac_enum(unsafe )'"); + } + if attrs.len() > 2 { + panic!( + "Wrong attribute format. Expected: 'riscv_pac::pac_enum(unsafe )'" + ); + } + if attrs[0] != "unsafe" { + panic!("Attribute does not start with 'unsafe'. Expected: 'riscv_pac::pac_enum(unsafe )'"); } -} - -#[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() + let trait_impl = match attrs[1] { + "ExceptionNumber" => pac_enum.quote("ExceptionNumber", "u16", "MAX_EXCEPTION_NUMBER"), + "InterruptNumber" => pac_enum.quote("InterruptNumber", "u16", "MAX_INTERRUPT_NUMBER"), + "PriorityNumber" => pac_enum.quote("PriorityNumber", "u8", "MAX_PRIORITY_NUMBER"), + "HartIdNumber" => pac_enum.quote("HartIdNumber", "u16", "MAX_HART_ID_NUMBER"), + _ => panic!("Unknown trait '{}'. Expected: 'ExceptionNumber', 'InterruptNumber', 'PriorityNumber', or 'HartIdNumber'", attrs[1]), + }; + quote! { + #input + #trait_impl + } + .into() } diff --git a/riscv-pac/src/lib.rs b/riscv-pac/src/lib.rs index 12bc722c..d1951c34 100644 --- a/riscv-pac/src/lib.rs +++ b/riscv-pac/src/lib.rs @@ -1,11 +1,12 @@ #![no_std] +#[cfg(feature = "riscv-pac-macros")] 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 -/// exceptions for a specific device. Alternatively, the [`riscv`] crate provides a default +/// exceptions for a specific device. Alternatively, the `riscv` crate provides a default /// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its exception number. /// /// # Safety @@ -31,7 +32,7 @@ pub unsafe trait ExceptionNumber: Copy { /// Trait for enums of target-specific interrupt numbers. /// /// This trait should be implemented by a peripheral access crate (PAC) on its enum of available -// interrupts for a specific device. Alternatively, the [`riscv`] crate provides a default +/// interrupts for a specific device. Alternatively, the `riscv` crate provides a default /// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its interrupt number. /// /// # Safety @@ -54,12 +55,11 @@ pub unsafe trait InterruptNumber: Copy { fn from_number(value: u16) -> Result; } -/// Trait for enums of target-specific core interrupt numbers. +/// Marker trait for enums of target-specific core interrupt numbers. /// -/// Core interrupts are interrupts are retrieved from the `mcause` CSR. -/// Usually, vectored mode is only available for core interrupts. -/// The `riscv` crate provides a default implementation for the RISC-V ISA. -/// However, a PAC may override the default implementation if the target has a +/// Core interrupts are interrupts are retrieved from the `mcause` CSR. Usually, vectored mode is +/// only available for core interrupts. The `riscv` crate provides a default implementation for +/// the RISC-V ISA. However, a PAC may override the default implementation if the target has a /// different interrupt numbering scheme (e.g., ESP32C3). /// /// # Safety @@ -67,7 +67,7 @@ pub unsafe trait InterruptNumber: Copy { /// Each enum variant must represent a valid core interrupt number read from the `mcause` CSR. pub unsafe trait CoreInterruptNumber: InterruptNumber {} -/// Trait for enums of target-specific external interrupt numbers. +/// Marker trait for enums of target-specific external interrupt numbers. /// /// External interrupts are interrupts caused by external sources (e.g., GPIO, UART, SPI). /// External interrupts are **not** retrieved from the `mcause` CSR. @@ -81,9 +81,8 @@ pub unsafe trait ExternalInterruptNumber: InterruptNumber {} /// Trait for enums of priority levels. /// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available priority numbers for a specific device. -/// Each variant must convert to a `u8` of its priority level. +/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available +/// priority numbers for a specific device. Each variant must convert to a `u8` of its priority level. /// /// # Safety /// @@ -107,9 +106,8 @@ pub unsafe trait PriorityNumber: Copy { /// Trait for enums of HART identifiers. /// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available HARTs for a specific device. -/// Each variant must convert to a `u16` of its HART ID number. +/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available +/// HARTs for a specific device. Each variant must convert to a `u16` of its HART ID number. /// /// # Safety /// @@ -130,92 +128,3 @@ pub unsafe trait HartIdNumber: Copy { /// If the conversion fails, it returns an error with the number back. fn from_number(value: u16) -> Result; } - -#[cfg(test)] -mod test { - use super::*; - - #[derive(Clone, Copy, Debug, Eq, PartialEq, ExceptionNumber)] - #[repr(u16)] - enum Exception { - E1 = 1, - E3 = 3, - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq, InterruptNumber)] - #[repr(u16)] - enum Interrupt { - I1 = 1, - I2 = 2, - I4 = 4, - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq, PriorityNumber)] - #[repr(u8)] - enum Priority { - P0 = 0, - P1 = 1, - P2 = 2, - P3 = 3, - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq, HartIdNumber)] - #[repr(u16)] - 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(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::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), Err(3)); - assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); - assert_eq!(Interrupt::from_number(5), Err(5)); - } - - #[test] - fn check_priority_enum() { - assert_eq!(Priority::P0.number(), 0); - assert_eq!(Priority::P1.number(), 1); - assert_eq!(Priority::P2.number(), 2); - assert_eq!(Priority::P3.number(), 3); - - assert_eq!(Priority::from_number(0), Ok(Priority::P0)); - 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_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)); - } -} diff --git a/riscv-pac/tests/test.rs b/riscv-pac/tests/test.rs new file mode 100644 index 00000000..c74b861b --- /dev/null +++ b/riscv-pac/tests/test.rs @@ -0,0 +1,6 @@ +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/fail_*.rs"); + t.pass("tests/ui/pass_*.rs"); +} diff --git a/riscv-pac/tests/ui/fail_empty_macro.rs b/riscv-pac/tests/ui/fail_empty_macro.rs new file mode 100644 index 00000000..a47a5711 --- /dev/null +++ b/riscv-pac/tests/ui/fail_empty_macro.rs @@ -0,0 +1,9 @@ +#[riscv_pac::pac_enum] +#[derive(Clone, Copy, Debug, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, +} + +fn main() {} diff --git a/riscv-pac/tests/ui/fail_empty_macro.stderr b/riscv-pac/tests/ui/fail_empty_macro.stderr new file mode 100644 index 00000000..37859747 --- /dev/null +++ b/riscv-pac/tests/ui/fail_empty_macro.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/ui/fail_empty_macro.rs:1:1 + | +1 | #[riscv_pac::pac_enum] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Attribute is empty. Expected: 'riscv_pac::pac_enum(unsafe )' diff --git a/riscv-pac/tests/ui/fail_more_than_one_trait.rs b/riscv-pac/tests/ui/fail_more_than_one_trait.rs new file mode 100644 index 00000000..7db5629e --- /dev/null +++ b/riscv-pac/tests/ui/fail_more_than_one_trait.rs @@ -0,0 +1,9 @@ +#[riscv_pac::pac_enum(unsafe InterruptNumber, unsafe PriorityNumber)] +#[derive(Clone, Copy, Debug, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, +} + +fn main() {} diff --git a/riscv-pac/tests/ui/fail_more_than_one_trait.stderr b/riscv-pac/tests/ui/fail_more_than_one_trait.stderr new file mode 100644 index 00000000..881d9f7a --- /dev/null +++ b/riscv-pac/tests/ui/fail_more_than_one_trait.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/ui/fail_more_than_one_trait.rs:1:1 + | +1 | #[riscv_pac::pac_enum(unsafe InterruptNumber, unsafe PriorityNumber)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Wrong attribute format. Expected: 'riscv_pac::pac_enum(unsafe )' diff --git a/riscv-pac/tests/ui/fail_no_unsafe.rs b/riscv-pac/tests/ui/fail_no_unsafe.rs new file mode 100644 index 00000000..865a68fd --- /dev/null +++ b/riscv-pac/tests/ui/fail_no_unsafe.rs @@ -0,0 +1,9 @@ +#[riscv_pac::pac_enum(InterruptNumber)] +#[derive(Clone, Copy, Debug, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, +} + +fn main() {} diff --git a/riscv-pac/tests/ui/fail_no_unsafe.stderr b/riscv-pac/tests/ui/fail_no_unsafe.stderr new file mode 100644 index 00000000..e459a17c --- /dev/null +++ b/riscv-pac/tests/ui/fail_no_unsafe.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/ui/fail_no_unsafe.rs:1:1 + | +1 | #[riscv_pac::pac_enum(InterruptNumber)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Attribute does not start with 'unsafe'. Expected: 'riscv_pac::pac_enum(unsafe )' diff --git a/riscv-pac/tests/ui/fail_unknown_trait.rs b/riscv-pac/tests/ui/fail_unknown_trait.rs new file mode 100644 index 00000000..50192085 --- /dev/null +++ b/riscv-pac/tests/ui/fail_unknown_trait.rs @@ -0,0 +1,9 @@ +#[riscv_pac::pac_enum(unsafe CoreInterruptNumber)] +#[derive(Clone, Copy, Debug, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, +} + +fn main() {} diff --git a/riscv-pac/tests/ui/fail_unknown_trait.stderr b/riscv-pac/tests/ui/fail_unknown_trait.stderr new file mode 100644 index 00000000..5ca1ce48 --- /dev/null +++ b/riscv-pac/tests/ui/fail_unknown_trait.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/ui/fail_unknown_trait.rs:1:1 + | +1 | #[riscv_pac::pac_enum(unsafe CoreInterruptNumber)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Unknown trait 'CoreInterruptNumber'. Expected: 'ExceptionNumber', 'InterruptNumber', 'PriorityNumber', or 'HartIdNumber' diff --git a/riscv-pac/tests/ui/fail_wrong_repr.rs b/riscv-pac/tests/ui/fail_wrong_repr.rs new file mode 100644 index 00000000..82ebdc52 --- /dev/null +++ b/riscv-pac/tests/ui/fail_wrong_repr.rs @@ -0,0 +1,9 @@ +#[riscv_pac::pac_enum(unsafe InterruptNumber)] +#[derive(Clone, Copy, Debug, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, +} + +fn main() {} diff --git a/riscv-pac/tests/ui/fail_wrong_repr.stderr b/riscv-pac/tests/ui/fail_wrong_repr.stderr new file mode 100644 index 00000000..167a47ef --- /dev/null +++ b/riscv-pac/tests/ui/fail_wrong_repr.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui/fail_wrong_repr.rs:1:1 + | +1 | #[riscv_pac::pac_enum(unsafe InterruptNumber)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u16` (16 bits) + = note: target type: `Interrupt` (8 bits) + = note: this error originates in the attribute macro `riscv_pac::pac_enum` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/riscv-pac/tests/ui/pass_test.rs b/riscv-pac/tests/ui/pass_test.rs new file mode 100644 index 00000000..2001a986 --- /dev/null +++ b/riscv-pac/tests/ui/pass_test.rs @@ -0,0 +1,83 @@ +use riscv_pac::*; + +#[repr(u16)] +#[pac_enum(unsafe ExceptionNumber)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Exception { + E1 = 1, + E3 = 3, +} + +#[repr(u16)] +#[pac_enum(unsafe InterruptNumber)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, + I7 = 7, +} + +#[repr(u8)] +#[pac_enum(unsafe PriorityNumber)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Priority { + P0 = 0, + P1 = 1, + P2 = 2, + P3 = 3, +} + +#[repr(u16)] +#[pac_enum(unsafe HartIdNumber)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum HartId { + H0 = 0, + H1 = 1, + H2 = 2, +} + +fn main() { + assert_eq!(Exception::E1.number(), 1); + assert_eq!(Exception::E3.number(), 3); + + 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)); + + assert_eq!(Interrupt::I1.number(), 1); + assert_eq!(Interrupt::I2.number(), 2); + assert_eq!(Interrupt::I4.number(), 4); + assert_eq!(Interrupt::I7.number(), 7); + + 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), Err(3)); + assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); + assert_eq!(Interrupt::from_number(5), Err(5)); + assert_eq!(Interrupt::from_number(6), Err(6)); + assert_eq!(Interrupt::from_number(7), Ok(Interrupt::I7)); + + assert_eq!(Priority::P0.number(), 0); + assert_eq!(Priority::P1.number(), 1); + assert_eq!(Priority::P2.number(), 2); + assert_eq!(Priority::P3.number(), 3); + + assert_eq!(Priority::from_number(0), Ok(Priority::P0)); + 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)); + + 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)); +} diff --git a/riscv-peripheral/Cargo.toml b/riscv-peripheral/Cargo.toml index 4655d9fa..c4461641 100644 --- a/riscv-peripheral/Cargo.toml +++ b/riscv-peripheral/Cargo.toml @@ -17,10 +17,11 @@ license = "ISC" embedded-hal = "1.0.0" embedded-hal-async = { version = "1.0.0", optional = true } riscv = { path = "../riscv", version = "0.11.2" } -riscv-pac = { path = "../riscv-pac", version = "0.1.2" } +riscv-pac = { path = "../riscv-pac", version = "0.1.2", default-features = false } [dev-dependencies] heapless = "0.8.0" +riscv-pac = { path = "../riscv-pac", version = "0.1.2", default-features = true } [features] aclint-hal-async = ["embedded-hal-async"] diff --git a/riscv-peripheral/src/plic.rs b/riscv-peripheral/src/plic.rs index 4f600835..703021db 100644 --- a/riscv-peripheral/src/plic.rs +++ b/riscv-peripheral/src/plic.rs @@ -144,8 +144,9 @@ impl CTX

{ #[cfg(test)] pub(crate) mod test { - use riscv_pac::{ExternalInterruptNumber, HartIdNumber, InterruptNumber, PriorityNumber}; + use riscv_pac::*; + #[pac_enum(unsafe InterruptNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] pub(crate) enum Interrupt { @@ -155,6 +156,7 @@ pub(crate) mod test { I4 = 4, } + #[pac_enum(unsafe PriorityNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] pub(crate) enum Priority { @@ -164,6 +166,7 @@ pub(crate) mod test { P3 = 3, } + #[pac_enum(unsafe HartIdNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] pub(crate) enum Context { @@ -172,64 +175,64 @@ pub(crate) mod test { C2 = 2, } - unsafe impl InterruptNumber for Interrupt { - const MAX_INTERRUPT_NUMBER: u16 = 4; - - #[inline] - fn number(self) -> u16 { - self as _ - } - - #[inline] - fn from_number(number: u16) -> Result { - if number > Self::MAX_INTERRUPT_NUMBER || number == 0 { - Err(number) - } else { - // SAFETY: valid interrupt number - Ok(unsafe { core::mem::transmute(number) }) - } - } - } + // unsafe impl InterruptNumber for Interrupt { + // const MAX_INTERRUPT_NUMBER: u16 = 4; + + // #[inline] + // fn number(self) -> u16 { + // self as _ + // } + + // #[inline] + // fn from_number(number: u16) -> Result { + // if number > Self::MAX_INTERRUPT_NUMBER || number == 0 { + // Err(number) + // } else { + // // SAFETY: valid interrupt number + // Ok(unsafe { core::mem::transmute(number) }) + // } + // } + // } unsafe impl ExternalInterruptNumber for Interrupt {} - unsafe impl PriorityNumber for Priority { - const MAX_PRIORITY_NUMBER: u8 = 3; - - #[inline] - fn number(self) -> u8 { - self as _ - } - - #[inline] - fn from_number(number: u8) -> Result { - 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 = 2; - - #[inline] - fn number(self) -> u16 { - self as _ - } - - #[inline] - fn from_number(number: u16) -> Result { - if number > Self::MAX_HART_ID_NUMBER { - Err(number) - } else { - // SAFETY: valid context number - Ok(unsafe { core::mem::transmute(number) }) - } - } - } + // unsafe impl PriorityNumber for Priority { + // const MAX_PRIORITY_NUMBER: u8 = 3; + + // #[inline] + // fn number(self) -> u8 { + // self as _ + // } + + // #[inline] + // fn from_number(number: u8) -> Result { + // 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 = 2; + + // #[inline] + // fn number(self) -> u16 { + // self as _ + // } + + // #[inline] + // fn from_number(number: u16) -> Result { + // if number > Self::MAX_HART_ID_NUMBER { + // Err(number) + // } else { + // // SAFETY: valid context number + // Ok(unsafe { core::mem::transmute(number) }) + // } + // } + // } #[test] fn check_interrupt_enum() { diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index ec706fdf..e55ece39 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -26,4 +26,4 @@ critical-section-single-hart = ["critical-section/restore-state-bool"] [dependencies] critical-section = "1.1.2" embedded-hal = "1.0.0" -riscv-pac = { path = "../riscv-pac", version = "0.1.2" } +riscv-pac = { path = "../riscv-pac", version = "0.1.2", default-features = false }