diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0733aebd..f5fdc5fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,4 +30,8 @@ jobs: with: toolchain: ${{ matrix.rust }} - name: Run tests + if: ${{ matrix.rust != '1.61.0' }} run: cargo test --all --exclude cortex-m-rt --exclude testsuite --features cortex-m/critical-section-single-core + - name: Run tests for 1.61.0 (need to exclude cortex-m-pac) + if: ${{ matrix.rust == '1.61.0' }} + run: cargo test --all --exclude cortex-m-rt --exclude testsuite --exclude cortex-m-pac --features cortex-m/critical-section-single-core diff --git a/Cargo.toml b/Cargo.toml index 059853a0..c8624efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "cortex-m", + "cortex-m-pac", "cortex-m-rt", "cortex-m-semihosting", "panic-itm", diff --git a/cortex-m-pac/CHANGELOG.md b/cortex-m-pac/CHANGELOG.md new file mode 100644 index 00000000..c07d965a --- /dev/null +++ b/cortex-m-pac/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] diff --git a/cortex-m-pac/Cargo.toml b/cortex-m-pac/Cargo.toml new file mode 100644 index 00000000..be458a15 --- /dev/null +++ b/cortex-m-pac/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cortex-m-pac" +version = "0.1.0" +edition = "2021" +rust-version = "1.81" +repository = "https://github.com/rust-embedded/cortex-m" +authors = ["The Cortex-M Team ",] +categories = ["embedded", "hardware-support", "no-std"] +description = "Low level access to Cortex-M processors" +documentation = "https://docs.rs/cortex-m-pac" +keywords = ["arm", "cortex-m", "register", "peripheral"] +license = "MIT OR Apache-2.0" +readme = "README.md" + +[package.metadata.docs.rs] +targets = [ + "thumbv8m.main-none-eabihf", + "thumbv6m-none-eabi", + "thumbv7em-none-eabi", + "thumbv7em-none-eabihf", + "thumbv7m-none-eabi", + "thumbv8m.base-none-eabi", + "thumbv8m.main-none-eabi" +] diff --git a/cortex-m-pac/README.md b/cortex-m-pac/README.md new file mode 100644 index 00000000..a48faee7 --- /dev/null +++ b/cortex-m-pac/README.md @@ -0,0 +1,40 @@ +[![crates.io](https://img.shields.io/crates/d/cortex-m-pac.svg)](https://crates.io/crates/cortex-m-pac) +[![crates.io](https://img.shields.io/crates/v/cortex-m-pac.svg)](https://crates.io/crates/cortex-m-pac) + +# `cortex-m-pac` + +> Target-specific traits to be implemented by PACs + +This project is developed and maintained by the [Cortex-M team][team]. + +## [Documentation](https://docs.rs/crate/cortex-m-pac) + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* +compile with older versions but that may change in any new patch release. + +## License + +Copyright 2024 [Cortex-M team][team] + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +## Code of Conduct + +Contribution to this crate is organized under the terms of the [Rust Code of +Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises +to intervene to uphold that code of conduct. + +[CoC]: CODE_OF_CONDUCT.md +[team]: https://github.com/rust-embedded/wg#the-cortex-m-team diff --git a/cortex-m-pac/src/lib.rs b/cortex-m-pac/src/lib.rs new file mode 100644 index 00000000..ea175ce5 --- /dev/null +++ b/cortex-m-pac/src/lib.rs @@ -0,0 +1,264 @@ +#![no_std] + +pub mod result; + +use result::Result; + +/// 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 `cortex-m` crate provides a default +/// implementation. Each variant must convert to a `usize` of its exception number. +/// +/// # Safety +/// +/// * This trait must only be implemented on the `cortex-m` crate or on a PAC of a Cortex-M target. +/// * This trait must only be implemented on enums of exceptions. +/// * Each enum variant must represent a distinct value (no duplicates are permitted), +/// * Each enum variant must always return the same value (do not change at runtime). +/// * All the exception numbers must be less than or equal to `MAX_EXCEPTION_NUMBER`. +/// * `MAX_EXCEPTION_NUMBER` must coincide with the highest allowed exception number. +pub unsafe trait ExceptionNumber: Copy { + /// Highest number assigned to an exception. + const MAX_EXCEPTION_NUMBER: usize; + + /// Converts an exception to its corresponding number. + fn number(self) -> usize; + + /// Tries to convert a number to a valid exception. + fn from_number(value: usize) -> Result; +} + +/// 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. Each variant must convert to a `usize` of its interrupt number. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a Cortex-M target. +/// * This trait must only be implemented on enums of interrupts. +/// * Each enum variant must represent a distinct value (no duplicates are permitted), +/// * Each enum variant must always return the same value (do not change at runtime). +/// * All the interrupt numbers must be less than or equal to `MAX_INTERRUPT_NUMBER`. +/// * `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number. +pub unsafe trait InterruptNumber: Copy { + /// Highest number assigned to an interrupt source. + const MAX_INTERRUPT_NUMBER: usize; + + /// Converts an interrupt source to its corresponding number. + fn number(self) -> usize; + + /// Tries to convert a number to a valid interrupt. + fn from_number(value: usize) -> Result; +} + +/// 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 `usize` of its priority level. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a Cortex-M target. +/// * This trait must only be implemented on enums of priority levels. +/// * Each enum variant must represent a distinct value (no duplicates are permitted). +/// * Each enum variant must always return the same value (do not change at runtime). +/// * All the priority level numbers must be less than or equal to `MAX_PRIORITY_NUMBER`. +/// * `MAX_PRIORITY_NUMBER` must coincide with the highest allowed priority number. +pub unsafe trait PriorityNumber: Copy { + /// Number assigned to the highest priority level. + const MAX_PRIORITY_NUMBER: usize; + + /// Converts a priority level to its corresponding number. + fn number(self) -> usize; + + /// Tries to convert a number to a valid priority level. + fn from_number(value: usize) -> Result; +} + +/// Trait for enums of core identifiers. +/// +/// This trait should be implemented by a peripheral access crate (PAC) on its enum of available +/// cores for a specific device. Each variant must convert to a `usize` of its core ID number. +/// +/// # Safety +/// +/// * This trait must only be implemented on a PAC of a Cortex-M target. +/// * This trait must only be implemented on enums of core IDs. +/// * Each enum variant must represent a distinct value (no duplicates are permitted), +/// * Each anum variant must always return the same value (do not change at runtime). +/// * All the core ID numbers must be less than or equal to `MAX_CORE_ID_NUMBER`. +/// * `MAX_CORE_ID_NUMBER` must coincide with the highest allowed core ID number. +pub unsafe trait CoreIdNumber: Copy { + /// Highest number assigned to a core. + const MAX_CORE_ID_NUMBER: usize; + + /// Converts a Core ID to its corresponding number. + fn number(self) -> usize; + + /// Tries to convert a number to a valid Core ID. + fn from_number(value: usize) -> Result; +} + +#[cfg(test)] +mod test { + use super::*; + use crate::result::Error; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum Exception { + E1 = 1, + E3 = 3, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum Interrupt { + I1 = 1, + I2 = 2, + I4 = 4, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum Priority { + P0 = 0, + P1 = 1, + P2 = 2, + P3 = 3, + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum CoreId { + H0 = 0, + H1 = 1, + H2 = 2, + } + + unsafe impl ExceptionNumber for Exception { + const MAX_EXCEPTION_NUMBER: usize = Self::E3 as usize; + + #[inline] + fn number(self) -> usize { + self as _ + } + + #[inline] + fn from_number(number: usize) -> Result { + match number { + 1 => Ok(Exception::E1), + 3 => Ok(Exception::E3), + _ => Err(Error::InvalidVariant(number)), + } + } + } + + unsafe impl InterruptNumber for Interrupt { + const MAX_INTERRUPT_NUMBER: usize = Self::I4 as usize; + + #[inline] + fn number(self) -> usize { + self as _ + } + + #[inline] + fn from_number(number: usize) -> Result { + match number { + 1 => Ok(Interrupt::I1), + 2 => Ok(Interrupt::I2), + 4 => Ok(Interrupt::I4), + _ => Err(Error::InvalidVariant(number)), + } + } + } + + unsafe impl PriorityNumber for Priority { + const MAX_PRIORITY_NUMBER: usize = Self::P3 as usize; + + #[inline] + fn number(self) -> usize { + self as _ + } + + #[inline] + fn from_number(number: usize) -> Result { + match number { + 0 => Ok(Priority::P0), + 1 => Ok(Priority::P1), + 2 => Ok(Priority::P2), + 3 => Ok(Priority::P3), + _ => Err(Error::InvalidVariant(number)), + } + } + } + + unsafe impl CoreIdNumber for CoreId { + const MAX_CORE_ID_NUMBER: usize = Self::H2 as usize; + + #[inline] + fn number(self) -> usize { + self as _ + } + + #[inline] + fn from_number(number: usize) -> Result { + match number { + 0 => Ok(CoreId::H0), + 1 => Ok(CoreId::H1), + 2 => Ok(CoreId::H2), + _ => Err(Error::InvalidVariant(number)), + } + } + } + + #[test] + fn check_exception_enum() { + assert_eq!(Exception::E1.number(), 1); + assert_eq!(Exception::E3.number(), 3); + + assert_eq!(Exception::from_number(0), Err(Error::InvalidVariant(0))); + assert_eq!(Exception::from_number(1), Ok(Exception::E1)); + assert_eq!(Exception::from_number(2), Err(Error::InvalidVariant(2))); + assert_eq!(Exception::from_number(3), Ok(Exception::E3)); + assert_eq!(Exception::from_number(4), Err(Error::InvalidVariant(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(Error::InvalidVariant(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(Error::InvalidVariant(3))); + assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); + assert_eq!(Interrupt::from_number(5), Err(Error::InvalidVariant(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(Error::InvalidVariant(4))); + } + + #[test] + fn check_core_id_enum() { + assert_eq!(CoreId::H0.number(), 0); + assert_eq!(CoreId::H1.number(), 1); + assert_eq!(CoreId::H2.number(), 2); + + assert_eq!(CoreId::from_number(0), Ok(CoreId::H0)); + assert_eq!(CoreId::from_number(1), Ok(CoreId::H1)); + assert_eq!(CoreId::from_number(2), Ok(CoreId::H2)); + assert_eq!(CoreId::from_number(3), Err(Error::InvalidVariant(3))); + } +} diff --git a/cortex-m-pac/src/result.rs b/cortex-m-pac/src/result.rs new file mode 100644 index 00000000..ceee019a --- /dev/null +++ b/cortex-m-pac/src/result.rs @@ -0,0 +1,60 @@ +use core::fmt; + +/// Convenience alias for the [Result](core::result::Result) type for the library. +pub type Result = core::result::Result; + +/// Represents error variants for the library. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Error { + /// Attempted out-of-bounds access. + IndexOutOfBounds { + index: usize, + min: usize, + max: usize, + }, + /// Invalid field value. + InvalidFieldValue { + field: &'static str, + value: usize, + bitmask: usize, + }, + /// Invalid value of a register field that does not match any known variants. + InvalidFieldVariant { field: &'static str, value: usize }, + /// Invalid value. + InvalidValue { value: usize, bitmask: usize }, + /// Invalid value that does not match any known variants. + InvalidVariant(usize), + /// Unimplemented function or type. + Unimplemented, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IndexOutOfBounds { index, min, max } => write!( + f, + "out-of-bounds access, index: {index}, min: {min}, max: {max}" + ), + Self::InvalidFieldValue { + field, + value, + bitmask, + } => write!( + f, + "invalid {field} field value: {value:#x}, valid bitmask: {bitmask:#x}", + ), + Self::InvalidFieldVariant { field, value } => { + write!(f, "invalid {field} field variant: {value:#x}") + } + Self::InvalidValue { value, bitmask } => { + write!(f, "invalid value: {value:#x}, valid bitmask: {bitmask:#x}",) + } + Self::InvalidVariant(value) => { + write!(f, "invalid variant: {value:#x}") + } + Self::Unimplemented => write!(f, "unimplemented"), + } + } +} + +impl core::error::Error for Error {}