diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index 67fbb920..57d2104d 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Align assembly functions with `cortex-m` - Use CSR helper macros to define `marchid` register - Re-use `try_*` functions in `mcountinhibit` - Use CSR helper macros to define `mcause` register diff --git a/riscv/src/asm.rs b/riscv/src/asm.rs index b29e65db..5e471ff1 100644 --- a/riscv/src/asm.rs +++ b/riscv/src/asm.rs @@ -1,30 +1,24 @@ //! Assembly instructions macro_rules! instruction { - ($(#[$attr:meta])*, unsafe $fnname:ident, $asm:expr) => ( + ($(#[$attr:meta])*, unsafe $fnname:ident, $asm:expr, $($options:tt)*) => ( $(#[$attr])* - #[inline] + #[inline(always)] pub unsafe fn $fnname() { - match () { - #[cfg(riscv)] - () => core::arch::asm!($asm), - - #[cfg(not(riscv))] - () => unimplemented!(), - } + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + core::arch::asm!($asm, $($options)*); + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unimplemented!(); } ); - ($(#[$attr:meta])*, $fnname:ident, $asm:expr) => ( + ($(#[$attr:meta])*, $fnname:ident, $asm:expr, $($options:tt)*) => ( $(#[$attr])* - #[inline] + #[inline(always)] pub fn $fnname() { - match () { - #[cfg(riscv)] - () => unsafe { core::arch::asm!($asm) }, - - #[cfg(not(riscv))] - () => unimplemented!(), - } + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe { core::arch::asm!($asm, $($options)*) }; + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unimplemented!(); } ); } @@ -37,18 +31,35 @@ instruction!( /// /// This function generates a no-operation; it's useful to prevent delay loops from being /// optimized away. - , nop, "nop"); + , nop, "nop", options(nomem, nostack)); + +instruction!( + /// `WFI` instruction wrapper + /// + /// Provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing. + /// The WFI instruction is just a hint, and a legal implementation is to implement WFI as a NOP. + , wfi, "wfi", options(nomem, nostack)); + instruction!( /// `EBREAK` instruction wrapper /// /// Generates a breakpoint exception. - , unsafe ebreak, "ebreak"); + , unsafe ebreak, "ebreak", options(nomem, nostack)); + instruction!( - /// `WFI` instruction wrapper + /// `ECALL` instruction wrapper /// - /// Provides a hint to the implementation that the current hart can be stalled until an interrupt might need servicing. - /// The WFI instruction is just a hint, and a legal implementation is to implement WFI as a NOP. - , wfi, "wfi"); + /// Generates an exception for a service request to the execution environment. + /// When executed in U-mode, S-mode, or M-mode, it generates an environment-call-from-U-mode + /// exception, environment-call-from-S-mode exception, or environment-call-from-M-mode exception, + /// respectively, and performs no other operation. + /// + /// # Note + /// + /// The ECALL instruction will **NOT** save and restore the stack pointer, as it triggers an exception. + /// The stack pointer must be saved and restored accordingly by the exception handler. + , unsafe ecall, "ecall", options(nomem, nostack)); + instruction!( /// `SFENCE.VMA` instruction wrapper (all address spaces and page table levels) /// @@ -57,7 +68,8 @@ instruction!( /// are ordinarily not ordered with respect to loads and stores in the instruction stream. /// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the /// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`. - , sfence_vma_all, "sfence.vma"); + , sfence_vma_all, "sfence.vma", options(nostack)); + instruction!( /// `FENCE` instruction wrapper /// @@ -66,12 +78,12 @@ instruction!( /// (O), memory reads (R), and memory writes (W) may be ordered with respect to any combination /// of the same. Informally, no other RISC-V hart or external device can observe any operation in the /// successor set following a FENCE before any operation in the predecessor set preceding the FENCE. - /// Chapter 17 provides a precise description of the RISC-V memory consistency model. /// /// The FENCE instruction also orders memory reads and writes made by the hart as observed by /// memory reads and writes made by an external device. However, FENCE does not order observations /// of events made by an external device using any other signaling mechanism. - , fence, "fence"); + , fence, "fence", options(nostack)); + instruction!( /// `FENCE.I` instruction wrapper /// @@ -89,7 +101,7 @@ instruction!( /// The unused fields in the FENCE.I instruction, imm\[11:0\], rs1, and rd, are reserved for /// finer-grain fences in future extensions. For forward compatibility, base /// implementations shall ignore these fields, and standard software shall zero these fields. - , fence_i, "fence.i"); + , fence_i, "fence.i", options(nostack)); /// `SFENCE.VMA` instruction wrapper /// @@ -98,38 +110,18 @@ instruction!( /// are ordinarily not ordered with respect to loads and stores in the instruction stream. /// Executing an `SFENCE.VMA` instruction guarantees that any stores in the instruction stream prior to the /// `SFENCE.VMA` are ordered before all implicit references subsequent to the `SFENCE.VMA`. -#[inline] -#[allow(unused_variables)] -pub unsafe fn sfence_vma(asid: usize, addr: usize) { - match () { - #[cfg(riscv)] - () => core::arch::asm!("sfence.vma {0}, {1}", in(reg) addr, in(reg) asid), - - #[cfg(not(riscv))] - () => unimplemented!(), - } -} - -/// `ECALL` instruction wrapper -/// -/// Generates an exception for a service request to the execution environment. -/// When executed in U-mode, S-mode, or M-mode, it generates an environment-call-from-U-mode -/// exception, environment-call-from-S-mode exception, or environment-call-from-M-mode exception, -/// respectively, and performs no other operation. -/// -/// # Note -/// -/// The ECALL instruction will **NOT** save and restore the stack pointer, as it triggers an exception. -/// The stack pointer must be saved and restored accordingly by the exception handler. -#[inline] -pub unsafe fn ecall() { - match () { - #[cfg(riscv)] - () => core::arch::asm!("ecall", options(nostack)), - - #[cfg(not(riscv))] - () => unimplemented!(), - } +#[inline(always)] +#[cfg_attr( + not(any(target_arch = "riscv32", target_arch = "riscv64")), + allow(unused_variables) +)] +pub fn sfence_vma(asid: usize, addr: usize) { + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe { + core::arch::asm!("sfence.vma {0}, {1}", in(reg) addr, in(reg) asid, options(nostack)); + }; + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] + unimplemented!(); } /// Blocks the program for *at least* `cycles` CPU cycles. @@ -142,22 +134,26 @@ pub unsafe fn ecall() { /// timer-less initialization of peripherals if and only if accurate timing is not essential. In /// any other case please use a more accurate method to produce a delay. #[inline] -#[allow(unused_variables)] +#[cfg_attr( + not(any(target_arch = "riscv32", target_arch = "riscv64")), + allow(unused_variables) +)] pub fn delay(cycles: u32) { match () { - #[cfg(riscv)] - () => unsafe { + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + () => { let real_cyc = 1 + cycles / 2; - core::arch::asm!( - "2:", - "addi {0}, {0}, -1", - "bne {0}, zero, 2b", - inout(reg) real_cyc => _, - options(nomem, nostack), - ) - }, - - #[cfg(not(riscv))] + unsafe { + core::arch::asm!( + "2:", + "addi {0}, {0}, -1", + "bne {0}, zero, 2b", + inout(reg) real_cyc => _, + options(nomem, nostack), + ); + } + } + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] () => unimplemented!(), } }