@@ -12,7 +12,7 @@ use proc_macro2::Span;
1212use syn:: {
1313 parse:: { self , Parse } ,
1414 spanned:: Spanned ,
15- FnArg , ItemFn , LitInt , LitStr , PathArguments , ReturnType , Type , Visibility ,
15+ FnArg , ItemFn , LitInt , LitStr , Path , PathArguments , ReturnType , Type , Visibility ,
1616} ;
1717
1818use proc_macro:: TokenStream ;
@@ -313,12 +313,18 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
313313 res. parse ( ) . unwrap ( )
314314}
315315
316- #[ derive( Clone , Copy ) ]
316+ #[ derive( Clone , Copy , Debug ) ]
317317enum RiscvArch {
318318 Rv32 ,
319319 Rv64 ,
320320}
321321
322+ #[ derive( Clone , Copy , Debug ) ]
323+ enum RiscvPacItem {
324+ ExternalInterrupt ,
325+ CoreInterrupt ,
326+ }
327+
322328/// Size of the trap frame (in number of registers)
323329const TRAP_SIZE : usize = 16 ;
324330
@@ -505,20 +511,122 @@ _continue_interrupt_trap:
505511}
506512
507513#[ 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)
512603}
513604
514605#[ proc_macro_attribute]
515606/// 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)
519616}
520617
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 {
522630 let f = parse_macro_input ! ( input as ItemFn ) ;
523631
524632 // check the function arguments
@@ -553,30 +661,35 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
553661 . into ( ) ;
554662 }
555663
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) ;
561667
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+ } ;
565673
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+ } ;
570678
571679 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+
572686 #start_trap
573687 #[ export_name = #export_name]
574688 #f
575689 )
576690 . into ( )
577691}
578692
579- #[ cfg( feature = "v-trap" ) ]
580693fn start_interrupt_trap ( ident : & syn:: Ident , arch : RiscvArch ) -> proc_macro2:: TokenStream {
581694 let interrupt = ident. to_string ( ) ;
582695 let width = match arch {
0 commit comments