init commit
This commit is contained in:
202
zynq7000/src/gic.rs
Normal file
202
zynq7000/src/gic.rs
Normal 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
121
zynq7000/src/gpio.rs
Normal 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
60
zynq7000/src/gtc.rs
Normal 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
203
zynq7000/src/i2c.rs
Normal 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
85
zynq7000/src/lib.rs
Normal 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
97
zynq7000/src/mpcore.rs
Normal 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
364
zynq7000/src/slcr/clocks.rs
Normal 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
41
zynq7000/src/slcr/mio.rs
Normal 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
206
zynq7000/src/slcr/mod.rs
Normal 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) }
|
||||
}
|
||||
}
|
79
zynq7000/src/slcr/reset.rs
Normal file
79
zynq7000/src/slcr/reset.rs
Normal 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
236
zynq7000/src/spi.rs
Normal 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
189
zynq7000/src/ttc.rs
Normal 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
356
zynq7000/src/uart.rs
Normal 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) }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user