Add 'Wake CPU' functionality#24
Conversation
Returns the CPU to active mode after the interrupt returns
|
This looks fine to me (also, this is how I learn naked functions are stable), but I have at least one comment before I merge:
It's not a good API, but I think it would be possible to conditionally return whether to wake the CPU using a return argument or |
|
Some sort of conditional logic would be nice, though for now it's easy enough to check for the wake up condition in the main function, and if not satisfied then go straight back to sleep. Waking up only to go back to sleep does waste a bit of power, but the system already woke up to deal with the interrupt, so it's probably not too bad. Between an inout and a return value I think an inout would be more ergonomic for the user - if they only use it in one control path then that's the only place they need to touch it, unlike a return value which needs a value in every control path. It also leaves the door open for implementing other methods, like generic |
|
I don't think it's possible to return anything from a function marked as "msp430-interrupt", because such functions end with |
|
Yeah, you'd have to make the inner handler not "msp430-interrupt", do a proper function call in the naked assembly (rather than |
|
Yea, I knew there was a reason my proposal wouldn't work as-is... I just forgot why :). Changing the inner function to be non-
This'll do for now. At least LPM is now implemented :D! |
|
Ignore failed CI... I have to update the workflow, but lately I've not had bandwidth: #23 (comment) |
Following on from the discussion here and here, This PR adds an optional argument to the
#[interrupt]proc-macro that returns the CPU to Active Mode after the interrupt completes.This behaviour is usually controlled by the status register, which can be freely written to outside of an interrupt. However, if the CPU is turned off then the only time the CPU wakes up again is during an interrupt. Unfortunately the status register can't be naively written to during an interrupt, as the previous state of the status register is pushed onto the stack before an interrupt begins and this value is restored after the interrupt returns, making any changes to the status register itself during an interrupt temporary.
To make permanent changes to the system state we must modify the copy of the status register that was pushed to the stack. In msp430-gcc this is handled by a compiler intrinsic, which isn't available to us here. Instead, we can use a naked function to ensure that nothing is pushed to the stack before our code runs (proof of concept), modify the item most recently pushed to the stack (the copy of the status register), then run the user interrupt handler.
This is implemented as an optional argument to the
#[interrupt]proc-macro. The behaviour of#[interrupt]is untouched, and the new functionality is enabled when#[interrupt(wake_cpu)]is used.As a refresher, consider the existing interrupt handler macro:
This expands to the following:
With the new
wake_cpuoption:expands to:
All the bits related to low power modes are cleared, as only clearing CPU_OFF may put the MSP430 into one of the undocumented power modes.
This isn't an ideal solution, as we still can't conditionally write to the copy of the status register on the stack like you could with the compiler intrinsics (e.g. GCC has
__bic_sr_register_on_exit()which can be called anywhere within an interrupt handler)A full working example can be seen here, which runs on an MSP-EXP430FR2355 dev board (build with
cargo run --example lpm0).Any comments or modifications welcome.