diff --git a/.github/workflows/riscv-pac.yaml b/.github/workflows/riscv-pac.yaml index 9ab4c5db..a7dfe5af 100644 --- a/.github/workflows/riscv-pac.yaml +++ b/.github/workflows/riscv-pac.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, riscv-pac ] + branches: [ master ] pull_request: merge_group: @@ -11,8 +11,8 @@ jobs: build-riscv: strategy: matrix: - # All generated code should be running on stable now, MRSV is 1.60.0 - toolchain: [ stable, nightly, 1.60.0 ] + # All generated code should be running on stable now, MRSV is 1.61.0 + toolchain: [ stable, nightly, 1.61.0 ] target: - riscv32i-unknown-none-elf - riscv32imc-unknown-none-elf diff --git a/.github/workflows/riscv-peripheral.yaml b/.github/workflows/riscv-peripheral.yaml index ce2b45e6..57b986ab 100644 --- a/.github/workflows/riscv-peripheral.yaml +++ b/.github/workflows/riscv-peripheral.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, riscv-pac ] + branches: [ master ] pull_request: merge_group: diff --git a/.github/workflows/riscv.yaml b/.github/workflows/riscv.yaml index b52145a5..28e72126 100644 --- a/.github/workflows/riscv.yaml +++ b/.github/workflows/riscv.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, riscv-pac ] + branches: [ master ] pull_request: merge_group: @@ -11,8 +11,8 @@ jobs: build-riscv: strategy: matrix: - # All generated code should be running on stable now, MRSV is 1.60.0 - toolchain: [ stable, nightly, 1.60.0 ] + # All generated code should be running on stable now, MRSV is 1.61.0 + toolchain: [ stable, nightly, 1.61.0 ] target: - riscv32i-unknown-none-elf - riscv32imc-unknown-none-elf diff --git a/riscv-pac/Cargo.toml b/riscv-pac/Cargo.toml index 22d247ef..61c300c1 100644 --- a/riscv-pac/Cargo.toml +++ b/riscv-pac/Cargo.toml @@ -2,7 +2,7 @@ name = "riscv-pac" version = "0.1.2" edition = "2021" -rust-version = "1.60" +rust-version = "1.61" repository = "https://github.com/rust-embedded/riscv" authors = ["The RISC-V Team "] categories = ["embedded", "hardware-support", "no-std"] diff --git a/riscv-pac/macros/Cargo.toml b/riscv-pac/macros/Cargo.toml index 233a6616..c559182f 100644 --- a/riscv-pac/macros/Cargo.toml +++ b/riscv-pac/macros/Cargo.toml @@ -10,6 +10,7 @@ license = "MIT OR Apache-2.0" name = "riscv-pac-macros" repository = "https://github.com/rust-embedded/riscv" version = "0.1.0" +edition = "2021" [lib] proc-macro = true diff --git a/riscv-pac/macros/src/lib.rs b/riscv-pac/macros/src/lib.rs index 4f783db8..01090b1d 100644 --- a/riscv-pac/macros/src/lib.rs +++ b/riscv-pac/macros/src/lib.rs @@ -1,77 +1,214 @@ -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; use std::str::FromStr; -use syn::{parse_macro_input, Data, DeriveInput, Ident}; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, + spanned::Spanned, + Data, DeriveInput, Ident, Token, +}; + +/// Traits that can be implemented using the `pac_enum` macro +enum PacTrait { + Exception, + Interrupt(InterruptType), + Priority, + HartId, +} + +impl PacTrait { + /// Returns a token stream representing the trait name + fn trait_name(&self) -> TokenStream2 { + match self { + Self::Exception => quote!(ExceptionNumber), + Self::Interrupt(_) => quote!(InterruptNumber), + Self::Priority => quote!(PriorityNumber), + Self::HartId => quote!(HartIdNumber), + } + } + + /// Returns a token stream representing the data type used to represent the number + fn num_type(&self) -> TokenStream2 { + match self { + Self::Exception => quote!(usize), + Self::Interrupt(_) => quote!(usize), + Self::Priority => quote!(u8), + Self::HartId => quote!(u16), + } + } + + /// Returns a token stream representing the name of the constant that holds the maximum number + fn const_name(&self) -> TokenStream2 { + match self { + Self::Exception => quote!(MAX_EXCEPTION_NUMBER), + Self::Interrupt(_) => quote!(MAX_INTERRUPT_NUMBER), + Self::Priority => quote!(MAX_PRIORITY_NUMBER), + Self::HartId => quote!(MAX_HART_ID_NUMBER), + } + } +} + +impl Parse for PacTrait { + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + let trait_name: TokenStream2 = input.parse()?; + match trait_name.to_string().as_str() { + "ExceptionNumber" => Ok(Self::Exception), + "CoreInterruptNumber" => Ok(Self::Interrupt(InterruptType::Core)), + "ExternalInterruptNumber" => Ok(Self::Interrupt(InterruptType::External)), + "PriorityNumber" => Ok(Self::Priority), + "HartIdNumber" => Ok(Self::HartId), + _ => Err(syn::Error::new( + trait_name.span(), + "Unknown trait name. Expected: 'ExceptionNumber', 'CoreInterruptNumber', 'ExternalInterruptNumber', 'PriorityNumber', or 'HartIdNumber'", + )), + } + } +} -struct PacNumberEnum { +/// Marker traits for interrupts +enum InterruptType { + Core, + External, +} + +impl InterruptType { + /// Returns a token stream representing the name of the marker trait + fn marker_trait_name(&self) -> TokenStream2 { + match self { + Self::Core => quote!(CoreInterruptNumber), + Self::External => quote!(ExternalInterruptNumber), + } + } + + /// Returns a token stream representing the name of the array of interrupt service routines + fn isr_array_name(&self) -> TokenStream2 { + match self { + Self::Core => quote!(__CORE_INTERRUPTS), + Self::External => quote!(__EXTERNAL_INTERRUPTS), + } + } + + /// Returns a token stream representing the name of the interrupt dispatch function + fn dispatch_fn_name(&self) -> TokenStream2 { + match self { + Self::Core => quote!(_dispatch_core_interrupt), + Self::External => quote!(_dispatch_external_interrupt), + } + } +} + +/// Struct containing the information needed to implement the `riscv-pac` traits for an enum +struct PacEnumItem { + /// The name of the enum name: Ident, - numbers: Vec<(Ident, usize)>, + /// The maximum discriminant value + max_number: usize, + /// A map from discriminant values to variant names + numbers: HashMap, } -impl PacNumberEnum { +impl PacEnumItem { fn new(input: &DeriveInput) -> Self { let name = input.ident.clone(); + let (mut numbers, mut max_number) = (HashMap::new(), 0); let variants = match &input.data { Data::Enum(data) => &data.variants, _ => panic!("Input is not an enum"), }; - let numbers = variants - .iter() - .map(|variant| { - 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"), + for v in variants.iter() { + let ident = v.ident.clone(); + let value = match &v.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!("Variant must have a discriminant"), - }; - (ident.clone(), value) - }) - .collect(); + _ => panic!("All variant discriminants must be unsigned integers"), + }, + _ => panic!("Variant must have a discriminant"), + }; + + if numbers.insert(value, ident).is_some() { + panic!("Duplicate discriminant value"); + } + if value > max_number { + max_number = value; + } + } - Self { name, numbers } + Self { + name, + max_number, + numbers, + } } + /// Returns a token stream representing the maximum discriminant value of the enum fn max_discriminant(&self) -> TokenStream2 { - let max_discriminant = self.numbers.iter().map(|(_, num)| num).max().unwrap(); - TokenStream2::from_str(&format!("{max_discriminant}")).unwrap() + TokenStream2::from_str(&format!("{}", self.max_number)).unwrap() } + /// Returns a vector of token streams representing the valid matches in the `pac::from_number` function fn valid_matches(&self) -> Vec { self.numbers .iter() - .map(|(ident, num)| { + .map(|(num, ident)| { TokenStream2::from_str(&format!("{num} => Ok(Self::{ident})")).unwrap() }) .collect() } - fn quote(&self, trait_name: &str, num_type: &str, const_name: &str) -> TokenStream2 { + /// Returns a vector of token streams representing the interrupt handler functions + fn interrupt_handlers(&self) -> Vec { + self.numbers + .values() + .map(|ident| { + quote! { fn #ident () } + }) + .collect() + } + + /// Returns a sorted vector of token streams representing all the elements of the interrupt array. + /// If an interrupt number is not present in the enum, the corresponding element is `None`. + /// Otherwise, it is `Some()`. + fn interrupt_array(&self) -> Vec { + let mut vectors = vec![]; + for i in 0..=self.max_number { + if let Some(ident) = self.numbers.get(&i) { + vectors.push(quote! { Some(#ident) }); + } else { + vectors.push(quote! { None }); + } + } + vectors + } + + /// Returns a vector of token streams representing the trait implementations for + /// the enum. If the trait is an interrupt trait, the implementation also includes + /// the interrupt handler functions and the interrupt array. + fn impl_trait(&self, attr: &PacTrait) -> Vec { + let mut res = vec![]; + let name = &self.name; + + let trait_name = attr.trait_name(); + let num_type = attr.num_type(); + let const_name = attr.const_name(); + let max_discriminant = self.max_discriminant(); let valid_matches = self.valid_matches(); - 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! { + // Push the trait implementation + res.push(quote! { unsafe impl riscv_pac::#trait_name for #name { const #const_name: #num_type = #max_discriminant; @@ -88,7 +225,53 @@ impl PacNumberEnum { } } } + }); + + // Interrupt traits require additional code + if let PacTrait::Interrupt(interrupt_type) = attr { + let marker_trait_name = interrupt_type.marker_trait_name(); + + let isr_array_name = interrupt_type.isr_array_name(); + let dispatch_fn_name = interrupt_type.dispatch_fn_name(); + + // Push the marker trait implementation + res.push(quote! { unsafe impl riscv_pac::#marker_trait_name for #name {} }); + + let interrupt_handlers = self.interrupt_handlers(); + let interrupt_array = self.interrupt_array(); + + // Push the interrupt handler functions and the interrupt array + res.push(quote! { + extern "C" { + #(#interrupt_handlers;)* + } + + #[no_mangle] + pub static #isr_array_name: [Option; #max_discriminant + 1] = [ + #(#interrupt_array),* + ]; + + #[no_mangle] + unsafe extern "C" fn #dispatch_fn_name(code: usize) { + extern "C" { + fn DefaultHandler(); + } + + if code < #isr_array_name.len() { + let h = &#isr_array_name[code]; + if let Some(handler) = h { + handler(); + } else { + DefaultHandler(); + } + } else { + DefaultHandler(); + } + } + }); } + + res } } @@ -131,35 +314,14 @@ impl PacNumberEnum { #[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 )'"); - } + let pac_enum = PacEnumItem::new(&input); + + let attr = parse_macro_input!(attr as PacTrait); - let trait_impl = match attrs[1] { - "ExceptionNumber" => pac_enum.quote("ExceptionNumber", "usize", "MAX_EXCEPTION_NUMBER"), - "InterruptNumber" => pac_enum.quote("InterruptNumber", "usize", "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]), - }; + let trait_impl = pac_enum.impl_trait(&attr); quote! { #input - #trait_impl + #(#trait_impl)* } .into() } diff --git a/riscv-pac/tests/ui/fail_empty_macro.stderr b/riscv-pac/tests/ui/fail_empty_macro.stderr index 37859747..22211826 100644 --- a/riscv-pac/tests/ui/fail_empty_macro.stderr +++ b/riscv-pac/tests/ui/fail_empty_macro.stderr @@ -1,7 +1,7 @@ -error: custom attribute panicked +error: unexpected end of input, expected `unsafe` --> tests/ui/fail_empty_macro.rs:1:1 | 1 | #[riscv_pac::pac_enum] | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: message: Attribute is empty. Expected: 'riscv_pac::pac_enum(unsafe )' + = 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/fail_more_than_one_trait.rs b/riscv-pac/tests/ui/fail_more_than_one_trait.rs deleted file mode 100644 index 7db5629e..00000000 --- a/riscv-pac/tests/ui/fail_more_than_one_trait.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[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 deleted file mode 100644 index 881d9f7a..00000000 --- a/riscv-pac/tests/ui/fail_more_than_one_trait.stderr +++ /dev/null @@ -1,7 +0,0 @@ -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.stderr b/riscv-pac/tests/ui/fail_no_unsafe.stderr index e459a17c..a12dfad7 100644 --- a/riscv-pac/tests/ui/fail_no_unsafe.stderr +++ b/riscv-pac/tests/ui/fail_no_unsafe.stderr @@ -1,7 +1,5 @@ -error: custom attribute panicked - --> tests/ui/fail_no_unsafe.rs:1:1 +error: expected `unsafe` + --> tests/ui/fail_no_unsafe.rs:1:23 | 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 index 50192085..82ebdc52 100644 --- a/riscv-pac/tests/ui/fail_unknown_trait.rs +++ b/riscv-pac/tests/ui/fail_unknown_trait.rs @@ -1,4 +1,4 @@ -#[riscv_pac::pac_enum(unsafe CoreInterruptNumber)] +#[riscv_pac::pac_enum(unsafe InterruptNumber)] #[derive(Clone, Copy, Debug, PartialEq)] enum Interrupt { I1 = 1, diff --git a/riscv-pac/tests/ui/fail_unknown_trait.stderr b/riscv-pac/tests/ui/fail_unknown_trait.stderr index 5ca1ce48..4f03439d 100644 --- a/riscv-pac/tests/ui/fail_unknown_trait.stderr +++ b/riscv-pac/tests/ui/fail_unknown_trait.stderr @@ -1,7 +1,5 @@ -error: custom attribute panicked - --> tests/ui/fail_unknown_trait.rs:1:1 +error: Unknown trait name. Expected: 'ExceptionNumber', 'CoreInterruptNumber', 'ExternalInterruptNumber', 'PriorityNumber', or 'HartIdNumber' + --> tests/ui/fail_unknown_trait.rs:1:30 | -1 | #[riscv_pac::pac_enum(unsafe CoreInterruptNumber)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: message: Unknown trait 'CoreInterruptNumber'. Expected: 'ExceptionNumber', 'InterruptNumber', 'PriorityNumber', or 'HartIdNumber' +1 | #[riscv_pac::pac_enum(unsafe InterruptNumber)] + | ^^^^^^^^^^^^^^^ diff --git a/riscv-pac/tests/ui/pass_test.rs b/riscv-pac/tests/ui/pass_test.rs index 2be913eb..07141391 100644 --- a/riscv-pac/tests/ui/pass_test.rs +++ b/riscv-pac/tests/ui/pass_test.rs @@ -9,7 +9,7 @@ enum Exception { } #[repr(usize)] -#[pac_enum(unsafe InterruptNumber)] +#[pac_enum(unsafe CoreInterruptNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Interrupt { I1 = 1, @@ -37,6 +37,23 @@ enum HartId { H2 = 2, } +mod isr { + #[export_name = "DefaultHandler"] + fn default_handler() {} + + #[export_name = "I1"] + fn i1() {} + + #[export_name = "I2"] + fn i2() {} + + #[export_name = "I4"] + fn i4() {} + + #[export_name = "I7"] + fn i7() {} +} + fn main() { assert_eq!(Exception::E1.number(), 1); assert_eq!(Exception::E3.number(), 3); @@ -47,6 +64,8 @@ fn main() { assert_eq!(Exception::from_number(3), Ok(Exception::E3)); assert_eq!(Exception::from_number(4), Err(4)); + assert_eq!(Exception::MAX_EXCEPTION_NUMBER, 3); + assert_eq!(Interrupt::I1.number(), 1); assert_eq!(Interrupt::I2.number(), 2); assert_eq!(Interrupt::I4.number(), 4); @@ -61,6 +80,19 @@ fn main() { assert_eq!(Interrupt::from_number(6), Err(6)); assert_eq!(Interrupt::from_number(7), Ok(Interrupt::I7)); + assert_eq!(Interrupt::MAX_INTERRUPT_NUMBER, 7); + + assert_eq!(__CORE_INTERRUPTS.len(), Interrupt::MAX_INTERRUPT_NUMBER + 1); + + assert!(__CORE_INTERRUPTS[0].is_none()); + assert!(__CORE_INTERRUPTS[1].is_some()); + assert!(__CORE_INTERRUPTS[2].is_some()); + assert!(__CORE_INTERRUPTS[3].is_none()); + assert!(__CORE_INTERRUPTS[4].is_some()); + assert!(__CORE_INTERRUPTS[5].is_none()); + assert!(__CORE_INTERRUPTS[6].is_none()); + assert!(__CORE_INTERRUPTS[7].is_some()); + assert_eq!(Priority::P0.number(), 0); assert_eq!(Priority::P1.number(), 1); assert_eq!(Priority::P2.number(), 2); @@ -72,6 +104,8 @@ fn main() { assert_eq!(Priority::from_number(3), Ok(Priority::P3)); assert_eq!(Priority::from_number(4), Err(4)); + assert_eq!(Priority::MAX_PRIORITY_NUMBER, 3); + assert_eq!(HartId::H0.number(), 0); assert_eq!(HartId::H1.number(), 1); assert_eq!(HartId::H2.number(), 2); @@ -80,4 +114,6 @@ fn main() { 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)); + + assert_eq!(HartId::MAX_HART_ID_NUMBER, 2); } diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 14cfd361..96df33b2 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -21,7 +21,7 @@ If `v-trap` feature is enabled, this macro also generates its corresponding trap - `abort` is now `weak`, so it is possible to link third-party libraries including this symbol. - Made `cfg` variable selection more robust for custom targets - `_start_trap_rust` now only deals with exceptions. When an interrupt is detected, it now calls -to `_dispatch_interrupt`. +to `_dispatch_core_interrupt`. ### Removed diff --git a/riscv-rt/macros/Cargo.toml b/riscv-rt/macros/Cargo.toml index 17b8e9d1..d78ab01b 100644 --- a/riscv-rt/macros/Cargo.toml +++ b/riscv-rt/macros/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT OR Apache-2.0" name = "riscv-rt-macros" repository = "https://github.com/rust-embedded/riscv" version = "0.2.1" +edition = "2021" [lib] proc-macro = true diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 97b45f06..7e63dc8e 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -520,11 +520,11 @@ pub struct TrapFrame { pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { extern "C" { fn ExceptionHandler(trap_frame: &TrapFrame); - fn _dispatch_interrupt(code: usize); + fn _dispatch_core_interrupt(code: usize); } match xcause::read().cause() { - xcause::Trap::Interrupt(code) => _dispatch_interrupt(code), + xcause::Trap::Interrupt(code) => _dispatch_core_interrupt(code), xcause::Trap::Exception(code) => { let trap_frame = &*trap_frame; if code < __EXCEPTIONS.len() { @@ -579,8 +579,8 @@ pub static __EXCEPTIONS: [Option; 16] = [ Some(StorePageFault), ]; -#[export_name = "_dispatch_interrupt"] -unsafe extern "C" fn dispatch_interrupt(code: usize) { +#[export_name = "_dispatch_core_interrupt"] +unsafe extern "C" fn dispatch_core_interrupt(code: usize) { extern "C" { fn DefaultHandler(); } diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 2a2458a6..a6165b17 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Bump MSRV to 1.61. - Implementation of `riscv-pac` traits for `Interrupt` and `Exception` enums. - Tests for the `riscv-pac` trait implementations of `Interrupt` and `Exception` enums. - Add `Mcause::from(usize)` for use in unit tests diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index e55ece39..5e37ddd5 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -2,7 +2,7 @@ name = "riscv" version = "0.11.2" edition = "2021" -rust-version = "1.60" +rust-version = "1.61" repository = "https://github.com/rust-embedded/riscv" authors = ["The RISC-V Team "] categories = ["embedded", "hardware-support", "no-std"]