Skip to content

Commit 9ac2ec9

Browse files
committed
First draft
1 parent aca6410 commit 9ac2ec9

File tree

9 files changed

+216
-9
lines changed

9 files changed

+216
-9
lines changed

.github/workflows/riscv-rt.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
on:
22
push:
3-
branches: [ master, riscv-rt-asm ]
3+
branches: [ master, vectored-rt ]
44
pull_request:
55
merge_group:
66

@@ -39,6 +39,8 @@ jobs:
3939
run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode
4040
- name : Build (single-hart)
4141
run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=single-hart
42+
- name : Build (v-trap)
43+
run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=v-trap
4244
- name: Build (all features)
4345
run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --all-features
4446

riscv-rt/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Add `pre_init_trap` to detect early errors during the boot process.
13+
- Add `v-trap` feature to enable interrupt handling in vectored mode.
14+
- Add `interrupt` proc macro to help defining interrupt handlers.
15+
If `v-trap` feature is enabled, this macro also generates its corresponding trap.
1316

1417
### Changed
1518

riscv-rt/Cargo.toml

+5-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ license = "ISC"
1212
edition = "2021"
1313
links = "riscv-rt" # Prevent multiple versions of riscv-rt being linked
1414

15-
[features]
16-
s-mode = []
17-
single-hart = []
18-
1915
[dependencies]
2016
riscv = {path = "../riscv", version = "0.11.1"}
2117
riscv-rt-macros = { path = "macros", version = "0.2.1" }
2218

2319
[dev-dependencies]
2420
panic-halt = "0.2.0"
21+
22+
[features]
23+
s-mode = ["riscv-rt-macros/s-mode"]
24+
single-hart = []
25+
v-trap = ["riscv-rt-macros/v-trap"]

riscv-rt/examples/empty.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
extern crate panic_halt;
55
extern crate riscv_rt;
66

7-
use riscv_rt::entry;
7+
use riscv_rt::{entry, interrupt};
88

99
#[entry]
1010
fn main() -> ! {
1111
// do something here
1212
loop {}
1313
}
14+
15+
#[interrupt]
16+
fn MachineSoft() {
17+
// do something here
18+
loop {}
19+
}

riscv-rt/link.x.in

+17
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,23 @@ PROVIDE(_max_hart_id = 0);
2828
PROVIDE(_hart_stack_size = 2K);
2929
PROVIDE(_heap_size = 0);
3030

31+
/** TRAP ENTRY POINTS **/
32+
33+
/* Default trap entry point. The riscv-rt crate provides a weak alias of this function,
34+
which saves caller saved registers, calls _start_trap_rust, restores caller saved registers
35+
and then returns. Users can override this alias by defining the symbol themselves */
36+
EXTERN(_start_trap);
37+
38+
/* When vectored trap mode is enabled, each interrupt source must implement its own
39+
trap entry point. By default, all interrupts start in _start_trap. However, users can
40+
override these alias by defining the symbol themselves */
41+
PROVIDE(_start_SupervisorSoft_trap = _start_trap);
42+
PROVIDE(_start_MachineSoft_trap = _start_trap);
43+
PROVIDE(_start_SupervisorTimer_trap = _start_trap);
44+
PROVIDE(_start_MachineTimer_trap = _start_trap);
45+
PROVIDE(_start_SupervisorExternal_trap = _start_trap);
46+
PROVIDE(_start_MachineExternal_trap = _start_trap);
47+
3148
/** EXCEPTION HANDLERS **/
3249

3350
/* Default exception handler. The riscv-rt crate provides a weak alias of this function,

riscv-rt/macros/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ proc-macro2 = "1.0"
2222
[dependencies.syn]
2323
version = "1.0"
2424
features = ["extra-traits", "full"]
25+
26+
[features]
27+
s-mode = []
28+
v-trap = []

riscv-rt/macros/src/lib.rs

+133
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,136 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
312312
let res = format!("core::arch::global_asm!(\n\"{}\"\n);", instructions);
313313
res.parse().unwrap()
314314
}
315+
316+
enum RiscvArch {
317+
Rv32,
318+
Rv64,
319+
}
320+
321+
#[proc_macro_attribute]
322+
pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream {
323+
interrupt(args, input, RiscvArch::Rv32)
324+
}
325+
326+
#[proc_macro_attribute]
327+
pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream {
328+
interrupt(args, input, RiscvArch::Rv64)
329+
}
330+
331+
fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenStream {
332+
let f = parse_macro_input!(input as ItemFn);
333+
334+
// check the function signature
335+
let valid_signature = f.sig.constness.is_none()
336+
&& f.sig.asyncness.is_none()
337+
&& f.vis == Visibility::Inherited
338+
&& f.sig.abi.is_none()
339+
&& f.sig.generics.params.is_empty()
340+
&& f.sig.generics.where_clause.is_none()
341+
&& f.sig.variadic.is_none()
342+
&& match f.sig.output {
343+
ReturnType::Default => true,
344+
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
345+
};
346+
347+
if !valid_signature {
348+
return parse::Error::new(
349+
f.span(),
350+
"`#[interrupt]` function must have signature `[unsafe] fn() [-> !]`",
351+
)
352+
.to_compile_error()
353+
.into();
354+
}
355+
356+
if !args.is_empty() {
357+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
358+
.to_compile_error()
359+
.into();
360+
}
361+
362+
// XXX should we blacklist other attributes?
363+
let attrs = f.attrs;
364+
let ident = f.sig.ident;
365+
let block = f.block;
366+
367+
#[cfg(not(feature = "v-trap"))]
368+
let start_trap = proc_macro2::TokenStream::new();
369+
#[cfg(feature = "v-trap")]
370+
let start_trap = v_trap::start_interrupt_trap_asm(&ident, _arch);
371+
372+
quote!(
373+
#start_trap
374+
#[export_name = #ident]
375+
#(#attrs)*
376+
pub unsafe fn #ident() #block
377+
)
378+
.into()
379+
}
380+
381+
#[cfg(feature = "v-trap")]
382+
mod v_trap {
383+
use super::*;
384+
385+
const TRAP_SIZE: usize = 16;
386+
387+
#[rustfmt::skip]
388+
const TRAP_FRAME: [&str; TRAP_SIZE] = [
389+
"ra",
390+
"t0",
391+
"t1",
392+
"t2",
393+
"t3",
394+
"t4",
395+
"t5",
396+
"t6",
397+
"a0",
398+
"a1",
399+
"a2",
400+
"a3",
401+
"a4",
402+
"a5",
403+
"a6",
404+
"a7",
405+
];
406+
407+
pub fn start_interrupt_trap_asm(
408+
ident: &syn::Ident,
409+
arch: RiscvArch,
410+
) -> proc_macro2::TokenStream {
411+
let function = ident.to_string();
412+
let (width, store, load) = match arch {
413+
RiscvArch::Rv32 => (4, "sw", "lw"),
414+
RiscvArch::Rv64 => (8, "sd", "ld"),
415+
};
416+
417+
let (mut stores, mut loads) = (Vec::new(), Vec::new());
418+
for (i, r) in TRAP_FRAME.iter().enumerate() {
419+
stores.push(format!(" {store} {r}, {i}*{width}(sp)"));
420+
loads.push(format!(" {load} {r}, {i}*{width}(sp)"));
421+
}
422+
let store = stores.join("\n");
423+
let load = loads.join("\n");
424+
425+
#[cfg(feature = "s-mode")]
426+
let ret = "sret";
427+
#[cfg(not(feature = "s-mode"))]
428+
let ret = "mret";
429+
430+
let instructions = format!(
431+
"
432+
core::arch::global_asm!(
433+
\".section .trap, \\\"ax\\\"
434+
.align {width}
435+
_start_{function}_trap:
436+
addi sp, sp, - {TRAP_SIZE} * {width}
437+
{store}
438+
call {function}
439+
{load}
440+
addi sp, sp, {TRAP_SIZE} * {width}
441+
{ret}\"
442+
);"
443+
);
444+
445+
instructions.parse().unwrap()
446+
}
447+
}

riscv-rt/src/asm.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,14 @@ _mp_hook:
245245
2: li a0, 1
246246
ret",
247247
// Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap`.
248-
// Trap mode is set to `Direct` by default.
249248
// Users can override this function by defining their own `_setup_interrupts`
250249
".weak _setup_interrupts
251-
_setup_interrupts:
252-
la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode
250+
_setup_interrupts:",
251+
#[cfg(not(feature = "v-trap"))]
252+
"la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode
253+
#[cfg(feature = "v-trap")]
254+
"la t0, _vector_table
255+
ori t0, t0, 0x1", // _vector_table is 16-byte aligned, so we must set the bit 0 to activate the Vectored trap mode
253256
#[cfg(feature = "s-mode")]
254257
"csrw stvec, t0",
255258
#[cfg(not(feature = "s-mode"))]
@@ -334,6 +337,38 @@ trap_handler!(
334337
(a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
335338
);
336339

340+
#[cfg(feature = "v-trap")]
341+
cfg_global_asm!(
342+
// Set the vector mode to vectored.
343+
".section .trap, \"ax\"
344+
.weak _vector_table
345+
.type _vector_table, @function
346+
347+
.option push
348+
.balign 0x100 // TODO check if this is the correct alignment
349+
.option norelax
350+
.option norvc
351+
352+
_vector_table:
353+
j _start_trap // Interrupt 0 is used for exceptions
354+
j _start_SupervisorSoft_trap
355+
j _start_trap // Interrupt 2 is reserved
356+
j _start_MachineSoft_trap
357+
j _start_trap // Interrupt 4 is reserved
358+
j _start_SupervisorTimer_trap
359+
j _start_trap // Interrupt 6 is reserved
360+
j _start_MachineTimer_trap
361+
j _start_trap // Interrupt 8 is reserved
362+
j _start_SupervisorExternal_trap
363+
j _start_trap // Interrupt 10 is reserved
364+
j _start_MachineExternal_trap
365+
366+
// default table does not include the remaining interrupts.
367+
// Targets with extra interrupts should override this table.
368+
369+
.option pop",
370+
);
371+
337372
#[rustfmt::skip]
338373
global_asm!(
339374
".section .text.abort

riscv-rt/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,12 @@ use riscv::register::mcause as xcause;
429429

430430
pub use riscv_rt_macros::{entry, pre_init};
431431

432+
#[cfg(riscv32)]
433+
pub use riscv_rt_macros::interrupt_riscv32 as interrupt;
434+
435+
#[cfg(riscv64)]
436+
pub use riscv_rt_macros::interrupt_riscv64 as interrupt;
437+
432438
/// We export this static with an informative name so that if an application attempts to link
433439
/// two copies of riscv-rt together, linking will fail. We also declare a links key in
434440
/// Cargo.toml which is the more modern way to solve the same problem, but we have to keep

0 commit comments

Comments
 (0)