@@ -13,7 +13,7 @@ use syn::{
13
13
parse:: { self , Parse } ,
14
14
punctuated:: Punctuated ,
15
15
spanned:: Spanned ,
16
- FnArg , ItemFn , LitInt , LitStr , PatType , ReturnType , Type , Visibility ,
16
+ FnArg , ItemFn , LitInt , LitStr , PatType , Path , ReturnType , Type , Visibility ,
17
17
} ;
18
18
19
19
use proc_macro:: TokenStream ;
@@ -357,12 +357,18 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
357
357
res. parse ( ) . unwrap ( )
358
358
}
359
359
360
- #[ derive( Clone , Copy ) ]
360
+ #[ derive( Clone , Copy , Debug ) ]
361
361
enum RiscvArch {
362
362
Rv32 ,
363
363
Rv64 ,
364
364
}
365
365
366
+ #[ derive( Clone , Copy , Debug ) ]
367
+ enum RiscvPacItem {
368
+ ExternalInterrupt ,
369
+ CoreInterrupt ,
370
+ }
371
+
366
372
/// Size of the trap frame (in number of registers)
367
373
const TRAP_SIZE : usize = 16 ;
368
374
@@ -549,26 +555,122 @@ _continue_interrupt_trap:
549
555
}
550
556
551
557
#[ proc_macro_attribute]
552
- /// Attribute to declare an interrupt handler.
553
- ///
554
- /// The function must have the signature `[unsafe] fn() [-> !]`.
555
- /// If the `v-trap` feature is enabled, this macro generates the
556
- /// interrupt trap handler in assembly for RISCV-32 targets.
557
- pub fn interrupt_riscv32 ( args : TokenStream , input : TokenStream ) -> TokenStream {
558
- interrupt ( args, input, RiscvArch :: Rv32 )
558
+ /// Attribute to declare an exception handler. The function must have the signature `[unsafe] fn(&[mut] riscv_rt::TrapFrame) [-> !]`.
559
+ pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
560
+ let f = parse_macro_input ! ( input as ItemFn ) ;
561
+
562
+ // check the function arguments
563
+ if f. sig . inputs . len ( ) > 1 {
564
+ return parse:: Error :: new (
565
+ f. sig . inputs . span ( ) ,
566
+ "`#[exception]` function must at have most one input argument" ,
567
+ )
568
+ . to_compile_error ( )
569
+ . into ( ) ;
570
+ }
571
+
572
+ if let Some ( param) = f. sig . inputs . first ( ) {
573
+ let first_param_type = match param {
574
+ FnArg :: Typed ( t) => * t. ty . clone ( ) ,
575
+ _ => {
576
+ return parse:: Error :: new ( param. span ( ) , "invalid argument" )
577
+ . to_compile_error ( )
578
+ . into ( ) ;
579
+ }
580
+ } ;
581
+
582
+ let expected_types: Vec < Type > = vec ! [
583
+ parse_quote!( & riscv_rt:: TrapFrame ) ,
584
+ parse_quote!( & mut riscv_rt:: TrapFrame ) ,
585
+ ] ;
586
+
587
+ if !expected_types. iter ( ) . any ( |t| first_param_type == * t) {
588
+ return parse:: Error :: new (
589
+ first_param_type. span ( ) ,
590
+ "`#[exception]` function argument must be `&[mut] riscv_rt::TrapFrame`" ,
591
+ )
592
+ . to_compile_error ( )
593
+ . into ( ) ;
594
+ }
595
+ }
596
+
597
+ // check the function signature
598
+ let valid_signature = f. sig . constness . is_none ( )
599
+ && f. sig . asyncness . is_none ( )
600
+ && f. vis == Visibility :: Inherited
601
+ && f. sig . abi . is_none ( )
602
+ && f. sig . generics . params . is_empty ( )
603
+ && f. sig . generics . where_clause . is_none ( )
604
+ && f. sig . variadic . is_none ( )
605
+ && match f. sig . output {
606
+ ReturnType :: Default => true ,
607
+ ReturnType :: Type ( _, ref ty) => matches ! ( * * ty, Type :: Never ( _) ) ,
608
+ } ;
609
+
610
+ if !valid_signature {
611
+ return parse:: Error :: new (
612
+ f. span ( ) ,
613
+ "`#[exception]` function must have signature `[unsafe] fn(&riscv_rt::TrapFrame) [-> !]`" ,
614
+ )
615
+ . to_compile_error ( )
616
+ . into ( ) ;
617
+ }
618
+
619
+ let int_path = parse_macro_input ! ( args as Path ) ;
620
+ let int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ;
621
+ let export_name = format ! ( "{:#}" , int_ident) ;
622
+
623
+ quote ! (
624
+ // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait
625
+ const _: fn ( ) = || {
626
+ fn assert_impl<T : riscv_rt:: ExceptionNumber >( _arg: T ) { }
627
+ assert_impl( #int_path) ;
628
+ } ;
629
+
630
+ #[ export_name = #export_name]
631
+ #f
632
+ )
633
+ . into ( )
559
634
}
560
635
561
636
#[ proc_macro_attribute]
562
- /// Attribute to declare an interrupt handler.
563
- ///
564
- /// The function must have the signature `[unsafe] fn() [-> !]`.
565
- /// If the `v-trap` feature is enabled, this macro generates the
566
- /// interrupt trap handler in assembly for RISCV-64 targets.
567
- pub fn interrupt_riscv64 ( args : TokenStream , input : TokenStream ) -> TokenStream {
568
- interrupt ( args, input, RiscvArch :: Rv64 )
637
+ /// Attribute to declare an core interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
638
+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
639
+ pub fn core_interrupt_riscv32 ( args : TokenStream , input : TokenStream ) -> TokenStream {
640
+ let arch = match ( ) {
641
+ #[ cfg( feature = "v-trap" ) ]
642
+ ( ) => Some ( RiscvArch :: Rv32 ) ,
643
+ #[ cfg( not( feature = "v-trap" ) ) ]
644
+ ( ) => None ,
645
+ } ;
646
+ interrupt ( args, input, RiscvPacItem :: CoreInterrupt , arch)
647
+ }
648
+
649
+ #[ proc_macro_attribute]
650
+ /// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
651
+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
652
+ pub fn core_interrupt_riscv64 ( args : TokenStream , input : TokenStream ) -> TokenStream {
653
+ let arch = match ( ) {
654
+ #[ cfg( feature = "v-trap" ) ]
655
+ ( ) => Some ( RiscvArch :: Rv64 ) ,
656
+ #[ cfg( not( feature = "v-trap" ) ) ]
657
+ ( ) => None ,
658
+ } ;
659
+ interrupt ( args, input, RiscvPacItem :: CoreInterrupt , arch)
569
660
}
570
661
571
- fn interrupt ( args : TokenStream , input : TokenStream , _arch : RiscvArch ) -> TokenStream {
662
+ #[ proc_macro_attribute]
663
+ /// Attribute to declare an external interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
664
+ pub fn external_interrupt ( args : TokenStream , input : TokenStream ) -> TokenStream {
665
+ interrupt ( args, input, RiscvPacItem :: ExternalInterrupt , None )
666
+ }
667
+
668
+ fn interrupt (
669
+ args : TokenStream ,
670
+ input : TokenStream ,
671
+ pac_item : RiscvPacItem ,
672
+ arch : Option < RiscvArch > ,
673
+ ) -> TokenStream {
572
674
let f = parse_macro_input ! ( input as ItemFn ) ;
573
675
574
676
// check the function arguments
@@ -603,30 +705,35 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
603
705
. into ( ) ;
604
706
}
605
707
606
- if !args. is_empty ( ) {
607
- return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
608
- . to_compile_error ( )
609
- . into ( ) ;
610
- }
708
+ let int_path = parse_macro_input ! ( args as Path ) ;
709
+ let int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ;
710
+ let export_name = format ! ( "{:#}" , int_ident) ;
611
711
612
- // XXX should we blacklist other attributes?
613
- let ident = & f. sig . ident ;
614
- let export_name = format ! ( "{:#}" , ident) ;
712
+ let start_trap = match arch {
713
+ Some ( RiscvArch :: Rv32 ) => start_interrupt_trap ( int_ident, RiscvArch :: Rv32 ) ,
714
+ Some ( RiscvArch :: Rv64 ) => start_interrupt_trap ( int_ident, RiscvArch :: Rv64 ) ,
715
+ None => proc_macro2:: TokenStream :: new ( ) ,
716
+ } ;
615
717
616
- # [ cfg ( not ( feature = "v-trap" ) ) ]
617
- let start_trap = proc_macro2 :: TokenStream :: new ( ) ;
618
- # [ cfg ( feature = "v-trap" ) ]
619
- let start_trap = start_interrupt_trap ( ident , _arch ) ;
718
+ let pac_trait = match pac_item {
719
+ RiscvPacItem :: ExternalInterrupt => quote ! ( riscv_rt :: ExternalInterruptNumber ) ,
720
+ RiscvPacItem :: CoreInterrupt => quote ! ( riscv_rt :: CoreInterruptNumber ) ,
721
+ } ;
620
722
621
723
quote ! (
724
+ // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait
725
+ const _: fn ( ) = || {
726
+ fn assert_impl<T : #pac_trait>( _arg: T ) { }
727
+ assert_impl( #int_path) ;
728
+ } ;
729
+
622
730
#start_trap
623
731
#[ export_name = #export_name]
624
732
#f
625
733
)
626
734
. into ( )
627
735
}
628
736
629
- #[ cfg( feature = "v-trap" ) ]
630
737
fn start_interrupt_trap ( ident : & syn:: Ident , arch : RiscvArch ) -> proc_macro2:: TokenStream {
631
738
let interrupt = ident. to_string ( ) ;
632
739
let width = match arch {
0 commit comments