init commit

This commit is contained in:
2025-02-19 11:00:04 +01:00
commit f842673e3a
104 changed files with 21595 additions and 0 deletions

202
zynq7000/src/gic.rs Normal file
View File

@ -0,0 +1,202 @@
//! # GIC (Generic Interrupt Controller) register module.
pub use crate::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR};
use arbitrary_int::{u3, u5, u10};
use static_assertions::const_assert_eq;
/// Distributor Control Register
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Dcr {
#[bit(1, rw)]
enable_non_secure: bool,
#[bit(0, rw)]
enable_secure: bool,
}
/// Read only bit. This register only returns fixed constants.
#[bitbybit::bitfield(u32)]
pub struct TypeRegister {
#[bits(11..=15, r)]
lspi: u5,
#[bit(10, r)]
security_extension: bool,
#[bits(5..=7, r)]
cpu_number: u3,
#[bits(0..=4, r)]
it_lines_number: u5,
}
impl TypeRegister {
pub const SECURITY_EXTNS_BIT: bool = true;
/// 31 LSPIs.
pub const NUM_LSPI: usize = 0x1f;
/// Encoding: 0b001 means that the Cortex-A9 MPCore has 2 processors.
pub const CPU_NUMBER_BITS: u8 = 0b001;
/// The distributor provides 96 interrupts.
pub const IT_LINES_NUMBER: u8 = 0x2;
pub const NUM_OF_CPUS: usize = 2;
pub const NUM_OF_INTERRUPTS: usize = 96;
}
pub type Typer = TypeRegister;
/// GIC Distributor registers.
#[derive(derive_mmio::Mmio)]
#[repr(C, align(8))]
pub struct Gicd {
/// Distributor Control Register
pub dcr: Dcr,
/// Interrupt Controller Type Register
#[mmio(PureRead)]
pub ictr: Typer,
/// Distributor Implementer Identification Register
#[mmio(PureRead)]
pub iidr: u32,
_reserved_0: [u32; 0x1D],
/// Interrupt security registers
pub isr: [u32; 3],
_reserved_1: [u32; 0x1D],
/// Interrupt Set-Enable Registers
pub iser: [u32; 0x3],
_reserved_3: [u32; 0x1D],
/// Interrupt Clear-Enable Registers
pub icer: [u32; 0x3],
_reserved_4: [u32; 0x1D],
/// Interrupt Set-Pending Registers
pub ispr: [u32; 0x3],
_reserved_5: [u32; 0x1D],
/// Interrupt Clear-Pending Registers
pub icpr: [u32; 0x3],
_reserved_6: [u32; 0x1D],
/// Active Bit Registers
pub abr: [u32; 0x3],
_reserved_10: [u32; 0x3D],
/// Interrupt Priority Registers
pub ipr: [u32; 0x18],
_reserved_11: [u32; 0xE8],
/// Interrupt Processor Targes Registers
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
/// Interupt sensitivity register for software generated interrupts (SGI)
pub icfr_0_sgi: u32,
/// Interupt sensitivity register for private peripheral interrupts (PPI)
pub icfr_1_ppi: u32,
pub icfr_2_spi: u32,
pub icfr_3_spi: u32,
pub icfr_4_spi: u32,
pub icfr_5_spi: u32,
_reserved_13: [u32; 0x3A],
pub ppi_status: u32,
pub spi_status_0: u32,
pub spi_status_1: u32,
_reserved_14: [u32; 0x7D],
/// Software Generated Interrupt Register.
pub sgir: u32,
_reserved_15: [u32; 0x33],
pub pidr_4: u32,
pub pidr_5: u32,
pub pidr_6: u32,
pub pidr_7: u32,
pub pidr_0: u32,
pub pidr_1: u32,
pub pidr_2: u32,
pub pidr_3: u32,
pub cidr: [u32; 4],
}
const_assert_eq!(core::mem::size_of::<Gicd>(), 0x1000);
impl Gicd {
/// Create a new Global Interrupt Controller Distributor MMIO instance at the fixed address of
/// the processing system.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioGicd<'static> {
unsafe { Self::new_mmio_at(GICD_BASE_ADDR) }
}
}
/// CPU interface control register.
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Icr {
#[bit(4, rw)]
sbpr: bool,
#[bit(3, rw)]
fiq_en: bool,
#[bit(2, rw)]
ack_ctrl: bool,
#[bit(1, rw)]
enable_non_secure: bool,
#[bit(0, rw)]
enable_secure: bool,
}
/// Priority Mask Register
#[bitbybit::bitfield(u32)]
pub struct PriorityRegister {
#[bits(0..=7, rw)]
priority: u8,
}
/// Interrupt acknowledge register.
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptSignalRegister {
#[bits(10..=12, rw)]
cpu_id: u3,
#[bits(0..=9, rw)]
ack_int_id: u10,
}
/// GIC CPU interface registers.
#[derive(derive_mmio::Mmio)]
#[repr(C, align(8))]
pub struct Gicc {
/// CPU Interface Control Register.
pub icr: Icr,
/// Interrupt Priority Mask Register.
pub pmr: PriorityRegister,
/// Binary Point Register.
pub bpr: u32,
/// Interrupt Acknowledge Register.
pub iar: InterruptSignalRegister,
/// End of Interrupt Register.
pub eoir: InterruptSignalRegister,
/// Running Priority Register.
pub rpr: PriorityRegister,
/// Highest Pending Interrupt Register.
pub hpir: InterruptSignalRegister,
/// Aliased Binary Point Register
pub abpr: u32,
_reserved_0: [u32; 0x37],
/// CPU Interface Identification Register.
#[mmio(PureRead)]
pub iidr: u32,
}
const_assert_eq!(core::mem::size_of::<Gicc>(), 0x100);
impl Gicc {
/// Create a new Global Interrupt Controller CPU MMIO instance at the fixed address of the
/// processing system.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioGicc<'static> {
unsafe { Self::new_mmio_at(GICC_BASE_ADDR) }
}
}

121
zynq7000/src/gpio.rs Normal file
View File

@ -0,0 +1,121 @@
//! # GPIO register module.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct MaskedOutput {
#[bits(16..=31, w)]
pub mask: u16,
#[bits(0..=15, rw)]
pub output: u16,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct BankCtrl {
/// Direction mode
dirm: u32,
/// Output enable
out_en: u32,
/// Interrupt mask status
#[mmio(PureRead)]
int_mask: u32,
/// Interrupt enable/unmask
#[mmio(Write)]
int_en: u32,
/// Interrupt disable/mask
#[mmio(Write)]
int_dis: u32,
/// Interrupt status
#[mmio(PureRead, Write)]
int_sts: u32,
/// Interrupt type
int_type: u32,
/// Interrupt polarity
int_pol: u32,
/// Interrupt any edge sensitivity
int_any: u32,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Gpio {
/// Maskable output data (GPIO bank 0, MIO, lower 16 bits)
masked_out_0_lsw: MaskedOutput,
/// Maskable output data (GPIO bank 0, MIO, upper 16 bits)
masked_out_0_msw: MaskedOutput,
/// Maskable output data (GPIO bank 1, MIO, lower 16 bits)
masked_out_1_lsw: MaskedOutput,
/// Maskable output data (GPIO bank 1, MIO, upper 16 bits)
masked_out_1_msw: MaskedOutput,
/// Maskable output data (GPIO bank 2, EMIO, lower 16 bits)
masked_out_2_lsw: MaskedOutput,
/// Maskable output data (GPIO bank 2, EMIO, upper 16 bits)
masked_out_2_msw: MaskedOutput,
/// Maskable output data (GPIO bank 3, EMIO, lower 16 bits)
masked_out_3_lsw: MaskedOutput,
/// Maskable output data (GPIO bank 3, EMIO, upper 16 bits)
masked_out_3_msw: MaskedOutput,
_reserved_0: [u32; 8],
/// Output data (GPIO bank 0, MIO)
out_0: u32,
/// Output data (GPIO bank 1, MIO)
out_1: u32,
/// Output data (GPIO bank 2, EMIO)
out_2: u32,
/// Output data (GPIO bank 3, EMIO)
out_3: u32,
_reserved_1: [u32; 4],
/// Input data (GPIO bank 0, MIO)
#[mmio(PureRead)]
in_0: u32,
/// Input data (GPIO bank 1, MIO)
#[mmio(PureRead)]
in_1: u32,
/// Input data (GPIO bank 2, EMIO)
#[mmio(PureRead)]
in_2: u32,
/// Input data (GPIO bank 3, EMIO)
#[mmio(PureRead)]
in_3: u32,
_reserved_2: [u32; 101],
#[mmio(inner)]
bank_0: BankCtrl,
_reserved_3: [u32; 7],
#[mmio(inner)]
bank_1: BankCtrl,
_reserved_4: [u32; 7],
#[mmio(inner)]
bank_2: BankCtrl,
_reserved_5: [u32; 7],
#[mmio(inner)]
bank_3: BankCtrl,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x2E8);
impl Gpio {
/// Create a new XGPIOPS GPIO MMIO instance.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed() -> MmioGpio<'static> {
MmioGpio {
ptr: 0xE000A000 as *mut Gpio,
phantom: core::marker::PhantomData,
}
}
}

60
zynq7000/src/gtc.rs Normal file
View File

@ -0,0 +1,60 @@
//! # Global timer counter module.
pub const GTC_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0200;
#[bitbybit::bitfield(u32)]
pub struct GtcCtrl {
#[bits(8..=15, rw)]
prescaler: u8,
#[bit(3, rw)]
auto_increment: bool,
#[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)]
#[repr(C)]
pub struct Gtc {
/// Count register 0, lower 32 bits
count_lower: u32,
/// Count register 1, upper 32 bits
count_upper: u32,
/// Control register
ctrl: GtcCtrl,
/// Interrupt status register
#[mmio(PureRead, Write)]
isr: InterruptStatus,
/// Comparator 0, lower 32 bits
comparator_lower: u32,
/// Comparator 1, upper 32 bits
comparator_upper: u32,
/// Auto-increment register
auto_increment: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Gtc>(), 0x1C);
impl Gtc {
/// Create a new GTC MMIO instance at the fixed base address.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioGtc<'static> {
unsafe { Gtc::new_mmio_at(GTC_BASE_ADDR) }
}
}

203
zynq7000/src/i2c.rs Normal file
View File

@ -0,0 +1,203 @@
//! SPI register module.
use arbitrary_int::{u2, u6, u10};
pub const I2C_0_BASE_ADDR: usize = 0xE000_4000;
pub const I2C_1_BASE_ADDR: usize = 0xE000_5000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum Direction {
Receiver = 0b1,
Transmitter = 0b0,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum Mode {
Slave = 0b0,
Master = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Control {
/// Divides the input PCLK frequency by this value + 1
#[bits(14..=15, rw)]
div_a: u2,
/// Divides the output from divisor A by this value + 1
#[bits(8..=13, rw)]
div_b: u6,
#[bit(6, rw)]
clear_fifo: bool,
#[bit(5, rw)]
slv_mon: bool,
/// 0: Allow transfer to terminate as soon as all data has been transmitted or received.
/// 1: When no more data is avilable for transmit or no more data can be received, hold
/// the SCK line low until services by the host.
#[bit(4, rw)]
hold_bus: bool,
/// Should be set to 1. 0: Disabled, NACK transmitted. 1: Enabled, ACK transmitted.
#[bit(3, rw)]
acken: bool,
/// Only used in master mode. 0: Reserved. 1: Normal 7-bit address.
#[bit(2, rw)]
addressing: bool,
#[bit(1, rw)]
mode: Mode,
#[bit(0, rw)]
dir: Direction,
}
#[bitbybit::bitfield(u32)]
pub struct Status {
#[bit(8, r)]
bus_active: bool,
/// FIFO is full and new byte was received. The new byte is not acknowledged and the contents
/// of the FIFO remain unchanged.
#[bit(6, r)]
rx_overflow: bool,
/// 1: There is still a byte of data to be transmitted by the interface.
#[bit(6, r)]
tx_busy: bool,
/// Receiver data valid, ca be read from the interface.
#[bit(5, r)]
rx_valid: bool,
#[bit(3, r)]
rx_rw: bool,
}
#[bitbybit::bitfield(u32)]
pub struct Addr {
#[bits(0..=9, rw)]
addr: u10,
}
#[bitbybit::bitfield(u32)]
pub struct Fifo {
#[bits(0..=7, rw)]
data: u8,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptStatus {
#[bit(9, rw)]
arbitration_lost: bool,
#[bit(7, rw)]
rx_underflow: bool,
#[bit(6, rw)]
tx_overflow: bool,
#[bit(5, rw)]
rx_overflow: bool,
#[bit(4, rw)]
slave_ready: bool,
#[bit(3, rw)]
timeout: bool,
#[bit(2, rw)]
nack: bool,
#[bit(1, rw)]
data: bool,
#[bit(0, rw)]
complete: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptMask {
#[bit(9, r)]
arbitration_lost: bool,
#[bit(7, r)]
rx_underflow: bool,
#[bit(6, r)]
tx_overflow: bool,
#[bit(5, r)]
rx_overflow: bool,
#[bit(4, r)]
slave_ready: bool,
#[bit(3, r)]
timeout: bool,
#[bit(2, r)]
nack: bool,
#[bit(1, r)]
data: bool,
#[bit(0, r)]
complete: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptControl {
#[bit(9, w)]
arbitration_lost: bool,
#[bit(7, w)]
rx_underflow: bool,
#[bit(6, w)]
tx_overflow: bool,
#[bit(5, w)]
rx_overflow: bool,
#[bit(4, w)]
slave_ready: bool,
#[bit(3, w)]
timeout: bool,
#[bit(2, w)]
nack: bool,
#[bit(1, w)]
data: bool,
#[bit(0, w)]
complete: bool,
}
#[bitbybit::bitfield(u32)]
pub struct Timeout {
/// Reset value: 0x1F.
#[bits(0..=7, rw)]
timeout: u8,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct TransferSize {
#[bits(0..=7, rw)]
size: u8,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct I2c {
cr: Control,
#[mmio(PureRead)]
sr: Status,
addr: Addr,
#[mmio(Read, Write)]
data: Fifo,
#[mmio(PureRead, Write, Modify)]
isr: InterruptStatus,
transfer_size: TransferSize,
slave_pause: u32,
timeout: Timeout,
#[mmio(PureRead)]
imr: InterruptMask,
#[mmio(Write)]
ier: InterruptControl,
#[mmio(Write)]
idr: InterruptControl,
}
impl I2c {
/// Create a new I2C MMIO instance for I2C0 at address [I2C_0_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioI2c<'static> {
unsafe { Self::new_mmio_at(I2C_0_BASE_ADDR) }
}
/// Create a new I2C MMIO instance for I2C1 at address [I2C_1_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioI2c<'static> {
unsafe { Self::new_mmio_at(I2C_1_BASE_ADDR) }
}
}

85
zynq7000/src/lib.rs Normal file
View File

@ -0,0 +1,85 @@
//! # Rust peripheral acess crate to the AMD Zynq 7000 SoCs
//!
//! This crate provides a low-level register access API building on the
//! [`derive-mmio` crate](https://crates.io/crates/derive-mmio). However, its structure
//! is similar to the crates auto-generated by [`svd2rust`](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
//!
//! This crate is purposely kept low-level to allow building higher level abstractions like HALs
//! on top of it.
#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(test)]
extern crate std;
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod i2c;
pub mod mpcore;
pub mod slcr;
pub mod spi;
pub mod ttc;
pub mod uart;
static PERIPHERALS_TAKEN: AtomicBool = AtomicBool::new(false);
/// This is a collection of all the processing peripherals.
///
/// It is a singleton which exposes all peripherals supported by this crate.
/// The [`svd2rust` documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api)
/// provides some more information about this.
pub struct PsPeripherals {
pub gicc: gic::MmioGicc<'static>,
pub gicd: gic::MmioGicd<'static>,
pub uart_0: uart::MmioUart<'static>,
pub uart_1: uart::MmioUart<'static>,
pub spi_0: spi::MmioSpi<'static>,
pub spi_1: spi::MmioSpi<'static>,
pub i2c_0: i2c::MmioI2c<'static>,
pub i2c_1: i2c::MmioI2c<'static>,
pub gtc: gtc::MmioGtc<'static>,
pub gpio: gpio::MmioGpio<'static>,
pub slcr: slcr::MmioSlcr<'static>,
pub ttc_0: ttc::MmioTtc<'static>,
pub ttc_1: ttc::MmioTtc<'static>,
}
impl PsPeripherals {
/// Returns all supported processing system peripherals *once*.
pub fn take() -> Option<Self> {
let taken = PERIPHERALS_TAKEN.swap(true, Ordering::Relaxed);
if taken {
return None;
}
Some(unsafe { Self::steal() })
}
/// Unchecked version of [Self::take].
///
/// # Safety
///
/// Each of the returned peripherals must be used at most once.
pub unsafe fn steal() -> Self {
unsafe {
Self {
gicc: gic::Gicc::new_mmio_fixed(),
gicd: gic::Gicd::new_mmio_fixed(),
uart_0: uart::Uart::new_mmio_fixed_0(),
uart_1: uart::Uart::new_mmio_fixed_1(),
gtc: gtc::Gtc::new_mmio_fixed(),
gpio: gpio::Gpio::new_mmio_fixed(),
slcr: slcr::Slcr::new_mmio_fixed(),
spi_0: spi::Spi::new_mmio_fixed_0(),
spi_1: spi::Spi::new_mmio_fixed_1(),
i2c_0: i2c::I2c::new_mmio_fixed_0(),
i2c_1: i2c::I2c::new_mmio_fixed_1(),
ttc_0: ttc::Ttc::new_mmio_fixed_0(),
ttc_1: ttc::Ttc::new_mmio_fixed_1(),
}
}
}
}

97
zynq7000/src/mpcore.rs Normal file
View File

@ -0,0 +1,97 @@
//! Application Processing Unit Registers (mpcore)
//!
//! Based on p.1483 of the Zynq-7000 TRM.
use static_assertions::const_assert_eq;
use crate::{
gic::{Gicc, Gicd, MmioGicc, MmioGicd},
gtc::{Gtc, MmioGtc},
};
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
pub const SCU_BASE_ADDR: usize = MPCORE_BASE_ADDR;
pub const GICC_BASE_ADDR: usize = MPCORE_BASE_ADDR + 0x100;
pub const GICD_BASE_ADDR: usize = MPCORE_BASE_ADDR + 0x1000;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Scu {
ctrl: u32,
config: u32,
cpu_power_status: u32,
invalidate_all_regs_in_secure_state: u32,
_reserved_0: [u32; 0xC],
filtering_start_addr: u32,
filtering_end_addr: u32,
_reserved_1: [u32; 0x2],
access_ctrl: u32,
non_secure_access_ctrl: u32,
}
impl Scu {
/// Create a new Snoop Control Unit interface at the fixed base address.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioScu<'static> {
unsafe { Self::new_mmio_at(SCU_BASE_ADDR) }
}
}
const_assert_eq!(core::mem::size_of::<Scu>(), 0x58);
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Mpcore {
#[mmio(inner)]
scu: Scu,
_reserved_0: [u32; 0x2A],
#[mmio(inner)]
gicc: Gicc,
#[mmio(inner)]
gt: Gtc,
_reserved_1: [u32; 0xF9],
private_timer_load: u32,
private_timer_counter: u32,
private_timer_ctrl: u32,
private_interrupt_status: u32,
_reserved_2: [u32; 0x4],
watchdog_load: u32,
watchdog_counter: u32,
watchdog_ctrl: u32,
watchdog_interrupt_status: u32,
watchdog_reset_status: u32,
watchdog_disable: u32,
_reserved_3: [u32; 0x272],
#[mmio(inner)]
gicd: Gicd,
}
const_assert_eq!(core::mem::size_of::<Mpcore>(), 0x2000);
impl Mpcore {
/// Create a MP core peripheral interface at the fixed base address.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioMpcore<'static> {
unsafe { Self::new_mmio_at(MPCORE_BASE_ADDR) }
}
}

364
zynq7000/src/slcr/clocks.rs Normal file
View File

@ -0,0 +1,364 @@
//! SLCR clock control registers.
//!
//! Writing any of these registers required unlocking the SLCR first.
use super::{CLOCK_CONTROL_OFFSET, SLCR_BASE_ADDR};
use arbitrary_int::{u4, u6, u7, u10};
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum BypassForce {
EnabledOrSetByBootMode = 0b0,
Bypassed = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum BypassQual {
BypassForceBit = 0b0,
BootModeFourthBit = 0b1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct PllCtrl {
/// Feedback divisor for the PLL.
///
/// NOTE: Before changing this value, the PLL must first be bypassed and then put into
/// reset mode.
#[bits(12..=18, rw)]
fdiv: u7,
/// Select source for the ARM PLL bypass control
#[bit(4, rw)]
bypass_force: BypassForce,
/// Select source for the ARM PLL bypass control
#[bit(3, rw)]
bypass_qual: BypassQual,
// Power-down control
#[bit(1, rw)]
pwrdwn: bool,
/// Reset control
#[bit(0, rw)]
reset: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct PllCfg {
#[bits(12..=21, rw)]
lock_count: u10,
/// Charge Pump control
#[bits(8..=11, rw)]
pll_cp: u4,
/// Loop resistor control
#[bits(4..=7, rw)]
pll_res: u4,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct PllStatus {
#[bit(5)]
io_pll_stable: bool,
#[bit(4)]
ddr_pll_stable: bool,
#[bit(3)]
arm_pll_stable: bool,
#[bit(2)]
io_pll_lock: bool,
#[bit(1)]
drr_pll_lock: bool,
#[bit(0)]
arm_pll_lock: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FpgaClkControl {
// Reset value 0x1
#[bits(20..=25, rw)]
divisor_1: u6,
// Reset value 0x18
#[bits(8..=13, rw)]
divisor_0: u6,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct FpgaClkBlock {
clk_ctrl: FpgaClkControl,
thr_ctrl: u32,
thr_cnt: u32,
thr_status: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<FpgaClkBlock>(), 0x10);
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum SrcSelArm {
ArmPll = 0b00,
ArmPllAlt = 0b01,
DdrPll = 0b10,
IoPll = 0b11,
}
#[bitbybit::bitfield(u32)]
pub struct ArmClkCtrl {
#[bit(28, rw)]
cpu_peri_clk_act: bool,
#[bit(27, rw)]
cpu_1x_clk_act: bool,
#[bit(26, rw)]
cpu_2x_clk_act: bool,
#[bit(25, rw)]
cpu_3or2x_clk_act: bool,
#[bit(24, rw)]
cpu_6or4x_clk_act: bool,
/// Reset value: 0x4. There is a requirement for the quality of the high speed clock that
/// it has to be divided by an even number. This field must be equal to or greater than 2.
#[bits(8..=13, rw)]
divisor: u6,
/// Reset value: 0x0
#[bits(4..=5, rw)]
srcsel: SrcSelArm,
}
#[bitbybit::bitfield(u32)]
pub struct DdrClkCtrl {
/// Divisor for DDR 2x clock. Reset value: 0x6
#[bits(26..=31, rw)]
div_2x_clk: u6,
/// Divisor for DDR 3x clock. Only even divisors are allowed! Reset value: 0x4
#[bits(20..=25, rw)]
div_3x_clk: u6,
/// Reset value: 0x1
#[bit(1, rw)]
ddr_2x_clk_act: bool,
/// Reset value: 0x1
#[bit(0, rw)]
ddr_3x_clk_act: bool,
}
#[bitbybit::bitfield(u32)]
pub struct DciClkCtrl {
/// Second cascade divider. Reset value: 0x1E
#[bits(20..=25, rw)]
divisor_1: u6,
/// Reset value: 0x32
#[bits(8..=13, rw)]
divisor_0: u6,
/// Reset value: 0x1
#[bit(0, rw)]
clk_act: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct ClockRatioSelectReg {
/// Reset value: 0x1 (6:2:1 clock)
#[bit(0, rw)]
sel: ClockRatioSelect,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum ClockRatioSelect {
/// 4:2:1 clock ratio, which is an abbreviation for 4:2:2:1.
FourToTwoToOne = 0b0,
/// 6:2:1 clock ratio, which is an abbreviation for 6:3:2:1.
SixToTwoToOne = 0b1,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum SrcSelIo {
IoPll = 0b00,
IoPllAlt = 0b01,
ArmPll = 0b10,
DdrPll = 0b11,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct GigEthClkCtrl {
#[bits(20..=25, rw)]
divisor_1: u6,
#[bits(8..=13, rw)]
divisor_0: u6,
#[bit(6, rw)]
use_emio_tx_clk: bool,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
#[bit(0, rw)]
clk_act: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct CanClkCtrl {
#[bits(20..=25, rw)]
divisor_1: u6,
#[bits(8..=13, rw)]
divisor_0: u6,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
#[bit(1, rw)]
clk_1_act: bool,
#[bit(0, rw)]
clk_0_act: bool,
}
#[bitbybit::bitfield(u32)]
pub struct SingleCommonPeriphIoClkCtrl {
#[bits(8..=13, rw)]
divisor: u6,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
#[bit(0, rw)]
clk_act: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct DualCommonPeriphIoClkCtrl {
#[bits(8..=13, rw)]
divisor: u6,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
#[bit(1, rw)]
clk_1_act: bool,
#[bit(0, rw)]
clk_0_act: bool,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug)]
pub enum SrcSelTpiu {
IoPll = 0b000,
IoPllAlt = 0b001,
ArmPll = 0b010,
DdrPll = 0b011,
EmioTraceClk = 0b100,
EmioTraceClkAlt0 = 0b101,
EmioTraceClkAlt1 = 0b110,
EmioTraceClkAlt2 = 0b111,
}
#[bitbybit::bitfield(u32)]
pub struct TracePortClkCtrl {
#[bits(8..=13, rw)]
divisor: u6,
#[bits(4..=6, rw)]
srcsel: SrcSelTpiu,
#[bit(1, rw)]
clk_1x_clk_act: bool,
#[bit(0, rw)]
clk_act: bool,
}
/// AMBA peripheral clock control.
///
/// These clocks must be enabled if you want to read from the peripheral register space.
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct AperClkCtrl {
#[bit(24, rw)]
smc_1x_clk_act: bool,
#[bit(23, rw)]
lqspi_1x_clk_act: bool,
#[bit(22, rw)]
gpio_1x_clk_act: bool,
#[bit(21, rw)]
uart_1_1x_clk_act: bool,
#[bit(20, rw)]
uart_0_1x_clk_act: bool,
#[bit(19, rw)]
i2c_1_1x_clk_act: bool,
#[bit(18, rw)]
i2c_0_1x_clk_act: bool,
#[bit(17, rw)]
can_1_1x_clk_act: bool,
#[bit(16, rw)]
can_0_1x_clk_act: bool,
#[bit(15, rw)]
spi_1_1x_clk_act: bool,
#[bit(14, rw)]
spi_0_1x_clk_act: bool,
#[bit(11, rw)]
sdio_1_1x_clk_act: bool,
#[bit(10, rw)]
sdio_0_1x_clk_act: bool,
#[bit(7, rw)]
gem_1_1x_clk_act: bool,
#[bit(6, rw)]
gem_0_1x_clk_act: bool,
#[bit(3, rw)]
usb_1_cpu_1x_clk_act: bool,
#[bit(2, rw)]
usb_0_cpu_1x_clk_act: bool,
#[bit(0, rw)]
dma_cpu_2x_clk_act: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct ClockControl {
arm_pll: PllCtrl,
ddr_pll: PllCtrl,
io_pll: PllCtrl,
pll_status: PllStatus,
arm_pll_cfg: PllCfg,
ddr_pll_cfg: PllCfg,
io_pll_cfg: PllCfg,
_gap0: u32,
arm_clk_ctrl: ArmClkCtrl,
ddr_clk_ctrl: DdrClkCtrl,
dci_clk_ctrl: DciClkCtrl,
/// AMBA peripheral clock control
aper_clk_ctrl: AperClkCtrl,
usb_0_clk_ctrl: u32,
usb_1_clk_ctrl: u32,
gem_0_rclk_ctrl: u32,
gem_1_rclk_ctrl: u32,
gem_0_clk_ctrl: GigEthClkCtrl,
gem_1_clk_ctrl: GigEthClkCtrl,
smc_clk_ctrl: SingleCommonPeriphIoClkCtrl,
lqspi_clk_ctrl: SingleCommonPeriphIoClkCtrl,
sdio_clk_ctrl: DualCommonPeriphIoClkCtrl,
uart_clk_ctrl: DualCommonPeriphIoClkCtrl,
spi_clk_ctrl: DualCommonPeriphIoClkCtrl,
can_clk_ctrl: CanClkCtrl,
can_mioclk_ctrl: u32,
/// Debug or Trace Port clock control.
dbg_clk_ctrl: TracePortClkCtrl,
pcap_clk_ctrl: SingleCommonPeriphIoClkCtrl,
topsw_clk_ctrl: u32,
#[mmio(inner)]
fpga_0_clk_ctrl: FpgaClkBlock,
#[mmio(inner)]
fpga_1_clk_ctrl: FpgaClkBlock,
#[mmio(inner)]
fpga_2_clk_ctrl: FpgaClkBlock,
#[mmio(inner)]
fpga_3_clk_ctrl: FpgaClkBlock,
_gap1: [u32; 5],
clk_621_true: ClockRatioSelectReg,
}
impl ClockControl {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
///
/// # Safety
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioClockControl<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + CLOCK_CONTROL_OFFSET) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<ClockControl>(), 0xC8);

41
zynq7000/src/slcr/mio.rs Normal file
View File

@ -0,0 +1,41 @@
//! # SLCR MIO (Multiplexed I/O) configuration registers
//!
//! Writing any of these registers required unlocking the SLCR first.
use arbitrary_int::{u2, u3};
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum Speed {
SlowCmosEdge = 0b0,
FastCmosEdge = 0b1,
}
#[bitbybit::bitenum(u3)]
pub enum IoType {
LvCmos18 = 0b001,
LvCmos25 = 0b010,
LvCmos33 = 0b011,
Hstl = 0b100,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Config {
#[bit(13, rw)]
disable_hstl_rcvr: bool,
#[bit(12, rw)]
pullup: bool,
#[bits(9..=11, rw)]
io_type: Option<IoType>,
#[bit(8, rw)]
speed: Speed,
#[bits(5..=7, rw)]
l3_sel: u3,
#[bits(3..=4, rw)]
l2_sel: u2,
#[bit(2, rw)]
l1_sel: bool,
#[bit(1, rw)]
l0_sel: bool,
#[bit(0, rw)]
tri_enable: bool,
}

206
zynq7000/src/slcr/mod.rs Normal file
View File

@ -0,0 +1,206 @@
//! System Level Control Registers (slcr)
//!
//! Writing any of these registers required unlocking the SLCR first.
use arbitrary_int::u4;
pub use clocks::{ClockControl, MmioClockControl};
pub use reset::{MmioResetControl, ResetControl};
const SLCR_BASE_ADDR: usize = 0xF8000000;
const CLOCK_CONTROL_OFFSET: usize = 0x100;
const RESET_BLOCK_OFFSET: usize = 0x200;
const GPIOB_OFFSET: usize = 0xB00;
const DDRIOB_OFFSET: usize = 0xB40;
pub mod clocks;
pub mod mio;
pub mod reset;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DdrIoB {
ddriob_addr0: u32,
ddriob_addr1: u32,
ddriob_data0: u32,
ddriob_data1: u32,
ddriob_diff0: u32,
ddriob_diff1: u32,
ddriob_clock: u32,
ddriob_drive_slew_addr: u32,
ddriob_drive_slew_data: u32,
ddriob_drive_slew_diff: u32,
ddriob_drive_slew_clock: u32,
ddriob_ddr_ctrl: u32,
ddriob_dci_ctrl: u32,
ddriob_dci_status: u32,
}
impl DdrIoB {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
///
/// # Safety
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioDdrIoB<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + DDRIOB_OFFSET) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct GpiobCtrl {
ctrl: u32,
cfg_cmos18: u32,
cfg_cmos25: u32,
cfg_cmos33: u32,
_gap17: u32,
cfg_hstl: u32,
drvr_bias_ctrl: u32,
}
impl GpiobCtrl {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
///
/// # Safety
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioGpiobCtrl<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + GPIOB_OFFSET) }
}
}
#[bitbybit::bitfield(u32)]
pub struct BootModeRegister {
#[bit(4, r)]
pll_bypass: bool,
#[bits(0..=3, r)]
boot_mode: u4,
}
#[bitbybit::bitenum(u4)]
#[derive(Debug, PartialEq, Eq)]
pub enum LevelShifterConfig {
DisableAll = 0x00,
EnablePsToPl = 0xA,
EnableAll = 0xF,
}
#[bitbybit::bitfield(u32)]
pub struct LevelShifterReg {
#[bits(0..=3, rw)]
user_lvl_shftr_en: Option<LevelShifterConfig>,
}
/// System Level Control Registers
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Slcr {
/// Secure configuration lock.
scl: u32,
/// SLCR write protection lock
lock: u32,
/// SLCR write protection unlock
unlock: u32,
/// SLCR write protection status
lock_status: u32,
_gap0: [u32; 0x3C],
#[mmio(inner)]
clk_ctrl: ClockControl,
_gap1: [u32; 0x0E],
#[mmio(inner)]
reset_ctrl: ResetControl,
_gap2: [u32; 0x02],
reboot_status: u32,
boot_mode: BootModeRegister,
_gap3: [u32; 0x28],
apu_ctrl: u32,
wdt_clk_set: u32,
_gap4: [u32; 0x4E],
tz_dma_ns: u32,
tz_dma_irq_ns: u32,
tz_dma_periph_ns: u32,
_gap5: [u32; 0x39],
pss_idcode: u32,
_gap6: [u32; 0x33],
ddr_urgent: u32,
_gap7: [u32; 0x02],
ddr_cal_start: u32,
_gap8: u32,
ddr_ref_start: u32,
ddr_cmd_status: u32,
ddr_urgent_sel: u32,
ddr_dfi_status: u32,
_gap9: [u32; 0x37],
mio_pins: [mio::Config; 0x36],
_gap10: [u32; 0x0B],
mio_loopback: u32,
_gap11: u32,
mio_mst_tri_0: u32,
mio_mst_tri_1: u32,
_gap12: [u32; 7],
sd_0_wp_cd_sel: u32,
sd_1_wp_cd_sel: u32,
_gap13: [u32; 0x32],
lvl_shftr_en: LevelShifterReg,
_gap14: [u32; 0x03],
ocm_cfg: u32,
_gap15: [u32; 0x42],
reserved: u32,
_gap16: [u32; 0x38],
_gap18: [u32; 0x09],
#[mmio(inner)]
gpiob: GpiobCtrl,
#[mmio(inner)]
ddriob: DdrIoB,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Slcr>(), 0xB78);
impl Slcr {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
///
/// # Safety
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioSlcr<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR) }
}
}

View File

@ -0,0 +1,79 @@
use super::{RESET_BLOCK_OFFSET, SLCR_BASE_ADDR};
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DualClockReset {
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
periph1_cpu1x_rst: bool,
/// Peripheral 0 AMBA software reset.
#[bit(0, rw)]
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DualRefAndClockReset {
/// Periperal 1 Reference software reset.
#[bit(3, rw)]
periph1_ref_rst: bool,
/// Peripheral 0 Reference software reset.
#[bit(2, rw)]
periph0_ref_rst: bool,
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
periph1_cpu1x_rst: bool,
/// Peripheral 0 AMBA software reset.
#[bit(0, rw)]
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct GpioClockReset {
#[bit(0, rw)]
gpio_cpu1x_rst: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct ResetControl {
/// PS Software reset control
pss: u32,
ddr: u32,
/// Central interconnect reset control
topsw: u32,
dmac: u32,
usb: u32,
gem: u32,
sdio: DualRefAndClockReset,
spi: DualRefAndClockReset,
can: DualClockReset,
i2c: DualClockReset,
uart: DualRefAndClockReset,
gpio: GpioClockReset,
lqspi: u32,
smc: u32,
ocm: u32,
_gap0: u32,
fpga: u32,
a9_cpu: u32,
_gap1: u32,
rs_awdt: u32,
}
impl ResetControl {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
///
/// # Safety
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioResetControl<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + RESET_BLOCK_OFFSET) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<ResetControl>(), 0x50);

236
zynq7000/src/spi.rs Normal file
View File

@ -0,0 +1,236 @@
//! SPI register module.
use arbitrary_int::{Number, u4};
pub const SPI_0_BASE_ADDR: usize = 0xE000_6000;
pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
/// The SPI reference block will be divided by a divisor value.
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
pub enum BaudDivSelect {
By4 = 0b001,
By8 = 0b010,
By16 = 0b011,
By32 = 0b100,
By64 = 0b101,
By128 = 0b110,
By256 = 0b111,
}
impl BaudDivSelect {
pub const fn div_value(&self) -> usize {
match self {
BaudDivSelect::By4 => 4,
BaudDivSelect::By8 => 8,
BaudDivSelect::By16 => 16,
BaudDivSelect::By32 => 32,
BaudDivSelect::By64 => 64,
BaudDivSelect::By128 => 128,
BaudDivSelect::By256 => 256,
}
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(17, rw)]
modefail_gen_en: bool,
#[bit(16, w)]
manual_start: bool,
#[bit(15, rw)]
manual_start_enable: bool,
#[bit(14, rw)]
manual_cs: bool,
#[bits(10..=13, rw)]
cs_raw: u4,
/// Peripheral select decode, 1: Allow external 3-to-8 decode.
/// I am not sure how exactly this work, but I suspect the last three bits of the chip
/// select bits will be output directly to the 3 chip select output lines.
#[bit(9, rw)]
peri_sel: bool,
/// Uses SPI reference clock, value 1 is not supported.
#[bit(8, r)]
ref_clk: bool,
#[bits(3..=5, rw)]
baud_rate_div: Option<BaudDivSelect>,
/// Clock phase. 1: The SPI clock is inactive outside the word.
#[bit(2, rw)]
cpha: bool,
/// Clock phase. 1: The SPI clock is quiescent high.
#[bit(1, rw)]
cpol: bool,
/// Master mode enable. 1 is master mode.
#[bit(0, rw)]
master_ern: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(6, rw)]
tx_underflow: bool,
#[bit(5, rw)]
rx_full: bool,
#[bit(4, rw)]
rx_not_empty: bool,
#[bit(3, rw)]
tx_full: bool,
#[bit(2, rw)]
tx_not_full: bool,
#[bit(1, rw)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, rw)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(6, w)]
tx_underflow: bool,
#[bit(5, w)]
rx_full: bool,
#[bit(4, w)]
rx_not_empty: bool,
#[bit(3, w)]
tx_full: bool,
#[bit(2, w)]
tx_trig: bool,
#[bit(1, w)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, w)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptMask {
#[bit(6, r)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_not_empty: bool,
#[bit(3, r)]
tx_full: bool,
#[bit(2, r)]
tx_trig: bool,
#[bit(1, r)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, r)]
rx_ovr: bool,
}
#[derive(Debug)]
pub struct FifoWrite(arbitrary_int::UInt<u32, 8>);
impl FifoWrite {
#[inline]
pub fn new(data: u8) -> Self {
Self(data.into())
}
#[inline]
pub fn value(&self) -> u8 {
self.0.as_u8()
}
#[inline]
pub fn write(&mut self, value: u8) {
self.0 = value.into();
}
}
#[derive(Debug)]
pub struct FifoRead(arbitrary_int::UInt<u32, 8>);
impl FifoRead {
#[inline]
pub fn new(data: u8) -> Self {
Self(data.into())
}
#[inline]
pub fn value(&self) -> u8 {
self.0.as_u8()
}
}
/// The numbers specified in the register fields are always specified in number of
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DelayControl {
/// Number of cycles the chip select is de-asserted between words when CPHA = 0
#[bits(24..=31, rw)]
inter_word_cs_deassert: u8,
/// Delay between one chip select being de-activated, and activation of another.
#[bits(16..=23, rw)]
between_cs_assertion: u8,
/// Delay between words.
#[bits(8..=15, rw)]
inter_word: u8,
/// Added delay between assertion of slave select and first bit transfer.
#[bits(0..=7, rw)]
cs_to_first_bit: u8,
}
/// Register block specification for both PS SPIs.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Spi {
cr: Config,
#[mmio(PureRead, Write)]
isr: InterruptStatus,
/// Interrupt Enable Register.
#[mmio(Write)]
ier: InterruptControl,
/// Interrupt Disable Register.
#[mmio(Write)]
idr: InterruptControl,
/// Interrupt Mask Register.
#[mmio(PureRead)]
imr: InterruptMask,
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]
txd: FifoWrite,
#[mmio(Read)]
rxd: FifoRead,
sicr: u32,
tx_trig: u32,
rx_trig: u32,
_reserved: [u32; 0x33],
// Reset value: 0x90106
#[mmio(PureRead)]
mod_id: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x100);
impl Spi {
/// Create a new SPI MMIO instance for SPI0 at address [SPI_0_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_0_BASE_ADDR) }
}
/// Create a new SPI MMIO instance for SPI1 at address [SPI_1_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_1_BASE_ADDR) }
}
}

189
zynq7000/src/ttc.rs Normal file
View File

@ -0,0 +1,189 @@
//! Triple-timer counter (TTC) register module.
use arbitrary_int::u4;
pub const TTC_0_BASE_ADDR: usize = 0xF800_1000;
pub const TTC_1_BASE_ADDR: usize = 0xF800_2000;
#[derive(Debug, Default)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum ClockSource {
/// PS internal bus clock.
#[default]
Pclk = 0b0,
External = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct ClockControl {
/// When this bit is set and the external clock is selected, the counter clocks on the
/// negative edge of the external clock input.
#[bit(6, rw)]
ext_clk_edge: bool,
#[bit(5, rw)]
clk_src: ClockSource,
#[bits(1..=4, rw)]
prescaler: u4,
#[bit(0, rw)]
prescale_enable: bool,
}
#[derive(Debug)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum Mode {
Overflow = 0b0,
Interval = 0b1,
}
#[derive(Debug, Default)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum WavePolarity {
/// The waveform output goes from high to low on a match 0 interrupt and returns high on
/// overflow or interval interrupt.
#[default]
HighToLowOnMatch1 = 0b0,
/// The waveform output goes from low to high on a match 0 interrupt and returns low on
/// overflow or interval interrupt.
LowToHighOnMatch1 = 0b1,
}
#[derive(Debug)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum WaveEnable {
Enable = 0b0,
Disable = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct CounterControl {
#[bit(6, rw)]
wave_polarity: WavePolarity,
/// Output waveform enable, active low. Reset value 1.
#[bit(5, rw)]
wave_enable_n: WaveEnable,
/// Resets the counter and restarts counting. Automatically cleared on restart.
#[bit(4, rw)]
reset: bool,
/// When this bit is set, an interrupt is generated when the count value matches one of the
/// three match registers and the corresponding bit is set in the IER register.
#[bit(3, rw)]
match_enable: bool,
/// When this bit is high, the timer counts down.
#[bit(2, rw)]
decrementing: bool,
#[bit(1, rw)]
mode: Mode,
#[bit(0, rw)]
disable: bool,
}
#[bitbybit::bitfield(u32)]
pub struct Counter {
#[bits(0..=15, r)]
count: u16,
}
#[bitbybit::bitfield(u32)]
pub struct RwValue {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptStatus {
/// Even timer overflow interrupt.
#[bit(5, r)]
event: bool,
#[bit(4, r)]
counter_overflow: bool,
#[bit(3, r)]
match_2: bool,
#[bit(2, r)]
match_1: bool,
#[bit(1, r)]
match_0: bool,
#[bit(0, r)]
interval: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptControl {
/// Even timer overflow interrupt.
#[bit(5, rw)]
event: bool,
#[bit(4, rw)]
counter_overflow: bool,
#[bit(3, rw)]
match_2: bool,
#[bit(2, rw)]
match_1: bool,
#[bit(1, rw)]
match_0: bool,
#[bit(0, rw)]
interval: bool,
}
#[bitbybit::bitfield(u32)]
pub struct EventControl {
/// E_Ov bit. When set to 0, the event timer is disabled and set to 0 when an event timer
/// register overflow occurs. Otherwise, continue counting on overflow.
#[bit(2, rw)]
continuous_mode: bool,
/// E_Lo bit. When set to 1, counts PCLK cycles during low level duration of the external
/// clock. Otherwise, counts it during high level duration.
#[bit(1, rw)]
count_low_level_of_ext_clk: bool,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32)]
pub struct EventCount {
#[bits(0..=15, r)]
count: u16,
}
/// Triple-timer counter
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Ttc {
clk_cntr: [ClockControl; 3],
cnt_ctrl: [CounterControl; 3],
#[mmio(PureRead)]
current_counter: [Counter; 3],
interval_value: [RwValue; 3],
match_value_0: [RwValue; 3],
match_value_1: [RwValue; 3],
match_value_2: [RwValue; 3],
#[mmio(Read)]
isr: [InterruptStatus; 3],
ier: [InterruptControl; 3],
event_cntrl: [EventControl; 3],
#[mmio(PureRead)]
event_reg: [EventCount; 3],
}
static_assertions::const_assert_eq!(core::mem::size_of::<Ttc>(), 0x84);
impl Ttc {
/// Create a new TTC MMIO instance for TTC0 at address [TTC_0_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioTtc<'static> {
unsafe { Self::new_mmio_at(TTC_0_BASE_ADDR) }
}
/// Create a new TTC MMIO instance for TTC1 at address [TTC_1_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioTtc<'static> {
unsafe { Self::new_mmio_at(TTC_1_BASE_ADDR) }
}
}

356
zynq7000/src/uart.rs Normal file
View File

@ -0,0 +1,356 @@
//! PS UART register module.
use arbitrary_int::u6;
pub const UART_0_BASE: usize = 0xE000_0000;
pub const UART_1_BASE: usize = 0xE000_1000;
#[bitbybit::bitenum(u3, exhaustive = true)]
pub enum Parity {
Even = 0b000,
Odd = 0b001,
/// Forced to 0 (Space)
ForcedTo0 = 0b010,
/// Forced to 1 (Mark)
ForcedTo1 = 0b011,
NoParity = 0b100,
NoParityAlt0 = 0b101,
NoParityAlt1 = 0b110,
NoParityAlt2 = 0b111,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum Chrl {
SixBits = 0b11,
SevenBits = 0b10,
#[default]
EightBits = 0b00,
EightBitsAlt = 0b01,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum ClkSel {
#[default]
UartRefClk = 0b0,
UartRefClkDiv8 = 0b1,
}
#[bitbybit::bitenum(u2)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum Stopbits {
#[default]
One = 0b00,
OnePointFive = 0b01,
Two = 0b10,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, Default)]
pub enum ChMode {
#[default]
Normal = 0b00,
AutoEcho = 0b01,
LocalLoopback = 0b10,
RemoteLoopback = 0b11,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Ctrl {
/// Stop transmitter break.
#[bit(8, rw)]
stopbrk: bool,
/// Start transmitter break.
#[bit(7, rw)]
startbrk: bool,
/// Restart receiver timeout counter.
#[bit(6, rw)]
rstto: bool,
/// TX disable. If this is 1, TX is disabled, regardless of TXEN.
#[bit(5, rw)]
tx_dis: bool,
/// TX enable. TX will be enabled if this bit is 1 and the TXDIS is 0.
#[bit(4, rw)]
tx_en: bool,
/// RX disable. If this is 1, RX is disabled, regardless of RXEN.
#[bit(3, rw)]
rx_dis: bool,
/// RX enable. RX will be enabled if this bit is 1 and the RXDIS is 0.
#[bit(2, rw)]
rx_en: bool,
/// TX soft reset.
#[bit(1, rw)]
tx_rst: bool,
/// RX soft reset.
#[bit(0, rw)]
rx_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Mode {
#[bits(8..=9, rw)]
chmode: ChMode,
#[bits(6..=7, rw)]
nbstop: Option<Stopbits>,
#[bits(3..=5, rw)]
par: Parity,
/// Char length.
#[bits(1..=2, rw)]
chrl: Chrl,
#[bit(0, rw)]
clksel: ClkSel,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Baudgen {
#[bits(0..=15, rw)]
cd: u16,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct BaudRateDiv {
#[bits(0..=7, rw)]
bdiv: u8,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
fifo: u8,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum Ttrig {
LessThanTTrig = 0b0,
GreaterEqualTTrig = 0b1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
#[bit(14, r)]
tx_near_full: bool,
#[bit(13, r)]
tx_trig: Ttrig,
#[bit(12, r)]
flowdel: bool,
/// Transmitter state machine active.
#[bit(11, r)]
tx_active: bool,
/// Receiver state machine active.
#[bit(10, r)]
rx_active: bool,
#[bit(4, r)]
tx_full: bool,
#[bit(3, r)]
tx_empty: bool,
#[bit(2, r)]
rx_full: bool,
#[bit(1, r)]
rx_empty: bool,
/// RX FIFO trigger level was reached.
#[bit(0, r)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(12, w)]
tx_over: bool,
#[bit(11, w)]
tx_near_full: bool,
#[bit(10, w)]
tx_trig: bool,
#[bit(9, w)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, w)]
rx_timeout: bool,
#[bit(7, w)]
rx_parity: bool,
#[bit(6, w)]
rx_framing: bool,
#[bit(5, w)]
rx_over: bool,
#[bit(4, w)]
tx_full: bool,
#[bit(3, w)]
tx_empty: bool,
#[bit(2, w)]
rx_full: bool,
#[bit(1, w)]
rx_empty: bool,
#[bit(0, w)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FifoTrigger {
#[bits(0..=5, rw)]
trig: u6,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptMask {
#[bit(12, r)]
tx_over: bool,
#[bit(11, r)]
tx_near_full: bool,
#[bit(10, r)]
tx_trig: bool,
#[bit(9, r)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, r)]
rx_timeout: bool,
#[bit(7, r)]
rx_parity: bool,
#[bit(6, r)]
rx_framing: bool,
#[bit(5, r)]
rx_over: bool,
#[bit(4, r)]
tx_full: bool,
#[bit(3, r)]
tx_empty: bool,
#[bit(2, r)]
rx_full: bool,
#[bit(1, r)]
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, r)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(12, rw)]
tx_over: bool,
#[bit(11, rw)]
tx_near_full: bool,
#[bit(10, rw)]
tx_trig: bool,
#[bit(9, rw)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, rw)]
rx_timeout: bool,
#[bit(7, rw)]
rx_parity: bool,
#[bit(6, rw)]
rx_framing: bool,
#[bit(5, rw)]
rx_over: bool,
#[bit(4, rw)]
tx_full: bool,
#[bit(3, rw)]
tx_empty: bool,
#[bit(2, rw)]
rx_full: bool,
#[bit(1, rw)]
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, rw)]
rx_trg: bool,
}
impl InterruptStatus {
pub fn new_for_clearing_rx_errors() -> Self {
Self::builder()
.with_tx_over(false)
.with_tx_near_full(false)
.with_tx_trig(false)
.with_rx_dms(false)
.with_rx_timeout(false)
.with_rx_parity(true)
.with_rx_framing(true)
.with_rx_over(true)
.with_tx_full(false)
.with_tx_empty(false)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.build()
}
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Uart {
/// Control Register
cr: Ctrl,
/// Mode register
mr: Mode,
/// Interrupt enable register
#[mmio(Write)]
ier: InterruptControl,
/// Interrupt disable register
#[mmio(Write)]
idr: InterruptControl,
/// Interrupt mask register, showing enabled interrupts.
#[mmio(PureRead)]
imr: InterruptMask,
/// Interrupt status register
#[mmio(PureRead, Write)]
isr: InterruptStatus,
/// Baudgen register
baudgen: Baudgen,
/// RX timeout register
rx_tout: u32,
/// RX FIFO trigger level register
rx_fifo_trigger: FifoTrigger,
/// Modem control register
modem_cr: u32,
/// Modem status register
modem_sr: u32,
/// Channel status register
#[mmio(PureRead)]
sr: Status,
/// FIFO register
#[mmio(Read, Write)]
fifo: Fifo,
/// Baud rate divider register
baud_rate_div: BaudRateDiv,
/// Flow control delay register
flow_delay: u32,
_reserved: [u32; 2],
/// TX fifo trigger level
tx_fifo_trigger: FifoTrigger,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Uart>(), 0x48);
impl Uart {
/// Create a new UART MMIO instance for uart0 at address 0xE000_0000.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioUart<'static> {
unsafe { Self::new_mmio_at(UART_0_BASE) }
}
/// Create a new UART MMIO instance for uart1 at address 0xE000_1000.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioUart<'static> {
unsafe { Self::new_mmio_at(UART_1_BASE) }
}
}