Skip to content

Commit 3c24f5e

Browse files
committed
core_interrupt, exception, and external_interrupt macros
1 parent 7ce6769 commit 3c24f5e

File tree

4 files changed

+226
-30
lines changed

4 files changed

+226
-30
lines changed

riscv-rt/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ links = "riscv-rt" # Prevent multiple versions of riscv-rt being linked
1414

1515
[dependencies]
1616
riscv = { path = "../riscv", version = "0.12.0" }
17+
riscv-pac = { path = "../riscv-pac", version = "0.2.0" }
1718
riscv-rt-macros = { path = "macros", version = "0.2.1" }
1819

1920
[dev-dependencies]

riscv-rt/examples/empty.rs

+84-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,97 @@
44
extern crate panic_halt;
55
extern crate riscv_rt;
66

7-
use riscv_rt::{entry, interrupt};
7+
use riscv_rt::{core_interrupt, entry, exception, external_interrupt};
8+
9+
use riscv::{
10+
interrupt::{Exception, Interrupt},
11+
result::*,
12+
};
13+
14+
/// Just a dummy type to test the `ExternalInterrupt` trait.
15+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16+
pub enum ExternalInterrupt {
17+
GPIO,
18+
UART,
19+
}
20+
21+
unsafe impl riscv::InterruptNumber for ExternalInterrupt {
22+
const MAX_INTERRUPT_NUMBER: usize = 1;
23+
24+
#[inline]
25+
fn number(self) -> usize {
26+
self as usize
27+
}
28+
29+
#[inline]
30+
fn from_number(value: usize) -> Result<Self> {
31+
match value {
32+
0 => Ok(Self::GPIO),
33+
1 => Ok(Self::UART),
34+
_ => Err(Error::InvalidVariant(value)),
35+
}
36+
}
37+
}
38+
unsafe impl riscv::ExternalInterruptNumber for ExternalInterrupt {}
839

940
#[entry]
1041
fn main() -> ! {
1142
// do something here
1243
loop {}
1344
}
1445

15-
#[interrupt]
16-
fn MachineSoft() {
46+
/* EXAMPLES OF USING THE core_interrupt MACRO FOR CORE INTERRUPT HANDLERS.
47+
IF v-trap ENABLED, THE MACRO ALSO DEFINES _start_COREINTERRUPT_trap routines */
48+
49+
/// Handler with the simplest signature.
50+
#[core_interrupt(Interrupt::SupervisorSoft)]
51+
fn supervisor_soft() {
52+
// do something here
53+
loop {}
54+
}
55+
56+
/// Handler with the most complete signature.
57+
#[core_interrupt(Interrupt::SupervisorTimer)]
58+
unsafe fn supervisor_timer() -> ! {
59+
// do something here
60+
loop {}
61+
}
62+
63+
/* EXAMPLES OF USING THE external_interrupt MACRO FOR EXTERNAL INTERRUPT HANDLERS. */
64+
65+
/// Handler with the simplest signature.
66+
#[external_interrupt(ExternalInterrupt::GPIO)]
67+
fn external_gpio() {
68+
// do something here
69+
loop {}
70+
}
71+
72+
/// Handler with the most complete signature.
73+
#[external_interrupt(ExternalInterrupt::UART)]
74+
unsafe fn external_uart() -> ! {
75+
// do something here
76+
loop {}
77+
}
78+
79+
/* EXAMPLES OF USING THE exception MACRO FOR EXCEPTION HANDLERS. */
80+
81+
/// Handler with the simplest signature.
82+
#[exception(Exception::InstructionMisaligned)]
83+
fn instruction_misaligned() {
84+
// do something here
85+
loop {}
86+
}
87+
88+
/// Handler with the most complete signature.
89+
#[exception(Exception::IllegalInstruction)]
90+
unsafe fn illegal_instruction(_trap: &riscv_rt::TrapFrame) -> ! {
91+
// do something here
92+
loop {}
93+
}
94+
95+
// The reference to TrapFrame can be mutable if the handler needs to modify it.
96+
#[exception(Exception::Breakpoint)]
97+
unsafe fn breakpoint(_trap: &mut riscv_rt::TrapFrame) -> ! {
1798
// do something here
1899
loop {}
19100
}

riscv-rt/macros/src/lib.rs

+136-23
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use proc_macro2::Span;
1212
use 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

1818
use 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)]
317317
enum 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)
323329
const 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")]
580693
fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream {
581694
let interrupt = ident.to_string();
582695
let width = match arch {

riscv-rt/src/lib.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -472,13 +472,14 @@ use riscv::register::scause as xcause;
472472
#[cfg(not(feature = "s-mode"))]
473473
use riscv::register::mcause as xcause;
474474

475-
pub use riscv_rt_macros::{entry, pre_init};
475+
pub use riscv_rt_macros::{entry, exception, external_interrupt, pre_init};
476476

477-
#[cfg(riscv32)]
478-
pub use riscv_rt_macros::interrupt_riscv32 as interrupt;
477+
pub use riscv_pac::*;
479478

479+
#[cfg(riscv32)]
480+
pub use riscv_rt_macros::core_interrupt_riscv32 as core_interrupt;
480481
#[cfg(riscv64)]
481-
pub use riscv_rt_macros::interrupt_riscv64 as interrupt;
482+
pub use riscv_rt_macros::core_interrupt_riscv64 as core_interrupt;
482483

483484
/// We export this static with an informative name so that if an application attempts to link
484485
/// two copies of riscv-rt together, linking will fail. We also declare a links key in

0 commit comments

Comments
 (0)