Skip to content

riscv-rt: Add RISCV_MTVEC_ALIGN to control the alignment of the vector table #259

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

Merged
merged 1 commit into from
Jan 25, 2025
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
3 changes: 2 additions & 1 deletion riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Changed

- Ensure the `.heap` section is 4-byte aligned
- Use `RISCV_MTVEC_ALIGN` to control the alignment constraint of the vector table.
- Ensure the `.heap` section is 4-byte aligned.
- Limit rustc cfg flags to `riscvi`, `riscvm`, `riscvf`, and `riscvd`.
- Temporary use of `RISCV_RT_LLVM_ARCH_PATCH` environment variable to include the
temporary patch required for avoid LLVM spurious errors.
Expand Down
7 changes: 7 additions & 0 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ fn main() {
// make sure that these env variables are not changed without notice.
println!("cargo:rerun-if-env-changed=RISCV_RT_BASE_ISA");
println!("cargo:rerun-if-env-changed=RISCV_RT_LLVM_ARCH_PATCH");
if env::var_os("CARGO_FEATURE_V_TRAP").is_some()
&& env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none()
{
// This environment variable is used by the `#[riscv::pac_enum()]` call in
// `src/interrupts.rs` (when `v-trap` is enabled and `no-interrupts` disabled).
println!("cargo:rerun-if-env-changed=RISCV_MTVEC_ALIGN");
}

for flag in target.rustc_flags() {
// Required until target_feature risc-v is stable and in-use
Expand Down
105 changes: 16 additions & 89 deletions riscv-rt/src/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,97 +7,24 @@
//!
//! In vectored mode (i.e., `v-trap` feature enabled), interrupt dispatching is handled by hardware.
//! To support this mode, we provide inline assembly code that defines the interrupt vector table.
//! Since the alignment constraint of this vector table is implementation-specific, it can be
//! changed by setting the `RISCV_MTVEC_ALIGN` environment variable (the default is 4).
//!
//! # Note
//!
//! If your target has custom core interrupt sources, the target PAC might provide equivalent
//! code to adapt for the target needs. In this case, you may need to opt out this module.
//! To do so, activate the `no-interrupts` feature of the `riscv-rt` crate.

#[cfg(not(feature = "v-trap"))]
extern "C" {
fn SupervisorSoft();
fn MachineSoft();
fn SupervisorTimer();
fn MachineTimer();
fn SupervisorExternal();
fn MachineExternal();
}

/// Array with all the core interrupt handlers sorted according to their interrupt source code.
///
/// # Note
///
/// This array is necessary only in direct mode (i.e., `v-trap` feature disabled).
#[cfg(not(feature = "v-trap"))]
#[no_mangle]
pub static __CORE_INTERRUPTS: [Option<unsafe extern "C" fn()>; 12] = [
None,
Some(SupervisorSoft),
None,
Some(MachineSoft),
None,
Some(SupervisorTimer),
None,
Some(MachineTimer),
None,
Some(SupervisorExternal),
None,
Some(MachineExternal),
];

/// It calls the corresponding interrupt handler depending on the interrupt source code.
///
/// # Note
///
/// This function is only required in direct mode (i.e., `v-trap` feature disabled).
/// In vectored mode, interrupt handler dispatching is performed directly by hardware.
///
/// # Safety
///
/// This function must be called only from the [`crate::start_trap_rust`] function.
/// Do **NOT** call this function directly.
#[cfg(not(feature = "v-trap"))]
#[inline]
#[no_mangle]
pub unsafe extern "C" fn _dispatch_core_interrupt(code: usize) {
extern "C" {
fn DefaultHandler();
}
match __CORE_INTERRUPTS.get(code) {
Some(Some(handler)) => handler(),
_ => DefaultHandler(),
}
}
//! If your target has custom core interrupt sources, the target PAC might provide equivalent code
//! to adapt for the target needs (and is responsible for any alignment constraint). In this case,
//! you may need to opt out this module. To do so, activate the `no-interrupts` feature of the
//! `riscv-rt` crate.

// In vectored mode, we also must provide a vector table
#[cfg(all(
any(target_arch = "riscv32", target_arch = "riscv64"),
feature = "v-trap"
))]
core::arch::global_asm!(
r#" .section .trap, "ax"
.weak _vector_table
.type _vector_table, @function

.option push
.balign 0x4 // TODO check if this is the correct alignment
.option norelax
.option norvc

_vector_table:
j _start_trap // Interrupt 0 is used for exceptions
j _start_SupervisorSoft_trap
j _start_DefaultHandler_trap // Interrupt 2 is reserved
j _start_MachineSoft_trap
j _start_DefaultHandler_trap // Interrupt 4 is reserved
j _start_SupervisorTimer_trap
j _start_DefaultHandler_trap // Interrupt 6 is reserved
j _start_MachineTimer_trap
j _start_DefaultHandler_trap // Interrupt 8 is reserved
j _start_SupervisorExternal_trap
j _start_DefaultHandler_trap // Interrupt 10 is reserved
j _start_MachineExternal_trap

.option pop"#
);
#[riscv::pac_enum(unsafe CoreInterruptNumber)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Interrupt {
SupervisorSoft = 1,
MachineSoft = 3,
SupervisorTimer = 5,
MachineTimer = 7,
SupervisorExternal = 9,
MachineExternal = 11,
}
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

- Use `RISCV_MTVEC_ALIGN` to control the alignment constraint of the vector table
- Simplify register macros with `cfg` field
- Align assembly functions with `cortex-m`
- Use CSR helper macros to define `marchid` register
Expand Down
28 changes: 25 additions & 3 deletions riscv/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,20 @@ impl PacEnumItem {
}

fn vector_table(&self) -> TokenStream2 {
let mut asm = String::from(
let align = match std::env::var("RISCV_MTVEC_ALIGN") {
Ok(x) => x.parse::<u32>().ok(),
Err(std::env::VarError::NotPresent) => Some(4),
Err(std::env::VarError::NotUnicode(_)) => None,
};
let align = match align {
Some(x) if x.is_power_of_two() && 4 <= x => x,
_ => {
return quote!(compile_error!(
"RISCV_MTVEC_ALIGN is not a power of 2 (minimum 4)"
))
}
};
let mut asm = format!(
r#"
#[cfg(all(feature = "v-trap", any(target_arch = "riscv32", target_arch = "riscv64")))]
core::arch::global_asm!("
Expand All @@ -274,7 +287,7 @@ core::arch::global_asm!("
.type _vector_table, @function

.option push
.balign 0x4 // TODO check if this is the correct alignment
.balign {align}
.option norelax
.option norvc

Expand Down Expand Up @@ -315,6 +328,8 @@ core::arch::global_asm!("
let max_discriminant = self.max_number;
let valid_matches = self.valid_matches();

let is_core_interrupt = matches!(attr, PacTrait::Interrupt(InterruptType::Core));

// Push the trait implementation
res.push(quote! {
unsafe impl riscv::#trait_name for #name {
Expand Down Expand Up @@ -350,19 +365,26 @@ core::arch::global_asm!("

let handlers = self.handlers(&trap_config);
let interrupt_array = self.handlers_array();
let cfg_v_trap = match is_core_interrupt {
true => Some(quote!(#[cfg(not(feature = "v-trap"))])),
false => None,
};

// Push the interrupt handler functions and the interrupt array
res.push(quote! {
#cfg_v_trap
extern "C" {
#(#handlers;)*
}

#cfg_v_trap
#[doc(hidden)]
#[no_mangle]
pub static #vector_table: [Option<unsafe extern "C" fn(#(#array_signature),*)>; #max_discriminant + 1] = [
#(#interrupt_array),*
];

#cfg_v_trap
#[inline]
#[no_mangle]
unsafe extern "C" fn #dispatch_fn_name(#(#dispatch_fn_args),*) {
Expand All @@ -378,7 +400,7 @@ core::arch::global_asm!("
});
}

if let PacTrait::Interrupt(InterruptType::Core) = attr {
if is_core_interrupt {
res.push(self.vector_table());
}

Expand Down
Loading