Skip to content

Commit 7bdb6e9

Browse files
committed
_dispatch_interrupt function and docs
1 parent e3aa1c2 commit 7bdb6e9

File tree

6 files changed

+185
-84
lines changed

6 files changed

+185
-84
lines changed

.github/workflows/riscv-rt.yaml

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

riscv-rt/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ If `v-trap` feature is enabled, this macro also generates its corresponding trap
1919
- Moved all the assembly code to `asm.rs`
2020
- Use `weak` symbols for functions such as `_mp_hook` or `_start_trap`
2121
- `abort` is now `weak`, so it is possible to link third-party libraries including this symbol.
22+
- `_start_trap_rust` now only deals with exceptions. When an interrupt is detected, it now calls
23+
to `_dispatch_interrupt`.
2224

2325
### Removed
2426

riscv-rt/link.x.in

+12-7
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,20 @@ PROVIDE(_heap_size = 0);
3535
and then returns. Users can override this alias by defining the symbol themselves */
3636
EXTERN(_start_trap);
3737

38+
/* Default interrupt trap entry point. When vectored trap mode is enabled,
39+
the riscv-rt crate provides an implementation of this function, which saves caller saved
40+
registers, calls the the DefaultHandler ISR, restores caller saved registers and returns. */
41+
PROVIDE(_start_DefaultHandler_trap = _start_trap);
42+
3843
/* When vectored trap mode is enabled, each interrupt source must implement its own
3944
trap entry point. By default, all interrupts start in _start_trap. However, users can
4045
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);
46+
PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap);
47+
PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap);
48+
PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap);
49+
PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap);
50+
PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap);
51+
PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap);
4752

4853
/** EXCEPTION HANDLERS **/
4954

@@ -61,7 +66,7 @@ PROVIDE(Breakpoint = ExceptionHandler);
6166
PROVIDE(LoadMisaligned = ExceptionHandler);
6267
PROVIDE(LoadFault = ExceptionHandler);
6368
PROVIDE(StoreMisaligned = ExceptionHandler);
64-
PROVIDE(StoreFault = ExceptionHandler);;
69+
PROVIDE(StoreFault = ExceptionHandler);
6570
PROVIDE(UserEnvCall = ExceptionHandler);
6671
PROVIDE(SupervisorEnvCall = ExceptionHandler);
6772
PROVIDE(MachineEnvCall = ExceptionHandler);

riscv-rt/macros/src/lib.rs

+101-62
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,11 @@ enum RiscvArch {
319319
Rv64,
320320
}
321321

322+
/// Size of the trap frame (in number of registers)
322323
const TRAP_SIZE: usize = 16;
323324

324325
#[rustfmt::skip]
326+
/// List of the register names to be stored in the trap frame
325327
const TRAP_FRAME: [&str; TRAP_SIZE] = [
326328
"ra",
327329
"t0",
@@ -341,6 +343,14 @@ const TRAP_FRAME: [&str; TRAP_SIZE] = [
341343
"a7",
342344
];
343345

346+
/// Generate the assembly instructions to store the trap frame.
347+
///
348+
/// The `arch` parameter is used to determine the width of the registers.
349+
///
350+
/// The `filter` function is used to filter which registers to store.
351+
/// This is useful to optimize the binary size in vectored interrupt mode, which divides the trap
352+
/// frame storage in two parts: the first part saves space in the stack and stores only the `a0` register,
353+
/// while the second part stores the remaining registers.
344354
fn store_trap<T: FnMut(&str) -> bool>(arch: RiscvArch, mut filter: T) -> String {
345355
let (width, store) = match arch {
346356
RiscvArch::Rv32 => (4, "sw"),
@@ -357,6 +367,8 @@ fn store_trap<T: FnMut(&str) -> bool>(arch: RiscvArch, mut filter: T) -> String
357367
stores.join("\n")
358368
}
359369

370+
/// Generate the assembly instructions to load the trap frame.
371+
/// The `arch` parameter is used to determine the width of the registers.
360372
fn load_trap(arch: RiscvArch) -> String {
361373
let (width, load) = match arch {
362374
RiscvArch::Rv32 => (4, "lw"),
@@ -369,16 +381,31 @@ fn load_trap(arch: RiscvArch) -> String {
369381
loads.join("\n")
370382
}
371383

384+
/// Generates weak `_start_trap` function in assembly for RISCV-32 targets.
385+
///
386+
/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`.
387+
/// The trap frame is allocated on the stack and deallocated after the call.
372388
#[proc_macro]
373389
pub fn weak_start_trap_riscv32(_input: TokenStream) -> TokenStream {
374390
weak_start_trap(RiscvArch::Rv32)
375391
}
376392

393+
/// Generates weak `_start_trap` function in assembly for RISCV-64 targets.
394+
///
395+
/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`.
396+
/// The trap frame is allocated on the stack and deallocated after the call.
377397
#[proc_macro]
378398
pub fn weak_start_trap_riscv64(_input: TokenStream) -> TokenStream {
379399
weak_start_trap(RiscvArch::Rv64)
380400
}
381401

402+
/// Generates weak `_start_trap` function in assembly.
403+
///
404+
/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`.
405+
/// The trap frame is allocated on the stack and deallocated after the call.
406+
///
407+
/// The `arch` parameter is used to determine the width of the registers.
408+
/// The macro also ensures that the trap frame size is 16-byte aligned.
382409
fn weak_start_trap(arch: RiscvArch) -> TokenStream {
383410
let width = match arch {
384411
RiscvArch::Rv32 => 4,
@@ -398,7 +425,7 @@ fn weak_start_trap(arch: RiscvArch) -> TokenStream {
398425
#[cfg(not(feature = "s-mode"))]
399426
let ret = "mret";
400427

401-
let instructions: proc_macro2::TokenStream = format!(
428+
format!(
402429
"
403430
core::arch::global_asm!(
404431
\".section .trap, \\\"ax\\\"
@@ -415,26 +442,76 @@ _start_trap:
415442
\");"
416443
)
417444
.parse()
418-
.unwrap();
445+
.unwrap()
446+
}
419447

420-
#[cfg(feature = "v-trap")]
421-
let v_trap = v_trap::continue_interrupt_trap(arch);
422-
#[cfg(not(feature = "v-trap"))]
423-
let v_trap = proc_macro2::TokenStream::new();
448+
/// Generates vectored interrupt trap functions in assembly for RISCV-32 targets.
449+
#[cfg(feature = "v-trap")]
450+
#[proc_macro]
451+
pub fn vectored_interrupt_trap_riscv32(_input: TokenStream) -> TokenStream {
452+
vectored_interrupt_trap(RiscvArch::Rv32)
453+
}
424454

425-
quote!(
426-
#instructions
427-
#v_trap
428-
)
429-
.into()
455+
/// Generates vectored interrupt trap functions in assembly for RISCV-64 targets.
456+
#[cfg(feature = "v-trap")]
457+
#[proc_macro]
458+
pub fn vectored_interrupt_trap_riscv64(_input: TokenStream) -> TokenStream {
459+
vectored_interrupt_trap(RiscvArch::Rv64)
460+
}
461+
462+
#[cfg(feature = "v-trap")]
463+
/// Generates global '_start_DefaultHandler_trap' and '_continue_interrupt_trap' functions in assembly.
464+
/// The '_start_DefaultHandler_trap' function stores the trap frame partially (only register a0) and
465+
/// jumps to the interrupt handler. The '_continue_interrupt_trap' function stores the trap frame
466+
/// partially (all registers except a0), jumps to the interrupt handler, and restores the trap frame.
467+
fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream {
468+
let width = match arch {
469+
RiscvArch::Rv32 => 4,
470+
RiscvArch::Rv64 => 8,
471+
};
472+
let store_start = store_trap(arch, |reg| reg == "a0");
473+
let store_continue = store_trap(arch, |reg| reg != "a0");
474+
let load = load_trap(arch);
475+
476+
#[cfg(feature = "s-mode")]
477+
let ret = "sret";
478+
#[cfg(not(feature = "s-mode"))]
479+
let ret = "mret";
480+
481+
let instructions = format!(
482+
"
483+
core::arch::global_asm!(
484+
\".section .trap, \\\"ax\\\"
485+
486+
.global _start_DefaultHandler_trap
487+
_start_DefaultHandler_trap:
488+
addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame
489+
{store_start} // store trap partially (only register a0)
490+
la a0, DefaultHandler // load interrupt handler address into a0
491+
492+
.global _continue_interrupt_trap
493+
_continue_interrupt_trap:
494+
{store_continue} // store trap partially (all registers except a0)
495+
jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0)
496+
{load} // restore trap frame
497+
addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame
498+
{ret} // return from interrupt
499+
\");"
500+
);
501+
502+
instructions.parse().unwrap()
430503
}
431504

432505
#[proc_macro_attribute]
506+
/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
507+
/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets.
433508
pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream {
434509
interrupt(args, input, RiscvArch::Rv32)
435510
}
436511

437512
#[proc_macro_attribute]
513+
/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
514+
/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets.
438515
pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream {
439516
interrupt(args, input, RiscvArch::Rv64)
440517
}
@@ -487,7 +564,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
487564
#[cfg(not(feature = "v-trap"))]
488565
let start_trap = proc_macro2::TokenStream::new();
489566
#[cfg(feature = "v-trap")]
490-
let start_trap = v_trap::start_interrupt_trap(ident, _arch);
567+
let start_trap = start_interrupt_trap(ident, _arch);
491568

492569
quote!(
493570
#start_trap
@@ -498,65 +575,27 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
498575
}
499576

500577
#[cfg(feature = "v-trap")]
501-
mod v_trap {
502-
use super::*;
503-
504-
pub(crate) fn start_interrupt_trap(
505-
ident: &syn::Ident,
506-
arch: RiscvArch,
507-
) -> proc_macro2::TokenStream {
508-
let interrupt = ident.to_string();
509-
let width = match arch {
510-
RiscvArch::Rv32 => 4,
511-
RiscvArch::Rv64 => 8,
512-
};
513-
let store = store_trap(arch, |r| r == "a0");
578+
fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream {
579+
let interrupt = ident.to_string();
580+
let width = match arch {
581+
RiscvArch::Rv32 => 4,
582+
RiscvArch::Rv64 => 8,
583+
};
584+
let store = store_trap(arch, |r| r == "a0");
514585

515-
let instructions = format!(
516-
"
586+
let instructions = format!(
587+
"
517588
core::arch::global_asm!(
518589
\".section .trap, \\\"ax\\\"
519-
.align {width}
590+
.align 2
520591
.global _start_{interrupt}_trap
521592
_start_{interrupt}_trap:
522593
addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame
523594
{store} // store trap partially (only register a0)
524595
la a0, {interrupt} // load interrupt handler address into a0
525596
j _continue_interrupt_trap // jump to common part of interrupt trap
526597
\");"
527-
);
528-
529-
instructions.parse().unwrap()
530-
}
531-
532-
pub(crate) fn continue_interrupt_trap(arch: RiscvArch) -> proc_macro2::TokenStream {
533-
let width = match arch {
534-
RiscvArch::Rv32 => 4,
535-
RiscvArch::Rv64 => 8,
536-
};
537-
let store = store_trap(arch, |reg| reg != "a0");
538-
let load = load_trap(arch);
598+
);
539599

540-
#[cfg(feature = "s-mode")]
541-
let ret = "sret";
542-
#[cfg(not(feature = "s-mode"))]
543-
let ret = "mret";
544-
545-
let instructions = format!(
546-
"
547-
core::arch::global_asm!(
548-
\".section .trap, \\\"ax\\\"
549-
.align {width} // TODO is this necessary?
550-
.global _continue_interrupt_trap
551-
_continue_interrupt_trap:
552-
{store} // store trap partially (all registers except a0)
553-
jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0)
554-
{load} // restore trap frame
555-
addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame
556-
{ret} // return from interrupt
557-
\");"
558-
);
559-
560-
instructions.parse().unwrap()
561-
}
600+
instructions.parse().unwrap()
562601
}

riscv-rt/src/asm.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ riscv_rt_macros::weak_start_trap_riscv32!();
282282
#[cfg(riscv64)]
283283
riscv_rt_macros::weak_start_trap_riscv64!();
284284

285+
#[cfg(all(riscv32, feature = "v-trap"))]
286+
riscv_rt_macros::vectored_interrupt_trap_riscv32!();
287+
#[cfg(all(riscv64, feature = "v-trap"))]
288+
riscv_rt_macros::vectored_interrupt_trap_riscv64!();
289+
285290
#[cfg(feature = "v-trap")]
286291
cfg_global_asm!(
287292
// Set the vector mode to vectored.
@@ -297,15 +302,15 @@ cfg_global_asm!(
297302
_vector_table:
298303
j _start_trap // Interrupt 0 is used for exceptions
299304
j _start_SupervisorSoft_trap
300-
j _start_trap // Interrupt 2 is reserved
305+
j _start_DefaultHandler_trap // Interrupt 2 is reserved
301306
j _start_MachineSoft_trap
302-
j _start_trap // Interrupt 4 is reserved
307+
j _start_DefaultHandler_trap // Interrupt 4 is reserved
303308
j _start_SupervisorTimer_trap
304-
j _start_trap // Interrupt 6 is reserved
309+
j _start_DefaultHandler_trap // Interrupt 6 is reserved
305310
j _start_MachineTimer_trap
306-
j _start_trap // Interrupt 8 is reserved
311+
j _start_DefaultHandler_trap // Interrupt 8 is reserved
307312
j _start_SupervisorExternal_trap
308-
j _start_trap // Interrupt 10 is reserved
313+
j _start_DefaultHandler_trap // Interrupt 10 is reserved
309314
j _start_MachineExternal_trap
310315
311316
// default table does not include the remaining interrupts.

0 commit comments

Comments
 (0)