Skip to content
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 @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- Add `miselect` CSR
- Improved assembly macro handling in asm.rs

## [v0.15.0] - 2025-09-08

Expand Down
81 changes: 52 additions & 29 deletions riscv/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,70 @@ macro_rules! instruction {
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
unimplemented!();
}
);
)
}

instruction!(
/// `nop` instruction wrapper
/// `NOP` instruction wrapper
///
/// The `NOP` instruction does not change any architecturally visible state, except for
/// advancing the pc and incrementing any applicable performance counters.
/// advancing the PC and incrementing any applicable performance counters.
///
/// This function generates a no-operation; it's useful to prevent delay loops from being
/// optimized away.
, nop, "nop", options(nomem, nostack));
, nop, "nop", options(nomem, nostack, preserves_flags));

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));
/// 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.
///
/// # Behavior
///
/// - May cause the hart to enter a low-power state
/// - Will be interrupted by any enabled interrupt
/// - No guarantee of actual power savings (implementation-dependent)
,wfi, "wfi", options(nomem, nostack, preserves_flags));

instruction!(
/// `EBREAK` instruction wrapper
///
/// Generates a breakpoint exception.
, unsafe ebreak, "ebreak", options(nomem, nostack));
/// Generates a breakpoint exception for use by debuggers.
///
/// # Behavior
///
/// When executed, this instruction causes a breakpoint exception to be raised,
/// which will typically be handled by a debugger or exception handler.
///
/// # Safety
///
/// This function is unsafe because it unconditionally generates an exception,
/// which can disrupt normal program flow. Only call this when you intend to
/// trigger a breakpoint.
, unsafe ebreak, "ebreak", options(nomem, nostack, preserves_flags));

instruction!(
/// `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.
/// Generates an environment call exception for system services.
///
/// # Note
/// # Behavior
///
/// 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));
/// When executed in different privilege modes:
/// - U-mode: Generates environment-call-from-U-mode exception
/// - S-mode: Generates environment-call-from-S-mode exception
/// - M-mode: Generates environment-call-from-M-mode exception
///
/// # Safety
///
/// This function is unsafe because:
/// - It unconditionally generates an exception
/// - The stack pointer is **NOT** automatically saved/restored
/// - The exception handler is responsible for proper context management
/// - Improper use can crash the system
, unsafe ecall, "ecall", options(nomem, nostack, preserves_flags));

instruction!(
/// `SFENCE.VMA` instruction wrapper (all address spaces and page table levels)
Expand Down Expand Up @@ -118,8 +143,8 @@ instruction!(
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));
};
core::arch::asm!("sfence.vma {}, {}", in(reg) addr, in(reg) asid, options(nostack));
}
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
unimplemented!();
}
Expand All @@ -139,21 +164,19 @@ pub fn sfence_vma(asid: usize, addr: usize) {
allow(unused_variables)
)]
pub fn delay(cycles: u32) {
match () {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
() => {
let real_cyc = 1 + cycles / 2;
unsafe {
core::arch::asm!(
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
{
let real_cyc = 1 + cycles / 2;
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!(),
}
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
unimplemented!();
}
Loading