diff --git a/zynq-examples/src/main.rs b/zynq-examples/src/main.rs index 4fa8a17..5b10e6a 100644 --- a/zynq-examples/src/main.rs +++ b/zynq-examples/src/main.rs @@ -3,6 +3,7 @@ use core::panic::PanicInfo; use cortex_ar::asm::nop; +use zynq7000::{hal::gic::Gic, pac::gic::{Gicc, Gicd}}; use zynq7000_rt as _; /// One user LED is MIO7 @@ -19,6 +20,11 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[unsafe(export_name = "main")] pub fn main() -> ! { + let mut gic = Gic::new(unsafe { Gicc::new_mmio() }, unsafe { Gicd::new_mmio() }); + gic.enable_all_interrupts(); + gic.set_all_spi_interrupt_targets_cpu0(); + gic.enable(); + let mut gpio = unsafe { zynq7000::pac::gpio::Gpio::new_mmio() }; gpio.modify_dirm_0(|v| v | ZEDBOARD_LED_MASK); gpio.modify_out_en_0(|v| v | ZEDBOARD_LED_MASK); @@ -30,6 +36,12 @@ pub fn main() -> ! { } } +#[unsafe(no_mangle)] +pub extern "C" fn _irq_handler() { + let mut gic = Gic::new(unsafe { Gicc::new_mmio() }, unsafe { Gicd::new_mmio() }); + +} + /// Panic handler #[panic_handler] fn panic(_info: &PanicInfo) -> ! { diff --git a/zynq7000/Cargo.toml b/zynq7000/Cargo.toml index 5041c6a..a99d202 100644 --- a/zynq7000/Cargo.toml +++ b/zynq7000/Cargo.toml @@ -16,6 +16,10 @@ derive-mmio = { path = "../../derive-mmio", default-features = false } bitbybit = "1.3" arbitrary-int = "1.3" thiserror = { version = "2", default-features = false } +num_enum = { version = "0.7", default-features = false } +critical-section = "1" +embassy-time-driver = "0.2" +embassy-time-queue-utils = "0.1" # cortex-r # defmt = { version = "0.3", optional = true } # critical-section = { version = "1", optional = true } diff --git a/zynq7000/src/hal/gic.rs b/zynq7000/src/hal/gic.rs index a1928fc..2668c6a 100644 --- a/zynq7000/src/hal/gic.rs +++ b/zynq7000/src/hal/gic.rs @@ -1,14 +1,4 @@ -use crate::pac::gic::{MmioGicc, MmioGicd}; - -pub struct Gic { - pub gicc: MmioGicc<'static>, - pub gicd: MmioGicd<'static>, -} - -pub enum SpiSensitivity { - Level = 0b01, - Edge = 0b11, -} +use crate::pac::gic::{Dcr, Icr, MmioGicc, MmioGicd}; pub const ICFR_2_FIXED_VALUE: u32 = 0b01010101010111010101010001011111; /// This configures PL[2:0] to high-level sensitivity. @@ -18,13 +8,159 @@ pub const ICFR_4_FIXED_VALUE: u32 = 0b01110101010101010101010101010101; /// This configures PL[15:8] to high-level sensitivity. pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101; +pub const TARGETS_ALL_CPU_0_IPTR_VAL: u32 = 0x01010101; +pub const TARGETS_ALL_CPU_1_IPTR_VAL: u32 = 0x02020202; + +pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF; +pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000; + +pub enum SpiSensitivity { + Level = 0b01, + Edge = 0b11, +} + +pub enum TargetCpu { + None = 0b00, + Cpu0 = 0b01, + Cpu1 = 0b10, + Both = 0b11, +} + +/// Private Peripheral Interrupt (PPI) which are private to the CPU. +#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] +#[repr(u8)] +pub enum PpiInterrupt { + GlobalTimer = 27, + // Interrupt signal from the PL. CPU0: IRQF2P[18] and CPU1: IRQF2P[19] + NFiq = 28, + CpuPrivateTimer = 29, + /// AWDT0 and AWDT1 for each CPU. + Awdt = 30, + // Interrupt signal from the PL. CPU0: IRQF2P[16] and CPU1: IRQF2P[17] + NIrq = 31, +} +#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] +#[repr(u8)] +pub enum SpiInterrupt { + Cpu0 = 32, + Cpu1 = 33, + L2Cache = 34, + Ocm = 35, + _Reserved0 = 36, + Pmu0 = 37, + Pmu1 = 38, + Xadc = 39, + DevC = 40, + Swdt = 41, + Ttc00 = 42, + Ttc01 = 43, + Ttc02 = 44, + DmacAbort = 45, + Dmac0 = 46, + Dmac1 = 47, + Dmac2 = 48, + Dmac3 = 49, + Smc = 50, + Qspi = 51, + Gpio = 52, + Usb0 = 53, + Eth0 = 54, + Eth0Wakeup = 55, + Sdio0 = 56, + I2c0 = 57, + Spi0 = 58, + Uart0 = 59, + Can0 = 60, + Pl0 = 61, + Pl1 = 62, + Pl2 = 63, + Pl3 = 64, + Pl4 = 65, + Pl5 = 66, + Pl6 = 67, + Pl7 = 68, + Ttc10 = 69, + Ttc11 = 70, + Ttc12 = 71, + Dmac4 = 72, + Dmac5 = 73, + Dmac6 = 74, + Dmac7 = 75, + Usb1 = 76, + Eth1 = 77, + Eth1Wakeup = 78, + Sdio1 = 79, + I2c1 = 80, + Spi1 = 81, + Uart1 = 82, + Can1 = 83, + Pl8 = 84, + Pl9 = 85, + Pl10 = 86, + Pl11 = 87, + Pl12 = 88, + Pl13 = 89, + Pl14 = 90, + Pl15 = 91, + ScuParity = 92, +} + #[derive(Debug, thiserror::Error)] #[error("Invalid PL interrupt ID {0}")] -pub struct InvalidPlInterruptId(pub u32); +pub struct InvalidPlInterruptId(pub usize); + +/// Invalid Shared Peripheral Interrupt (SPI) ID. +#[derive(Debug, thiserror::Error)] +#[error("Invalid SPI interrupt ID {0}")] +pub struct InvalidSpiInterruptId(pub usize); + +/// Invalid Software Generated Interrupt (SGI) ID. +#[derive(Debug, thiserror::Error)] +#[error("Invalid SGI interrupt ID {0}")] +pub struct InvalidSgiInterruptId(pub usize); +/// Higher-level GIC controller for the Zynq70000 SoC. +/// +/// The flow of using this controller is as follows: +/// +/// 1. Create the controller using [Self::new]. You can use the [crate::pac::gic::Gicc::new_mmio] +/// and [crate::pac::gic::Gicd::new_mmio] functions to create the MMIO instances. The +/// constructor configures all PL interrupts sensivities to high-level sensitivity and +/// configures all sensitivities which are expected to have a certain value. +/// 2. Perform the configuration of the interrupt targets and the interrupt sensitivities. +/// The CPU targets are encoded with [TargetCpu] while the sensitivities are encoded by +/// the [SpiSensitivity] enum. You can use the following API to configure the interrupts: +/// +/// - [Self::set_spi_interrupt_cpu_target] +/// - [Self::set_all_spi_interrupt_targets_cpu0] +/// - [Self::set_pl_interrupt_sensitivity] +/// +/// 3. Enable all required interrupts. The following API can be used for this: +/// +/// - [Self::enable_sgi_interrupt] +/// - [Self::enable_ppi_interrupt] +/// - [Self::enable_spi_interrupt] +/// - [Self::enable_all_spi_interrupts] +/// - [Self::enable_all_ppi_interrupts] +/// - [Self::enable_all_sgi_interrupts] +/// - [Self::enable_all_interrupts] +/// +/// You might also chose to enable these interrupts at run-time after the GIC was started. +/// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or +/// with [Self::enable] which assumes a certain configuration. +pub struct Gic { + pub gicc: MmioGicc<'static>, + pub gicd: MmioGicd<'static>, +} impl Gic { + /// Create a new GIC controller instance and calls [Self::initialize] to perform + /// strongly recommended initialization routines for the GIC. + /// + #[inline] pub fn new(gicc: MmioGicc<'static>, gicd: MmioGicd<'static>) -> Self { - Gic { gicc, gicd } + let mut gic = Gic { gicc, gicd }; + gic.initialize(); + gic } /// Sets up the GIC by configuring the required sensitivites for the shared peripheral @@ -35,6 +171,7 @@ impl Gic { /// logic. These are configured to high level sensitivity by this function. /// If you need a different sensitivity, you need to update the bits using the /// [Self::set_pl_interrupt_sensitivity] function. + #[inline] pub fn initialize(&mut self) { self.gicd.write_icfr_2_spi(ICFR_2_FIXED_VALUE); self.gicd.write_icfr_3_spi(ICFR_3_FIXED_VALUE); @@ -42,9 +179,15 @@ impl Gic { self.gicd.write_icfr_5_spi(ICFR_5_FIXED_VALUE); } + /// Set the sensitivity of a the Programmable Logic SPI interrupts. + /// + /// These are the only interrupt IDs which are configurable for SPI. They are set + /// to high-level sensitivity by default by the [Self::initialize] function. You can + /// use this method to override certain sensitivies. + #[inline] pub fn set_pl_interrupt_sensitivity( &mut self, - pl_int_id: u32, + pl_int_id: usize, sensitivity: SpiSensitivity, ) -> Result<(), InvalidPlInterruptId> { if pl_int_id >= 16 { @@ -73,4 +216,144 @@ impl Gic { } Ok(()) } + + /// Set the CPU target for a SPI interrupt. + /// + /// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all + /// interrupts with one core. + #[inline] + pub fn set_spi_interrupt_cpu_target(&mut self, spi_int: SpiInterrupt, target: TargetCpu) { + let spi_int_raw = spi_int as u32; + let spi_offset_to_0 = spi_int_raw as usize - 32; + // Unwrap okay, calculated index is always valid. + self.gicd + .write_iptr_spi( + spi_offset_to_0 / 4, + (target as u32) << ((spi_offset_to_0 % 4) * 8), + ) + .unwrap(); + } + + /// Utility function to set all SGI interrupt targets to CPU0. + /// + /// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in + /// the system. + #[inline] + pub fn set_all_spi_interrupt_targets_cpu0(&mut self) { + self.gicd + .write_iptr_spi(0, TARGETS_ALL_CPU_0_IPTR_VAL) + .unwrap(); + self.gicd + .write_iptr_spi(1, TARGETS_ALL_CPU_0_IPTR_VAL) + .unwrap(); + } + + #[inline] + pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> { + if int_id >= 16 { + return Err(InvalidSpiInterruptId(int_id)); + } + unsafe { self.gicd.write_iser_unchecked(0, 1 << int_id) }; + Ok(()) + } + + #[inline] + pub fn enable_all_sgi_interrupts(&mut self) { + // Unwrap okay, index is valid. + self.gicd + .modify_iser(0, |mut v| { + v |= ACTIVATE_ALL_SGIS_MASK_ISER; + v + }) + .unwrap(); + } + + #[inline] + pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) { + // Unwrap okay, index is valid. + self.gicd + .modify_iser(0, |mut v| { + v |= 1 << (ppi_int as u32); + v + }) + .unwrap(); + } + + #[inline] + pub fn enable_all_ppi_interrupts(&mut self) { + unsafe { + self.gicd.modify_iser_unchecked(0, |mut v| { + v |= ACTIVATE_ALL_PPIS_MASK_ISER; + v + }) + }; + } + + #[inline] + pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) { + let spi_int_raw = spi_int as u32; + match spi_int_raw { + 32..=63 => { + let bit_pos = spi_int_raw - 32; + // Unwrap okay, valid index. + self.gicd.write_iser(1, 1 << bit_pos).unwrap(); + } + 64..=92 => { + let bit_pos = spi_int_raw - 64; + // Unwrap okay, valid index. + self.gicd.write_iser(2, 1 << bit_pos).unwrap(); + } + _ => unreachable!(), + } + } + + #[inline] + pub fn enable_all_spi_interrupts(&mut self) { + self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap(); + self.gicd.write_iser(2, 0xFFFF_FFFF).unwrap(); + } + + /// Enables all interrupts by calling [Self::enable_all_sgi_interrupts], + /// [Self::enable_all_ppi_interrupts] and [Self::enable_all_spi_interrupts]. + pub fn enable_all_interrupts(&mut self) { + self.enable_all_sgi_interrupts(); + self.enable_all_ppi_interrupts(); + self.enable_all_spi_interrupts(); + } + + /// Enable the GIC assuming a possibly non-secure configuration. + /// + /// This function will NOT configure and enable the various interrupt sources. You need to + /// set the interrupt + /// This function configured the control registers with the following settings: + /// + /// - CPU interface: Secure and non-secure interrupts are enabled. SBPR, FIQen and AckCtrl + /// fields set to default value 0. + /// - Distributor interface: Both non-secure and secure interrupt distribution enabled. + /// + /// It calls [Self::update_ctrl_regs] to update the control registers. + /// If you need custom settings, you can call [Self::update_ctrl_regs] with your required + /// settings. + pub fn enable(&mut self) { + self.update_ctrl_regs( + Icr::builder() + .with_sbpr(false) + .with_fiq_en(false) + .with_ack_ctrl(false) + .with_enable_non_secure(true) + .with_enable_secure(true) + .build(), + Dcr::builder() + .with_enable_non_secure(true) + .with_enable_secure(true) + .build(), + ); + } + + /// Update the control registers which control the safety configuration and which also enable + /// the GIC. + pub fn update_ctrl_regs(&mut self, icr: Icr, dcr: Dcr) { + self.gicc.write_icr(icr); + self.gicd.write_dcr(dcr); + } } diff --git a/zynq7000/src/hal/gtc.rs b/zynq7000/src/hal/gtc.rs new file mode 100644 index 0000000..96c4a93 --- /dev/null +++ b/zynq7000/src/hal/gtc.rs @@ -0,0 +1,34 @@ +use core::{ + cell::RefCell, + sync::atomic::{compiler_fence, Ordering}, +}; + +use crate::pac::gtc::MmioGtc; + +/// High level GTC driver. +pub struct Gtc(pub RefCell>); + +impl Gtc { + #[inline] + pub fn new(mmio_gtc: MmioGtc<'static>) -> Self { + Self(RefCell::new(mmio_gtc)) + } + + #[inline] + pub unsafe fn steal() -> Self { + Self::new(unsafe { crate::pac::gtc::Gtc::new_mmio() }) + } + + #[inline] + pub fn read_timer(&self) -> u64 { + let mut periph = self.0.borrow_mut(); + let upper = periph.read_count_upper(); + loop { + let lower = periph.read_count_lower(); + if periph.read_count_upper() == upper { + return ((upper as u64) << 32) | (lower as u64); + } + // Overflow, read upper again. + } + } +} diff --git a/zynq7000/src/hal/mod.rs b/zynq7000/src/hal/mod.rs index 3dee22b..3d8664e 100644 --- a/zynq7000/src/hal/mod.rs +++ b/zynq7000/src/hal/mod.rs @@ -1 +1,2 @@ pub mod gic; +pub mod gtc; diff --git a/zynq7000/src/lib.rs b/zynq7000/src/lib.rs index 67e89a1..a1c1fcd 100644 --- a/zynq7000/src/lib.rs +++ b/zynq7000/src/lib.rs @@ -3,5 +3,6 @@ pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000; -pub mod pac; pub mod hal; +pub mod pac; +pub mod time_driver; diff --git a/zynq7000/src/pac/gic.rs b/zynq7000/src/pac/gic.rs index 42761e2..192f753 100644 --- a/zynq7000/src/pac/gic.rs +++ b/zynq7000/src/pac/gic.rs @@ -1,9 +1,9 @@ pub use crate::pac::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR}; -use arbitrary_int::{u10, u3, u5}; +use arbitrary_int::{u3, u5, u10}; use static_assertions::const_assert_eq; /// Distributor Control Register -#[bitbybit::bitfield(u32)] +#[bitbybit::bitfield(u32, default = 0x0)] pub struct Dcr { #[bit(1, rw)] enable_non_secure: bool, @@ -75,7 +75,10 @@ pub struct Gicd { pub ipr: [u32; 0x18], _reserved_11: [u32; 0xE8], /// Interrupt Processor Targes Registers - pub iptr: [u32; 0x18], + pub iptr_sgi: [u32; 0x4], + // TODO: Mark those read-only as soon as that works for arrays. + pub iptr_ppi: [u32; 0x4], + pub iptr_spi: [u32; 0x10], // Those are split in the ARM documentation for some reason.. _reserved_12: [u32; 0xE8], /// Interrupt Configuration Registers @@ -125,7 +128,7 @@ impl Gicd { } /// CPU interface control register. -#[bitbybit::bitfield(u32)] +#[bitbybit::bitfield(u32, default = 0x0)] pub struct Icr { #[bit(4, rw)] sbpr: bool, diff --git a/zynq7000/src/pac/gtc.rs b/zynq7000/src/pac/gtc.rs index efd5cd5..d6b7d54 100644 --- a/zynq7000/src/pac/gtc.rs +++ b/zynq7000/src/pac/gtc.rs @@ -2,6 +2,24 @@ pub const GTC_BASE_ADDR: usize = crate::pac::mpcore::MPCORE_BASE_ADDR + 0x0000_0200; +#[bitbybit::bitfield(u32)] +pub struct GtcCtrl { + #[bits(8..=15, rw)] + prescaler: u8, + #[bit(2, rw)] + irq_enable: bool, + #[bit(1, rw)] + comparator_enable: bool, + #[bit(0, rw)] + enable: bool, +} + +#[bitbybit::bitfield(u32)] +pub struct InterruptStatus { + #[bit(0, rw)] + event_flag: bool, +} + /// Global timer counter. #[derive(derive_mmio::Mmio)] #[mmio(no_ctors)] @@ -12,9 +30,9 @@ pub struct Gtc { /// Count register 1, upper 32 bits count_upper: u32, /// Control register - ctrl: u32, + ctrl: GtcCtrl, /// Interrupt status register - isr: u32, + isr: InterruptStatus, /// Comparator 0, lower 32 bits comparator_lower: u32, /// Comparator 1, upper 32 bits diff --git a/zynq7000/src/pac/mod.rs b/zynq7000/src/pac/mod.rs index 27a4ff0..e859df9 100644 --- a/zynq7000/src/pac/mod.rs +++ b/zynq7000/src/pac/mod.rs @@ -1,6 +1,6 @@ -pub mod gpio; -pub mod uart; -pub mod gtc; -pub mod slcr; -pub mod mpcore; pub mod gic; +pub mod gpio; +pub mod gtc; +pub mod mpcore; +pub mod slcr; +pub mod uart; diff --git a/zynq7000/src/time_driver.rs b/zynq7000/src/time_driver.rs new file mode 100644 index 0000000..8a74111 --- /dev/null +++ b/zynq7000/src/time_driver.rs @@ -0,0 +1,77 @@ +use core::{ + cell::{Cell, RefCell}, + sync::atomic::AtomicU64, +}; + +use critical_section::{CriticalSection, Mutex}; +use embassy_time_driver::Driver; +use embassy_time_queue_utils::Queue; + +struct AlarmState { + timestamp: Cell, +} + +impl AlarmState { + const fn new() -> Self { + Self { + timestamp: Cell::new(u64::MAX), + } + } +} + +unsafe impl Send for AlarmState {} + +pub struct TimerDriver { + periods: AtomicU64, + /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. + alarms: Mutex, + queue: Mutex>, +} + +impl TimerDriver { + + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + true + } + +} +impl Driver for TimerDriver { + fn now(&self) -> u64 { + /* + if SCALE.get().is_none() { + return 0; + } + let mut period1: u32; + let mut period2: u32; + let mut counter_val: u32; + + loop { + // Acquire ensures that we get the latest value of `periods` and + // no instructions can be reordered before the load. + period1 = self.periods.load(Ordering::Acquire); + + counter_val = u32::MAX - Self::timekeeper_tim().cnt_value().read().bits(); + + // Double read to protect against race conditions when the counter is overflowing. + period2 = self.periods.load(Ordering::Relaxed); + if period1 == period2 { + let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap(); + return now; + } + } + */ + } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +}