Skip to content

Commit e177b0c

Browse files
committed
core_interrupt, exception, and external_interrupt macros
1 parent a929ae1 commit e177b0c

File tree

5 files changed

+230
-38
lines changed

5 files changed

+230
-38
lines changed

riscv-rt/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313
- Add `no-interrupts` feature to opt-out the default implementation of `_dispatch_core_interrupt`
1414
- Add `pre_init_trap` to detect early errors during the boot process.
1515
- Add `v-trap` feature to enable interrupt handling in vectored mode.
16-
- Add `interrupt` proc macro to help defining interrupt handlers.
16+
- Add `core_interrupt` proc macro to help defining core interrupt handlers.
17+
- Add `external_interrupt` proc macro to help defining external interrupt handlers.
18+
- Add `exception` proc macro to help defining exception handlers.
1719
If `v-trap` feature is enabled, this macro also generates its corresponding trap.
1820
- Add `u-boot` feature, so that you can start your elf binary with u-boot and
1921
work with passed arguments.

riscv-rt/Cargo.toml

Lines changed: 1 addition & 0 deletions
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

Lines changed: 84 additions & 3 deletions
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

Lines changed: 137 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1919
use 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)]
361361
enum 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)
367373
const 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")]
630737
fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream {
631738
let interrupt = ident.to_string();
632739
let width = match arch {

riscv-rt/src/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,13 +485,14 @@ use riscv::register::scause as xcause;
485485
#[cfg(not(feature = "s-mode"))]
486486
use riscv::register::mcause as xcause;
487487

488-
pub use riscv_rt_macros::{entry, pre_init};
488+
pub use riscv_rt_macros::{entry, exception, external_interrupt, pre_init};
489489

490-
#[cfg(riscv32)]
491-
pub use riscv_rt_macros::interrupt_riscv32 as interrupt;
490+
pub use riscv_pac::*;
492491

492+
#[cfg(riscv32)]
493+
pub use riscv_rt_macros::core_interrupt_riscv32 as core_interrupt;
493494
#[cfg(riscv64)]
494-
pub use riscv_rt_macros::interrupt_riscv64 as interrupt;
495+
pub use riscv_rt_macros::core_interrupt_riscv64 as core_interrupt;
495496

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

0 commit comments

Comments
 (0)