@@ -13,7 +13,7 @@ use syn::{
1313 parse:: { self , Parse } ,
1414 punctuated:: Punctuated ,
1515 spanned:: Spanned ,
16- FnArg , ItemFn , LitInt , LitStr , PatType , ReturnType , Type , Visibility ,
16+ FnArg , ItemFn , LitInt , LitStr , PatType , Path , ReturnType , Type , Visibility ,
1717} ;
1818
1919use proc_macro:: TokenStream ;
@@ -357,12 +357,18 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
357357 res. parse ( ) . unwrap ( )
358358}
359359
360- #[ derive( Clone , Copy ) ]
360+ #[ derive( Clone , Copy , Debug ) ]
361361enum RiscvArch {
362362 Rv32 ,
363363 Rv64 ,
364364}
365365
366+ #[ derive( Clone , Copy , Debug ) ]
367+ enum RiscvPacItem {
368+ ExternalInterrupt ,
369+ CoreInterrupt ,
370+ }
371+
366372/// Size of the trap frame (in number of registers)
367373const TRAP_SIZE : usize = 16 ;
368374
@@ -549,26 +555,122 @@ _continue_interrupt_trap:
549555}
550556
551557#[ 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 ( )
559634}
560635
561636#[ 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)
569660}
570661
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 {
572674 let f = parse_macro_input ! ( input as ItemFn ) ;
573675
574676 // check the function arguments
@@ -603,30 +705,35 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
603705 . into ( ) ;
604706 }
605707
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) ;
611711
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+ } ;
615717
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+ } ;
620722
621723 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+
622730 #start_trap
623731 #[ export_name = #export_name]
624732 #f
625733 )
626734 . into ( )
627735}
628736
629- #[ cfg( feature = "v-trap" ) ]
630737fn start_interrupt_trap ( ident : & syn:: Ident , arch : RiscvArch ) -> proc_macro2:: TokenStream {
631738 let interrupt = ident. to_string ( ) ;
632739 let width = match arch {
0 commit comments