hpm_riscv_rt_macros/
lib.rs1use proc_macro::TokenStream;
10use quote::quote;
11use syn::{
12 parse::Parse, parse::ParseStream, parse_macro_input, spanned::Spanned, Expr, Item, ItemFn,
13};
14
15#[proc_macro_attribute]
28pub fn entry(_args: TokenStream, input: TokenStream) -> TokenStream {
29 let f = parse_macro_input!(input as ItemFn);
30
31 let fn_attrs = &f.attrs;
32 let fn_vis = &f.vis;
33 let fn_sig = &f.sig;
34 let fn_block = &f.block;
35
36 quote!(
37 #(#fn_attrs)*
38 #[unsafe(export_name = "main")]
39 #fn_vis #fn_sig #fn_block
40 )
41 .into()
42}
43
44#[proc_macro_attribute]
61pub fn pre_init(_args: TokenStream, input: TokenStream) -> TokenStream {
62 let f = parse_macro_input!(input as ItemFn);
63
64 let fn_attrs = &f.attrs;
65 let fn_vis = &f.vis;
66 let fn_sig = &f.sig;
67 let fn_block = &f.block;
68
69 quote!(
70 #(#fn_attrs)*
71 #[unsafe(export_name = "__pre_init")]
72 #fn_vis #fn_sig #fn_block
73 )
74 .into()
75}
76
77#[proc_macro_attribute]
96pub fn fast(_args: TokenStream, input: TokenStream) -> TokenStream {
97 let item = parse_macro_input!(input as Item);
98
99 match item {
100 Item::Fn(f) => quote!(
101 #[unsafe(link_section = ".fast.text")]
102 #[inline(never)]
103 #f
104 )
105 .into(),
106 Item::Static(item) => {
107 let section = if is_uninit_expr(&item.expr) {
109 quote!(#[unsafe(link_section = ".fast.bss")])
110 } else {
111 quote!(#[unsafe(link_section = ".fast.data")])
112 };
113
114 quote!(
115 #section
116 #item
117 )
118 .into()
119 }
120 _ => {
121 let span = item.span();
122 syn::Error::new(span, "#[fast] can only be applied to functions or statics")
123 .to_compile_error()
124 .into()
125 }
126 }
127}
128
129fn is_uninit_expr(expr: &Expr) -> bool {
130 if let Expr::Call(call) = expr {
131 let s = quote!(#call).to_string();
132 s.contains("MaybeUninit") && (s.contains("uninit()") || s.contains("uninit_array()"))
133 } else {
134 false
135 }
136}
137
138struct ExternalInterruptArg {
140 interrupt: syn::Path,
141}
142
143impl Parse for ExternalInterruptArg {
144 fn parse(input: ParseStream) -> syn::Result<Self> {
145 Ok(ExternalInterruptArg {
146 interrupt: input.parse()?,
147 })
148 }
149}
150
151#[proc_macro_attribute]
176pub fn external_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
177 let args = parse_macro_input!(args as ExternalInterruptArg);
178 let f = parse_macro_input!(input as ItemFn);
179
180 let interrupt_path = &args.interrupt;
181 let fn_name = &f.sig.ident;
182 let fn_body = &f.block;
183 let fn_attrs = &f.attrs;
184 let fn_vis = &f.vis;
185
186 let interrupt_name = interrupt_path
188 .segments
189 .last()
190 .map(|s| &s.ident)
191 .expect("interrupt path should have at least one segment");
192 quote!(
193 #(#fn_attrs)*
194 #[unsafe(no_mangle)]
195 #[unsafe(link_section = ".fast.text")]
196 #fn_vis unsafe extern "riscv-interrupt-m" fn #interrupt_name() {
197 #[inline(always)]
199 unsafe fn #fn_name() #fn_body
200
201 let mepc: usize;
202 let mstatus: usize;
203 let mxstatus: usize;
204 let mcctlbeginaddr: usize;
205 let mcctldata: usize;
206 core::arch::asm!(
207 "csrr {mepc}, mepc",
208 "csrr {mstatus}, mstatus",
209 "csrr {mxstatus}, 0x7c4",
210 "csrr {mcctlbeginaddr}, 0x7cb",
211 "csrr {mcctldata}, 0x7cd",
212 mepc = out(reg) mepc,
213 mstatus = out(reg) mstatus,
214 mxstatus = out(reg) mxstatus,
215 mcctlbeginaddr = out(reg) mcctlbeginaddr,
216 mcctldata = out(reg) mcctldata,
217 options(nomem, nostack),
218 );
219
220 #fn_name();
221
222 core::arch::asm!(
225 "csrw mstatus, {mstatus}",
226 "csrw mepc, {mepc}",
227 "csrw 0x7c4, {mxstatus}",
228 "csrw 0x7cd, {mcctldata}",
229 "csrw 0x7cb, {mcctlbeginaddr}",
230 "fence io, io",
231 mstatus = in(reg) mstatus,
232 mepc = in(reg) mepc,
233 mxstatus = in(reg) mxstatus,
234 mcctldata = in(reg) mcctldata,
235 mcctlbeginaddr = in(reg) mcctlbeginaddr,
236 options(nostack),
237 );
238 }
239 )
240 .into()
241}