@@ -12,7 +12,7 @@ use proc_macro2::Span;
12
12
use syn:: {
13
13
parse:: { self , Parse } ,
14
14
spanned:: Spanned ,
15
- FnArg , ItemFn , LitInt , LitStr , PathArguments , ReturnType , Type , Visibility ,
15
+ FnArg , ItemFn , LitInt , LitStr , Path , PathArguments , ReturnType , Type , Visibility ,
16
16
} ;
17
17
18
18
use proc_macro:: TokenStream ;
@@ -313,12 +313,18 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
313
313
res. parse ( ) . unwrap ( )
314
314
}
315
315
316
- #[ derive( Clone , Copy ) ]
316
+ #[ derive( Clone , Copy , Debug ) ]
317
317
enum RiscvArch {
318
318
Rv32 ,
319
319
Rv64 ,
320
320
}
321
321
322
+ #[ derive( Clone , Copy , Debug ) ]
323
+ enum RiscvPacItem {
324
+ ExternalInterrupt ,
325
+ CoreInterrupt ,
326
+ }
327
+
322
328
/// Size of the trap frame (in number of registers)
323
329
const TRAP_SIZE : usize = 16 ;
324
330
@@ -505,20 +511,122 @@ _continue_interrupt_trap:
505
511
}
506
512
507
513
#[ proc_macro_attribute]
508
- /// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
509
- /// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets.
510
- pub fn interrupt_riscv32 ( args : TokenStream , input : TokenStream ) -> TokenStream {
511
- interrupt ( args, input, RiscvArch :: Rv32 )
514
+ /// Attribute to declare an exception handler. The function must have the signature `[unsafe] fn(&[mut] riscv_rt::TrapFrame) [-> !]`.
515
+ pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
516
+ let f = parse_macro_input ! ( input as ItemFn ) ;
517
+
518
+ // check the function arguments
519
+ if f. sig . inputs . len ( ) > 1 {
520
+ return parse:: Error :: new (
521
+ f. sig . inputs . span ( ) ,
522
+ "`#[exception]` function must at have most one input argument" ,
523
+ )
524
+ . to_compile_error ( )
525
+ . into ( ) ;
526
+ }
527
+
528
+ if let Some ( param) = f. sig . inputs . first ( ) {
529
+ let first_param_type = match param {
530
+ FnArg :: Typed ( t) => * t. ty . clone ( ) ,
531
+ _ => {
532
+ return parse:: Error :: new ( param. span ( ) , "invalid argument" )
533
+ . to_compile_error ( )
534
+ . into ( ) ;
535
+ }
536
+ } ;
537
+
538
+ let expected_types: Vec < Type > = vec ! [
539
+ parse_quote!( & riscv_rt:: TrapFrame ) ,
540
+ parse_quote!( & mut riscv_rt:: TrapFrame ) ,
541
+ ] ;
542
+
543
+ if !expected_types. iter ( ) . any ( |t| first_param_type == * t) {
544
+ return parse:: Error :: new (
545
+ first_param_type. span ( ) ,
546
+ "`#[exception]` function argument must be `&[mut] riscv_rt::TrapFrame`" ,
547
+ )
548
+ . to_compile_error ( )
549
+ . into ( ) ;
550
+ }
551
+ }
552
+
553
+ // check the function signature
554
+ let valid_signature = f. sig . constness . is_none ( )
555
+ && f. sig . asyncness . is_none ( )
556
+ && f. vis == Visibility :: Inherited
557
+ && f. sig . abi . is_none ( )
558
+ && f. sig . generics . params . is_empty ( )
559
+ && f. sig . generics . where_clause . is_none ( )
560
+ && f. sig . variadic . is_none ( )
561
+ && match f. sig . output {
562
+ ReturnType :: Default => true ,
563
+ ReturnType :: Type ( _, ref ty) => matches ! ( * * ty, Type :: Never ( _) ) ,
564
+ } ;
565
+
566
+ if !valid_signature {
567
+ return parse:: Error :: new (
568
+ f. span ( ) ,
569
+ "`#[exception]` function must have signature `[unsafe] fn(&riscv_rt::TrapFrame) [-> !]`" ,
570
+ )
571
+ . to_compile_error ( )
572
+ . into ( ) ;
573
+ }
574
+
575
+ let int_path = parse_macro_input ! ( args as Path ) ;
576
+ let int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ;
577
+ let export_name = format ! ( "{:#}" , int_ident) ;
578
+
579
+ quote ! (
580
+ // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait
581
+ const _: fn ( ) = || {
582
+ fn assert_impl<T : riscv_rt:: ExceptionNumber >( _arg: T ) { }
583
+ assert_impl( #int_path) ;
584
+ } ;
585
+
586
+ #[ export_name = #export_name]
587
+ #f
588
+ )
589
+ . into ( )
590
+ }
591
+
592
+ #[ proc_macro_attribute]
593
+ /// Attribute to declare an core interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
594
+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
595
+ pub fn core_interrupt_riscv32 ( args : TokenStream , input : TokenStream ) -> TokenStream {
596
+ let arch = match ( ) {
597
+ #[ cfg( feature = "v-trap" ) ]
598
+ ( ) => Some ( RiscvArch :: Rv32 ) ,
599
+ #[ cfg( not( feature = "v-trap" ) ) ]
600
+ ( ) => None ,
601
+ } ;
602
+ interrupt ( args, input, RiscvPacItem :: CoreInterrupt , arch)
512
603
}
513
604
514
605
#[ proc_macro_attribute]
515
606
/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
516
- /// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets.
517
- pub fn interrupt_riscv64 ( args : TokenStream , input : TokenStream ) -> TokenStream {
518
- interrupt ( args, input, RiscvArch :: Rv64 )
607
+ /// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
608
+ pub fn core_interrupt_riscv64 ( args : TokenStream , input : TokenStream ) -> TokenStream {
609
+ let arch = match ( ) {
610
+ #[ cfg( feature = "v-trap" ) ]
611
+ ( ) => Some ( RiscvArch :: Rv64 ) ,
612
+ #[ cfg( not( feature = "v-trap" ) ) ]
613
+ ( ) => None ,
614
+ } ;
615
+ interrupt ( args, input, RiscvPacItem :: CoreInterrupt , arch)
519
616
}
520
617
521
- fn interrupt ( args : TokenStream , input : TokenStream , _arch : RiscvArch ) -> TokenStream {
618
+ #[ proc_macro_attribute]
619
+ /// Attribute to declare an external interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`.
620
+ pub fn external_interrupt ( args : TokenStream , input : TokenStream ) -> TokenStream {
621
+ interrupt ( args, input, RiscvPacItem :: ExternalInterrupt , None )
622
+ }
623
+
624
+ fn interrupt (
625
+ args : TokenStream ,
626
+ input : TokenStream ,
627
+ pac_item : RiscvPacItem ,
628
+ arch : Option < RiscvArch > ,
629
+ ) -> TokenStream {
522
630
let f = parse_macro_input ! ( input as ItemFn ) ;
523
631
524
632
// check the function arguments
@@ -553,30 +661,35 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
553
661
. into ( ) ;
554
662
}
555
663
556
- if !args. is_empty ( ) {
557
- return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
558
- . to_compile_error ( )
559
- . into ( ) ;
560
- }
664
+ let int_path = parse_macro_input ! ( args as Path ) ;
665
+ let int_ident = & int_path. segments . last ( ) . unwrap ( ) . ident ;
666
+ let export_name = format ! ( "{:#}" , int_ident) ;
561
667
562
- // XXX should we blacklist other attributes?
563
- let ident = & f. sig . ident ;
564
- let export_name = format ! ( "{:#}" , ident) ;
668
+ let start_trap = match arch {
669
+ Some ( RiscvArch :: Rv32 ) => start_interrupt_trap ( int_ident, RiscvArch :: Rv32 ) ,
670
+ Some ( RiscvArch :: Rv64 ) => start_interrupt_trap ( int_ident, RiscvArch :: Rv64 ) ,
671
+ None => proc_macro2:: TokenStream :: new ( ) ,
672
+ } ;
565
673
566
- # [ cfg ( not ( feature = "v-trap" ) ) ]
567
- let start_trap = proc_macro2 :: TokenStream :: new ( ) ;
568
- # [ cfg ( feature = "v-trap" ) ]
569
- let start_trap = start_interrupt_trap ( ident , _arch ) ;
674
+ let pac_trait = match pac_item {
675
+ RiscvPacItem :: ExternalInterrupt => quote ! ( riscv_rt :: ExternalInterruptNumber ) ,
676
+ RiscvPacItem :: CoreInterrupt => quote ! ( riscv_rt :: CoreInterruptNumber ) ,
677
+ } ;
570
678
571
679
quote ! (
680
+ // Compile-time check to ensure the interrupt path implements the CoreInterruptNumber trait
681
+ const _: fn ( ) = || {
682
+ fn assert_impl<T : #pac_trait>( _arg: T ) { }
683
+ assert_impl( #int_path) ;
684
+ } ;
685
+
572
686
#start_trap
573
687
#[ export_name = #export_name]
574
688
#f
575
689
)
576
690
. into ( )
577
691
}
578
692
579
- #[ cfg( feature = "v-trap" ) ]
580
693
fn start_interrupt_trap ( ident : & syn:: Ident , arch : RiscvArch ) -> proc_macro2:: TokenStream {
581
694
let interrupt = ident. to_string ( ) ;
582
695
let width = match arch {
0 commit comments