Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: align assembly functions with cortex-m #246

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
142 changes: 69 additions & 73 deletions riscv/src/asm.rs
Original file line number Diff line number Diff line change
@@ -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!();
}
);
}
Expand All @@ -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)
///
Expand All @@ -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
///
Expand All @@ -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
///
Expand All @@ -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
///
Expand All @@ -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!();
rmsyn marked this conversation as resolved.
Show resolved Hide resolved
}

/// Blocks the program for *at least* `cycles` CPU cycles.
Expand All @@ -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!(),
}
}
Loading