@@ -319,9 +319,11 @@ enum RiscvArch {
319319 Rv64 ,
320320}
321321
322+ /// Size of the trap frame (in number of registers)
322323const TRAP_SIZE : usize = 16 ;
323324
324325#[ rustfmt:: skip]
326+ /// List of the register names to be stored in the trap frame
325327const 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.
344354fn 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.
360372fn 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]
373389pub 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]
378398pub 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.
382409fn 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 "
403430core::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.
433508pub 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.
438515pub 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+ "
517588core::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}
0 commit comments