introduce interrupt registry #78

Merged
muellerr merged 1 commits from interrupt-registry into main 2026-05-08 16:30:59 +02:00
28 changed files with 373 additions and 396 deletions
@@ -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)]
@@ -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)]
@@ -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)]
+8 -18
View File
@@ -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)]
+5 -16
View File
@@ -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)]
@@ -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) => {
+4 -5
View File
@@ -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);
}
}
+26 -31
View File
@@ -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)]
@@ -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)]
@@ -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)]
@@ -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)]
+7 -25
View File
@@ -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)]
+8 -17
View File
@@ -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)]
+7 -18
View File
@@ -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)]
@@ -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)]
@@ -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| {
+6 -17
View File
@@ -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)]
+7 -18
View File
@@ -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)]
+10
View File
@@ -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)
+5
View File
@@ -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
+53 -19
View File
@@ -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());
}
}
}
+54
View File
@@ -0,0 +1,54 @@
use crate::gic::{Interrupt, InterruptGuard};
pub type InterruptMap = heapless::index_map::FnvIndexMap<Interrupt, unsafe fn(), 128>;
static INTERRUPT_MAP: critical_section::Mutex<core::cell::RefCell<InterruptMap>> =
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(())
}
+5 -2
View File
@@ -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<zynq7000::Peripherals, InitError> {
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();
+28 -2
View File
@@ -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)
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -631,7 +631,7 @@ impl Uart {
},
tx: Tx {
regs: reg_block,
idx: uart_id,
id: uart_id,
},
cfg,
}
+8 -8
View File
@@ -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.
+37 -4
View File
@@ -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 }
}