From 0a29b8cec4cfbbea8a73909db2cb249498f515e8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 8 May 2026 16:28:18 +0200 Subject: [PATCH] generic interrupt registry --- .../embassy/src/bin/dht22-open-drain-pins.rs | 4 +- .../examples/embassy/src/bin/embassy-hello.rs | 25 +++----- .../embassy/src/bin/logger-non-blocking.rs | 6 +- firmware/examples/embassy/src/bin/pwm.rs | 4 +- firmware/examples/embassy/src/main.rs | 4 +- firmware/examples/simple/src/bin/gtc-ticks.rs | 9 +-- firmware/examples/simple/src/bin/logger.rs | 8 +-- .../examples/zedboard/src/bin/ethernet.rs | 4 +- .../zedboard/src/bin/l3gd20h-i2c-mio.rs | 4 +- .../zedboard/src/bin/l3gd20h-spi-mio.rs | 4 +- .../examples/zedboard/src/bin/oled-async.rs | 2 +- firmware/examples/zedboard/src/bin/oled.rs | 2 +- firmware/examples/zedboard/src/bin/qspi.rs | 2 +- firmware/examples/zedboard/src/bin/sdio.rs | 25 +++----- .../zedboard/src/bin/uart-blocking.rs | 26 +++----- .../zedboard/src/bin/uart-non-blocking.rs | 4 +- firmware/examples/zedboard/src/main.rs | 23 ++----- firmware/zedboard-fsbl/src/main.rs | 25 +++----- firmware/zynq7000-hal/CHANGELOG.md | 5 ++ firmware/zynq7000-hal/src/gic.rs | 63 ++++++++++++++----- firmware/zynq7000-hal/src/interrupt.rs | 25 +++++--- firmware/zynq7000-hal/src/lib.rs | 4 +- firmware/zynq7000-hal/src/spi/mod.rs | 2 +- firmware/zynq7000-hal/src/uart/tx_async.rs | 4 ++ 24 files changed, 137 insertions(+), 147 deletions(-) diff --git a/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs b/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs index c82aeb4..54ccd2b 100644 --- a/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs +++ b/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs @@ -13,7 +13,7 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, generic_interrupt_handler, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic::Configurator, gpio::{Flex, Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/embassy/src/bin/embassy-hello.rs b/firmware/examples/embassy/src/bin/embassy-hello.rs index dac242f..a76a7c7 100644 --- a/firmware/examples/embassy/src/bin/embassy-hello.rs +++ b/firmware/examples/embassy/src/bin/embassy-hello.rs @@ -8,7 +8,9 @@ use embassy_time::{Duration, Ticker}; use embedded_hal::digital::StatefulOutputPin; use embedded_io::Write; use log::{error, info}; -use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart}; +use zynq7000_hal::{ + BootMode, InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz, uart, +}; use zynq7000_rt as _; @@ -71,23 +73,12 @@ async fn main(_spawner: Spawner) -> ! { } #[zynq7000_rt::irq] -fn irq_handler() { - let mut gic_helper = gic::GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); - match irq_info.interrupt() { - gic::Interrupt::Sgi(_) => (), - gic::Interrupt::Ppi(ppi_interrupt) => { - if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer { - unsafe { - zynq7000_embassy::on_interrupt(); - } - } - } - gic::Interrupt::Spi(_spi_interrupt) => (), - gic::Interrupt::Invalid(_) => (), - gic::Interrupt::Spurious => (), +pub fn irq_handler() { + // Safety: Called here once. + let result = unsafe { generic_interrupt_handler() }; + if let Err(e) = result { + panic!("Generic interrupt handler failed handling {:?}", e); } - gic_helper.end_of_interrupt(irq_info); } #[zynq7000_rt::exception(DataAbort)] diff --git a/firmware/examples/embassy/src/bin/logger-non-blocking.rs b/firmware/examples/embassy/src/bin/logger-non-blocking.rs index 84002e7..bb75480 100644 --- a/firmware/examples/embassy/src/bin/logger-non-blocking.rs +++ b/firmware/examples/embassy/src/bin/logger-non-blocking.rs @@ -14,12 +14,12 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, generic_interrupt_handler, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic::Configurator, gpio::{Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, time::Hertz, - uart::{ClockConfig, Config, TxAsync, Uart, on_interrupt_tx}, + uart::{ClockConfig, Config, TxAsync, Uart}, }; use zynq7000_rt as _; @@ -41,7 +41,7 @@ async fn main(spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/embassy/src/bin/pwm.rs b/firmware/examples/embassy/src/bin/pwm.rs index 46ac5cd..7813f46 100644 --- a/firmware/examples/embassy/src/bin/pwm.rs +++ b/firmware/examples/embassy/src/bin/pwm.rs @@ -19,7 +19,7 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, generic_interrupt_handler, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic::Configurator, gpio::{Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/embassy/src/main.rs b/firmware/examples/embassy/src/main.rs index 7ce1179..56e76ec 100644 --- a/firmware/examples/embassy/src/main.rs +++ b/firmware/examples/embassy/src/main.rs @@ -7,9 +7,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Ticker}; use embedded_hal::digital::StatefulOutputPin; use log::error; -use zynq7000_hal::{ - InteruptConfig, clocks, generic_interrupt_handler, gic, gpio, gtc, time::Hertz, -}; +use zynq7000_hal::{InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz}; // Define the clock frequency as a constant const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); diff --git a/firmware/examples/simple/src/bin/gtc-ticks.rs b/firmware/examples/simple/src/bin/gtc-ticks.rs index 48fc703..ca9bafe 100644 --- a/firmware/examples/simple/src/bin/gtc-ticks.rs +++ b/firmware/examples/simple/src/bin/gtc-ticks.rs @@ -8,8 +8,9 @@ use embedded_hal::digital::StatefulOutputPin; use embedded_io::Write; use log::{error, info}; use zynq7000_hal::{ + Interrupt, clocks::Clocks, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic, gpio::{Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -31,7 +32,7 @@ fn main() -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); @@ -83,8 +84,8 @@ fn main() -> ! { #[zynq7000_rt::irq] fn irq_handler() { - let mut gic_helper = GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); + let mut gic_helper = gic::InterruptGuard::new(); + let irq_info = gic_helper.interrupt_info(); match irq_info.interrupt() { Interrupt::Sgi(_) => (), Interrupt::Ppi(ppi_interrupt) => { diff --git a/firmware/examples/simple/src/bin/logger.rs b/firmware/examples/simple/src/bin/logger.rs index 8c6966c..0daac34 100644 --- a/firmware/examples/simple/src/bin/logger.rs +++ b/firmware/examples/simple/src/bin/logger.rs @@ -10,7 +10,7 @@ use log::{error, info}; use zynq7000_hal::{ BootMode, clocks::Clocks, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic::{self, Configurator, Interrupt}, gpio::{Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -32,7 +32,7 @@ fn main() -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); @@ -85,8 +85,8 @@ fn main() -> ! { #[zynq7000_rt::irq] fn irq_handler() { - let mut gic_helper = GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); + let mut gic_helper = gic::InterruptGuard::new(); + let irq_info = gic_helper.interrupt_info(); match irq_info.interrupt() { Interrupt::Sgi(_) => (), Interrupt::Ppi(ppi_interrupt) => { diff --git a/firmware/examples/zedboard/src/bin/ethernet.rs b/firmware/examples/zedboard/src/bin/ethernet.rs index 19ed174..ec5e262 100644 --- a/firmware/examples/zedboard/src/bin/ethernet.rs +++ b/firmware/examples/zedboard/src/bin/ethernet.rs @@ -43,7 +43,7 @@ use zynq7000_hal::{ AlignedBuffer, ClockDivSet, EthernetConfig, EthernetLowLevel, embassy_net::InterruptResult, }, generic_interrupt_handler, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + gic::{Configurator, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, @@ -217,7 +217,7 @@ async fn main(spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs b/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs index f8ada6d..747a463 100644 --- a/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs +++ b/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs @@ -21,7 +21,7 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, configure_level_shifter, generic_interrupt_handler, - gic::GicConfigurator, + gic::Configurator, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, i2c, l2_cache, @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) -> ! { let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs b/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs index 5b59a97..cb94f5a 100644 --- a/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs +++ b/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs @@ -22,7 +22,7 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, configure_level_shifter, generic_interrupt_handler, - gic::GicConfigurator, + gic::Configurator, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, @@ -70,7 +70,7 @@ async fn main(spawner: Spawner) -> ! { ); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); diff --git a/firmware/examples/zedboard/src/bin/oled-async.rs b/firmware/examples/zedboard/src/bin/oled-async.rs index 74378bd..cca0fab 100644 --- a/firmware/examples/zedboard/src/bin/oled-async.rs +++ b/firmware/examples/zedboard/src/bin/oled-async.rs @@ -16,7 +16,7 @@ use log::{error, info}; use ssd1306::{Ssd1306Async, prelude::*}; use zedboard::PS_CLOCK_FREQUENCY; use zynq7000_hal::{ - BootMode, clocks, generic_interrupt_handler, gic, gpio, gtc, + BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi::{self, SpiAsync}, time::Hertz, uart::{self, TxAsync}, diff --git a/firmware/examples/zedboard/src/bin/oled.rs b/firmware/examples/zedboard/src/bin/oled.rs index 35c883c..a1b2f71 100644 --- a/firmware/examples/zedboard/src/bin/oled.rs +++ b/firmware/examples/zedboard/src/bin/oled.rs @@ -16,7 +16,7 @@ use log::{error, info}; use ssd1306::{Ssd1306, prelude::*}; use zedboard::PS_CLOCK_FREQUENCY; use zynq7000_hal::{ - BootMode, clocks, generic_interrupt_handler, gic, gpio, gtc, spi, + BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi, time::Hertz, uart::{self, TxAsync}, }; diff --git a/firmware/examples/zedboard/src/bin/qspi.rs b/firmware/examples/zedboard/src/bin/qspi.rs index 84b1a0f..e3ec846 100644 --- a/firmware/examples/zedboard/src/bin/qspi.rs +++ b/firmware/examples/zedboard/src/bin/qspi.rs @@ -12,7 +12,7 @@ use log::{error, info}; use zedboard::PS_CLOCK_FREQUENCY; use zedboard_bsp::qspi_spansion; use zynq7000_hal::{ - BootMode, clocks, generic_interrupt_handler, gic, gpio, gtc, prelude::*, qspi, uart, + BootMode, clocks, generic_interrupt_handler, gpio, gtc, prelude::*, qspi, uart, }; use zynq7000_rt as _; diff --git a/firmware/examples/zedboard/src/bin/sdio.rs b/firmware/examples/zedboard/src/bin/sdio.rs index e99879a..d764740 100644 --- a/firmware/examples/zedboard/src/bin/sdio.rs +++ b/firmware/examples/zedboard/src/bin/sdio.rs @@ -10,9 +10,9 @@ use embedded_io::Write; use log::error; use zedboard::PS_CLOCK_FREQUENCY; use zynq7000_hal::gpio::Input; -use zynq7000_hal::prelude::*; use zynq7000_hal::sd::SdClockConfig; -use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, sd::SdCardUninit, uart}; +use zynq7000_hal::{BootMode, clocks, gpio, gtc, sd::SdCardUninit, uart}; +use zynq7000_hal::{generic_interrupt_handler, prelude::*}; use zynq7000_rt as _; @@ -227,23 +227,12 @@ async fn main(_spawner: Spawner) -> ! { } #[zynq7000_rt::irq] -fn irq_handler() { - let mut gic_helper = gic::GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); - match irq_info.interrupt() { - gic::Interrupt::Sgi(_) => (), - gic::Interrupt::Ppi(ppi_interrupt) => { - if ppi_interrupt == gic::PpiInterrupt::GlobalTimer { - unsafe { - zynq7000_embassy::on_interrupt(); - } - } - } - gic::Interrupt::Spi(_spi_interrupt) => (), - gic::Interrupt::Invalid(_) => (), - gic::Interrupt::Spurious => (), +pub fn irq_handler() { + // Safety: Called here once. + let result = unsafe { generic_interrupt_handler() }; + if let Err(e) = result { + panic!("Generic interrupt handler failed handling {:?}", e); } - gic_helper.end_of_interrupt(irq_info); } #[zynq7000_rt::exception(DataAbort)] diff --git a/firmware/examples/zedboard/src/bin/uart-blocking.rs b/firmware/examples/zedboard/src/bin/uart-blocking.rs index c10dde7..11842e7 100644 --- a/firmware/examples/zedboard/src/bin/uart-blocking.rs +++ b/firmware/examples/zedboard/src/bin/uart-blocking.rs @@ -14,8 +14,7 @@ use zedboard::PS_CLOCK_FREQUENCY; use zynq7000_hal::{ BootMode, clocks::Clocks, - configure_level_shifter, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + configure_level_shifter, generic_interrupt_handler, gic, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, @@ -104,7 +103,7 @@ async fn main(_spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); @@ -215,23 +214,12 @@ async fn main(_spawner: Spawner) -> ! { } #[zynq7000_rt::irq] -fn irq_handler() { - let mut gic_helper = GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); - match irq_info.interrupt() { - Interrupt::Sgi(_) => (), - Interrupt::Ppi(ppi_interrupt) => { - if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer { - unsafe { - zynq7000_embassy::on_interrupt(); - } - } - } - Interrupt::Spi(_spi_interrupt) => (), - Interrupt::Invalid(_) => (), - Interrupt::Spurious => (), +pub fn irq_handler() { + // Safety: Called here once. + let result = unsafe { generic_interrupt_handler() }; + if let Err(e) = result { + panic!("Generic interrupt handler failed handling {:?}", e); } - gic_helper.end_of_interrupt(irq_info); } #[zynq7000_rt::exception(DataAbort)] diff --git a/firmware/examples/zedboard/src/bin/uart-non-blocking.rs b/firmware/examples/zedboard/src/bin/uart-non-blocking.rs index 57ecf83..a23c44d 100644 --- a/firmware/examples/zedboard/src/bin/uart-non-blocking.rs +++ b/firmware/examples/zedboard/src/bin/uart-non-blocking.rs @@ -42,7 +42,7 @@ use zynq7000_hal::{ BootMode, clocks::Clocks, configure_level_shifter, generic_interrupt_handler, - gic::{GicConfigurator, Interrupt}, + gic::{Configurator, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, @@ -171,7 +171,7 @@ async fn main(spawner: Spawner) -> ! { // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. - let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); + let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); // AXI UARTLite documentation mentions that a rising-edge sensitive interrupt is generated, diff --git a/firmware/examples/zedboard/src/main.rs b/firmware/examples/zedboard/src/main.rs index 7ba30c6..e531ba2 100644 --- a/firmware/examples/zedboard/src/main.rs +++ b/firmware/examples/zedboard/src/main.rs @@ -9,7 +9,7 @@ use embedded_hal::digital::StatefulOutputPin; use embedded_io::Write; use log::{error, info}; use zedboard::PS_CLOCK_FREQUENCY; -use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart}; +use zynq7000_hal::{BootMode, clocks, generic_interrupt_handler, gpio, gtc, uart}; use zynq7000_rt as _; @@ -88,23 +88,12 @@ async fn main(_spawner: Spawner) -> ! { } #[zynq7000_rt::irq] -fn irq_handler() { - let mut gic_helper = gic::GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); - match irq_info.interrupt() { - gic::Interrupt::Sgi(_) => (), - gic::Interrupt::Ppi(ppi_interrupt) => { - if ppi_interrupt == gic::PpiInterrupt::GlobalTimer { - unsafe { - zynq7000_embassy::on_interrupt(); - } - } - } - gic::Interrupt::Spi(_spi_interrupt) => (), - gic::Interrupt::Invalid(_) => (), - gic::Interrupt::Spurious => (), +pub fn irq_handler() { + // Safety: Called here once. + let result = unsafe { generic_interrupt_handler() }; + if let Err(e) = result { + panic!("Generic interrupt handler failed handling {:?}", e); } - gic_helper.end_of_interrupt(irq_info); } #[zynq7000_rt::exception(DataAbort)] diff --git a/firmware/zedboard-fsbl/src/main.rs b/firmware/zedboard-fsbl/src/main.rs index beafb58..278b083 100644 --- a/firmware/zedboard-fsbl/src/main.rs +++ b/firmware/zedboard-fsbl/src/main.rs @@ -17,7 +17,7 @@ use log::{error, info}; use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode}; use zynq7000_boot_image::DestinationDevice; use zynq7000_hal::clocks::ArmClocks; -use zynq7000_hal::priv_tim; +use zynq7000_hal::{generic_interrupt_handler, priv_tim}; use zynq7000_hal::{ BootMode, clocks::{ @@ -119,7 +119,7 @@ fn main() -> ! { }; // Set up the global interrupt controller. - let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd); + let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd); gic.enable_all_interrupts(); gic.set_all_spi_interrupt_targets_cpu0(); gic.enable(); @@ -353,23 +353,12 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu } #[zynq7000_rt::irq] -fn interrupt_handler() { - let mut gic_helper = gic::GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); - match irq_info.interrupt() { - gic::Interrupt::Sgi(_) => (), - gic::Interrupt::Ppi(ppi_interrupt) => { - log::warn!("unexpected PPI interrupt: {:?}", ppi_interrupt); - } - gic::Interrupt::Spi(spi_interrupt) => { - log::warn!("unexpected SPI interrupt: {:?}", spi_interrupt); - } - gic::Interrupt::Invalid(_) => (), - gic::Interrupt::Spurious => { - log::warn!("spurious interrupt"); - } +pub fn irq_handler() { + // Safety: Called here once. + let result = unsafe { generic_interrupt_handler() }; + if let Err(e) = result { + log::warn!("Generic interrupt handler failed handling {:?}", e); } - gic_helper.end_of_interrupt(irq_info); } #[zynq7000_rt::exception(DataAbort)] diff --git a/firmware/zynq7000-hal/CHANGELOG.md b/firmware/zynq7000-hal/CHANGELOG.md index 3dde1fd..87ca800 100644 --- a/firmware/zynq7000-hal/CHANGELOG.md +++ b/firmware/zynq7000-hal/CHANGELOG.md @@ -28,12 +28,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/). respectively. - `log::rb` module replaced by `log::asynch` module which uses an asynchronous embassy pipe for logging. +- GIC data structures: Removed the `Gic` prefix which already is part of the module name. +- Renamed `GicInterruptHelper` to `InterruptGuard`. It acknowledges the end of interrupts on drop. ## Added - Method to de-assert PL reset. - ARM clock initialization for the `ArmClocks` structure - The `ArmClocks` structure now caches the CPU clock ratio +- New generic interrupt registry and generic interrupt handler which uses the registry. + Primary interface is the `crate::generic_interrupt_handler` function and the + `crate::register_interrupt` function. # [v0.1.1] 2025-10-10 diff --git a/firmware/zynq7000-hal/src/gic.rs b/firmware/zynq7000-hal/src/gic.rs index 9830528..135daaa 100644 --- a/firmware/zynq7000-hal/src/gic.rs +++ b/firmware/zynq7000-hal/src/gic.rs @@ -1,7 +1,10 @@ //! # Global Interrupt Controller (GIC) module //! //! The primary interface to configure and allow handling the interrupts are the -//! [GicConfigurator] and the [GicInterruptHelper] structures. +//! [Configurator] and the [InterruptGuard] structures. +//! +//! The HAL provides a more convenient interface through the [crate::register_interrupt] and +//! [crate::generic_interrupt_handler] functions. #![deny(missing_docs)] use arbitrary_int::prelude::*; @@ -250,8 +253,9 @@ pub enum Interrupt { } /// Interrupt information structure. -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct InterruptInfo { raw_reg: InterruptSignalRegister, interrupt: Interrupt, @@ -337,16 +341,16 @@ pub struct InvalidSgiInterruptId(pub usize); /// with [Self::enable] which assumes a certain configuration. /// 5. Enable interrupts for the Cortex-A core by calling [Self::enable_interrupts]. /// -/// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a +/// For the handling of the interrupts, you can use the [InterruptGuard] which assumes a /// properly configured GIC. -pub struct GicConfigurator { +pub struct Configurator { /// GIC CPU interface registers. pub gicc: MmioCpuInterfaceRegisters<'static>, /// GIC Distributor interface registers. pub gicd: MmioDistributorRegisters<'static>, } -impl GicConfigurator { +impl Configurator { /// Create a new GIC controller instance and calls [Self::initialize] to perform /// strongly recommended initialization routines for the GIC. #[inline] @@ -354,7 +358,7 @@ impl GicConfigurator { gicc: MmioCpuInterfaceRegisters<'static>, gicd: MmioDistributorRegisters<'static>, ) -> Self { - let mut gic = GicConfigurator { gicc, gicd }; + let mut gic = Configurator { gicc, gicd }; gic.initialize(); gic } @@ -368,7 +372,7 @@ impl GicConfigurator { /// used inside the interrupt handler. #[inline] pub unsafe fn steal() -> Self { - GicConfigurator { + Configurator { gicc: unsafe { CpuInterfaceRegisters::new_mmio_fixed() }, gicd: unsafe { DistributorRegisters::new_mmio_fixed() }, } @@ -651,21 +655,31 @@ impl GicConfigurator { } /// Helper structure which should only be used inside the interrupt handler once the GIC has -/// been configured with the [GicConfigurator]. -pub struct GicInterruptHelper(MmioCpuInterfaceRegisters<'static>); +/// been configured with the [Configurator]. +pub struct InterruptGuard { + regs: MmioCpuInterfaceRegisters<'static>, + interrupt_info: InterruptInfo, + acknowledged: bool, +} -impl GicInterruptHelper { +impl InterruptGuard { /// Create the interrupt helper with the fixed GICC MMIO instance. - pub const fn new() -> Self { - GicInterruptHelper(unsafe { CpuInterfaceRegisters::new_mmio_fixed() }) + pub fn new() -> Self { + let mut regs = unsafe { CpuInterfaceRegisters::new_mmio_fixed() }; + let interrupt_info = Self::acknowledge_interrupt(&mut regs); + InterruptGuard { + regs: unsafe { CpuInterfaceRegisters::new_mmio_fixed() }, + interrupt_info, + acknowledged: false, + } } /// Acknowledges an interrupt by reading the IAR register and returning the interrupt context /// information structure. /// /// This should be called at the start of an interrupt handler. - pub fn acknowledge_interrupt(&mut self) -> InterruptInfo { - let iar = self.0.read_iar(); + fn acknowledge_interrupt(regs: &mut MmioCpuInterfaceRegisters<'static>) -> InterruptInfo { + let iar = regs.read_iar(); let int_id = iar.ack_int_id().as_u32(); let interrupt = match int_id { 0..=15 => Interrupt::Sgi(int_id as usize), @@ -681,16 +695,33 @@ impl GicInterruptHelper { } } + /// Returns the interrupt information structure which was read from the IAR register. + /// + /// This is used to determine the cause of the interrupt. + #[inline] + pub fn interrupt_info(&self) -> InterruptInfo { + self.interrupt_info + } + /// Acknowledges the end of an interrupt by writing the EOIR register of the GICC. /// /// This should be called at the end of an interrupt handler. pub fn end_of_interrupt(&mut self, irq_info: InterruptInfo) { - self.0.write_eoir(irq_info.raw_reg()) + self.acknowledged = true; + self.regs.write_eoir(irq_info.raw_reg()) } } -impl Default for GicInterruptHelper { +impl Default for InterruptGuard { fn default() -> Self { Self::new() } } + +impl Drop for InterruptGuard { + fn drop(&mut self) { + if !self.acknowledged { + self.regs.write_eoir(self.interrupt_info.raw_reg()); + } + } +} diff --git a/firmware/zynq7000-hal/src/interrupt.rs b/firmware/zynq7000-hal/src/interrupt.rs index f8b8d78..ada7447 100644 --- a/firmware/zynq7000-hal/src/interrupt.rs +++ b/firmware/zynq7000-hal/src/interrupt.rs @@ -1,12 +1,17 @@ -use crate::gic::{GicInterruptHelper, Interrupt}; +use crate::gic::{Interrupt, InterruptGuard}; -static INTERRUPT_MAP: critical_section::Mutex< - core::cell::RefCell>, -> = critical_section::Mutex::new(core::cell::RefCell::new( - heapless::index_map::FnvIndexMap::new(), -)); +pub type InterruptMap = heapless::index_map::FnvIndexMap; +static INTERRUPT_MAP: critical_section::Mutex> = + critical_section::Mutex::new(core::cell::RefCell::new( + heapless::index_map::FnvIndexMap::new(), + )); /// Register an interrupt handler for a specific [Interrupt]. +/// +/// It should be noted that the current implementation only allows one function for each interrupts. +/// If the HAL provided interrupt handler does not fulfill all your requirements, you need +/// to define your own interrupt handler and register it. +/// For example, you might need to handle both UART RX and TX, and the HAL handler only handles TX. pub fn register_interrupt(interrupt: Interrupt, handler: unsafe fn()) { critical_section::with(|cs| { let mut map = INTERRUPT_MAP.borrow(cs).borrow_mut(); @@ -25,11 +30,11 @@ pub fn register_interrupt(interrupt: Interrupt, handler: unsafe fn()) { /// This needs to be called ONCE in the interrupt handler function, which is any function annotated /// with the `irq` attribute provided by `aarch32-rt`. pub unsafe fn generic_interrupt_handler() -> Result<(), Interrupt> { - let mut gic_helper = GicInterruptHelper::new(); - let irq_info = gic_helper.acknowledge_interrupt(); + let mut gic_guard = InterruptGuard::new(); + let irq_info = gic_guard.interrupt_info(); let interrupt = irq_info.interrupt(); if let Interrupt::Invalid(_) = interrupt { - gic_helper.end_of_interrupt(irq_info); + gic_guard.end_of_interrupt(irq_info); return Err(interrupt); } let opt_interrupt_handler = critical_section::with(|cs| { @@ -43,7 +48,7 @@ pub unsafe fn generic_interrupt_handler() -> Result<(), Interrupt> { interrupt_handler(); } } - gic_helper.end_of_interrupt(irq_info); + gic_guard.end_of_interrupt(irq_info); opt_interrupt_handler.ok_or(interrupt)?; Ok(()) } diff --git a/firmware/zynq7000-hal/src/lib.rs b/firmware/zynq7000-hal/src/lib.rs index 51b9539..51d3b37 100644 --- a/firmware/zynq7000-hal/src/lib.rs +++ b/firmware/zynq7000-hal/src/lib.rs @@ -10,7 +10,7 @@ //! //! ## Examples //! -//! All exaples can be found inside the [examples folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/examples) +//! All examples can be found inside the [examples folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/examples) //! and [firmware folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware) of the project #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -83,7 +83,7 @@ pub fn init(config: Config) -> Result { configure_level_shifter(config); } if let Some(interrupt_config) = config.interrupt_config { - let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd); + let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd); match interrupt_config { InteruptConfig::AllInterruptsToCpu0 => { gic.enable_all_interrupts(); diff --git a/firmware/zynq7000-hal/src/spi/mod.rs b/firmware/zynq7000-hal/src/spi/mod.rs index 89e01b4..8a046f4 100644 --- a/firmware/zynq7000-hal/src/spi/mod.rs +++ b/firmware/zynq7000-hal/src/spi/mod.rs @@ -1172,7 +1172,7 @@ pub fn reset(id: SpiId) { /// The Zynq7000 SPI peripheral has the following requirement for the SPI reference clock: /// It must be larger than the CPU 1X clock. Therefore, the divisor used to calculate the reference /// clock has a maximum value, which can be calculated with this function. -/// [configure_spi_ref_clk_with_divisor] can be used to configure the SPI reference clock with a +/// [configure_spi_ref_clock_with_divisor] can be used to configure the SPI reference clock with a /// divisor. /// /// *NOTE* - It is recommended to avoid the largest theoretical value which was proven to be diff --git a/firmware/zynq7000-hal/src/uart/tx_async.rs b/firmware/zynq7000-hal/src/uart/tx_async.rs index f5ed5f3..64d9ce4 100644 --- a/firmware/zynq7000-hal/src/uart/tx_async.rs +++ b/firmware/zynq7000-hal/src/uart/tx_async.rs @@ -198,6 +198,10 @@ pub struct TxAsync { impl TxAsync { /// Constructor. + /// + /// The second argument specifies whether the [on_interrupt_tx] function will be registered + /// in the HAL interrupt map. You might need to skip this in case you have your own + /// interrupt handler which also handles RX interrupts. pub fn new(tx: Tx, register_interrupt_handler: bool) -> Self { if register_interrupt_handler { match tx.uart_id() {