Introduce Rust FSBL
Some checks failed
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled

This PR introduces some major features while also changing the project structure to be more flexible
for multiple platforms (e.g. host tooling). It also includes a lot of
bugfixes, renamings for consistency purposes and dependency updates.

Added features:

1. Pure Rust FSBL for the Zedboard. This first variant is simplistic. It
   is currently only capable of QSPI boot. It searches for a bitstream
   and ELF file inside the boot binary, flashes them and jumps to them.
2. QSPI flasher for the Zedboard.
3. DDR, QSPI, DEVC, private CPU timer and PLL configuration modules
3. Tooling to auto-generate board specific DDR and DDRIOB config
   parameters from the vendor provided ps7init.tcl file

Changed project structure:

1. All target specific project are inside a dedicated workspace inside
   the `zynq` folder now.
2. All tool intended to be run on a host are inside a `tools` workspace
3. All other common projects are at the project root

Major bugfixes:

1. SPI module: CPOL was not configured properly
2. Logger flush implementation was empty, implemented properly now.
This commit is contained in:
2025-08-01 14:32:08 +02:00
committed by Robin Mueller
parent 0cf5bf6885
commit 5d0f2837d1
166 changed files with 9496 additions and 979 deletions

886
zynq/zynq7000/src/ddrc.rs Normal file
View File

@@ -0,0 +1,886 @@
pub const DDRC_BASE_ADDR: usize = 0xF800_6000;
pub mod regs {
pub use crate::slcr::ddriob::DdriobConfig;
use arbitrary_int::{u2, u3, u4, u5, u6, u7, u9, u10, u11, u12, u20};
#[bitbybit::bitenum(u2)]
#[derive(Debug, PartialEq, Eq)]
pub enum DataBusWidth {
_32Bit = 0b00,
_16Bit = 0b01,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum SoftReset {
Reset = 0,
Active = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DdrcControl {
#[bit(16, rw)]
disable_auto_refresh: bool,
#[bit(15, rw)]
disable_active_bypass: bool,
#[bit(14, rw)]
disable_read_bypass: bool,
#[bits(7..=13, rw)]
read_write_idle_gap: u7,
#[bits(4..=6, rw)]
burst8_refresh: u3,
#[bits(2..=3, rw)]
data_bus_width: Option<DataBusWidth>,
#[bit(1, rw)]
power_down_enable: bool,
#[bit(0, rw)]
soft_reset: SoftReset,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct TwoRankConfig {
#[bits(14..=18, rw)]
addrmap_cs_bit0: u5,
/// Reserved register, but for some reason, Xilinx tooling writes a 1 here?
#[bits(12..=13, rw)]
ddrc_active_ranks: u2,
/// tREFI - Average time between refreshes, in multiples of 32 clocks.
#[bits(0..=11, rw)]
rfc_nom_x32: u12,
}
/// Queue control for the low priority and high priority read queues.
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LprHprQueueControl {
#[bits(22..=25, rw)]
xact_run_length: u4,
#[bits(11..=21, rw)]
max_starve_x32: u11,
#[bits(0..=10, rw)]
min_non_critical_x32: u11,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct WriteQueueControl {
#[bits(15..=25, rw)]
max_starve_x32: u11,
#[bits(11..=14, rw)]
xact_run_length: u4,
#[bits(0..=10, rw)]
min_non_critical_x32: u11,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg0 {
/// Minimum time to wait after coming out of self refresh before doing anything. This must be
/// bigger than all the constraints that exist.
#[bits(14..=20, rw)]
post_selfref_gap_x32: u7,
/// tRFC(min) - Minimum time from refresh to refresh or activate in clock
/// cycles.
#[bits(6..=13, rw)]
t_rfc_min: u8,
/// tRC - Min time between activates to the same bank.
#[bits(0..=5, rw)]
t_rc: u6,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg1 {
#[bits(28..=31, rw)]
t_cke: u4,
#[bits(22..=26, rw)]
t_ras_min: u5,
#[bits(16..=21, rw)]
t_ras_max: u6,
#[bits(10..=15, rw)]
t_faw: u6,
#[bits(5..=9, rw)]
powerdown_to_x32: u5,
#[bits(0..=4, rw)]
wr2pre: u5,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg2 {
#[bits(28..=31, rw)]
t_rcd: u4,
#[bits(23..=27, rw)]
rd2pre: u5,
#[bits(20..=22, rw)]
pad_pd: u3,
#[bits(15..=19, rw)]
t_xp: u5,
#[bits(10..=14, rw)]
wr2rd: u5,
#[bits(5..=9, rw)]
rd2wr: u5,
#[bits(0..=4, rw)]
write_latency: u5,
}
/// Weird naming.
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum MobileSetting {
Ddr2Ddr3 = 0,
Lpddr2 = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg3 {
#[bit(30, rw)]
disable_pad_pd_feature: bool,
#[bits(24..=28, rw)]
read_latency: u5,
#[bit(23, rw)]
enable_dfi_dram_clk_disable: bool,
/// 0: DDR2 or DDR3. 1: LPDDR2.
#[bit(22, rw)]
mobile: MobileSetting,
/// Must be set to 0.
#[bit(21, rw)]
sdram: bool,
#[bits(16..=20, rw)]
refresh_to_x32: u5,
#[bits(12..=15, rw)]
t_rp: u4,
#[bits(8..=11, rw)]
refresh_margin: u4,
#[bits(5..=7, rw)]
t_rrd: u3,
#[bits(2..=4, rw)]
t_ccd: u3,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum ModeRegisterType {
Write = 0,
Read = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg4 {
#[bit(27, rw)]
mr_rdata_valid: bool,
#[bit(26, rw)]
mr_type: ModeRegisterType,
#[bit(25, rw)]
mr_wr_busy: bool,
#[bits(9..=24, rw)]
mr_data: u16,
#[bits(7..=8, rw)]
mr_addr: u2,
#[bit(6, rw)]
mr_wr: bool,
#[bit(1, rw)]
prefer_write: bool,
#[bit(0, rw)]
enable_2t_timing_mode: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramInitParam {
#[bits(11..=13, rw)]
t_mrd: u3,
#[bits(7..=10, rw)]
pre_ocd_x32: u4,
#[bits(0..=6, rw)]
final_wait_x32: u7,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramEmr {
#[bits(16..=31, rw)]
emr3: u16,
#[bits(0..=15, rw)]
emr2: u16,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramEmrMr {
#[bits(16..=31, rw)]
emr: u16,
#[bits(0..=15, rw)]
mr: u16,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramBurst8ReadWrite {
#[bits(0..=3, rw)]
burst_rdwr: u4,
#[bits(4..=13, rw)]
pre_cke_x1024: u10,
#[bits(16..=25, rw)]
post_cke_x1024: u10,
#[bit(26, rw)]
burstchop: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DisableDq {
#[bit(1, rw)]
dis_dq: bool,
#[bit(0, rw)]
force_low_pri_n: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapBank {
#[bits(16..=19, rw)]
addrmap_bank_b6: u4,
#[bits(12..=15, rw)]
addrmap_bank_b5: u4,
#[bits(8..=11, rw)]
addrmap_bank_b2: u4,
#[bits(4..=7, rw)]
addrmap_bank_b1: u4,
#[bits(0..=3, rw)]
addrmap_bank_b0: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapColumn {
#[bits(28..=31, rw)]
addrmap_col_b11: u4,
#[bits(24..=27, rw)]
addrmap_col_b10: u4,
#[bits(20..=23, rw)]
addrmap_col_b9: u4,
#[bits(16..=19, rw)]
addrmap_col_b8: u4,
#[bits(12..=15, rw)]
addrmap_col_b7: u4,
#[bits(8..=11, rw)]
addrmap_col_b4: u4,
#[bits(4..=7, rw)]
addrmap_col_b3: u4,
#[bits(0..=3, rw)]
addrmap_col_b2: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapRow {
#[bits(24..=27, rw)]
addrmap_row_b15: u4,
#[bits(20..=23, rw)]
addrmap_row_b14: u4,
#[bits(16..=19, rw)]
addrmap_row_b13: u4,
#[bits(12..=15, rw)]
addrmap_row_b12: u4,
#[bits(8..=11, rw)]
addrmap_row_b2_11: u4,
#[bits(4..=7, rw)]
addrmap_row_b1: u4,
#[bits(0..=3, rw)]
addrmap_row_b0: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramOdt {
#[bits(16..=17, rw)]
phy_idle_local_odt: u2,
#[bits(14..=15, rw)]
phy_write_local_odt: u2,
#[bits(12..=13, rw)]
phy_read_local_odt: u2,
#[bits(3..=5, rw)]
rank0_wr_odt: u3,
#[bits(0..=2, rw)]
rank0_rd_odt: u3,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyCmdTimeoutRdDataCpt {
#[bits(28..=31, rw)]
wrlvl_num_of_dq0: u4,
#[bits(24..=27, rw)]
gatelvl_num_of_dq0: u4,
#[bit(19, rw)]
clk_stall_level: bool,
#[bit(18, rw)]
dis_phy_ctrl_rstn: bool,
#[bit(17, rw)]
rdc_fifo_rst_err_cnt_clr: bool,
#[bit(16, rw)]
use_fixed_re: bool,
#[bits(8..=11, rw)]
rdc_we_to_re_delay: u4,
#[bits(4..=7, rw)]
wr_cmd_to_data: u4,
#[bits(0..=3, rw)]
rd_cmd_to_data: u4,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum DllCalibSel {
Periodic = 0,
Manual = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DllCalib {
#[bit(16, rw)]
sel: DllCalibSel,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct OdtDelayHold {
#[bits(12..=15, rw)]
wr_odt_hold: u4,
#[bits(8..=11, rw)]
rd_odt_hold: u4,
#[bits(4..=7, rw)]
wr_odt_delay: u4,
#[bits(0..=3, rw)]
rd_odt_delay: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg1 {
#[bit(12, rw)]
selfref_enable: bool,
#[bit(10, rw)]
dis_collision_page_opt: bool,
#[bit(9, rw)]
dis_wc: bool,
#[bit(8, rw)]
refresh_update_level: bool,
#[bit(7, rw)]
auto_pre_en: bool,
#[bits(1..=6, rw)]
lpr_num_entries: u6,
#[bit(0, rw)]
pageclose: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg2 {
#[bit(17, rw)]
go_2_critcal_enable: bool,
#[bits(5..=12, rw)]
go_2_critical_hysteresis: u8,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg3 {
#[bits(16..=25, rw)]
dfi_t_wlmrd: u10,
#[bits(8..=15, rw)]
rdlvl_rr: u8,
#[bits(0..=7, rw)]
wrlvl_ww: u8,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg4 {
#[bits(8..=15, rw)]
dfi_t_ctrlupd_interval_max_x1024: u8,
#[bits(0..=7, rw)]
dfi_t_ctrlupd_interval_min_x1024: u8,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg5 {
#[bits(20..=25, rw)]
t_ckesr: u6,
#[bits(16..=19, rw)]
t_cksrx: u4,
#[bits(12..=15, rw)]
t_ckrse: u4,
#[bits(8..=11, rw)]
dfi_t_dram_clk_enable: u4,
#[bits(4..=7, rw)]
dfi_t_dram_clk_disable: u4,
#[bits(0..=3, rw)]
dfi_t_ctrl_delay: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg6 {
#[bits(16..=19, rw)]
t_cksx: u4,
#[bits(12..=15, rw)]
t_ckdpdx: u4,
#[bits(8..=11, rw)]
t_ckdpde: u4,
#[bits(4..=7, rw)]
t_ckpdx: u4,
#[bits(0..=3, rw)]
t_ckpde: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheTZq {
#[bits(22..=31, rw)]
t_zq_short_nop: u10,
#[bits(12..=21, rw)]
t_zq_long_nop: u10,
#[bits(2..=11, rw)]
t_mode: u10,
#[bit(1, rw)]
ddr3: bool,
#[bit(0, rw)]
dis_auto_zq: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheTZqShortInterval {
#[bits(20..=27, rw)]
dram_rstn_x1024: u8,
#[bits(0..=19, rw)]
t_zq_short_interval: u20,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DeepPowerdown {
#[bits(1..=8, rw)]
deep_powerdown_to_x1024: u8,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg2c {
#[bit(28, rw)]
dfi_rd_data_eye_train: bool,
#[bit(27, rw)]
dfi_rd_dqs_gate_level: bool,
#[bit(26, rw)]
dfi_wr_level_enable: bool,
#[bit(25, rw)]
trdlvl_max_error: bool,
#[bit(24, rw)]
twrlvl_max_error: bool,
#[bits(12..=23, rw)]
dfi_rdlvl_max_x1024: u12,
#[bits(0..=11, rw)]
dfi_wrlvl_max_x1024: u12,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg2d {
#[bit(9, rw)]
skip_ocd: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DfiTiming {
#[bits(15..=24, rw)]
dfi_t_ctrlup_max: u10,
#[bits(5..=14, rw)]
dfi_t_ctrlup_min: u10,
#[bits(0..=4, rw)]
dfi_t_rddata_enable: u5,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheEccControl {
#[bit(1, rw)]
clear_correctable_errors: bool,
#[bit(0, rw)]
clear_uncorrectable_errors: bool,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug)]
pub enum EccMode {
NoEcc = 0b000,
SecDecOverOneBeat = 0b100,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct EccScrub {
#[bit(3, rw)]
disable_scrub: bool,
#[bits(0..=2, rw)]
ecc_mode: Option<EccMode>,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyReceiverEnable {
#[bits(4..=7, rw)]
phy_dif_off: u4,
#[bits(0..=3, rw)]
phy_dif_on: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyConfig {
#[bits(24..=30, rw)]
dq_offset: u7,
#[bit(3, rw)]
wrlvl_inc_mode: bool,
#[bit(2, rw)]
gatelvl_inc_mode: bool,
#[bit(1, rw)]
rdlvl_inc_mode: bool,
#[bit(0, rw)]
data_slice_in_use: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyInitRatio {
#[bits(10..=19, rw)]
gatelvl_init_ratio: u10,
#[bits(0..=9, rw)]
wrlvl_init_ratio: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyDqsConfig {
#[bits(11..=19, rw)]
dqs_slave_delay: u9,
#[bit(10, rw)]
dqs_slave_force: bool,
#[bits(0..=9, rw)]
dqs_slave_ratio: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyWriteEnableConfig {
#[bits(12..=20, rw)]
fifo_we_in_delay: u9,
#[bit(11, rw)]
fifo_we_in_force: bool,
#[bits(0..=10, rw)]
fifo_we_slave_ratio: u11,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyWriteDataSlaveConfig {
#[bits(11..=19, rw)]
wr_data_slave_delay: u9,
#[bit(10, rw)]
wr_data_slave_force: bool,
#[bits(0..=9, rw)]
wr_data_slave_ratio: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg64 {
#[bit(30, rw)]
cmd_latency: bool,
#[bit(29, rw)]
lpddr: bool,
#[bits(21..=27, rw)]
ctrl_slave_delay: u7,
#[bit(20, rw)]
ctrl_slave_force: bool,
#[bits(10..=19, rw)]
ctrl_slave_ratio: u10,
#[bit(9, rw)]
sel_logic: bool,
#[bit(7, rw)]
invert_clkout: bool,
#[bit(1, rw)]
bl2: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg65 {
#[bits(18..=19, rw)]
ctrl_slave_delay: u2,
#[bit(17, rw)]
dis_calib_rst: bool,
#[bit(16, rw)]
use_rd_data_eye_level: bool,
#[bit(15, rw)]
use_rd_dqs_gate_level: bool,
#[bit(14, rw)]
use_wr_level: bool,
#[bits(10..=13, rw)]
dll_lock_diff: u4,
#[bits(5..=9, rw)]
rd_rl_delay: u5,
#[bits(0..=4, rw)]
wr_rl_delay: u5,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AxiPriorityWritePort {
#[bit(18, rw)]
disable_page_match: bool,
#[bit(17, rw)]
disable_urgent: bool,
#[bit(16, rw)]
disable_aging: bool,
#[bits(0..=9, rw)]
pri_wr_port: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AxiPriorityReadPort {
#[bit(19, rw)]
enable_hpr: bool,
#[bit(18, rw)]
disable_page_match: bool,
#[bit(17, rw)]
disable_urgent: bool,
#[bit(16, rw)]
disable_aging: bool,
#[bits(0..=9, rw)]
pri_rd_port_n: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct ExclusiveAccessConfig {
#[bits(9..=17, rw)]
access_id1_port: u9,
#[bits(0..=8, rw)]
access_id0_port: u9,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum LpddrBit {
Ddr2Ddr3 = 0,
Lpddr2 = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl0 {
#[bits(4..=11, rw)]
mr4_margin: u8,
#[bit(2, rw)]
derate_enable: bool,
#[bit(1, rw)]
per_bank_refresh: bool,
#[bit(0, rw)]
lpddr2: LpddrBit,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl1 {
#[bits(0..=31, rw)]
mr4_read_interval: u32,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl2 {
#[bits(12..=21, rw)]
t_mrw: u10,
#[bits(4..=11, rw)]
idle_after_reset_x32: u8,
#[bits(0..=3, rw)]
min_stable_clock_x1: u4,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl3 {
#[bits(8..=17, rw)]
dev_zqinit_x32: u10,
#[bits(0..=7, rw)]
max_auto_init_x1024: u8,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum OperatingMode {
DdrcInit = 0,
NormalOperation = 1,
Powerdown = 2,
SelfRefresh = 3,
DeepPowerdown = 4,
DeepPowerdownAlt1 = 5,
DeepPowerdownAlt2 = 6,
DeepPowerdownAlt3 = 7,
}
impl OperatingMode {
pub fn is_deep_powerdown(&self) -> bool {
matches!(
self,
OperatingMode::DeepPowerdown
| OperatingMode::DeepPowerdownAlt1
| OperatingMode::DeepPowerdownAlt2
| OperatingMode::DeepPowerdownAlt3
)
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum DebugStallBit {
CommandsAccepted = 0,
CommandsNotAccepted = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct ModeStatus {
#[bits(16..=20, r)]
dbg_hpr_queue_depth: u5,
#[bits(10..=15, r)]
dbg_lpr_queue_depth: u6,
#[bits(4..=9, r)]
dbg_wr_queue_depth: u6,
#[bit(3, r)]
dbg_stall: DebugStallBit,
#[bits(0..=2, r)]
operating_mode: OperatingMode,
}
}
use regs::*;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DdrController {
ddrc_ctrl: DdrcControl,
two_rank_cfg: TwoRankConfig,
hpr_queue_ctrl: LprHprQueueControl,
lpr_queue_ctrl: LprHprQueueControl,
wr_reg: WriteQueueControl,
dram_param_reg0: DramParamReg0,
dram_param_reg1: DramParamReg1,
dram_param_reg2: DramParamReg2,
dram_param_reg3: DramParamReg3,
dram_param_reg4: DramParamReg4,
dram_init_param: DramInitParam,
dram_emr: DramEmr,
dram_emr_mr: DramEmrMr,
dram_burst8_rdwr: DramBurst8ReadWrite,
dram_disable_dq: DisableDq,
dram_addr_map_bank: DramAddrMapBank,
dram_addr_map_col: DramAddrMapColumn,
dram_addr_map_row: DramAddrMapRow,
dram_odt_reg: DramOdt,
#[mmio(PureRead)]
phy_debug_reg: u32,
phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRdDataCpt,
#[mmio(PureRead)]
mode_status: ModeStatus,
dll_calib: DllCalib,
odt_delay_hold: OdtDelayHold,
ctrl_reg1: CtrlReg1,
ctrl_reg2: CtrlReg2,
ctrl_reg3: CtrlReg3,
ctrl_reg4: CtrlReg4,
_reserved0: [u32; 0x2],
ctrl_reg5: CtrlReg5,
ctrl_reg6: CtrlReg6,
_reserved1: [u32; 0x8],
che_refresh_timer_01: u32,
che_t_zq: CheTZq,
che_t_zq_short_interval_reg: CheTZqShortInterval,
deep_powerdown_reg: DeepPowerdown,
reg_2c: Reg2c,
reg_2d: Reg2d,
dfi_timing: DfiTiming,
_reserved2: [u32; 0x2],
che_ecc_control: CheEccControl,
#[mmio(PureRead)]
che_corr_ecc_log: u32,
#[mmio(PureRead)]
che_corr_ecc_addr: u32,
#[mmio(PureRead)]
che_corr_ecc_data_31_0: u32,
#[mmio(PureRead)]
che_corr_ecc_data_63_32: u32,
#[mmio(PureRead)]
che_corr_ecc_data_71_64: u32,
/// Clear on write, but the write is performed on another register.
#[mmio(PureRead)]
che_uncorr_ecc_log: u32,
#[mmio(PureRead)]
che_uncorr_ecc_addr: u32,
#[mmio(PureRead)]
che_uncorr_ecc_data_31_0: u32,
#[mmio(PureRead)]
che_uncorr_ecc_data_63_32: u32,
#[mmio(PureRead)]
che_uncorr_ecc_data_71_64: u32,
#[mmio(PureRead)]
che_ecc_stats: u32,
ecc_scrub: EccScrub,
#[mmio(PureRead)]
che_ecc_corr_bit_mask_31_0: u32,
#[mmio(PureRead)]
che_ecc_corr_bit_mask_63_32: u32,
_reserved3: [u32; 0x5],
phy_receiver_enable: PhyReceiverEnable,
phy_config: [PhyConfig; 0x4],
_reserved4: u32,
phy_init_ratio: [PhyInitRatio; 4],
_reserved5: u32,
phy_rd_dqs_cfg: [PhyDqsConfig; 4],
_reserved6: u32,
phy_wr_dqs_cfg: [PhyDqsConfig; 4],
_reserved7: u32,
phy_we_cfg: [PhyWriteEnableConfig; 4],
_reserved8: u32,
phy_wr_data_slave: [PhyWriteDataSlaveConfig; 4],
_reserved9: u32,
reg_64: Reg64,
reg_65: Reg65,
_reserved10: [u32; 3],
#[mmio(PureRead)]
reg69_6a0: u32,
#[mmio(PureRead)]
reg69_6a1: u32,
_reserved11: u32,
#[mmio(PureRead)]
reg69_6d2: u32,
#[mmio(PureRead)]
reg69_6d3: u32,
#[mmio(PureRead)]
reg69_710: u32,
#[mmio(PureRead)]
reg6e_711: u32,
#[mmio(PureRead)]
reg6e_712: u32,
#[mmio(PureRead)]
reg6e_713: u32,
_reserved12: u32,
#[mmio(PureRead)]
phy_dll_status: [u32; 4],
_reserved13: u32,
#[mmio(PureRead)]
dll_lock_status: u32,
#[mmio(PureRead)]
phy_control_status: u32,
#[mmio(PureRead)]
phy_control_status_2: u32,
_reserved14: [u32; 0x5],
// DDRI registers.
#[mmio(PureRead)]
axi_id: u32,
page_mask: u32,
axi_priority_wr_port: [AxiPriorityWritePort; 0x4],
axi_priority_rd_port: [AxiPriorityReadPort; 0x4],
_reserved15: [u32; 0x1B],
excl_access_cfg: [ExclusiveAccessConfig; 0x4],
#[mmio(PureRead)]
mode_reg_read: u32,
lpddr_ctrl_0: LpddrControl0,
lpddr_ctrl_1: LpddrControl1,
lpddr_ctrl_2: LpddrControl2,
lpddr_ctrl_3: LpddrControl3,
}
static_assertions::const_assert_eq!(core::mem::size_of::<DdrController>(), 0x2B8);
impl DdrController {
/// Create a new DDR MMIO instance for the DDR controller at address [DDRC_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() -> MmioDdrController<'static> {
unsafe { Self::new_mmio_at(DDRC_BASE_ADDR) }
}
}

303
zynq/zynq7000/src/devcfg.rs Normal file
View File

@@ -0,0 +1,303 @@
use arbitrary_int::{u4, u5, u7};
pub const DEVCFG_BASE_ADDR: usize = 0xF8007000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PlConfigAccess {
/// Used for JTAG access
TapController = 0,
/// Used for PCAP or ICAP access.
ConfigAccessPort = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum ConfigAccessPortSelect {
/// Internal Configuration Access Port (ICAP), using PL or PS-based software.
Icap = 0,
/// Processor Configuration Access Port (PCAP), using PS-based software.
Pcap = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum TimerSelect {
_64kTimer = 0,
_4kTimer = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum AesEnable {
Disable = 0b000,
Enable = 0b111,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PsBootMode {
NonSecure = 0,
Secure = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum ArmDapEnable {
Enabled = 0b111,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Control {
#[bit(31, rw)]
force_reset: bool,
/// Program singal used to reset the PL. It acts at the PROG_B signal in the PL.
#[bit(30, rw)]
prog_b_bit: bool,
/// Called PCFG_POR_CNT_4K by Xilinx.
#[bit(29, rw)]
timer_select: TimerSelect,
/// Called XDCFG_CTRL_PCAP_PR_MASK by Xilinx.
#[bit(27, rw)]
access_port_select: ConfigAccessPortSelect,
#[bit(26, rw)]
config_access_select: PlConfigAccess,
#[bit(25, rw)]
pcap_rate_enable: bool,
#[bit(24, rw)]
multiboot_enable: bool,
#[bit(23, rw)]
jtag_chain_disable: bool,
#[bit(12, rw)]
pcfg_aes_fuse: bool,
#[bits(9..=11, rw)]
pcfg_aes_enable: Option<AesEnable>,
#[bit(8, rw)]
seu_enable: bool,
/// Read-only because this is set and locked by BootROM.
#[bit(7, r)]
ps_boot_mode: PsBootMode,
/// SPNIDEN
#[bit(6, rw)]
secure_non_invasive_debug_enable: bool,
/// SPIDEN
#[bit(5, rw)]
secure_invasive_debug_enable: bool,
/// NIDEN
#[bit(4, rw)]
non_invasive_debug_enable: bool,
/// DBGEN
#[bit(3, rw)]
invasive_debug_enable: bool,
#[bits(0..=2, rw)]
dap_enable: Option<ArmDapEnable>,
}
/// The bits in this register and read/write, set-only, which means that only a PS_POR_B reset
/// can clear the bits.
#[bitbybit::bitfield(u32, debug)]
pub struct Lock {
#[bit(4, rw)]
aes_fuse: bool,
#[bit(3, rw)]
aes: bool,
#[bit(2, rw)]
seu: bool,
/// Locks the SEC_EN bit. BootROM will set this bit.
#[bit(1, rw)]
sec: bool,
/// Locks SPNIDEN, SPIDEN, NIDEN, DBGEN and DAP_EN
#[bit(0, rw)]
debug: bool,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum EdgeConfig {
Falling = 0,
Rising = 1,
}
/// Related to the full level for reads, and the empty level for writes.
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum FifoThresholdConfig {
OneFourth = 0b00,
HalfEmpty = 0b01,
ThreeFourth = 0b10,
EmptyOrFull = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Config {
#[bits(10..=11, rw)]
read_fifo_threshhold: FifoThresholdConfig,
#[bits(8..=9, rw)]
write_fifo_threshold: FifoThresholdConfig,
#[bit(7, rw)]
read_data_active_clock_edge: EdgeConfig,
#[bit(6, rw)]
write_data_active_clock_edge: EdgeConfig,
#[bit(5, rw)]
disable_src_increment: bool,
#[bit(4, rw)]
disable_dst_incremenet: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Interrupt {
/// Tri-state PL IO during HIZ.
#[bit(31, rw)]
gts_usr_b: bool,
#[bit(30, rw)]
first_config_done: bool,
#[bit(29, rw)]
global_powerdown: bool,
/// Tri-state PL IO during configuration.
#[bit(28, rw)]
gts_cfg_b: bool,
/// PSS_CFG_RESET_B_INT
#[bit(27, rw)]
pl_config_reset: bool,
#[bit(23, rw)]
axi_write_timeout: bool,
#[bit(22, rw)]
axi_write_response_error: bool,
#[bit(21, rw)]
axi_read_timeout: bool,
#[bit(20, rw)]
axi_read_response_error: bool,
#[bit(18, rw)]
rx_overflow: bool,
#[bit(17, rw)]
tx_fifo_below_threshold: bool,
#[bit(16, rw)]
rx_fifo_above_threshold: bool,
#[bit(15, rw)]
dma_illegal_command: bool,
#[bit(14, rw)]
dma_queue_overflow: bool,
#[bit(13, rw)]
dma_done: bool,
#[bit(12, rw)]
dma_pcap_done: bool,
#[bit(11, rw)]
inconsistent_pcap_to_dma_transfer_len: bool,
#[bit(6, rw)]
hamc_error: bool,
#[bit(5, rw)]
seu_error: bool,
#[bit(4, rw)]
pl_power_loss_por_b_low: bool,
#[bit(3, rw)]
pl_config_controller_under_reset: bool,
#[bit(2, rw)]
pl_programming_done: bool,
#[bit(1, rw)]
positive_edge_pl_init: bool,
#[bit(0, rw)]
negative_edge_pl_init: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct MiscControl {
#[bits(28..=31, r)]
ps_version: u4,
#[bit(8, r)]
por_b_signal: bool,
#[bit(4, rw)]
loopback: bool,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum UnacknowledgedDmaTransfers {
None = 0b00,
One = 0b01,
Two = 0b10,
ThreeOrMore = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Status {
#[bit(31, rw)]
dma_command_queue_full: bool,
#[bit(30, rw)]
dma_command_queue_empty: bool,
#[bits(28..=29, rw)]
unacknowledged_dma_transfers: UnacknowledgedDmaTransfers,
#[bits(20..=24, rw)]
rx_fifo_level: u5,
#[bits(12..=18, rw)]
tx_fifo_level: u7,
#[bit(11, rw)]
gts_usr_b: bool,
#[bit(10, rw)]
first_config_done: bool,
#[bit(9, rw)]
global_powerdown: bool,
#[bit(8, rw)]
gts_cfg_b: bool,
#[bit(7, rw)]
secure_lockdown: bool,
#[bit(6, rw)]
illegal_apb_access: bool,
/// Active low reset bit.
#[bit(5, rw)]
pl_reset_n: bool,
#[bit(4, rw)]
pcfg_init: bool,
#[bit(3, rw)]
efuse_bbram_aes_key_disabled: bool,
#[bit(2, rw)]
efuse_sec_enable: bool,
#[bit(1, rw)]
efuse_jtag_disabled: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DevCfg {
control: Control,
lock: Lock,
config: Config,
/// Interrupt is cleared by writing to this register.
interrupt_status: Interrupt,
/// Bits can be set to one to mask the interrupts.
interrupt_mask: Interrupt,
status: Status,
dma_source_addr: u32,
dma_dest_addr: u32,
dma_source_len: u32,
dma_dest_len: u32,
_reserved0: u32,
multiboot_addr: u32,
_reserved1: u32,
unlock_control: u32,
_reserved2: [u32; 0x12],
misc_control: MiscControl,
_reserved3: [u32; 0x1F],
// Included here but not exposed to avoid providing multiple references to the same peripheral.
// Exposed in [crate::xadc].
_xadc: crate::xadc::XAdc,
}
static_assertions::const_assert_eq!(core::mem::size_of::<DevCfg>(), 0x11C);
impl DevCfg {
/// Create a new DevCfg MMIO instance for for device configuration peripheral at address
/// [DEVCFG_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 unsafe fn new_mmio_fixed() -> MmioDevCfg<'static> {
unsafe { Self::new_mmio_at(DEVCFG_BASE_ADDR) }
}
}

596
zynq/zynq7000/src/eth.rs Normal file
View File

@@ -0,0 +1,596 @@
//! # Gigabit Ethernet Module (GEM) register module.
use arbitrary_int::{u2, u5};
pub const GEM_0_BASE_ADDR: usize = 0xE000_B000;
pub const GEM_1_BASE_ADDR: usize = 0xE000_C000;
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct NetworkControl {
#[bit(18, w)]
flush_next_rx_dpram_pkt: bool,
#[bit(17, w)]
tx_pfc_pri_pause_frame: bool,
#[bit(16, w)]
enable_pfc_pri_pause_rx: bool,
#[bit(12, w)]
zero_pause_tx: bool,
#[bit(11, w)]
pause_tx: bool,
#[bit(10, w)]
stop_tx: bool,
#[bit(9, w)]
start_tx: bool,
#[bit(8, rw)]
back_pressure: bool,
#[bit(7, rw)]
statistics_write_enable: bool,
#[bit(6, w)]
increment_statistics: bool,
#[bit(5, w)]
clear_statistics: bool,
#[bit(4, rw)]
management_port_enable: bool,
#[bit(3, rw)]
tx_enable: bool,
#[bit(2, rw)]
rx_enable: bool,
#[bit(1, rw)]
loopback_local: bool,
}
/// The speed mode selects between 10 Mbps and 100 Mbps if the Gigabit enable bit is cleared.
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum SpeedMode {
Low10Mbps = 0,
High100Mbps = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PcsSelect {
GmiiMii = 0,
Tbi = 1,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum MdcClockDivisor {
Div8 = 0,
Div16 = 1,
Div32 = 2,
Div48 = 3,
Div64 = 4,
Div96 = 5,
Div128 = 6,
Div224 = 7,
}
impl MdcClockDivisor {
pub fn divisor(&self) -> usize {
match self {
MdcClockDivisor::Div8 => 8,
MdcClockDivisor::Div16 => 16,
MdcClockDivisor::Div32 => 32,
MdcClockDivisor::Div48 => 48,
MdcClockDivisor::Div64 => 64,
MdcClockDivisor::Div96 => 96,
MdcClockDivisor::Div128 => 128,
MdcClockDivisor::Div224 => 224,
}
}
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct NetworkConfig {
#[bit(30, rw)]
ignore_ipg_rx_error: bool,
#[bit(29, rw)]
allow_bad_preamble: bool,
#[bit(28, rw)]
ipg_stretch_enable: bool,
#[bit(27, rw)]
sgmii_enable: bool,
#[bit(26, rw)]
ignore_rx_fcs: bool,
#[bit(25, rw)]
half_duplex_rx_enable: bool,
#[bit(24, rw)]
rx_checksum_enable: bool,
#[bit(23, rw)]
disable_copy_pause_frames: bool,
/// Zynq defines this as 0b00 for 32-bit AMBA AHB data bus width.
#[bits(21..=22, r)]
dbus_width: u2,
#[bits(18..=20, rw)]
mdc_clk_div: MdcClockDivisor,
#[bit(17, rw)]
fcs_removal: bool,
#[bit(16, rw)]
length_field_error_discard: bool,
#[bits(14..=15, rw)]
rx_buf_offset: u2,
#[bit(13, rw)]
pause_enable: bool,
#[bit(12, rw)]
retry_test_enable: bool,
#[bit(11, rw)]
pcs_select: PcsSelect,
#[bit(10, rw)]
gigabit_enable: bool,
#[bit(9, rw)]
ext_addr_match_enable: bool,
#[bit(8, rw)]
rx_enable_1536: bool,
#[bit(7, rw)]
unicast_hash_enable: bool,
#[bit(6, rw)]
multicast_hash_enable: bool,
#[bit(5, rw)]
no_broadcast: bool,
#[bit(4, rw)]
copy_all_frames: bool,
#[bit(2, rw)]
discard_non_vlan: bool,
#[bit(1, rw)]
full_duplex: bool,
#[bit(0, rw)]
speed_mode: SpeedMode,
}
/// PHY management status information.
#[bitbybit::bitfield(u32, debug)]
pub struct NetworkStatus {
#[bit(6, r)]
pfc_pri_pause_neg: bool,
#[bit(5, r)]
pcs_autoneg_pause_tx_res: bool,
#[bit(4, r)]
pcs_autoneg_pause_rx_res: bool,
#[bit(3, r)]
pcs_autoneg_dup_res: bool,
#[bit(2, r)]
phy_mgmt_idle: bool,
#[bit(1, r)]
mdio_in: bool,
#[bit(0, r)]
pcs_link_state: bool,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum BurstLength {
Single,
#[default]
Incr4,
Incr8,
Incr16,
}
impl BurstLength {
pub const fn reg_value(&self) -> u5 {
u5::new(match self {
BurstLength::Single => 0b1,
BurstLength::Incr4 => 0b100,
BurstLength::Incr8 => 0b1000,
BurstLength::Incr16 => 0b10000,
})
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum AhbEndianess {
Little = 0,
Big = 1,
}
#[derive(Debug, PartialEq, Eq)]
pub struct DmaRxBufSize(u8);
impl DmaRxBufSize {
pub const fn new_with_raw_value(size: u8) -> Self {
Self(size)
}
pub const fn new(size: u8) -> Option<Self> {
if size == 0 {
return None;
}
Some(Self(size))
}
pub const fn raw_value(&self) -> u8 {
self.0
}
pub const fn size_in_bytes(&self) -> usize {
self.0 as usize * 64
}
pub const fn reg_value(&self) -> u8 {
self.0
}
}
#[bitbybit::bitfield(u32, debug)]
pub struct DmaConfig {
#[bit(24, rw)]
discard_when_ahb_full: bool,
/// DMA receive buffer size in AHB system memory.
#[bits(16..=23, rw)]
dma_rx_ahb_buf_size_sel: DmaRxBufSize,
/// Checksum offloading for TX.
#[bit(11, rw)]
chksum_offload_enable: bool,
/// Select size for packet buffer SRAM. Should be set to 1 to use the full configurable address
/// space of 4 kB for the packet buffer.
#[bit(10, rw)]
tx_packet_buf_size_sel: bool,
/// Select size for packet buffer SRAM. Should be set to 0b11 to use the full configurable
/// address space of 8 kB for the packet buffer.
#[bits(8..=9, rw)]
rx_packet_buf_size_sel: u2,
/// Default value is 0x1 (big endian). It is recommended to set this to little endian (0x0).
#[bit(7, rw)]
endian_swap_packet_data: AhbEndianess,
// Default value is 0x0 (little endian)
#[bit(6, rw)]
endian_swap_mgmt_descriptor: AhbEndianess,
#[bits(0..=4, rw)]
burst_length: u5,
}
#[bitbybit::bitfield(u32, debug)]
pub struct TxStatus {
#[bit(8, rw)]
hresp_not_ok: bool,
#[bit(7, rw)]
late_collision: bool,
/// This bit should never be se because the DMA is configured for packet buffer mode.
#[bit(6, rw)]
underrun: bool,
#[bit(5, rw)]
complete: bool,
#[bit(4, rw)]
frame_corruption_ahb_error: bool,
/// Its called "tx_go" inside the Zynq 7000 documentation, but I think this is just a
/// TX active bit.
#[bit(3, r)]
go: bool,
#[bit(2, rw)]
retry_limit_reached: bool,
#[bit(1, rw)]
collision: bool,
#[bit(0, rw)]
read_when_used: bool,
}
impl TxStatus {
pub fn new_clear_all() -> Self {
Self::new_with_raw_value(0xFF)
}
}
#[bitbybit::bitfield(u32, debug)]
pub struct RxStatus {
#[bit(3, rw)]
hresp_not_ok: bool,
#[bit(2, rw)]
overrun: bool,
#[bit(1, rw)]
frame_received: bool,
#[bit(0, rw)]
buf_not_available: bool,
}
impl RxStatus {
pub fn new_clear_all() -> Self {
Self::new_with_raw_value(0xF)
}
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
#[bit(26, rw)]
tsu_sec_incr: bool,
/// Marked N/A in datasheet.
#[bit(17, rw)]
partner_pg_rx: bool,
/// Marked N/A in datasheet.
#[bit(16, rw)]
auto_negotiation_complete: bool,
#[bit(15, rw)]
external_interrupt: bool,
#[bit(14, rw)]
pause_transmitted: bool,
#[bit(13, rw)]
pause_time_zero: bool,
#[bit(12, rw)]
pause_with_non_zero_quantum: bool,
#[bit(11, rw)]
hresp_not_ok: bool,
#[bit(10, rw)]
rx_overrun: bool,
/// Marked N/A in datasheet.
#[bit(9, rw)]
link_changed: bool,
#[bit(7, rw)]
frame_sent: bool,
/// Cleared on read.
#[bit(6, r)]
tx_frame_corruption_ahb_error: bool,
#[bit(5, rw)]
tx_retry_limit_reached_or_late_collision: bool,
#[bit(3, rw)]
tx_descr_read_when_used: bool,
#[bit(2, rw)]
rx_descr_read_when_used: bool,
#[bit(1, rw)]
frame_received: bool,
#[bit(0, rw)]
mgmt_frame_sent: bool,
}
#[bitbybit::bitfield(u32, default = 0x00)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(26, w)]
tsu_sec_incr: bool,
/// Marked N/A in datasheet. Probably because external PHYs are used.
#[bit(17, w)]
partner_pg_rx: bool,
/// Marked N/A in datasheet. Probably because external PHYs are used.
#[bit(16, w)]
auto_negotiation_complete: bool,
#[bit(15, w)]
external_interrupt: bool,
#[bit(14, w)]
pause_transmitted: bool,
#[bit(13, w)]
pause_time_zero: bool,
#[bit(12, w)]
pause_with_non_zero_quantum: bool,
#[bit(11, w)]
hresp_not_ok: bool,
#[bit(10, w)]
rx_overrun: bool,
/// Marked N/A in datasheet. Probably because external PHYs are used.
#[bit(9, w)]
link_changed: bool,
#[bit(7, w)]
frame_sent: bool,
#[bit(6, w)]
tx_frame_corruption_ahb_error: bool,
#[bit(5, w)]
tx_retry_limit_reached_or_late_collision: bool,
#[bit(3, w)]
tx_descr_read_when_used: bool,
#[bit(2, w)]
rx_descr_read_when_used: bool,
#[bit(1, w)]
frame_received: bool,
#[bit(0, w)]
mgmt_frame_sent: bool,
}
impl InterruptControl {
pub fn new_clear_all() -> Self {
Self::new_with_raw_value(0xFFFF_FFFF)
}
}
#[bitbybit::bitenum(u2, exhaustive = false)]
#[derive(Debug)]
pub enum PhyOperation {
Read = 0b10,
Write = 0b01,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyMaintenance {
/// Must be 1 for Clause 22 operations.
#[bit(30, rw)]
clause_22: bool,
#[bits(28..=29, rw)]
op: Option<PhyOperation>,
#[bits(23..=27, rw)]
phy_addr: u5,
#[bits(18..=22, rw)]
reg_addr: u5,
#[bits(16..=17, rw)]
must_be_0b10: u2,
#[bits(0..=15, rw)]
data: u16,
}
#[bitbybit::bitfield(u32, debug)]
pub struct PauseQuantum {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(u32, debug)]
pub struct MatchRegister {
#[bit(31, rw)]
copy_enable: bool,
#[bits(0..=15, rw)]
type_id: u16,
}
/// Gigabit Ethernet Controller (GEM) registers for Zynq-7000
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Ethernet {
net_ctrl: NetworkControl,
net_cfg: NetworkConfig,
#[mmio(PureRead)]
net_status: NetworkStatus,
_reserved0: u32,
dma_cfg: DmaConfig,
tx_status: TxStatus,
rx_buf_queue_base_addr: u32,
tx_buf_queue_base_addr: u32,
rx_status: RxStatus,
interrupt_status: InterruptStatus,
interrupt_enable: InterruptControl,
interrupt_disable: InterruptControl,
interrupt_mask: InterruptStatus,
phy_maintenance: PhyMaintenance,
#[mmio(PureRead)]
rx_pause_quantum: PauseQuantum,
tx_pause_quantum: PauseQuantum,
_reserved1: [u32; 0x10],
hash_low: u32,
hash_high: u32,
addr1_low: u32,
addr1_high: u32,
addr2_low: u32,
addr2_high: u32,
addr3_low: u32,
addr3_high: u32,
addr4_low: u32,
addr4_high: u32,
match_reg: [MatchRegister; 4],
wake_on_lan: u32,
ipg_stretch: u32,
stacked_vlan: u32,
tx_pfc: u32,
addr1_mask_low: u32,
addr1_mask_high: u32,
_reserved2: [u32; 0x0B],
/// Should be 0x20118.
#[mmio(PureRead)]
module_id: u32,
#[mmio(Inner)]
statistics: Statistics,
_reserved3: [u32; 0x34],
#[mmio(PureRead)]
design_cfg_2: u32,
#[mmio(PureRead)]
design_cfg_3: u32,
#[mmio(PureRead)]
design_cfg_4: u32,
#[mmio(PureRead)]
design_cfg_5: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Ethernet>(), 0x294);
/// GEM statistics registers
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Statistics {
#[mmio(PureRead)]
tx_octets_low: u32,
#[mmio(PureRead)]
tx_octets_high: u32,
#[mmio(PureRead)]
tx_count: u32,
#[mmio(PureRead)]
tx_broadcast: u32,
#[mmio(PureRead)]
tx_multicast: u32,
#[mmio(PureRead)]
tx_pause: u32,
#[mmio(PureRead)]
tx_64_bits: u32,
#[mmio(PureRead)]
tx_65_to_127_bits: u32,
#[mmio(PureRead)]
tx_128_to_255_bits: u32,
#[mmio(PureRead)]
tx_256_to_511_bits: u32,
#[mmio(PureRead)]
tx_512_to_1023_bits: u32,
#[mmio(PureRead)]
tx_1024_to_1518_bits: u32,
_reserved0: u32,
#[mmio(PureRead)]
tx_underruns: u32,
#[mmio(PureRead)]
single_collision_frames: u32,
#[mmio(PureRead)]
multi_collision_frames: u32,
#[mmio(PureRead)]
excessive_collisions: u32,
#[mmio(PureRead)]
late_collisions: u32,
#[mmio(PureRead)]
deferred_tx: u32,
#[mmio(PureRead)]
carrier_sense_errors: u32,
#[mmio(PureRead)]
rx_octets_low: u32,
#[mmio(PureRead)]
rx_octets_high: u32,
#[mmio(PureRead)]
rx_count: u32,
#[mmio(PureRead)]
rx_broadcast: u32,
#[mmio(PureRead)]
rx_multicast: u32,
#[mmio(PureRead)]
rx_pause: u32,
#[mmio(PureRead)]
rx_64_bits: u32,
#[mmio(PureRead)]
rx_65_to_127_bits: u32,
#[mmio(PureRead)]
rx_128_to_255_bits: u32,
#[mmio(PureRead)]
rx_256_to_511_bits: u32,
#[mmio(PureRead)]
rx_512_to_1023_bits: u32,
#[mmio(PureRead)]
rx_1024_to_1518_bits: u32,
_reserved1: u32,
#[mmio(PureRead)]
rx_undersize: u32,
#[mmio(PureRead)]
rx_oversize: u32,
#[mmio(PureRead)]
rx_jabber: u32,
#[mmio(PureRead)]
rx_frame_check_sequence_errors: u32,
#[mmio(PureRead)]
rx_length_field_errors: u32,
#[mmio(PureRead)]
rx_symbol_errors: u32,
#[mmio(PureRead)]
rx_alignment_errors: u32,
#[mmio(PureRead)]
rx_resource_errors: u32,
#[mmio(PureRead)]
rx_overrun_errors: u32,
#[mmio(PureRead)]
rx_ip_header_checksum_errors: u32,
#[mmio(PureRead)]
rx_tcp_checksum_errors: u32,
#[mmio(PureRead)]
rx_udp_checksum_errors: u32,
}
impl Ethernet {
/// Create a new Gigabit Ethernet MMIO instance for GEM 0 at address [GEM_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() -> MmioEthernet<'static> {
unsafe { Self::new_mmio_at(GEM_0_BASE_ADDR) }
}
/// Create a new Gigabit Ethernet MMIO instance for GEM 1 at address [GEM_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() -> MmioEthernet<'static> {
unsafe { Self::new_mmio_at(GEM_1_BASE_ADDR) }
}
}

201
zynq/zynq7000/src/gic.rs Normal file
View File

@@ -0,0 +1,201 @@
//! # 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, debug)]
pub struct DistributorControlRegister {
#[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, debug)]
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 GicDistributor {
/// Distributor Control Register
pub dcr: DistributorControlRegister,
/// 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::<GicDistributor>(), 0x1000);
impl GicDistributor {
/// 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() -> MmioGicDistributor<'static> {
unsafe { Self::new_mmio_at(GICD_BASE_ADDR) }
}
}
/// CPU interface control register.
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterfaceControl {
#[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, debug)]
pub struct PriorityRegister {
#[bits(0..=7, rw)]
priority: u8,
}
/// Interrupt acknowledge register.
#[bitbybit::bitfield(u32, 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 GicCpuInterface {
/// CPU Interface Control Register (ICR).
pub icr: InterfaceControl,
/// 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::<GicCpuInterface>(), 0x100);
impl GicCpuInterface {
/// 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() -> MmioGicCpuInterface<'static> {
unsafe { Self::new_mmio_at(GICC_BASE_ADDR) }
}
}

121
zynq/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)]
mask: u16,
#[bits(0..=15, rw)]
output: u16,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct BankControl {
/// 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: BankControl,
_reserved_3: [u32; 7],
#[mmio(Inner)]
bank_1: BankControl,
_reserved_4: [u32; 7],
#[mmio(Inner)]
bank_2: BankControl,
_reserved_5: [u32; 7],
#[mmio(Inner)]
bank_3: BankControl,
}
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
zynq/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, debug)]
pub struct GtcControl {
#[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, debug)]
pub struct InterruptStatus {
#[bit(0, rw)]
event_flag: bool,
}
/// Global timer counter.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct GlobalTimerCounter {
/// Count register 0, lower 32 bits
count_lower: u32,
/// Count register 1, upper 32 bits
count_upper: u32,
/// Control register
ctrl: GtcControl,
/// 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::<GlobalTimerCounter>(), 0x1C);
impl GlobalTimerCounter {
/// 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() -> MmioGlobalTimerCounter<'static> {
unsafe { GlobalTimerCounter::new_mmio_at(GTC_BASE_ADDR) }
}
}

205
zynq/zynq7000/src/i2c.rs Normal file
View File

@@ -0,0 +1,205 @@
//! 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, debug)]
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, debug)]
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, debug)]
pub struct Address {
#[bits(0..=9, rw)]
addr: u10,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
data: u8,
}
#[bitbybit::bitfield(u32, debug)]
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, debug)]
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, debug)]
pub struct Timeout {
/// Reset value: 0x1F.
#[bits(0..=7, rw)]
timeout: u8,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
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: Address,
#[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,
}
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x2C);
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) }
}
}

View File

@@ -0,0 +1,288 @@
use arbitrary_int::{u2, u3, u4, u6};
pub const L2C_BASE_ADDR: usize = 0xF8F0_2000;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct LockdownRegisters {
data: u32,
instruction: u32,
}
#[bitbybit::bitfield(u32, debug)]
pub struct CacheSync {
#[bit(0, r)]
busy: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DebugControl {
#[bit(2, rw)]
spniden: bool,
#[bit(1, rw)]
disable_write_back: bool,
#[bit(0, rw)]
disable_cache_linefill: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct CacheId {
#[bits(24..=31, r)]
implementer: u8,
#[bits(10..=15, r)]
cache_id: u6,
#[bits(6..=9, r)]
part_number: u4,
#[bits(0..=5, r)]
rtl_release: u6,
}
#[repr(transparent)]
pub struct Control(u32);
impl Control {
pub fn new_enabled() -> Self {
Self(0x1)
}
pub fn new_disabled() -> Self {
Self(0x0)
}
#[inline(always)]
pub fn enabled(&mut self) -> bool {
self.0 == 0x1
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum ReplacementPolicy {
PseudoRandomWithLfsr = 0,
RoundRobin = 1,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Default, Debug)]
pub enum WaySize {
__Reserved0 = 0b000,
_16kB = 0b001,
_32kB = 0b010,
#[default]
_64kB = 0b011,
_128kB = 0b100,
_256kB = 0b101,
_512kB = 0b110,
__Reserved1 = 0b111,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug)]
pub enum Associativity {
#[default]
_8Way = 0,
_16Way = 1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AuxControl {
#[bit(30, rw)]
early_bresp_enable: bool,
#[bit(29, rw)]
isntruction_prefetch_enable: bool,
#[bit(28, rw)]
data_prefetch_enable: bool,
#[bit(27, rw)]
nonsec_interrupt_access_control: bool,
#[bit(26, rw)]
nonsec_lockdown_enable: bool,
#[bit(25, rw)]
cache_replace_policy: ReplacementPolicy,
#[bits(23..=24, rw)]
force_write_alloc: u2,
#[bit(22, rw)]
shared_attr_override: bool,
#[bit(21, rw)]
parity_enable: bool,
#[bit(20, rw)]
event_monitor_bus_enable: bool,
#[bits(17..=19, rw)]
way_size: WaySize,
#[bit(16, rw)]
associativity: Associativity,
#[bit(13, rw)]
shared_attribute_invalidate: bool,
#[bit(12, rw)]
exclusive_cache_config: bool,
#[bit(11, rw)]
store_buff_device_limitation_enable: bool,
#[bit(10, rw)]
high_priority_so_dev_reads: bool,
/// Disabled by default.
#[bit(0, rw)]
full_line_zero_enable: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
#[derive(PartialEq, Eq)]
pub struct LatencyConfig {
/// Latency is the numerical value + 1 cycles.
#[bits(8..=10, rw)]
write_access_latency: u3,
/// Latency is the numerical value + 1 cycles.
#[bits(4..=6, rw)]
read_access_latency: u3,
/// Latency is the numerical value + 1 cycles.
#[bits(0..=2, rw)]
setup_latency: u3,
}
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
#[bit(8, r)]
dec_error_l3: bool,
#[bit(7, r)]
slave_error_l3: bool,
#[bit(6, r)]
error_data_ram_read: bool,
#[bit(5, r)]
error_tag_ram_read: bool,
#[bit(4, r)]
error_data_ram_write: bool,
#[bit(3, r)]
error_tag_ram_write: bool,
#[bit(2, r)]
parity_error_data_ram_read: bool,
#[bit(1, r)]
parity_error_tag_ram_read: bool,
/// ECNTR
#[bit(0, r)]
event_counter_overflow_increment: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(8, w)]
dec_error_l3: bool,
#[bit(7, w)]
slave_error_l3: bool,
#[bit(6, w)]
error_data_ram_read: bool,
#[bit(5, w)]
error_tag_ram_read: bool,
#[bit(4, w)]
error_data_ram_write: bool,
#[bit(3, w)]
error_tag_ram_write: bool,
#[bit(2, w)]
parity_error_data_ram_read: bool,
#[bit(1, w)]
parity_error_tag_ram_read: bool,
/// ECNTR
#[bit(0, w)]
event_counter_overflow_increment: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct L2Cache {
#[mmio(PureRead)]
cache_id: CacheId,
#[mmio(PureRead)]
cache_type: u32,
_reserved: [u32; 0x3E],
control: Control,
aux_control: AuxControl,
tag_ram_latency: LatencyConfig,
data_ram_latency: LatencyConfig,
_reserved2: [u32; 0x3C],
event_counter_control: u32,
event_counter_1_config: u32,
event_counter_0_config: u32,
event_counter_1: u32,
event_counter_0: u32,
interrupt_mask: u32,
#[mmio(PureRead)]
interrupt_mask_status: InterruptStatus,
#[mmio(PureRead)]
interrupt_raw_status: InterruptStatus,
#[mmio(Write)]
interrupt_clear: InterruptControl,
_reserved3: [u32; 0x143],
cache_sync: CacheSync,
_reserved4: [u32; 0xF],
invalidate_by_pa: u32,
_reserved5: [u32; 0x2],
invalidate_by_way: u32,
_reserved6: [u32; 0xC],
clean_by_pa: u32,
_reserved7: u32,
clean_by_index: u32,
clean_by_way: u32,
_reserved8: [u32; 0xC],
clean_invalidate_by_pa: u32,
_reserved9: u32,
clean_invalidate_by_index: u32,
clean_invalidate_by_way: u32,
_reserved10: [u32; 0x40],
#[mmio(Inner)]
lockdown_regs: [LockdownRegisters; 8],
_reserved11: [u32; 0x4],
lockdown_by_line_enable: u32,
unlock_way: u32,
_reserved12: [u32; 0xAA],
addr_filtering_start: u32,
addr_filtering_end: u32,
_reserved13: [u32; 0xCE],
debug_control: DebugControl,
_reserved14: [u32; 0x7],
prefetch_control: u32,
_reserved15: [u32; 0x7],
power_control: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<L2Cache>(), 0xF84);
impl L2Cache {
/// Create a new L2C MMIO instance for for L2 Cache at address [L2C_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() -> MmioL2Cache<'static> {
unsafe { Self::new_mmio_at(L2C_BASE_ADDR) }
}
}

122
zynq/zynq7000/src/lib.rs Normal file
View File

@@ -0,0 +1,122 @@
//! # 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.
//! [The Zynq7000 HAL library](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-hal)
//! contains such a HAL which builds on this PAC.
#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(test)]
extern crate std;
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
pub mod ddrc;
pub mod devcfg;
pub mod eth;
pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod i2c;
pub mod l2_cache;
pub mod mpcore;
pub mod priv_tim;
pub mod qspi;
pub mod slcr;
pub mod spi;
pub mod ttc;
pub mod uart;
pub mod xadc;
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 Peripherals {
pub gicc: gic::MmioGicCpuInterface<'static>,
pub gicd: gic::MmioGicDistributor<'static>,
pub l2c: l2_cache::MmioL2Cache<'static>,
pub ddrc: ddrc::MmioDdrController<'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::MmioGlobalTimerCounter<'static>,
pub gpio: gpio::MmioGpio<'static>,
pub slcr: slcr::MmioSlcr<'static>,
pub ttc_0: ttc::MmioTtc<'static>,
pub ttc_1: ttc::MmioTtc<'static>,
pub eth_0: eth::MmioEthernet<'static>,
pub eth_1: eth::MmioEthernet<'static>,
pub qspi: qspi::MmioQspi<'static>,
pub devcfg: devcfg::MmioDevCfg<'static>,
pub xadc: xadc::MmioXAdc<'static>,
}
impl Peripherals {
/// 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::GicCpuInterface::new_mmio_fixed(),
gicd: gic::GicDistributor::new_mmio_fixed(),
l2c: l2_cache::L2Cache::new_mmio_fixed(),
ddrc: ddrc::DdrController::new_mmio_fixed(),
uart_0: uart::Uart::new_mmio_fixed_0(),
uart_1: uart::Uart::new_mmio_fixed_1(),
gtc: gtc::GlobalTimerCounter::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(),
eth_0: eth::Ethernet::new_mmio_fixed_0(),
eth_1: eth::Ethernet::new_mmio_fixed_1(),
qspi: qspi::Qspi::new_mmio_fixed(),
devcfg: devcfg::DevCfg::new_mmio_fixed(),
xadc: xadc::XAdc::new_mmio_fixed(),
}
}
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum SpiClockPhase {
ActiveOutsideOfWord = 0,
InactiveOutsideOfWord = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum SpiClockPolarity {
QuiescentLow = 0,
QuiescentHigh = 1,
}

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::{GicCpuInterface, GicDistributor, MmioGicCpuInterface, MmioGicDistributor},
gtc::{GlobalTimerCounter, MmioGlobalTimerCounter},
};
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 SnoopControlUnit {
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 SnoopControlUnit {
/// 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() -> MmioSnoopControlUnit<'static> {
unsafe { Self::new_mmio_at(SCU_BASE_ADDR) }
}
}
const_assert_eq!(core::mem::size_of::<SnoopControlUnit>(), 0x58);
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct MpCore {
#[mmio(Inner)]
scu: SnoopControlUnit,
_reserved_0: [u32; 0x2A],
#[mmio(Inner)]
gicc: GicCpuInterface,
#[mmio(Inner)]
gt: GlobalTimerCounter,
_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: GicDistributor,
}
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) }
}
}

View File

@@ -0,0 +1,48 @@
//! # CPU private timer module.
pub const CPU_PRIV_TIM_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0600;
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Control {
#[bits(8..=15, rw)]
prescaler: u8,
#[bit(2, rw)]
interrupt_enable: bool,
#[bit(1, rw)]
auto_reload: bool,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
/// Cleared by writing a one.
#[bit(0, rw)]
event_flag: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct CpuPrivateTimer {
reload: u32,
counter: u32,
control: Control,
interrupt_status: InterruptStatus,
}
impl CpuPrivateTimer {
/// Create a new CPU Private Timer 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.
///
/// It should also be noted that the calls to this MMIO structure are private for each CPU
/// core, which might lead to unexpected results when using this in a SMP system.
#[inline]
pub const unsafe fn new_mmio_fixed() -> MmioCpuPrivateTimer<'static> {
unsafe { CpuPrivateTimer::new_mmio_at(CPU_PRIV_TIM_BASE_ADDR) }
}
}

280
zynq/zynq7000/src/qspi.rs Normal file
View File

@@ -0,0 +1,280 @@
use arbitrary_int::{u2, u3};
pub use crate::{SpiClockPhase, SpiClockPolarity};
const QSPI_BASE_ADDR: usize = 0xE000D000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum InterfaceMode {
LegacySpi = 0,
FlashMemoryInterface = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum Endianness {
Little = 0,
Big = 1,
}
/// Baud rate divisor register values.
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum BaudRateDivisor {
_2 = 0b000,
_4 = 0b001,
_8 = 0b010,
_16 = 0b011,
_32 = 0b100,
_64 = 0b101,
_128 = 0b110,
_256 = 0b111,
}
impl BaudRateDivisor {
/// Actual divisor value.
pub fn divisor(&self) -> usize {
match self {
BaudRateDivisor::_2 => 2,
BaudRateDivisor::_4 => 4,
BaudRateDivisor::_8 => 8,
BaudRateDivisor::_16 => 16,
BaudRateDivisor::_32 => 32,
BaudRateDivisor::_64 => 64,
BaudRateDivisor::_128 => 128,
BaudRateDivisor::_256 => 256,
}
}
}
// TODO: Use bitbybit debug support as soon as support for write fields has been implemented.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(31, rw)]
interface_mode: InterfaceMode,
#[bit(26, rw)]
edianness: Endianness,
#[bit(19, rw)]
holdb_dr: bool,
#[bit(16, w)]
manual_start_command: bool,
#[bit(15, rw)]
manual_start_enable: bool,
#[bit(14, rw)]
manual_cs: bool,
/// Directly drives the chip select line when CS is driven manually (bit 14 is set)
#[bit(10, rw)]
peripheral_chip_select: bool,
/// The only valid value is 0b11 (32 bits)
#[bits(6..=7, rw)]
fifo_width: u2,
#[bits(3..=5, rw)]
baud_rate_div: BaudRateDivisor,
#[bit(2, rw)]
clock_phase: SpiClockPhase,
#[bit(1, rw)]
clock_polarity: SpiClockPolarity,
/// Must be set to 1 before using QSPI, 0 is a reserved value.
#[bit(0, rw)]
mode_select: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
/// Write-to-clear bit.
#[bit(6, rw)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_above_threshold: bool,
#[bit(3, r)]
tx_full: bool,
#[bit(2, r)]
tx_below_threshold: bool,
/// Write-to-clear bit.
#[bit(0, rw)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
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_not_full: bool,
#[bit(0, w)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32, 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_not_full: bool,
#[bit(0, r)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct SpiEnable {
#[bit(0, rw)]
enable: bool,
}
/// All the delays are in SPI reference block or external clock cycles.
#[bitbybit::bitfield(u32, debug)]
pub struct Delay {
/// Length of the master mode chip select output de-asserts between words when CPHA = 0.
#[bits(24..=31, rw)]
deassert: u8,
/// Delay between one chip select being de-activated and another being activated.
#[bits(16..=23, rw)]
between: u8,
/// Length between last bit of current word and first bit of next word.
#[bits(8..=15, rw)]
after: u8,
/// Delay between setting chip select low and first bit transfer.
#[bits(0..=7, rw)]
init: u8,
}
#[bitbybit::bitfield(u32)]
pub struct Gpio {
/// Active low write-protect bit.
#[bit(0, rw)]
write_protect_n: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LoopbackMasterClockDelay {
/// Use internal loopback master clock for read data capturing when the baud rate divisor
/// is 2.
#[bit(5, rw)]
use_loopback: bool,
#[bits(3..=4,rw)]
delay_1: u2,
#[bits(0..=2 ,rw)]
delay_0: u3,
}
#[bitbybit::bitenum(u8, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum InstructionCode {
Read = 0x03,
FastRead = 0x0B,
FastReadDualOutput = 0x3B,
FastReadQuadOutput = 0x6B,
FastReadDualIo = 0xBB,
FastReadQuadIo = 0xEB,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LinearQspiConfig {
#[bit(31, rw)]
enable_linear_mode: bool,
#[bit(30, rw)]
both_memories: bool,
/// Only has a meaning is bit 30 is set (both memories).
#[bit(29, rw)]
separate_memory_bus: bool,
/// Upper memory page, if set. Only has a meaning if bit 30 is set and bit 29 / bit 31 are
/// cleared.
///
/// In LQSPI mode, address bit 25 will indicate the lower (0) or upper (1) page.
/// In IO mode, this bit selects the lower or upper memory.
#[bit(28, rw)]
upper_memory_page: bool,
#[bit(25, rw)]
mode_enable: bool,
#[bit(24, rw)]
mode_on: bool,
#[bits(16..=23, rw)]
mode_bits: u8,
#[bits(8..=10, rw)]
num_dummy_bytes: u3,
#[bits(0..=7, rw)]
instruction_code: Option<InstructionCode>,
}
#[bitbybit::bitfield(u32, debug)]
pub struct LinearQspiStatus {
#[bit(2, rw)]
data_fsm_error: bool,
#[bit(1, rw)]
axi_write_command_received: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Qspi {
config: Config,
interrupt_status: InterruptStatus,
#[mmio(Write)]
interrupt_enable: InterruptControl,
#[mmio(Write)]
interrupt_disable: InterruptControl,
#[mmio(PureRead)]
interupt_mask: InterruptMask,
spi_enable: SpiEnable,
delay: Delay,
/// Transmits 1-byte command and 3-byte data OR 4-byte data.
#[mmio(Write)]
tx_data_00: u32,
#[mmio(PureRead)]
rx_data: u32,
slave_idle_count: u32,
/// Defines the level at which the TX FIFO not full interrupt is generated.
tx_fifo_threshold: u32,
/// Defines the level at which the RX FIFO not empty interrupt is generated.
rx_fifo_threshold: u32,
gpio: Gpio,
_reserved0: u32,
loopback_master_clock_delay: LoopbackMasterClockDelay,
_reserved1: [u32; 0x11],
/// Transmits 1-byte command.
#[mmio(Write)]
tx_data_01: u32,
/// Transmits 1-byte command and 1-byte data.
#[mmio(Write)]
tx_data_10: u32,
/// Transmits 1-byte command and 2-byte data.
#[mmio(Write)]
tx_data_11: u32,
_reserved2: [u32; 0x5],
linear_qspi_config: LinearQspiConfig,
linear_qspi_status: LinearQspiStatus,
_reserved3: [u32; 0x15],
/// Module ID value with reset value 0x1090101.
module_id: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Qspi>(), 0x100);
impl Qspi {
/// Create a new QSPI MMIO instance for for QSPI controller at address [QSPI_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() -> MmioQspi<'static> {
unsafe { Self::new_mmio_at(QSPI_BASE_ADDR) }
}
}

View File

@@ -0,0 +1,419 @@
//! 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};
pub enum Bypass {
NotBypassed = 0b00,
/// This is the default reset value.
PinStrapSettings = 0b01,
Bypassed = 0b10,
BypassedRegardlessOfPinStrapping = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct PllControl {
/// 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: bool,
/// Select source for the ARM PLL bypass control
#[bit(3, rw)]
bypass_qual: bool,
// Power-down control
#[bit(1, rw)]
pwrdwn: bool,
/// Reset control
#[bit(0, rw)]
reset: bool,
}
impl PllControl {
#[inline]
pub fn set_bypass(&mut self, bypass: Bypass) {
match bypass {
Bypass::NotBypassed => {
self.set_bypass_force(false);
self.set_bypass_qual(false);
}
Bypass::PinStrapSettings => {
self.set_bypass_force(false);
self.set_bypass_qual(true);
}
Bypass::Bypassed => {
self.set_bypass_force(true);
self.set_bypass_qual(false);
}
Bypass::BypassedRegardlessOfPinStrapping => {
self.set_bypass_force(true);
self.set_bypass_qual(true);
}
}
}
#[inline]
pub fn bypass(&self) -> Bypass {
match (self.bypass_force(), self.bypass_qual()) {
(false, false) => Bypass::NotBypassed,
(false, true) => Bypass::PinStrapSettings,
(true, false) => Bypass::Bypassed,
(true, true) => Bypass::BypassedRegardlessOfPinStrapping,
}
}
}
#[bitbybit::bitfield(u32, debug)]
pub struct PllConfig {
#[bits(12..=21, rw)]
lock_count: u10,
/// Charge Pump control
#[bits(8..=11, rw)]
charge_pump: u4,
/// Loop resistor control
#[bits(4..=7, rw)]
loop_resistor: u4,
}
#[bitbybit::bitfield(u32, debug)]
pub struct PllStatus {
#[bit(5, r)]
io_pll_stable: bool,
#[bit(4, r)]
ddr_pll_stable: bool,
#[bit(3, r)]
arm_pll_stable: bool,
#[bit(2, r)]
io_pll_lock: bool,
#[bit(1, r)]
drr_pll_lock: bool,
#[bit(0, r)]
arm_pll_lock: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct FpgaClockControl {
// 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 FpgaClockControlBlock {
ctrl: FpgaClockControl,
thr_ctrl: u32,
thr_cnt: u32,
thr_status: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<FpgaClockControlBlock>(), 0x10);
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum SrcSelArm {
ArmPll = 0b00,
ArmPllAlt = 0b01,
DdrPll = 0b10,
IoPll = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct ArmClockControl {
#[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, debug)]
pub struct DdrClockControl {
/// 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, default = 0x0, debug)]
pub struct DciClockControl {
/// 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, debug)]
pub struct ClockRatioSelectReg {
/// Reset value: 0x1 (6:2:1 clock)
#[bit(0, rw)]
sel: ClockkRatioSelect,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum ClockkRatioSelect {
/// 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, Eq)]
pub enum SrcSelIo {
IoPll = 0b00,
IoPllAlt = 0b01,
ArmPll = 0b10,
DdrPll = 0b11,
}
impl PartialEq for SrcSelIo {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
// IoPll and IoPllAlt are equal to each other
(Self::IoPll, Self::IoPll)
| (Self::IoPll, Self::IoPllAlt)
| (Self::IoPllAlt, Self::IoPll)
| (Self::IoPllAlt, Self::IoPllAlt) => true,
// For other variants, only equal if exactly the same
(Self::ArmPll, Self::ArmPll) => true,
(Self::DdrPll, Self::DdrPll) => true,
// Otherwise, not equal
_ => false,
}
}
}
#[bitbybit::bitfield(u32, debug)]
pub struct GigEthClockControl {
#[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::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum SrcSelGigEthRclk {
Mio = 0,
Emio = 1,
}
#[bitbybit::bitfield(u32, debug)]
pub struct GigEthRclkControl {
#[bit(4, rw)]
srcsel: SrcSelGigEthRclk,
// Enable the ethernet controller RX clock.
#[bit(0, rw)]
clk_enable: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct CanClockControl {
#[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, default = 0x0)]
pub struct SingleCommonPeriphIoClockControl {
#[bits(8..=13, rw)]
divisor: u6,
#[bits(4..=5, rw)]
srcsel: SrcSelIo,
#[bit(0, rw)]
clk_act: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct DualCommonPeriphIoClockControl {
#[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, debug)]
pub struct TracePortClockControl {
#[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, debug)]
pub struct AperClockControl {
#[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_ctrl: PllControl,
ddr_pll_ctrl: PllControl,
io_pll_ctrl: PllControl,
pll_status: PllStatus,
arm_pll_cfg: PllConfig,
ddr_pll_cfg: PllConfig,
io_pll_cfg: PllConfig,
_gap0: u32,
arm_clk_ctrl: ArmClockControl,
ddr_clk_ctrl: DdrClockControl,
dci_clk_ctrl: DciClockControl,
/// AMBA peripheral clock control
aper_clk_ctrl: AperClockControl,
usb_0_clk_ctrl: u32,
usb_1_clk_ctrl: u32,
gem_0_rclk_ctrl: GigEthRclkControl,
gem_1_rclk_ctrl: GigEthRclkControl,
gem_0_clk_ctrl: GigEthClockControl,
gem_1_clk_ctrl: GigEthClockControl,
smc_clk_ctrl: SingleCommonPeriphIoClockControl,
lqspi_clk_ctrl: SingleCommonPeriphIoClockControl,
sdio_clk_ctrl: DualCommonPeriphIoClockControl,
uart_clk_ctrl: DualCommonPeriphIoClockControl,
spi_clk_ctrl: DualCommonPeriphIoClockControl,
can_clk_ctrl: CanClockControl,
can_mioclk_ctrl: u32,
/// Debug or Trace Port clock control.
dbg_clk_ctrl: TracePortClockControl,
pcap_clk_ctrl: SingleCommonPeriphIoClockControl,
topsw_clk_ctrl: u32,
#[mmio(Inner)]
fpga_0_clk_ctrl: FpgaClockControlBlock,
#[mmio(Inner)]
fpga_1_clk_ctrl: FpgaClockControlBlock,
#[mmio(Inner)]
fpga_2_clk_ctrl: FpgaClockControlBlock,
#[mmio(Inner)]
fpga_3_clk_ctrl: FpgaClockControlBlock,
_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);

View File

@@ -0,0 +1,140 @@
use arbitrary_int::{u2, u3};
#[bitbybit::bitenum(u4, exhaustive = false)]
#[derive(Debug)]
pub enum VRefSel {
/// VREF = 0.6 V
Lpddr2 = 0b0001,
/// VREF = 0.675 V
Ddr3l = 0b0010,
/// VREF = 0.75 V
Ddr3 = 0b0100,
/// VREF = 0.9 V
Ddr2 = 0b1000,
}
#[bitbybit::bitfield(u32, debug)]
pub struct DdrControl {
/// Enables VRP/VRN.
#[bit(9, rw)]
refio_enable: bool,
#[bit(6, rw)]
vref_ext_en_upper_bits: bool,
#[bit(5, rw)]
vref_ext_en_lower_bits: bool,
#[bits(1..=4, rw)]
vref_sel: Option<VRefSel>,
#[bit(0, rw)]
vref_int_en: bool,
}
#[bitbybit::bitfield(u32, default = 0x00, debug)]
pub struct DciControl {
#[bit(20, rw)]
update_control: bool,
#[bits(17..=19, rw)]
pref_opt2: u3,
#[bits(14..=15, rw)]
pref_opt1: u2,
#[bits(11..=13, rw)]
nref_opt4: u3,
#[bits(8..=10, rw)]
nref_opt2: u3,
#[bits(6..=7, rw)]
nref_opt1: u2,
#[bit(1, rw)]
enable: bool,
/// Reset value 0. Should be toggled once to initialize flops in DCI system.
#[bit(0, rw)]
reset: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct DciStatus {
#[bit(13, rw)]
done: bool,
#[bit(0, rw)]
lock: bool,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum OutputEnable {
IBuf = 0b00,
__Reserved0 = 0b01,
__Reserved1 = 0b10,
OBuf = 0b11,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum InputType {
Off = 0b00,
VRefBasedDifferentialReceiverForSstlHstl = 0b01,
DifferentialInputReceiver = 0b10,
LvcmosReceiver = 0b11,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug)]
pub enum DciType {
Disabled = 0b00,
DciDrive = 0b01,
__Reserved = 0b10,
DciTermination = 0b11,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DdriobConfig {
#[bit(11, rw)]
pullup_enable: bool,
#[bits(9..=10, rw)]
output_enable: OutputEnable,
#[bit(8, rw)]
term_disable_mode: bool,
#[bit(7, rw)]
ibuf_disable_mode: bool,
#[bits(5..=6, rw)]
dci_type: DciType,
#[bit(4, rw)]
termination_enable: bool,
#[bit(3, rw)]
dci_update_enable: bool,
#[bits(1..=2, rw)]
inp_type: InputType,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DdrIoB {
ddriob_addr0: DdriobConfig,
ddriob_addr1: DdriobConfig,
ddriob_data0: DdriobConfig,
ddriob_data1: DdriobConfig,
ddriob_diff0: DdriobConfig,
ddriob_diff1: DdriobConfig,
ddriob_clock: DdriobConfig,
ddriob_drive_slew_addr: u32,
ddriob_drive_slew_data: u32,
ddriob_drive_slew_diff: u32,
ddriob_drive_slew_clock: u32,
ddr_ctrl: DdrControl,
dci_ctrl: DciControl,
dci_status: DciStatus,
}
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(super::SLCR_BASE_ADDR + super::DDRIOB_OFFSET) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);

View File

@@ -0,0 +1,43 @@
//! # 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)]
#[derive(Debug)]
pub enum Speed {
SlowCmosEdge = 0b0,
FastCmosEdge = 0b1,
}
#[bitbybit::bitenum(u3)]
#[derive(Debug)]
pub enum IoType {
LvCmos18 = 0b001,
LvCmos25 = 0b010,
LvCmos33 = 0b011,
Hstl = 0b100,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
#[derive(PartialEq, Eq)]
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,
}

View File

@@ -0,0 +1,200 @@
//! 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 ddriob;
pub mod mio;
pub mod reset;
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug)]
pub enum VrefSel {
Disabled = 0b000,
Vref0_9V = 0b001,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct GpiobControl {
#[bit(11, rw)]
vref_sw_en: bool,
#[bits(4..=6, rw)]
vref_sel: Option<VrefSel>,
#[bit(0, rw)]
vref_en: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct GpiobRegisters {
ctrl: GpiobControl,
cfg_cmos18: u32,
cfg_cmos25: u32,
cfg_cmos33: u32,
_gap17: u32,
cfg_hstl: u32,
drvr_bias_ctrl: u32,
}
impl GpiobRegisters {
/// 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() -> MmioGpiobRegisters<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + GPIOB_OFFSET) }
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum BootPllConfig {
Enabled = 0,
/// Disabled and bypassed.
Bypassed = 1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct BootModeRegister {
#[bit(4, r)]
pll_config: BootPllConfig,
#[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, debug)]
pub struct LevelShifterRegister {
#[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: LevelShifterRegister,
_gap14: [u32; 0x03],
ocm_cfg: u32,
_gap15: [u32; 0x42],
/// Xilinx marks this as reserved but writes to it in their low-level L2 cache configuration.
magic_l2c_register: u32,
_gap16: [u32; 0x38],
_gap18: [u32; 0x09],
#[mmio(Inner)]
gpiob: GpiobRegisters,
#[mmio(Inner)]
ddriob: 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,100 @@
use super::{RESET_BLOCK_OFFSET, SLCR_BASE_ADDR};
#[bitbybit::bitfield(u32, default = 0x0, 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, 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, debug)]
pub struct GpioClockReset {
#[bit(0, rw)]
gpio_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct EthernetReset {
#[bit(5, rw)]
gem1_ref_rst: bool,
#[bit(4, rw)]
gem0_ref_rst: bool,
#[bit(3, rw)]
gem1_rx_rst: bool,
#[bit(2, rw)]
gem0_rx_rst: bool,
#[bit(1, rw)]
gem1_cpu1x_rst: bool,
#[bit(0, rw)]
gem0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct QspiResetControl {
#[bit(2, rw)]
qspi_ref_reset: bool,
#[bit(1, rw)]
cpu_1x_reset: 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,
eth: EthernetReset,
sdio: DualRefAndClockReset,
spi: DualRefAndClockReset,
can: DualClockReset,
i2c: DualClockReset,
uart: DualRefAndClockReset,
gpio: GpioClockReset,
lqspi: QspiResetControl,
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
zynq/zynq7000/src/spi.rs Normal file
View File

@@ -0,0 +1,236 @@
//! SPI register module.
use arbitrary_int::{prelude::*, u4};
pub use crate::{SpiClockPhase, SpiClockPolarity};
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 BaudDivSel {
By4 = 0b001,
By8 = 0b010,
By16 = 0b011,
By32 = 0b100,
By64 = 0b101,
By128 = 0b110,
By256 = 0b111,
}
impl BaudDivSel {
pub const fn div_value(&self) -> usize {
match self {
BaudDivSel::By4 => 4,
BaudDivSel::By8 => 8,
BaudDivSel::By16 => 16,
BaudDivSel::By32 => 32,
BaudDivSel::By64 => 64,
BaudDivSel::By128 => 128,
BaudDivSel::By256 => 256,
}
}
}
// TODO: Use bitbybit debug support as soon as it was added.
#[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<BaudDivSel>,
/// Clock phase. 1: The SPI clock is inactive outside the word.
#[bit(2, rw)]
cpha: SpiClockPhase,
/// Clock phase. 1: The SPI clock is quiescent high.
#[bit(1, rw)]
cpol: SpiClockPolarity,
/// Master mode enable. 1 is master mode.
#[bit(0, rw)]
master_ern: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, 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, 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, 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
zynq/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, debug)]
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,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum Mode {
Overflow = 0b0,
Interval = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, Default)]
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,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum WaveEnable {
Enable = 0b0,
Disable = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
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, debug)]
pub struct Counter {
#[bits(0..=15, r)]
count: u16,
}
#[bitbybit::bitfield(u32, debug)]
pub struct RwValue {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(u32, debug)]
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, debug)]
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, debug)]
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, debug)]
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) }
}
}

351
zynq/zynq7000/src/uart.rs Normal file
View File

@@ -0,0 +1,351 @@
//! 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)]
#[derive(Debug)]
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 CharLen {
SixBits = 0b11,
SevenBits = 0b10,
#[default]
EightBits = 0b00,
EightBitsAlt = 0b01,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum ClockSelect {
#[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, debug)]
pub struct Control {
/// 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, 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: CharLen,
#[bit(0, rw)]
clksel: ClockSelect,
}
#[bitbybit::bitfield(u32, default = 0, debug)]
pub struct Baudgen {
#[bits(0..=15, rw)]
cd: u16,
}
#[bitbybit::bitfield(u32, default = 0, debug)]
pub struct BaudRateDivisor {
#[bits(0..=7, rw)]
bdiv: u8,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
fifo: u8,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum Ttrig {
LessThanTTrig = 0b0,
GreaterEqualTTrig = 0b1,
}
#[bitbybit::bitfield(u32, 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, 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: Control,
/// 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: BaudRateDivisor,
/// 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) }
}
}

29
zynq/zynq7000/src/xadc.rs Normal file
View File

@@ -0,0 +1,29 @@
pub const XADC_BASE_ADDR: usize = 0xF8007100;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct XAdc {
config: u32,
interrupt_status: u32,
interrupt_mask: u32,
misc_status: u32,
command_fifo: u32,
data_fifo: u32,
misc_control: u32,
}
impl XAdc {
/// Create a new XADC MMIO instance for for device configuration peripheral at address
/// [XADC_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 unsafe fn new_mmio_fixed() -> MmioXAdc<'static> {
unsafe { XAdc::new_mmio_at(XADC_BASE_ADDR) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<XAdc>(), 0x1C);