Skip to content

Commit 258b6cf

Browse files
authored
Merge pull request #24 from RossPorter506/wake-cpu
Add 'Wake CPU' functionality
2 parents 261000f + 8d6e56a commit 258b6cf

File tree

1 file changed

+62
-18
lines changed

1 file changed

+62
-18
lines changed

macros/src/lib.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,15 @@ impl EntryInterruptEnable {
378378
/// `#[interrupt] fn DefaultHandler(..` can be used to override the default interrupt handler. When
379379
/// not overridden `DefaultHandler` defaults to an infinite loop.
380380
///
381+
/// `#[interrupt(wake_cpu)]` additionally returns the CPU to active mode after the interrupt
382+
/// returns. This cannot be done by naively writing to the status register, as the status register
383+
/// contents are pushed to the stack before an interrupt begins and this value is loaded back into
384+
/// the status register after an interrupt completes, effectively making any changes to the status
385+
/// register within an interrupt temporary.
386+
/// Using the `wake_cpu` variant incurs a delay of two instructions (6 cycles) before the interrupt
387+
/// handler begins.
388+
/// The following status register bits are cleared: SCG1, SCG0, OSC_OFF and CPU_OFF.
389+
///
381390
/// # Properties
382391
///
383392
/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer
@@ -417,11 +426,21 @@ impl EntryInterruptEnable {
417426
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
418427
let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
419428

420-
if !args.is_empty() {
421-
return parse::Error::new(Span::call_site(), "this attribute accepts no arguments")
429+
let maybe_arg = parse_macro_input::parse::<Option<Ident>>(args.clone());
430+
431+
let wake_cpu = match maybe_arg {
432+
Ok(None) => false,
433+
Ok(Some(ident)) if ident == "wake_cpu" => true,
434+
Ok(Some(_)) => {
435+
return parse::Error::new(
436+
Span::call_site(),
437+
"this attribute accepts only 'wake_cpu' as an argument",
438+
)
422439
.to_compile_error()
423-
.into();
424-
}
440+
.into()
441+
}
442+
Err(e) => return e.into_compile_error().into(),
443+
};
425444

426445
let fspan = f.sig.span();
427446
let ident = f.sig.ident;
@@ -491,21 +510,46 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
491510
let output = f.sig.output;
492511
let hash = random_ident();
493512
let ident = ident.to_string();
494-
quote!(
495-
#[export_name = #ident]
496-
#(#attrs)*
497-
#unsafety extern "msp430-interrupt" fn #hash() {
498-
#check
499-
500-
#[inline(always)]
501-
#unsafety fn #hash<'a>(#fn_param) #output {
502-
#(#vars)*
503-
#(#stmts)*
513+
if wake_cpu {
514+
quote!(
515+
#[export_name = #ident]
516+
#(#attrs)*
517+
#[unsafe(naked)]
518+
unsafe extern "msp430-interrupt" fn #hash() {
519+
#[inline(always)]
520+
#unsafety extern "msp430-interrupt" fn #hash<'a>(#fn_param) #output {
521+
#check
522+
#(#vars)*
523+
#(#stmts)*
524+
}
525+
{
526+
// Clear SCG1, SCG0, OSC_OFF, CPU_OFF in saved copy of SR register on stack
527+
const MASK: u8 = (1<<7) + (1<<6) + (1<<5) + (1<<4);
528+
core::arch::naked_asm!(
529+
"bic.b #{mask}, 0(r1)",
530+
"jmp {inner}",
531+
inner = sym #hash,
532+
mask = const MASK
533+
);
534+
}
504535
}
505-
{ #hash(#fn_arg) }
506-
}
507-
)
508-
.into()
536+
)
537+
} else {
538+
quote!(
539+
#[export_name = #ident]
540+
#(#attrs)*
541+
#unsafety extern "msp430-interrupt" fn #hash() {
542+
#check
543+
544+
#[inline(always)]
545+
#unsafety fn #hash<'a>(#fn_param) #output {
546+
#(#vars)*
547+
#(#stmts)*
548+
}
549+
{ #hash(#fn_arg) }
550+
}
551+
)
552+
}.into()
509553
} else {
510554
parse::Error::new(
511555
fspan,

0 commit comments

Comments
 (0)