diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 5549e15d..2d4a1d42 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -270,34 +270,24 @@ //! ### Core exception handlers //! //! This functions are called when corresponding exception occurs. -//! You can define an exception handler with one of the following names: -//! * `InstructionMisaligned` -//! * `InstructionFault` -//! * `IllegalInstruction` -//! * `Breakpoint` -//! * `LoadMisaligned` -//! * `LoadFault` -//! * `StoreMisaligned` -//! * `StoreFault` -//! * `UserEnvCall` -//! * `SupervisorEnvCall` -//! * `MachineEnvCall` -//! * `InstructionPageFault` -//! * `LoadPageFault` -//! * `StorePageFault` +//! You can define an exception handler with the [`exception`] attribute. +//! The attribute expects the path to the exception source as an argument. +//! +//! The [`exception`] attribute ensures at compile time that there is a valid +//! exception source for the given handler. //! //! For example: //! ``` no_run -//! #[export_name = "MachineEnvCall"] -//! fn custom_menv_call_handler(trap_frame: &riscv_rt::TrapFrame) { -//! // ... +//! use riscv::interrupt::Exception; // or a target-specific exception enum +//! +//! #[riscv_rt::exception(Exception::MachineEnvCall)] +//! fn custom_menv_call_handler(trap_frame: &mut riscv_rt::TrapFrame) { +//! todo!() //! } -//! ``` -//! or -//! ``` no_run -//! #[no_mangle] -//! fn MachineEnvCall(trap_frame: &riscv_rt::TrapFrame) -> ! { -//! // ... +//! +//! #[riscv_rt::exception(Exception::LoadFault)] +//! fn custom_load_fault_handler() -> ! { +//! loop {} //! } //! ``` //! @@ -320,51 +310,50 @@ //! or //! ``` no_run //! #[no_mangle] -//! fn ExceptionHandler(trap_frame: &riscv_rt::TrapFrame) -> ! { +//! fn ExceptionHandler(trap_frame: &mut riscv_rt::TrapFrame) { //! // ... //! } //! ``` //! //! Default implementation of this function stucks in a busy-loop. //! -//! //! ### Core interrupt handlers //! //! This functions are called when corresponding interrupt is occured. -//! You can define an interrupt handler with one of the following names: -//! * `SupervisorSoft` -//! * `MachineSoft` -//! * `SupervisorTimer` -//! * `MachineTimer` -//! * `SupervisorExternal` -//! * `MachineExternal` +//! You can define a core interrupt handler with the [`core_interrupt`] attribute. +//! The attribute expects the path to the interrupt source as an argument. +//! +//! The [`core_interrupt`] attribute ensures at compile time that there is a valid +//! core interrupt source for the given handler. //! //! For example: //! ``` no_run -//! #[export_name = "MachineTimer"] -//! fn custom_timer_handler() { -//! // ... +//! use riscv::interrupt::Interrupt; // or a target-specific core interrupt enum +//! +//! #[riscv_rt::core_interrupt(Interrupt::MachineSoft)] +//! unsafe fn custom_machine_soft_handler() { +//! todo!() //! } -//! ``` -//! or -//! ``` no_run -//! #[no_mangle] -//! fn MachineTimer() { -//! // ... +//! +//! #[riscv_rt::core_interrupt(Interrupt::MachineTimer)] +//! fn custom_machine_timer_handler() -> ! { +//! loop {} //! } //! ``` //! -//! You can also use the `#[interrupt]` macro to define interrupt handlers: +//! In vectored mode, this macro will also generate a proper trap handler for the interrupt. //! -//! ``` no_run -//! #[riscv_rt::interrupt] -//! fn MachineTimer() { -//! // ... -//! } -//! ``` +//! If interrupt handler is not explicitly defined, `DefaultHandler` is called. +//! +//! ### External interrupt handlers +//! +//! This functions are called when corresponding interrupt is occured. +//! You can define an external interrupt handler with the [`external_interrupt`] attribute. +//! The attribute expects the path to the interrupt source as an argument. //! -//! In direct mode, this macro is equivalent to defining a function with the same name. -//! However, in vectored mode, this macro will generate a proper trap handler for the interrupt. +//! The [`external_interrupt`] attribute ensures at compile time that there is a valid +//! external interrupt source for the given handler. +//! Note that external interrupts are target-specific and may not be available on all platforms. //! //! If interrupt handler is not explicitly defined, `DefaultHandler` is called. //! @@ -372,20 +361,22 @@ //! //! This function is called when interrupt without defined interrupt handler is occured. //! The interrupt reason can be decoded from the `mcause`/`scause` register. +//! If it is an external interrupt, the interrupt reason can be decoded from a +//! target-specific peripheral interrupt controller. //! //! This function can be redefined in the following way: //! //! ``` no_run //! #[export_name = "DefaultHandler"] -//! fn custom_interrupt_handler() { +//! unsafe fn custom_interrupt_handler() { //! // ... //! } //! ``` //! or //! ``` no_run //! #[no_mangle] -//! fn DefaultHandler() { -//! // ... +//! fn DefaultHandler() -> ! { +//! loop {} //! } //! ``` //! @@ -436,22 +427,7 @@ //! riscv-rt = {features=["v-trap"]} //! ``` //! When the vectored trap feature is enabled, the trap vector is set to `_vector_table` in vectored mode. -//! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the interrupt. -//! -//! ### Defining interrupt handlers in vectored mode -//! -//! In vectored mode, each interrupt must also have a corresponding trap handler. -//! Therefore, using `export_name` or `no_mangle` is not enough to define an interrupt handler. -//! The [`interrupt`] macro will generate the trap handler for the interrupt: -//! -//! ``` no_run -//! #[riscv_rt::interrupt] -//! fn MachineTimer() { -//! // ... -//! } -//! ``` -//! -//! This will generate a function named `_start_MachineTimer_trap` that calls the interrupt handler `MachineTimer`. +//! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the core interrupt. // NOTE: Adapted from cortex-m/src/lib.rs #![no_std] diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index 366dd390..35bc7fd8 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -29,7 +29,3 @@ critical-section = "1.1.2" embedded-hal = "1.0.0" riscv-pac = { path = "../riscv-pac", version = "0.2.0" } riscv-macros = { path = "macros", version = "0.1.0", optional = true } - -[dev-dependencies] -trybuild = "1.0" -riscv-rt = { path = "../riscv-rt", version = "0.13.0" } diff --git a/riscv/src/interrupt.rs b/riscv/src/interrupt.rs index 697d0345..93fda11c 100644 --- a/riscv/src/interrupt.rs +++ b/riscv/src/interrupt.rs @@ -15,7 +15,37 @@ pub use machine::*; #[cfg(feature = "s-mode")] pub use supervisor::*; -/// Trap Cause +/// Trap Cause. +/// +/// This enum represents the cause of a trap. It can be either an interrupt or an exception. +/// The [`mcause`](crate::register::mcause::Mcause::cause) and +/// [`scause`](crate::register::scause::Scause::cause) registers return a value of this type. +/// However, the trap cause is represented as raw numbers. To get a target-specific trap cause, +/// use [`Trap::try_into`] with your target-specific M-Mode or S-Mode trap cause types. +/// +/// # Example +/// +/// In targets that comply with the RISC-V standard, you can use the standard +/// [`Interrupt`] and [`Exception`] enums to represent the trap cause: +/// +/// ```no_run +/// use riscv::interrupt::{Trap, Interrupt, Exception}; +/// use riscv::register::mcause; +/// +/// let raw_trap: Trap = mcause::read().cause(); +/// let standard_trap: Trap = raw_trap.try_into().unwrap(); +/// ``` +/// +/// Targets that do not comply with the RISC-V standard usually have their own interrupt and exceptions. +/// You can find these types in the target-specific PAC. If it has been generated with `svd2rust`, +/// you can use the `pac::interrupt::CoreInterrupt` and `pac::interrupt::Exception` enums: +/// +/// ```ignore,no_run +/// use riscv::interrupt::Trap; +/// use pac::interrupt::{CoreInterrupt, Exception}; // pac is the target-specific PAC +/// +/// let standard_trap: Trap = pac::interrupt::cause(); +/// ``` #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Trap { Interrupt(I), diff --git a/riscv/tests/test.rs b/riscv/tests/test.rs deleted file mode 100644 index c74b861b..00000000 --- a/riscv/tests/test.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[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/tests/ui/fail_empty_macro.rs b/tests/tests/riscv/fail_empty_macro.rs similarity index 100% rename from riscv/tests/ui/fail_empty_macro.rs rename to tests/tests/riscv/fail_empty_macro.rs diff --git a/riscv/tests/ui/fail_empty_macro.stderr b/tests/tests/riscv/fail_empty_macro.stderr similarity index 85% rename from riscv/tests/ui/fail_empty_macro.stderr rename to tests/tests/riscv/fail_empty_macro.stderr index 28c594b7..d163ec43 100644 --- a/riscv/tests/ui/fail_empty_macro.stderr +++ b/tests/tests/riscv/fail_empty_macro.stderr @@ -1,5 +1,5 @@ error: unexpected end of input, expected `unsafe` - --> tests/ui/fail_empty_macro.rs:1:1 + --> tests/riscv/fail_empty_macro.rs:1:1 | 1 | #[riscv::pac_enum] | ^^^^^^^^^^^^^^^^^^ diff --git a/riscv/tests/ui/fail_no_unsafe.rs b/tests/tests/riscv/fail_no_unsafe.rs similarity index 100% rename from riscv/tests/ui/fail_no_unsafe.rs rename to tests/tests/riscv/fail_no_unsafe.rs diff --git a/riscv/tests/ui/fail_no_unsafe.stderr b/tests/tests/riscv/fail_no_unsafe.stderr similarity index 72% rename from riscv/tests/ui/fail_no_unsafe.stderr rename to tests/tests/riscv/fail_no_unsafe.stderr index 68fec6f8..cdb3789e 100644 --- a/riscv/tests/ui/fail_no_unsafe.stderr +++ b/tests/tests/riscv/fail_no_unsafe.stderr @@ -1,5 +1,5 @@ error: expected `unsafe` - --> tests/ui/fail_no_unsafe.rs:1:19 + --> tests/riscv/fail_no_unsafe.rs:1:19 | 1 | #[riscv::pac_enum(InterruptNumber)] | ^^^^^^^^^^^^^^^ diff --git a/riscv/tests/ui/fail_unknown_trait.rs b/tests/tests/riscv/fail_unknown_trait.rs similarity index 100% rename from riscv/tests/ui/fail_unknown_trait.rs rename to tests/tests/riscv/fail_unknown_trait.rs diff --git a/riscv/tests/ui/fail_unknown_trait.stderr b/tests/tests/riscv/fail_unknown_trait.stderr similarity index 84% rename from riscv/tests/ui/fail_unknown_trait.stderr rename to tests/tests/riscv/fail_unknown_trait.stderr index 337f8548..03fcad8b 100644 --- a/riscv/tests/ui/fail_unknown_trait.stderr +++ b/tests/tests/riscv/fail_unknown_trait.stderr @@ -1,5 +1,5 @@ error: Unknown trait name. Expected: 'ExceptionNumber', 'CoreInterruptNumber', 'ExternalInterruptNumber', 'PriorityNumber', or 'HartIdNumber' - --> tests/ui/fail_unknown_trait.rs:1:26 + --> tests/riscv/fail_unknown_trait.rs:1:26 | 1 | #[riscv::pac_enum(unsafe InterruptNumber)] | ^^^^^^^^^^^^^^^ diff --git a/riscv/tests/ui/pass_test.rs b/tests/tests/riscv/pass_test.rs similarity index 100% rename from riscv/tests/ui/pass_test.rs rename to tests/tests/riscv/pass_test.rs diff --git a/tests/tests/test.rs b/tests/tests/test.rs index 7506ddfc..82096db9 100644 --- a/tests/tests/test.rs +++ b/tests/tests/test.rs @@ -1,6 +1,10 @@ #[test] fn riscv_rt() { let t = trybuild::TestCases::new(); + + t.compile_fail("tests/riscv/fail_*.rs"); + t.pass("tests/riscv/pass_*.rs"); + t.compile_fail("tests/riscv-rt/*/fail_*.rs"); t.pass("tests/riscv-rt/*/pass_*.rs"); }