diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 86541174..0a3c2f81 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -23,6 +23,8 @@ jobs: - 'riscv-rt/**' riscv-pac: - 'riscv-pac/**' + riscv-peripheral: + - 'riscv-peripheral/**' - name: Check for CHANGELOG.md (riscv) if: steps.changes.outputs.riscv == 'true' @@ -47,3 +49,11 @@ jobs: changeLogPath: ./riscv-pac/CHANGELOG.md skipLabels: 'skip changelog' missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.' + + - name: Check for CHANGELOG.md (riscv-peripheral) + if: steps.changes.outputs.riscv-peripheral == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./riscv-peripheral/CHANGELOG.md + skipLabels: 'skip changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-peripheral/CHANGELOG.md file.' diff --git a/.github/workflows/riscv-peripheral.yaml b/.github/workflows/riscv-peripheral.yaml new file mode 100644 index 00000000..c9a3c744 --- /dev/null +++ b/.github/workflows/riscv-peripheral.yaml @@ -0,0 +1,61 @@ +on: + push: + branches: [ master ] + pull_request: + merge_group: + +name: Build check (riscv-peripheral) + +jobs: + # We check that the crate builds and links for all the toolchains and targets. + build-riscv: + strategy: + matrix: + # 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 + - riscv32imac-unknown-none-elf + - riscv64imac-unknown-none-elf + - riscv64gc-unknown-none-elf + include: + # Nightly is only for reference and allowed to fail + - toolchain: nightly + experimental: true + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || false }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + targets: ${{ matrix.target }} + - name: Build (no features) + run: cargo build --package riscv-peripheral --target ${{ matrix.target }} + - name: Build (all features) + run: cargo build --package riscv-peripheral --target ${{ matrix.target }} --all-features + + # On MacOS, Ubuntu, and Windows, we run the tests. + build-others: + strategy: + matrix: + os: [ macos-latest, ubuntu-latest, windows-latest ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - name: Build (no features) + run: cargo test --package riscv-peripheral + - name: Build (all features) + run: cargo test --package riscv-peripheral --all-features + + # Job to check that all the builds succeeded + build-check: + needs: + - build-riscv + - build-others + runs-on: ubuntu-latest + if: always() + steps: + - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/Cargo.toml b/Cargo.toml index b97d6de3..b08e6641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ resolver = "2" members = [ "riscv", "riscv-pac", + "riscv-peripheral", "riscv-rt", ] diff --git a/README.md b/README.md index d694f26c..feeb188c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ This repository contains various crates useful for writing Rust programs on RISC * [`riscv`]: CPU registers access and intrinsics * [`riscv-pac`]: Common traits to be implemented by RISC-V PACs +* [`riscv-peripheral`]: Interfaces for standard RISC-V peripherals * [`riscv-rt`]: Startup code and interrupt handling @@ -22,6 +23,8 @@ Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises to intervene to uphold that code of conduct. [`riscv`]: https://crates.io/crates/riscv +[`riscv-pac`]: https://crates.io/crates/riscv-pac +[`riscv-peripheral`]: https://crates.io/crates/riscv-peripheral [`riscv-rt`]: https://crates.io/crates/riscv-rt [team]: https://github.com/rust-embedded/wg#the-risc-v-team [CoC]: CODE_OF_CONDUCT.md diff --git a/riscv-pac/CHANGELOG.md b/riscv-pac/CHANGELOG.md index d17dc4a1..f40a9aae 100644 --- a/riscv-pac/CHANGELOG.md +++ b/riscv-pac/CHANGELOG.md @@ -10,3 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add `InterruptNumber`, `PriorityNumber`, and `HartIdNumber` traits. + +### Changed + +- Update `README.md` diff --git a/riscv-pac/README.md b/riscv-pac/README.md index 61c50447..9e96ac74 100644 --- a/riscv-pac/README.md +++ b/riscv-pac/README.md @@ -1,5 +1,5 @@ -[![crates.io](https://img.shields.io/crates/d/riscv.svg)](https://crates.io/crates/riscv) -[![crates.io](https://img.shields.io/crates/v/riscv.svg)](https://crates.io/crates/riscv) +[![crates.io](https://img.shields.io/crates/d/riscv-pac.svg)](https://crates.io/crates/riscv-pac) +[![crates.io](https://img.shields.io/crates/v/riscv-pac.svg)](https://crates.io/crates/riscv-pac) # `riscv-pac` @@ -7,7 +7,7 @@ This project is developed and maintained by the [RISC-V team][team]. -## [Documentation](https://docs.rs/crate/riscv) +## [Documentation](https://docs.rs/crate/riscv-pac) ## Minimum Supported Rust Version (MSRV) diff --git a/riscv-peripheral/.github/workflows/clippy.yml b/riscv-peripheral/.github/workflows/clippy.yml deleted file mode 100644 index 44d91413..00000000 --- a/riscv-peripheral/.github/workflows/clippy.yml +++ /dev/null @@ -1,44 +0,0 @@ -on: - push: - branches: [ main ] - pull_request: - merge_group: - -name: Lints compliance check - -env: - CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo - -jobs: - clippy: - strategy: - matrix: - toolchain: [ stable, nightly ] - cargo_flags: [ --all-features, --no-default-features ] - include: - # Nightly is only for reference and allowed to fail - - toolchain: nightly - experimental: true - # async traits are still not supported in stable - - toolchain: stable - cargo_flags: --all-features - experimental: true - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || false }} - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - components: clippy - - name: Run clippy - run: cargo clippy --all ${{ matrix.cargo_flags }} -- -D warnings - - # Job to check that all the lint checks succeeded - clippy-check: - needs: - - clippy - runs-on: ubuntu-latest - if: always() - steps: - - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/riscv-peripheral/.github/workflows/rust.yml b/riscv-peripheral/.github/workflows/rust.yml deleted file mode 100644 index 31000a27..00000000 --- a/riscv-peripheral/.github/workflows/rust.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Rust - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose diff --git a/riscv-peripheral/.github/workflows/rustfmt.yml b/riscv-peripheral/.github/workflows/rustfmt.yml deleted file mode 100644 index 3d792dc7..00000000 --- a/riscv-peripheral/.github/workflows/rustfmt.yml +++ /dev/null @@ -1,18 +0,0 @@ -on: - push: - branches: [ main ] - pull_request: - merge_group: - -name: Code formatting check - -jobs: - rustfmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - name: Run Rustfmt - run: cargo fmt --all -- --check --verbose diff --git a/riscv-peripheral/.gitignore b/riscv-peripheral/.gitignore deleted file mode 100644 index 6cfa27d8..00000000 --- a/riscv-peripheral/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb - -.DS_Store -.vscode/ diff --git a/riscv-peripheral/CHANGELOG.md b/riscv-peripheral/CHANGELOG.md new file mode 100644 index 00000000..5fb9bd90 --- /dev/null +++ b/riscv-peripheral/CHANGELOG.md @@ -0,0 +1,12 @@ +# 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] + +### Added + +- Add `ACLINT`, `CLINT`, and `PLIC` structs diff --git a/riscv-peripheral/Cargo.toml b/riscv-peripheral/Cargo.toml index 1eabf062..2b16c935 100644 --- a/riscv-peripheral/Cargo.toml +++ b/riscv-peripheral/Cargo.toml @@ -8,7 +8,8 @@ edition = "2021" [dependencies] embedded-hal = "1.0.0-rc.2" # embedded-hal-async = { version = "1.0.0-rc.1", optional = true } -riscv = { git = "https://github.com/rust-embedded/riscv", branch = "master" } +riscv = { path = "../riscv", version = "0.10" } +riscv-pac = { path = "../riscv-pac", version = "0.1.0" } [features] # hal-async = ["embedded-hal-async"] diff --git a/riscv-peripheral/README.md b/riscv-peripheral/README.md index fffd8329..289cdf96 100644 --- a/riscv-peripheral/README.md +++ b/riscv-peripheral/README.md @@ -1,8 +1,40 @@ +[![crates.io](https://img.shields.io/crates/d/riscv-peripheral.svg)](https://crates.io/crates/riscv-peripheral) +[![crates.io](https://img.shields.io/crates/v/riscv-peripheral.svg)](https://crates.io/crates/riscv-peripheral) + # `riscv-peripheral` -> Standard RISC-V peripherals for embedded systems written in Rust +> Interfaces for standard RISC-V peripherals + +This project is developed and maintained by the [RISC-V team][team]. + +## [Documentation](https://docs.rs/crate/riscv-peripheral) ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.61 and up. It *might* compile with older versions but that may change in any new patch release. + +## License + +Copyright 2023-2024s [RISC-V 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 [RISC-V team][team], promises +to intervene to uphold that code of conduct. + +[CoC]: CODE_OF_CONDUCT.md +[team]: https://github.com/rust-embedded/wg#the-risc-v-team diff --git a/riscv-peripheral/examples/e310x.rs b/riscv-peripheral/examples/e310x.rs index df21cf11..c3f4be6c 100644 --- a/riscv-peripheral/examples/e310x.rs +++ b/riscv-peripheral/examples/e310x.rs @@ -1,7 +1,8 @@ -use riscv_peripheral::{ - aclint::HartIdNumber, - plic::{ContextNumber, InterruptNumber, PriorityNumber}, -}; +//! Peripheral definitions for the E310x chip. +//! This is a simple example of how to use the `riscv-peripheral` crate to generate +//! peripheral definitions for a target. + +use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; #[repr(u16)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -28,25 +29,6 @@ unsafe impl HartIdNumber for HartId { } } -unsafe impl ContextNumber for HartId { - const MAX_CONTEXT_NUMBER: u16 = 0; - - #[inline] - fn number(self) -> u16 { - self as _ - } - - #[inline] - fn from_number(number: u16) -> Result { - if number > Self::MAX_CONTEXT_NUMBER { - Err(number) - } else { - // SAFETY: valid context number - Ok(unsafe { core::mem::transmute(number) }) - } - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u16)] pub enum Interrupt { @@ -162,4 +144,9 @@ riscv_peripheral::clint_codegen!( msips [msip0=(HartId::H0,"`H0`")], ); +riscv_peripheral::plic_codegen!( + base 0x0C00_0000, + ctxs [ctx0=(HartId::H0,"`H0`")], +); + fn main() {} diff --git a/riscv-peripheral/src/aclint.rs b/riscv-peripheral/src/aclint.rs index c19b6446..5aa08239 100644 --- a/riscv-peripheral/src/aclint.rs +++ b/riscv-peripheral/src/aclint.rs @@ -7,32 +7,7 @@ pub mod mswi; pub mod mtimer; pub mod sswi; -/// Trait for enums of HART IDs in (A)CLINT peripherals. -/// -/// # Note -/// -/// If your target only has one HART (HART ID 0), you don't need to implement this trait. -/// Instead, you can access directly to the base registers through the `(A)CLINT` structs. -/// -/// # Safety -/// -/// * This trait must only be implemented on a PAC of a target with a PLIC peripheral. -/// * This trait must only be implemented on enums of HART IDs. -/// * 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 HART ID numbers must be less than or equal to `MAX_HART_ID_NUMBER`. -/// * `MAX_HART_ID_NUMBER` must coincide with the highest allowed HART ID number. -pub unsafe trait HartIdNumber: Copy { - /// Highest number assigned to a HART ID. - const MAX_HART_ID_NUMBER: u16; - - /// Converts a HART Id to its corresponding number. - fn number(self) -> u16; - - /// Tries to convert a number to a valid HART ID. - /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; -} +pub use riscv_pac::HartIdNumber; // re-export useful riscv-pac traits /// Trait for a CLINT peripheral. /// diff --git a/riscv-peripheral/src/macros.rs b/riscv-peripheral/src/macros.rs index 38571dc2..16b28a6f 100644 --- a/riscv-peripheral/src/macros.rs +++ b/riscv-peripheral/src/macros.rs @@ -23,7 +23,7 @@ /// /// let mswi = CLINT::mswi(); // MSWI peripheral /// let mtimer = CLINT::mtimer(); // MTIMER peripheral -/// let delay = CLINT::delay(); // For the `embedded_hal::delay::DelayUs` and `embedded_hal_async::delay::DelayUs` traits +/// let delay = CLINT::delay(); // For the `embedded_hal::delay::DelayNs` trait /// ``` /// /// ## Base address and per-HART mtimecmp registers @@ -205,8 +205,7 @@ macro_rules! clint_codegen { /// /// # Note /// - /// You must export the `riscv_peripheral::hal::delay::DelayUs` trait in order to use delay methods. - /// You must export the `riscv_peripheral::hal_async::delay::DelayUs` trait in order to use async delay methods. + /// You must export the `riscv_peripheral::hal::delay::DelayNs` trait in order to use delay methods. #[inline] pub const fn delay() -> $crate::hal::aclint::Delay { $crate::hal::aclint::Delay::new(Self::mtime(), Self::freq()) @@ -303,10 +302,10 @@ macro_rules! plic_codegen { $crate::plic::PLIC::::pendings() } - /// Returns the context proxy of a given PLIC context. + /// Returns the context proxy of a given PLIC HART context. #[inline] - pub fn ctx(context: C) -> $crate::plic::CTX { - $crate::plic::PLIC::::ctx(context) + pub fn ctx(hart_id: H) -> $crate::plic::CTX { + $crate::plic::PLIC::::ctx(hart_id) } } $crate::plic_codegen!($($tail)*); @@ -314,7 +313,7 @@ macro_rules! plic_codegen { (ctxs [$($fn:ident = ($ctx:expr , $sctx:expr)),+], $($tail:tt)*) => { impl PLIC { $( - #[doc = "Returns a PLIC context proxy for context "] + #[doc = "Returns a PLIC context proxy for context of HART "] #[doc = $sctx] #[doc = "."] #[inline] diff --git a/riscv-peripheral/src/plic.rs b/riscv-peripheral/src/plic.rs index 3185a77e..7f192718 100644 --- a/riscv-peripheral/src/plic.rs +++ b/riscv-peripheral/src/plic.rs @@ -8,92 +8,7 @@ pub mod pendings; pub mod priorities; pub mod threshold; -/// Trait for enums of interrupt numbers. -/// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available external interrupts for a specific device. -/// Each variant must convert to a `u16` of its interrupt number. -/// -/// # Note -/// -/// Recall that the interrupt number `0` is reserved as "no interrupt". -/// -/// # Safety -/// -/// * This trait must only be implemented on a PAC of a target with a PLIC peripheral. -/// * This trait must only be implemented on enums of external 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: u16; - - /// Converts an interrupt source to its corresponding number. - fn number(self) -> u16; - - /// Tries to convert a number to a valid interrupt source. - /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> 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 `u8` of its priority level. -/// -/// # Note -/// -/// Recall that the priority number `0` is reserved as "never interrupt". -/// -/// # Safety -/// -/// * This trait must only be implemented on a PAC of a target with a PLIC peripheral. -/// * 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). -/// * There must be a valid priority number set to 0 (i.e., never interrupt). -/// * 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: u8; - - /// Converts a priority level to its corresponding number. - fn number(self) -> u8; - - /// Tries to convert a number to a valid priority level. - /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u8) -> Result; -} - -/// Trait for enums of PLIC contexts. -/// -/// This trait should be implemented by a peripheral access crate (PAC) -/// on its enum of available contexts for a specific device. -/// Each variant must convert to a `u16` of its context number. -/// -/// # Safety -/// -/// * This trait must only be implemented on a PAC of a target with a PLIC peripheral. -/// * This trait must only be implemented on enums of contexts. -/// * 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 context numbers must be less than or equal to `MAX_CONTEXT_NUMBER`. -/// * `MAX_CONTEXT_NUMBER` must coincide with the highest allowed context number. -pub unsafe trait ContextNumber: Copy { - /// Highest number assigned to a context. - const MAX_CONTEXT_NUMBER: u16; - - /// Converts an context to its corresponding number. - fn number(self) -> u16; - - /// Tries to convert a number to a valid context. - /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; -} +pub use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; // re-export useful riscv-pac traits /// Trait for a PLIC peripheral. /// @@ -144,11 +59,11 @@ impl PLIC

{ unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) } } - /// Returns a proxy to access to all the PLIC registers of a given context. + /// Returns a proxy to access to all the PLIC registers of a given HART context. #[inline] - pub fn ctx(context: C) -> CTX

{ + pub fn ctx(hart_id: H) -> CTX

{ // SAFETY: valid context number - unsafe { CTX::new(context.number()) } + unsafe { CTX::new(hart_id.number()) } } } @@ -216,7 +131,7 @@ impl CTX

{ #[cfg(test)] pub(crate) mod test { - use super::{ContextNumber, InterruptNumber, PriorityNumber}; + use super::{HartIdNumber, InterruptNumber, PriorityNumber}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] @@ -282,8 +197,8 @@ pub(crate) mod test { } } - unsafe impl ContextNumber for Context { - const MAX_CONTEXT_NUMBER: u16 = 2; + unsafe impl HartIdNumber for Context { + const MAX_HART_ID_NUMBER: u16 = 2; #[inline] fn number(self) -> u16 { @@ -292,7 +207,7 @@ pub(crate) mod test { #[inline] fn from_number(number: u16) -> Result { - if number > Self::MAX_CONTEXT_NUMBER { + if number > Self::MAX_HART_ID_NUMBER { Err(number) } else { // SAFETY: valid context number @@ -359,7 +274,7 @@ pub(crate) mod test { assert_eq!(priorities.address(), 0x0C00_0000); assert_eq!(pendings.address(), 0x0C00_1000); - for i in 0..=Context::MAX_CONTEXT_NUMBER { + for i in 0..=Context::MAX_HART_ID_NUMBER { let context = Context::from_number(i).unwrap(); let i = i as usize;