Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit masking and unmasking of hardware tasks? #621

Open
3442 opened this issue Feb 27, 2022 · 8 comments
Open

Explicit masking and unmasking of hardware tasks? #621

3442 opened this issue Feb 27, 2022 · 8 comments

Comments

@3442
Copy link

3442 commented Feb 27, 2022

This can be useful for delaying handling of edge-triggered interrupts gracefully in cases where lowering the interrupt line from within the handler can become cumbersome. You would mask the task from within itself and then unmask it once whatever handles it is done. Is it possible to do this with current RTIC? I guess that RTIC has enough knowledge to expose a safe interface for unmasking, but I'm not sure about how this might interact with locks.

@3442 3442 changed the title Explicit masking and unmasking of tasks? Explicit masking and unmasking of hardware tasks? Feb 27, 2022
@perlindgren
Copy link
Collaborator

We could e.g. have a primitive that disables a task/interrupt for a monotonic duration. In general, it is hard to ensure that a disabled task/interrupt will later be enabled, but monotonic gives you that guarantee.

@korken89
Copy link
Collaborator

korken89 commented Mar 7, 2022

If you want the all powerful hammer you can access and mask the NVIC yourself, however it would be nice to have a tool in RTIC to handle this.
What directly comes to mind is a TaskMaskHandle that you get when masking the task and that unmasks a task when it is dropped.

Something like:

#[task(binds = EXTI)]
fn task1(cx: task1::Context) {
    // ...
}

#[task]
fn task2(cx: task2::Context) {
    // Disable task 1
    let handle = task1::disable().unwrap();

    // Keep it disabled for 1 second
    task3::spawn_after(1.secs(), handle);
}

#[task]
fn task3(cx: task3::Context, handle: task1::TaskMaskHandle) {
    // Re-enbable task1
    drop(handle);
}

@perlindgren
Copy link
Collaborator

Yea that approach would work as an under the hood implementation, where the user facing API is enriched with a task::disable_until(Duration) and task::disable_until_at(Instance). The important thing is that you should not be able (from safe code) to disable an interrupt without a corresponding enable coupled to it.

/Per

@perlindgren
Copy link
Collaborator

What @korken89 suggests is also viable assuming the drop will eventually be executed. However due to the (unfortunate) design of Rust it is not the case, as you can from safe code https://doc.rust-lang.org/std/mem/fn.forget.html.
/Per

@perlindgren
Copy link
Collaborator

For all practical matters the approach by @korken89 is going to cut it. So its all just a matter on where to draw the line of guarantees provided by RTIC. It can be seen as a programming error (on par with all other programming errors) to call forget, and by that break the system. As the @korken89 approach is outside of RTIC it is fine (any errors is up to the programmer).
/Per

@korken89
Copy link
Collaborator

korken89 commented Mar 7, 2022

If a user calls mem::forget on a masked task it is because they'd like it to stay masked forever.
But yeah, you can think of many possible APIs for this. Manual handling for maximum flexibility, and a API linked to a monotonic for full handling by RTIC.

I think the bigger issue here is that this needs to be connected to the clearing of the associated interrupt somehow, else you'd only get the interrupt firing as soon as the masking is cleared.
Not sure how to handle that.

@perlindgren
Copy link
Collaborator

perlindgren commented Mar 7, 2022

Aha, I was just thinking of the masking here, not how the underlying hw would trigger the interrupt, I don't see a generic way of handling that. Perhaps some embedded-hal trait would be required.
/Per

@perlindgren
Copy link
Collaborator

A an aside, enabling and disabling task could be seen as affecting the RTIC model, essentially going from one task-set to another is a "mode-change", more on that at #23.
/Per

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants