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 05bf73e..54ccd2b 100644 --- a/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs +++ b/firmware/examples/embassy/src/bin/dht22-open-drain-pins.rs @@ -12,7 +12,8 @@ use log::{error, info, warn}; use zynq7000_hal::{ BootMode, clocks::Clocks, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + generic_interrupt_handler, + gic::Configurator, gpio::{Flex, Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -45,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(); @@ -157,23 +158,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/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 9e6f486..bb75480 100644 --- a/firmware/examples/embassy/src/bin/logger-non-blocking.rs +++ b/firmware/examples/embassy/src/bin/logger-non-blocking.rs @@ -13,12 +13,13 @@ use zynq7000::Peripherals; use zynq7000_hal::{ BootMode, clocks::Clocks, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + generic_interrupt_handler, + 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 _; @@ -40,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(); @@ -68,7 +69,7 @@ async fn main(spawner: Spawner) -> ! { uart.flush().unwrap(); let (tx, _rx) = uart.split(); - let mut logger = TxAsync::new(tx); + let mut logger = TxAsync::new(tx, true); let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap(); @@ -119,28 +120,13 @@ async fn hello_task() { } } -#[unsafe(no_mangle)] -pub extern "C" 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) => { - if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 { - on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1); - } - } - Interrupt::Invalid(_) => (), - Interrupt::Spurious => (), +#[zynq7000_rt::irq] +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/pwm.rs b/firmware/examples/embassy/src/bin/pwm.rs index 1a736f6..7813f46 100644 --- a/firmware/examples/embassy/src/bin/pwm.rs +++ b/firmware/examples/embassy/src/bin/pwm.rs @@ -18,7 +18,8 @@ use log::{error, info}; use zynq7000_hal::{ BootMode, clocks::Clocks, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + generic_interrupt_handler, + gic::Configurator, gpio::{Output, PinState, mio}, gtc::GlobalTimerCounter, l2_cache, @@ -46,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(); @@ -107,23 +108,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/embassy/src/main.rs b/firmware/examples/embassy/src/main.rs index d7be0c3..56e76ec 100644 --- a/firmware/examples/embassy/src/main.rs +++ b/firmware/examples/embassy/src/main.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Ticker}; use embedded_hal::digital::StatefulOutputPin; use log::error; -use zynq7000_hal::{InteruptConfig, clocks, 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); @@ -42,22 +42,11 @@ async fn main(_spawner: Spawner) -> ! { #[zynq7000_rt::irq] pub 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 => (), + // 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/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 7a0b5f9..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,13 +85,12 @@ 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) => { if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer { - // TODO: Call embassy on interrupt handler here soon. MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed); } } diff --git a/firmware/examples/zedboard/src/bin/ethernet.rs b/firmware/examples/zedboard/src/bin/ethernet.rs index ae6c10d..ec5e262 100644 --- a/firmware/examples/zedboard/src/bin/ethernet.rs +++ b/firmware/examples/zedboard/src/bin/ethernet.rs @@ -42,7 +42,8 @@ use zynq7000_hal::{ eth::{ AlignedBuffer, ClockDivSet, EthernetConfig, EthernetLowLevel, embassy_net::InterruptResult, }, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + generic_interrupt_handler, + gic::{Configurator, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, @@ -216,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(); @@ -278,6 +279,12 @@ async fn main(spawner: Spawner) -> ! { "Calculated RGMII clock configuration: {:?}, errors (missmatch from ideal rate in hertz): {:?}", clk_divs, clk_errors ); + + zynq7000_hal::register_interrupt( + Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Eth0), + custom_eth_interupt_handler, + ); + // Unwrap okay, we use a standard clock config, and the clock config should never fail. let eth_cfg = EthernetConfig::new( zynq7000_hal::eth::ClockConfig::new(clk_divs.cfg_1000_mbps), @@ -461,36 +468,24 @@ 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) => { - if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Eth0 { - // This generic library provided interrupt handler takes care of waking - // the driver on received or sent frames while also reporting anomalies - // and errors. - let result = zynq7000_hal::eth::embassy_net::on_interrupt( - zynq7000_hal::eth::EthernetId::Eth0, - ); - if result.has_errors() { - ETH_ERR_QUEUE.try_send(result).ok(); - } - } - } - Interrupt::Invalid(_) => (), - Interrupt::Spurious => (), +// Safety: Only called by interrupt handler, registered in global interrupt handler map. +unsafe fn custom_eth_interupt_handler() { + // This generic library provided interrupt handler takes care of waking + // the driver on received or sent frames while also reporting anomalies + // and errors. + let result = zynq7000_hal::eth::embassy_net::on_interrupt(zynq7000_hal::eth::EthernetId::Eth0); + if result.has_errors() { + ETH_ERR_QUEUE.try_send(result).ok(); + } +} + +#[zynq7000_rt::irq] +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/l3gd20h-i2c-mio.rs b/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs index 3649679..747a463 100644 --- a/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs +++ b/firmware/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs @@ -20,8 +20,8 @@ use log::{error, info}; use zynq7000_hal::{ BootMode, clocks::Clocks, - configure_level_shifter, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + configure_level_shifter, generic_interrupt_handler, + 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(); @@ -153,24 +153,13 @@ async fn main(_spawner: Spawner) -> ! { } } -#[unsafe(no_mangle)] -pub extern "C" 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 => (), +#[zynq7000_rt::irq] +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); } #[unsafe(no_mangle)] diff --git a/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs b/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs index e7d45c5..cb94f5a 100644 --- a/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs +++ b/firmware/examples/zedboard/src/bin/l3gd20h-spi-mio.rs @@ -21,14 +21,14 @@ use log::{error, info}; use zynq7000_hal::{ BootMode, clocks::Clocks, - configure_level_shifter, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + configure_level_shifter, generic_interrupt_handler, + gic::Configurator, gpio::{GpioPins, Output, PinState}, gtc::GlobalTimerCounter, l2_cache, - spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt}, + spi::{self, SpiAsync, SpiWithHwCs, SpiWithHwCsAsync}, time::Hertz, - uart::{self, TxAsync, on_interrupt_tx}, + uart::{self, TxAsync}, }; use zynq7000::{Peripherals, slcr::LevelShifterConfig, spi::DelayControl}; @@ -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(); @@ -177,7 +177,7 @@ pub async fn logger_task( reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>, ) -> ! { let (tx, _) = uart.split(); - let mut tx_async = TxAsync::new(tx); + let mut tx_async = TxAsync::new(tx, true); let mut log_buf: [u8; 2048] = [0; 2048]; loop { let read_bytes = reader.read(&mut log_buf).await; @@ -248,30 +248,13 @@ pub async fn non_blocking_application( } } -#[unsafe(no_mangle)] -pub extern "C" 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) => { - if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Spi1 { - on_interrupt(SpiId::Spi1); - } else if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 { - on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1); - } - } - Interrupt::Invalid(_) => (), - Interrupt::Spurious => (), +#[zynq7000_rt::irq] +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); } #[unsafe(no_mangle)] diff --git a/firmware/examples/zedboard/src/bin/oled-async.rs b/firmware/examples/zedboard/src/bin/oled-async.rs index 7b5751e..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, gic, gpio, gtc, + BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi::{self, SpiAsync}, time::Hertz, uart::{self, TxAsync}, @@ -221,7 +221,7 @@ pub async fn logger_task( reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>, ) -> ! { let (tx, _) = uart.split(); - let mut tx_async = TxAsync::new(tx); + let mut tx_async = TxAsync::new(tx, true); let mut log_buf: [u8; 2048] = [0; 2048]; loop { let read_bytes = reader.read(&mut log_buf).await; @@ -247,33 +247,12 @@ pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output } #[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) => match spi_interrupt { - gic::SpiInterrupt::Uart1 => { - zynq7000_hal::uart::tx_async::on_interrupt_tx(uart::UartId::Uart1); - } - gic::SpiInterrupt::Spi0 => { - zynq7000_hal::spi::asynch::on_interrupt(spi::SpiId::Spi0); - } - _ => { - log::warn!("Unhandled SPI interrupt: {:?}", 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/oled.rs b/firmware/examples/zedboard/src/bin/oled.rs index e9d30c3..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, gic, gpio, gtc, spi, + BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi, time::Hertz, uart::{self, TxAsync}, }; @@ -216,7 +216,7 @@ pub async fn logger_task( reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>, ) -> ! { let (tx, _) = uart.split(); - let mut tx_async = TxAsync::new(tx); + let mut tx_async = TxAsync::new(tx, true); let mut log_buf: [u8; 2048] = [0; 2048]; loop { let read_bytes = reader.read(&mut log_buf).await; @@ -242,30 +242,12 @@ pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output } #[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) => match spi_interrupt { - gic::SpiInterrupt::Uart1 => { - zynq7000_hal::uart::tx_async::on_interrupt_tx(uart::UartId::Uart1); - } - _ => { - log::warn!("Unhandled SPI interrupt: {:?}", 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/qspi.rs b/firmware/examples/zedboard/src/bin/qspi.rs index 4ed8253..e3ec846 100644 --- a/firmware/examples/zedboard/src/bin/qspi.rs +++ b/firmware/examples/zedboard/src/bin/qspi.rs @@ -11,7 +11,9 @@ use embedded_io::Write; use log::{error, info}; use zedboard::PS_CLOCK_FREQUENCY; use zedboard_bsp::qspi_spansion; -use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart}; +use zynq7000_hal::{ + BootMode, clocks, generic_interrupt_handler, gpio, gtc, prelude::*, qspi, uart, +}; use zynq7000_rt as _; @@ -193,23 +195,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/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 07f5780..a23c44d 100644 --- a/firmware/examples/zedboard/src/bin/uart-non-blocking.rs +++ b/firmware/examples/zedboard/src/bin/uart-non-blocking.rs @@ -41,8 +41,8 @@ use log::{error, info, warn}; use zynq7000_hal::{ BootMode, clocks::Clocks, - configure_level_shifter, - gic::{GicConfigurator, GicInterruptHelper, Interrupt}, + configure_level_shifter, generic_interrupt_handler, + 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, @@ -210,6 +210,20 @@ async fn main(spawner: Spawner) -> ! { unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } } + // Register the interrupts for the PL. + zynq7000_hal::register_interrupt( + Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl0), + on_interrupt_axi_uartlite, + ); + zynq7000_hal::register_interrupt( + Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl1), + on_interrupt_axi_16550, + ); + zynq7000_hal::register_interrupt( + Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Uart0), + on_interrupt_uart_0, + ); + // Safety: We are not multi-threaded yet. unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core( @@ -390,7 +404,8 @@ async fn uartlite_task(uartlite: axi_uartlite::Tx) { #[embassy_executor::task] async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) { let mut ticker = Ticker::every(Duration::from_millis(1000)); - let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx); + let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx, false); + let str0 = build_print_string("UART0:", "Hello World"); let str1 = build_print_string( "UART0:", @@ -432,36 +447,12 @@ async fn uart_16550_task(uart_tx: axi_uart16550::Tx) { } #[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) => match spi_interrupt { - zynq7000_hal::gic::SpiInterrupt::Pl0 => { - on_interrupt_axi_uartlite(); - } - zynq7000_hal::gic::SpiInterrupt::Pl1 => { - on_interrupt_axi_16550(); - } - zynq7000_hal::gic::SpiInterrupt::Uart0 => { - on_interrupt_uart_0(); - } - - _ => (), - }, - 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); } fn on_interrupt_axi_uartlite() { @@ -530,8 +521,9 @@ fn on_interrupt_uart_0() { .on_interrupt(&mut buf, true) .read_bytes(); }); + // Safety: This function is only called once inside the interrupt handler. // Handle TX next: Handle pending asynchronous TX operations. - zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0); + unsafe { zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0) }; // Send received RX data to main task. if read_bytes > 0 { critical_section::with(|cs| { 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-embassy/src/lib.rs b/firmware/zynq7000-embassy/src/lib.rs index 5652ddd..2bd2d84 100644 --- a/firmware/zynq7000-embassy/src/lib.rs +++ b/firmware/zynq7000-embassy/src/lib.rs @@ -66,6 +66,16 @@ impl GtcTimerDriver { /// /// This has to be called ONCE at system initialization. pub unsafe fn init(&'static self, arm_clock: &ArmClocks, mut gtc: GlobalTimerCounter) { + fn safe_interrupt_handler() { + // Safety: See safety notes of [zynq7000_hal::generic_interrupt_handler]. + unsafe { + on_interrupt(); + } + } + zynq7000_hal::register_interrupt( + zynq7000_hal::gic::Interrupt::Ppi(zynq7000_hal::gic::PpiInterrupt::GlobalTimer), + safe_interrupt_handler, + ); CPU_3X2X_CLK.set(arm_clock.cpu_3x2x_clk()).unwrap(); SCALE .set(arm_clock.cpu_3x2x_clk().to_raw() as u64 / TICK_HZ) 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 75a69a4..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::*; @@ -85,8 +88,9 @@ bitflags::bitflags! { } /// Private Peripheral Interrupt (PPI) which are private to the CPU. -#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, num_enum::TryFromPrimitive)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum PpiInterrupt { /// Global timer. @@ -102,8 +106,9 @@ pub enum PpiInterrupt { } /// Shared Peripheral Interrupt IDs. -#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, num_enum::TryFromPrimitive)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum SpiInterrupt { /// CPU 0. @@ -231,8 +236,9 @@ pub enum SpiInterrupt { } /// Interrupt ID wrapper. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Interrupt { /// Software-generated interrupt (SGI). Sgi(usize), @@ -247,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, @@ -334,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] @@ -351,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 } @@ -365,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() }, } @@ -648,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), @@ -678,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 new file mode 100644 index 0000000..ada7447 --- /dev/null +++ b/firmware/zynq7000-hal/src/interrupt.rs @@ -0,0 +1,54 @@ +use crate::gic::{Interrupt, InterruptGuard}; + +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(); + map.insert(interrupt, handler).ok(); + }); +} + +/// Generic interrupt handler which retrieves the interrupt handler for individual [Interrupt]s +/// from a registry and calls it. +/// +/// If no interrupt was registered or the number is [Interrupt::Invalid], returns the [Interrupt] +/// ID as an error. In any case, the generic handler acknowledges the interrupt in the GIC. +/// +/// # Safety +/// +/// 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_guard = InterruptGuard::new(); + let irq_info = gic_guard.interrupt_info(); + let interrupt = irq_info.interrupt(); + if let Interrupt::Invalid(_) = interrupt { + gic_guard.end_of_interrupt(irq_info); + return Err(interrupt); + } + let opt_interrupt_handler = critical_section::with(|cs| { + let map = INTERRUPT_MAP.borrow(cs).borrow_mut(); + map.get(&interrupt).copied() + }); + if let Some(interrupt_handler) = opt_interrupt_handler { + // Safety: The user made sure that this is only called once in the interrupt handler + // function. + unsafe { + interrupt_handler(); + } + } + 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 97e5333..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))] @@ -32,6 +32,7 @@ pub mod gic; pub mod gpio; pub mod gtc; pub mod i2c; +pub mod interrupt; pub mod l2_cache; pub mod log; pub mod pl; @@ -45,6 +46,8 @@ pub mod time; pub mod ttc; pub mod uart; +pub use gic::{Interrupt, PpiInterrupt, SpiInterrupt}; +pub use interrupt::{generic_interrupt_handler, register_interrupt}; pub use zynq7000 as pac; pub use zynq7000::slcr::LevelShifterConfig; @@ -80,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/asynch.rs b/firmware/zynq7000-hal/src/spi/asynch.rs index c1dea3f..750782f 100644 --- a/firmware/zynq7000-hal/src/spi/asynch.rs +++ b/firmware/zynq7000-hal/src/spi/asynch.rs @@ -29,9 +29,11 @@ impl embedded_hal_async::spi::Error for RxOverrunError { /// This is a generic interrupt handler to handle asynchronous SPI operations for a given /// SPI peripheral. /// -/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on +/// # Safety +/// +/// This needs to be called once in the interrupt handler responsible for the SPI interrupts on /// the given SPI bank. -pub fn on_interrupt(peripheral: SpiId) { +pub unsafe fn on_interrupt(peripheral: SpiId) { let mut spi = unsafe { SpiLowLevel::steal(peripheral) }; let index = peripheral as usize; let enabled_irqs = spi.read_enabled_interrupts(); @@ -513,6 +515,30 @@ pub struct SpiAsync(pub Spi); impl SpiAsync { pub fn new(spi: Spi) -> Self { + match spi.inner.id { + SpiId::Spi0 => { + unsafe fn spi0_interrupt_handler() { + unsafe { + on_interrupt(SpiId::Spi0); + } + } + crate::register_interrupt( + crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Spi0), + spi0_interrupt_handler, + ) + } + SpiId::Spi1 => { + unsafe fn spi1_interrupt_handler() { + unsafe { + on_interrupt(SpiId::Spi1); + } + } + crate::register_interrupt( + crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Spi1), + spi1_interrupt_handler, + ) + } + } Self(spi) } 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/mod.rs b/firmware/zynq7000-hal/src/uart/mod.rs index c8f1f64..56f04d7 100644 --- a/firmware/zynq7000-hal/src/uart/mod.rs +++ b/firmware/zynq7000-hal/src/uart/mod.rs @@ -631,7 +631,7 @@ impl Uart { }, tx: Tx { regs: reg_block, - idx: uart_id, + id: uart_id, }, cfg, } diff --git a/firmware/zynq7000-hal/src/uart/tx.rs b/firmware/zynq7000-hal/src/uart/tx.rs index e9b3687..78d9476 100644 --- a/firmware/zynq7000-hal/src/uart/tx.rs +++ b/firmware/zynq7000-hal/src/uart/tx.rs @@ -8,12 +8,12 @@ use super::UartId; /// Transmitter (TX) driver. pub struct Tx { pub(crate) regs: MmioRegisters<'static>, - pub(crate) idx: UartId, + pub(crate) id: UartId, } impl core::fmt::Debug for Tx { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Tx").field("idx", &self.idx).finish() + f.debug_struct("Tx").field("idx", &self.id).finish() } } @@ -24,17 +24,17 @@ impl Tx { /// /// Circumvents safety guarantees provided by the compiler. #[inline] - pub const unsafe fn steal(idx: UartId) -> Self { + pub const unsafe fn steal(id: UartId) -> Self { Tx { - regs: unsafe { idx.regs() }, - idx, + regs: unsafe { id.regs() }, + id, } } - /// UART index. + /// UART ID. #[inline] - pub const fn uart_idx(&self) -> UartId { - self.idx + pub const fn uart_id(&self) -> UartId { + self.id } /// Direct access to the UART MMIO registers. diff --git a/firmware/zynq7000-hal/src/uart/tx_async.rs b/firmware/zynq7000-hal/src/uart/tx_async.rs index b9fb220..64d9ce4 100644 --- a/firmware/zynq7000-hal/src/uart/tx_async.rs +++ b/firmware/zynq7000-hal/src/uart/tx_async.rs @@ -19,9 +19,11 @@ static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; /// This is a generic interrupt handler to handle asynchronous UART TX operations for a given /// UART peripheral. /// +/// # Safety +/// /// The user has to call this once in the interrupt handler responsible for the TX interrupts on /// the given UART bank. -pub fn on_interrupt_tx(peripheral: UartId) { +pub unsafe fn on_interrupt_tx(peripheral: UartId) { let mut tx_with_irq = unsafe { Tx::steal(peripheral) }; let idx = peripheral as usize; let enabled_irqs = tx_with_irq.regs().read_enabled_interrupts(); @@ -126,7 +128,7 @@ impl<'uart> TxFuture<'uart> { /// This function stores the raw pointer of the passed data slice. The user MUST ensure /// that the slice outlives the data structure. pub unsafe fn new(tx_with_irq: &'uart mut Tx, data: &[u8]) -> TxFuture<'uart> { - let idx = tx_with_irq.uart_idx() as usize; + let idx = tx_with_irq.uart_id() as usize; TX_DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed); tx_with_irq.disable_interrupts(); tx_with_irq.disable(); @@ -156,7 +158,7 @@ impl<'uart> TxFuture<'uart> { tx_with_irq.enable_interrupts(data.len() > FIFO_DEPTH); Self { - id: tx_with_irq.uart_idx(), + id: tx_with_irq.uart_id(), marker: PhantomData, } } @@ -196,7 +198,38 @@ pub struct TxAsync { impl TxAsync { /// Constructor. - pub fn new(tx: Tx) -> Self { + /// + /// 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() { + UartId::Uart0 => { + unsafe fn uart0_interrupt_handler() { + unsafe { + on_interrupt_tx(UartId::Uart0); + } + } + crate::register_interrupt( + crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Uart0), + uart0_interrupt_handler, + ) + } + UartId::Uart1 => { + unsafe fn uart1_interrupt_handler() { + unsafe { + on_interrupt_tx(UartId::Uart1); + } + } + crate::register_interrupt( + crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Uart1), + uart1_interrupt_handler, + ) + } + } + } + Self { tx } }