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

Xtensa vectored interrupts #103

Merged
merged 23 commits into from
Jul 25, 2022
Merged

Xtensa vectored interrupts #103

merged 23 commits into from
Jul 25, 2022

Conversation

MabezDev
Copy link
Member

@MabezDev MabezDev commented Jul 14, 2022

This only adds support for Xtensa, RISCV will be in a follow-up PR. Quite a biggy, so best reviewed commit by each commit.

Summary

  • Vectoring is enabled by default, as this is what most embedded Rust folks expect
  • CPU interrupts are dispatched from xtensa-lx-rt see CPU Interrupts xtensa-lx-rt#45
  • Only three priorities are available in Xtensa, and currently just one with this issue: Low Level interrupts cannot preempt eachother? xtensa-lx-rt#46. We may be able to supplement this with some software prioritization of interrupts (the Xtensa handbook eludes to this). Would like to hear some ideas on how to do this efficiently.
  • For each level and for each edge interrupt, one CPU interrupt at that level is reserved for handling interrupts, these are currently Interrupt10EdgePriority1, Interrupt22EdgePriority3, Interrupt1LevelPriority1, Interrupt19LevelPriority2 & Interrupt23LevelPriority3.
  • All handling code runs out of ram, vectored handlers can opt into running from ram with the #[ram] attribute

Issues

  • Should enable(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) & disable(core: Cpu, interrupt: Interrupt) be made unsafe? They may break vectoring unless you know what you're doing.
    • Maybe the CPU interrupts should be cfg'd away when vectoring is enabled?
    • It's quite hard to do this check actually, because vectoring is built on top of the basic interrupt stuff, black listing interrupts there makes vectoring fail too 🤔
  • Needs a CS impl from Xtensa CS implementation #90 but we can probably use the xtensa_lx::mutex's for now
  • Can this approach be used in esp-wifi, instead of raw level handlers? Note that the relevant WiFi and Bluetooth interrupts were added in the PACS in Add WiFi and bluetooth interrupts for each chip esp-pacs#26.
  • Need to fix CI, which will involve carrying oversome changes from Xtensa CS implementation #90
  • Wait until we release new xtensa-lx-rt version so we don't merge with patched deps
  • Update examples to use vectored interrupt handlers
  • Add examples of using non-vectoring? It will require some CI work, see smartled feature shouldn't be activated by default #105
    • Maybe we could add an example using the high-level interrupts (4-7) in another PR

@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from 17cc393 to 623d4d6 Compare July 14, 2022 15:22
@jessebraham jessebraham marked this pull request as draft July 14, 2022 16:30
@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from 3d55e7c to 1c19c96 Compare July 14, 2022 21:10
@MabezDev MabezDev marked this pull request as ready for review July 14, 2022 21:15
@MabezDev
Copy link
Member Author

This is now ready for review. @bjoernQ do you think you could try and use the vectored interrupts in esp-wifi and report back?

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 15, 2022

This is now ready for review. @bjoernQ do you think you could try and use the vectored interrupts in esp-wifi and report back?

After looking into it I don't know how to do this

While the WiFi driver calls set_intr CPU 0 SRC 0 NUM 0 PRIO 1 - so we could map that interrupt but for BLE it seems the driver just maps the interrupt on it's own

Following interrupts are used by WiFi and BLE

  • Interrupt0LevelPriority1
  • Interrupt7SoftwarePriority1
  • Interrupt8LevelPriority1
  • Interrupt9LevelPriority1

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 15, 2022

The GPIO interrupt example looks good but unfortunately timer_interrupt and serial_interrupts examples don't work anymore. For timer_interrupt it uses a reserved interrupt but even after changing it, it still doesn't work anymore

Probably we could change them to use vectored interrupts but we should keep one of the examples using the low-level way so we can always verify it is still working

Only tested on ESP32 so far

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 15, 2022

It's quite hard to do this check actually, because vectoring is built on top of the basic interrupt stuff, black listing interrupts there makes vectoring fail too

Maybe the low-level function could be renamed and made private and we add a new public function with the old name and signature which does the check?

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 15, 2022

For esp-rs/xtensa-lx-rt#46

We could try to change the interrupt handling to allow higher priority interrupt levels while executing the Rust ISR or the ISR could opt-in to get interrupted by lowering PS.INTLEVEL?

@MabezDev
Copy link
Member Author

Thanks for the review!

While the WiFi driver calls set_intr CPU 0 SRC 0 NUM 0 PRIO 1 - so we could map that interrupt but for BLE it seems the driver just maps the interrupt on it's own
Following interrupts are used by WiFi and BLE
Interrupt0LevelPriority1
Interrupt7SoftwarePriority1
Interrupt8LevelPriority1
Interrupt9LevelPriority1

Hmm, this would be fine but we need an entry for the interrupt in the INTERRUPT_LEVELS static. It would be really nice to remove this, but AFAICT there is no way to see what (peripheral) interrupts are enabled at a given priority. I think we could get away with 'remapping' the interrupt with the rust code, just get an entry in INTERRUPT_LEVELS.

Probably we could change them to use vectored interrupts but we should keep one of the examples using the low-level way so we can always verify it is still working

I don't think it will be possible to mix vectoring interrupts & non-vectoring interrupts in their current state, as vectoring requires registering all the level handlers to go to one handler. One thing I can see is not registering levels 4 through 7, and only using vectoring for 'low-level' interrupts (1 through 3). Given that we have this limitation in xtensa-lx-rt already it sort of makes sense. Then high prio interrupts could be written in the low level way. To write an example using the low-level handlers would require no-default-features passed when building in CI though, as vectoring is enabled by default.

Maybe the low-level function could be renamed and made private and we add a new public function with the old name and signature which does the check?

This is a good idea, I'll try this out, thanks!

We could try to change the interrupt handling to allow higher priority interrupt levels while executing the Rust ISR or the ISR could opt-in to get interrupted by lowering PS.INTLEVEL?

This sounds good! Having at least a couple of levels of preemption will be helpful. Only three priorities may be an issue if we ever want an RTIC port or something, but by that time we could look into software interrupt priorities.

@MabezDev
Copy link
Member Author

Hmm, this would be fine but we need an entry for the interrupt in the INTERRUPT_LEVELS static. It would be really nice to remove this, but AFAICT there is no way to see what (peripheral) interrupts are enabled at a given priority. I think we could get away with 'remapping' the interrupt with the rust code, just get an entry in INTERRUPT_LEVELS.

Actually, I guess I can just read the mapping registers, see what CPU interrupt it's assigned to a have a lookup table of priorities there... I'll give that a try

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 15, 2022

I don't think it will be possible to mix vectoring interrupts & non-vectoring interrupts in their current state, as vectoring requires registering all the level handlers to go to one handler.

Ah sure - I probably need more coffee 😄

One thing we shouldn't forget: get rid of the patch sections in the TOMLs ... just mentioning that because those things often happen to me

Copy link
Member

@jessebraham jessebraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to dig into the interrupt/xtensa.rs file a bit more and do some local testing, but mostly looks fine to me. Left a few comments, not all necessarily require action.

.vscode/settings.json Show resolved Hide resolved
esp-hal-common/Cargo.toml Outdated Show resolved Hide resolved
esp-hal-common/Cargo.toml Outdated Show resolved Hide resolved
esp-hal-common/src/interrupt/xtensa.rs Outdated Show resolved Hide resolved
@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch 2 times, most recently from 9116950 to 4591176 Compare July 20, 2022 14:55
@MabezDev
Copy link
Member Author

Rebased on the main, a couple of things are left:

  • New xtensa-lx-rt release, should be covered by allow interrupt level preemption xtensa-lx-rt#47
  • Can this be used within eps-wifi? I've removed the limitation of having to call enable_with_priority so in theory the interrupts the blobs assign should now be serviced too.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 20, 2022

PR in xtensa-lx-rt is reviewed and merged!

@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from a1f495f to 40401a4 Compare July 20, 2022 16:00
@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 21, 2022

Can this be used within eps-wifi? I've removed the limitation of having to call enable_with_priority so in theory the interrupts the blobs assign should now be serviced too.

There is one thing missing unfortunately (before I can actually try it): The task-scheduler needs a mutable reference to the trap frame - we don't have that for vectored interrupts yet

Besides that, it looks quite nice and convenient - can't wait to get that for RISCV

Two last things:

  • we need to adapt all the interrupt related examples - if I'm not wrong only the gpio example is adapted right now?
  • the vectored feature needs to be exposed by the chip specific HALs

@MabezDev
Copy link
Member Author

There is one thing missing, unfortunately (before I can actually try it): The task-scheduler needs a mutable reference to the trap frame - we don't have that for vectored interrupts yet

Hmm, we would need to change the handler generation in the pacs, right? Or maybe we can be cheeky and accept both handlers with args and without? We can (ab)use the linker to allow interrupt handlers to either have no arguments or the context. I don't think that will cause issues, provided we always call the handler with the arguments.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 21, 2022

We can (ab)use the linker to allow interrupt handlers to either have no arguments or the context. I don't think that will cause issues, provided we always call the handler with the arguments.

I also think that should work fine

@MabezDev
Copy link
Member Author

With the latest commit it's now possible to pass the context into interrupt handlers:

image.

#[interrupt]
fn GPIO(frame: &mut xtensa_lx_rt::exception::Context) {
    unsafe {
        esp_println::println!(
            "GPIO Interrupt with priority {}",
            xtensa_lx::interrupt::get_level()
        );
        esp_println::println!(
            "{:?}",
            frame
        );

        (&BUTTON).lock(|data| {
            let mut button = data.borrow_mut();
            let button = button.as_mut().unwrap();
            button.clear_interrupt();
        });
    }
}

The frame can be omitted if its not needed.

@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from 34c8628 to f674581 Compare July 22, 2022 10:08
@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 22, 2022

Still trying to get esp-wifi working with it but this

crate::pac::Interrupt::#ident_s;

Creates problems since crate is then not the HAL ... gives weird errors like help: a similar path exists: esp32_hal::pac for the macro invocation

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 22, 2022

🎉 I got at least the WiFi example working with the vectored interrupts ... need to look into BT-BLE but generally I'd say it should work

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 22, 2022

Oh, not that easy for BLE ... it needs Interrupt7SoftwarePriority1 but I can only register peripheral interrupts?

Ah yes I see: cpu_interrupt_nr_to_cpu_interrupt_handler always returns None ... so, we need a way to register handlers for CPU interrupts 😢

The interrupt levels static introduces a few issues
  - A lock is needed when configuring interrupts to keep
    INTERRUPT_LEVELS in a consistent state
  - Interrupts enabled from outside the Rust domain wouldn't be
    serviced, this is the case with the wifi blobs

To remove it, the prioty configuration is now calculated dynamically in
the interrupt handler. Essentially INTERRUPT_LEVELS is now created once
the interrupt triggers. It has some benefits, such as only having to
look at interrupts configured on the current core, not both, but there
is of course an overhead with doing this in the interrupt.
@jessebraham
Copy link
Member

I'm fine with pushing off examples to a later PR, if you'd like to get this wrapped up.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 22, 2022

BTLE is not yet fully working ... but I think it should and the reason it's not working is most probably not because of the vectored interrupts

@bjoernQ
Copy link
Contributor

bjoernQ commented Jul 22, 2022

I'm fine with pushing off examples to a later PR, if you'd like to get this wrapped up.

I'm not so sure about that ... I mean the examples will just not work at all if they don't get adjusted - ideally we should never have such a situation on the main branch

move vectored feature to chip specific hals
@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from 0f1aaaf to 1eb70a0 Compare July 22, 2022 14:12
- rename `enable_with_priority` to `enable`
- add docs for interrupt macro
@MabezDev
Copy link
Member Author

I'm not so sure about that ... I mean the examples will just not work at all if they don't get adjusted - ideally we should never have such a situation on the main branch

I'm going to fix the old examples to use vectoring, I think Jesse is referring to my last comment about a raw example using levels 4-7.

@jessebraham
Copy link
Member

Yes sorry I should have been more clear, I expect the existing examples to continue to work but adding new ones can be deferred for now.

@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch 2 times, most recently from be7bd14 to c0c6df1 Compare July 22, 2022 15:06
@MabezDev MabezDev force-pushed the feature/vectored-interrupts branch from c0c6df1 to 67ba5f0 Compare July 22, 2022 15:29
Copy link
Contributor

@bjoernQ bjoernQ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@jessebraham jessebraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all your work on this!

@jessebraham jessebraham merged commit 1789780 into main Jul 25, 2022
jrmoulton pushed a commit to jrmoulton/esp-hal that referenced this pull request Jul 30, 2022
* Xtensa interrupt vectoring: peripheral source

- Initial Xtensa vectoring, updated esp32 gpio example to use new interrupt macro.
- Only peripheral sources supported.
- Only level one priority supported.
- CPU & Edge interrupts still need to be handled.

* Xtensa interrupt vectoring: CPU & EDGE

- Add support for handling CPU interrupts and edge interrupts
- PR required to xtensa-lx-rt for CPU handlers

* Xtensa interrupt vectoring: Priority

- Finally implement priortization
- Only three priorities available at the moment. Xtensa programmer guide
  discourages using highpri interrupts in Rust/C. Guide also mentions
  using software priortization to increase the number of Priorities
  available

* support CPU interrupts, using patch xtensa-lx-rt

* Update example

* Add support & examples for the s2 & s3 too

* Fix formatting and missing imports

* Run interrupt handling in ram, optionally run the vector handler in ram in the examples

* Use xtensa_lx::Mutex CS when enabling interrupts

* Run clippy on each target

* Remove redundant features

* Fix C3 builds

* make enable unsafe. Add note about preallocated interrupts in vectored mode.

* Remove `INTERRUPT_LEVELS` static

The interrupt levels static introduces a few issues
  - A lock is needed when configuring interrupts to keep
    INTERRUPT_LEVELS in a consistent state
  - Interrupts enabled from outside the Rust domain wouldn't be
    serviced, this is the case with the wifi blobs

To remove it, the prioty configuration is now calculated dynamically in
the interrupt handler. Essentially INTERRUPT_LEVELS is now created once
the interrupt triggers. It has some benefits, such as only having to
look at interrupts configured on the current core, not both, but there
is of course an overhead with doing this in the interrupt.

* Allow raw interrupts on levels 4-7, whilst also supporting vectoring on levels 1-3

* rename core number features

* Fix examples and formatting

* use xtensa-lx-rt release, update pacs

* Support passing the trap frame into interrupt handlers

* cfg away the #[interrupt] macro when not using vectoring

* rename enable to map

move vectored feature to chip specific hals

* export vectored functions

- rename `enable_with_priority` to `enable`
- add docs for interrupt macro

* Update all examples to use vectored interrupts
@jrmoulton
Copy link
Contributor

It seems that this PR broke the esp32c3-hal. The Hal won't build unless I revert back to the commit before this one. Is anyone else having the same issue?

@jrmoulton
Copy link
Contributor

Never mind. The lock file was causing problems

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

Successfully merging this pull request may close these issues.

4 participants