Compare commits

..

13 Commits

14 changed files with 1003 additions and 2 deletions

16
Cargo.lock generated
View File

@ -107,6 +107,22 @@ dependencies = [
"volatile-register",
]
[[package]]
name = "libboard_zynq_us"
version = "0.0.0"
dependencies = [
"bit_field",
"embedded-hal",
"libasync",
"libboard_zynq",
"libregister",
"log",
"nb 0.1.3",
"smoltcp",
"void",
"volatile-register",
]
[[package]]
name = "libconfig"
version = "0.1.0"

View File

@ -8,6 +8,7 @@ members = [
"libconfig",
"experiments",
"szl",
"libboard_zynq_us",
]
[profile.release]

View File

@ -5,8 +5,8 @@ use libregister::*;
use super::slcr;
use super::clocks::Clocks;
mod regs;
mod baud_rate_gen;
pub mod regs;
pub mod baud_rate_gen;
pub struct Uart {
regs: &'static mut regs::RegisterBlock,

View File

@ -0,0 +1,28 @@
[package]
name = "libboard_zynq_us"
description = "Drivers for peripherals in the Zynq UltraScale+ PS"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zcu111 = []
ipv6 = [ "smoltcp/proto-ipv6" ]
default = [ "target_zcu111" ]
[dependencies]
volatile-register = "0.2"
bit_field = "0.10"
embedded-hal = "0.2"
nb = "0.1"
void = { version = "1", default-features = false }
log = "0.4"
libregister = { path = "../libregister" }
# libcortex_a9 = { path = "../libcortex_a9" }
libasync = { path = "../libasync" }
libboard_zynq = { path = "../libboard_zynq" }
[dependencies.smoltcp]
version = "0.7"
features = ["ethernet", "proto-ipv4", "socket-tcp"]
default-features = false

View File

@ -0,0 +1,89 @@
use libregister::{RegisterR, RegisterRW};
use super::slcr::{crf_apb, crl_apb, common::SlcrRegisterBlock};
pub use super::slcr::crf_apb::ApuClkSource;
pub mod source;
use source::*;
#[derive(Debug, Clone)]
pub struct Clocks {
/// ARM PLL: Recommended clock source for the APUs and the FPD interconnect
pub apu: u32,
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
pub ddr: u32,
/// Video PLL: Recommended clock for display port
pub video: u32,
/// I/O PLL: Recommended clock for I/O peripherals
pub io: u32,
/// RPU PLL: Recommended clock for RPUs and LPD interconnect
pub rpu: u32,
}
impl Clocks {
pub fn get() -> Self {
let fpd_regs = crf_apb::RegisterBlock::slcr();
let lpd_regs = crl_apb::RegisterBlock::slcr();
Clocks {
apu: ApuPll::freq(&mut fpd_regs.apu_pll_ctrl),
ddr: DdrPll::freq(&mut fpd_regs.ddr_pll_ctrl),
video: VideoPll::freq(&mut fpd_regs.video_pll_ctrl),
io: IoPll::freq(&mut lpd_regs.io_pll_ctrl),
rpu: RpuPll::freq(&mut lpd_regs.rpu_pll_ctrl),
}
}
pub fn set_cpu_freq(target_freq: u32) {
let fpd_regs = crf_apb::RegisterBlock::slcr();
let apu_pll = ApuPll::freq(&mut fpd_regs.apu_pll_ctrl);
let mut div = 1u8;
while div < 63 && apu_pll / u32::from(div) > target_freq {
div += 1;
}
crf_apb::RegisterBlock::unlocked(|slcr| {
slcr.apu_clk_ctrl.modify(|_, w| w
.srcsel(ApuClkSource::ApuPll)
.divisor0(div)
);
})
}
pub fn uart0_ref_clk(&self) -> u32 {
let lpd_regs = crl_apb::RegisterBlock::slcr();
self.uart_ref_clk(&mut lpd_regs.uart0_clk_ctrl)
}
pub fn uart1_ref_clk(&self) -> u32 {
let lpd_regs = crl_apb::RegisterBlock::slcr();
self.uart_ref_clk(&mut lpd_regs.uart1_clk_ctrl)
}
fn uart_ref_clk(&self, uart_regs: &mut crl_apb::UartClkCtrl) -> u32 {
let uart_clk_ctrl = uart_regs.read();
let pll = match uart_clk_ctrl.srcsel() {
crl_apb::IoClkSource::IoPll => self.io,
crl_apb::IoClkSource::RpuPll => self.rpu,
crl_apb::IoClkSource::DdrPllToLpd => {
let fpd_regs = crf_apb::RegisterBlock::slcr();
let divisor = u32::from(fpd_regs.ddr_pll_to_lpd_ctrl.read().divisor0());
self.ddr / divisor
}
};
pll / (u32::from(uart_clk_ctrl.divisor0()) * u32::from(uart_clk_ctrl.divisor1()))
}
// pub fn sdio_ref_clk(&self) -> u32 {
// let regs = slcr::RegisterBlock::slcr();
// let sdio_clk_ctrl = regs.sdio_clk_ctrl.read();
// let pll = match sdio_clk_ctrl.srcsel() {
// slcr::PllSource::ArmPll =>
// self.arm,
// slcr::PllSource::DdrPll =>
// self.ddr,
// slcr::PllSource::IoPll =>
// self.io,
// };
// pll / u32::from(sdio_clk_ctrl.divisor())
// }
}

View File

@ -0,0 +1,255 @@
use crate::slcr::common::{PllCfg, PllCtrl, PllFracCfg, SlcrRegisterBlock};
use crate::slcr::{crf_apb, crl_apb};
use libregister::{RegisterR, RegisterRW};
use log::debug;
pub const PS_CLK: u32 = 33_333_000;
// DS926 Table: PS PLL Switching Characteristics (same for both speed grades)
// const PS_PLL_MAX_LOCK_TIME: f32 = 100e-6; // 100 us
const PS_PLL_MAX_OUT_FREQ: u32 = 1_600_000_000;
const PS_PLL_MIN_OUT_FREQ: u32 = 750_000_000;
// const PS_PLL_MAX_VCO_FREQ: u32 = 3_000_000_000;
const PS_PLL_MIN_VCO_FREQ: u32 = 1_500_000_000;
/// UG1085 table 37-1
/// (pll_fdiv_max, (pll_cp, pll_res, lfhf, lock_dly, lock_cnt))
const PLL_FDIV_LOCK_PARAM: &[(u8, (u8, u8, u8, u8, u16))] = &[
(25, (3, 10, 3, 63, 1000)),
(26, (3, 10, 3, 63, 1000)),
(27, (4, 6, 3, 63, 1000)),
(28, (4, 6, 3, 63, 1000)),
(29, (4, 6, 3, 63, 1000)),
(30, (4, 6, 3, 63, 1000)),
(31, (6, 1, 3, 63, 1000)),
(32, (6, 1, 3, 63, 1000)),
(33, (4, 10, 3, 63, 1000)),
(34, (5, 6, 3, 63, 1000)),
(35, (5, 6, 3, 63, 1000)),
(36, (5, 6, 3, 63, 1000)),
(37, (5, 6, 3, 63, 1000)),
(38, (5, 6, 3, 63, 975)),
(39, (3, 12, 3, 63, 950)),
(40, (3, 12, 3, 63, 925)),
(41, (3, 12, 3, 63, 900)),
(42, (3, 12, 3, 63, 875)),
(43, (3, 12, 3, 63, 850)),
(44, (3, 12, 3, 63, 850)),
(45, (3, 12, 3, 63, 825)),
(46, (3, 12, 3, 63, 800)),
(47, (3, 12, 3, 63, 775)),
(48, (3, 12, 3, 63, 775)),
(49, (3, 12, 3, 63, 750)),
(50, (3, 12, 3, 63, 750)),
(51, (3, 2, 3, 63, 725)),
(52, (3, 2, 3, 63, 700)),
(53, (3, 2, 3, 63, 700)),
(54, (3, 2, 3, 63, 675)),
(55, (3, 2, 3, 63, 675)),
(56, (3, 2, 3, 63, 650)),
(57, (3, 2, 3, 63, 650)),
(58, (3, 2, 3, 63, 625)),
(59, (3, 2, 3, 63, 625)),
(60, (3, 2, 3, 63, 625)),
(82, (3, 2, 3, 63, 600)), // 61-82
(102, (4, 2, 3, 63, 600)), // 83-102
(103, (5, 2, 3, 63, 600)),
(104, (5, 2, 3, 63, 600)),
(105, (5, 2, 3, 63, 600)),
(106, (5, 2, 3, 63, 600)),
(125, (3, 4, 3, 63, 600)), // 107-125
];
pub trait ClockSource<T: SlcrRegisterBlock> {
/// picks this ClockSource's registers from the SLCR block
fn pll_ctrl_regs(slcr: &mut T) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg);
/// query PLL lock status
fn pll_locked() -> bool;
// todo: is there any situation in which we'll actually want to use fractional mode?
/// query fraction mode enable bit
fn frac_enabled(pll_frac_cfg: &mut PllFracCfg) -> bool {
bool::from(pll_frac_cfg.read().enabled())
}
/// get configured frequency
fn freq(pll_ctrl: &mut PllCtrl) -> u32 {
// todo: take into account fractional part (if enabled)
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
}
fn name() -> &'static str;
// UG1085 Chapter 37: PS Clock Subsystem
fn setup(target_freq: u32) {
assert!(target_freq >= PS_PLL_MIN_OUT_FREQ && target_freq <= PS_PLL_MAX_OUT_FREQ);
let div2 = target_freq < PS_PLL_MIN_VCO_FREQ;
let divisor = u32::from(div2) + 1;
let fdiv = (target_freq * divisor / PS_CLK).min(125) as u8;
let (pll_cp, pll_res, lfhf, lock_dly, lock_cnt) = PLL_FDIV_LOCK_PARAM
.iter()
.filter(|(fdiv_max, _)| fdiv <= *fdiv_max)
.nth(0)
.expect("PLL_FDIV_LOCK_PARAM")
.1
.clone();
debug!("Set {} to {} Hz", Self::name(), target_freq);
T::unlocked(|slcr| {
let (pll_ctrl, pll_cfg, _) = Self::pll_ctrl_regs(slcr);
// Write fdiv, div2
pll_ctrl.modify(|_, w| w.pll_fdiv(fdiv).pll_div2(div2));
// Configure
// no need to zero as we're writing every field
pll_cfg.modify(|_, w| {
w.lock_dly(lock_dly)
.lock_cnt(lock_cnt)
.lfhf(lfhf)
.pll_cp(pll_cp)
.pll_res(pll_res)
});
// Bypass
pll_ctrl.modify(|_, w| w.pll_bypass_force(true));
// Reset
pll_ctrl.modify(|_, w| w.pll_reset(true));
pll_ctrl.modify(|_, w| w.pll_reset(false));
// Wait for PLL lock
// todo: add timeout here according to the 100 us spec?
while !Self::pll_locked() {}
// Remove bypass
pll_ctrl.modify(|_, w| w.pll_bypass_force(false));
});
}
}
/// APU PLL: Recommended clock source for the APUs and the FPD interconnect
pub struct ApuPll;
impl ClockSource<crf_apb::RegisterBlock> for ApuPll {
#[inline]
fn pll_ctrl_regs(
slcr: &mut crf_apb::RegisterBlock,
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
(
&mut slcr.apu_pll_ctrl,
&mut slcr.apu_pll_cfg,
&mut slcr.apu_pll_frac_cfg,
)
}
#[inline]
fn pll_locked() -> bool {
let slcr = crf_apb::RegisterBlock::slcr();
slcr.pll_status.read().apu_pll_lock()
}
fn name() -> &'static str {
&"APU_PLL"
}
}
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
pub struct DdrPll;
impl ClockSource<crf_apb::RegisterBlock> for DdrPll {
#[inline]
fn pll_ctrl_regs(
slcr: &mut crf_apb::RegisterBlock,
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
(
&mut slcr.ddr_pll_ctrl,
&mut slcr.ddr_pll_cfg,
&mut slcr.ddr_pll_frac_cfg,
)
}
#[inline]
fn pll_locked() -> bool {
let slcr = crf_apb::RegisterBlock::slcr();
slcr.pll_status.read().ddr_pll_lock()
}
fn name() -> &'static str {
&"DDR_PLL"
}
}
/// Video PLL: Recommended clock for DisplayPort
pub struct VideoPll;
impl ClockSource<crf_apb::RegisterBlock> for VideoPll {
#[inline]
fn pll_ctrl_regs(
slcr: &mut crf_apb::RegisterBlock,
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
(
&mut slcr.video_pll_ctrl,
&mut slcr.video_pll_cfg,
&mut slcr.video_pll_frac_cfg,
)
}
#[inline]
fn pll_locked() -> bool {
let slcr = crf_apb::RegisterBlock::slcr();
slcr.pll_status.read().video_pll_lock()
}
fn name() -> &'static str {
&"VIDEO_PLL"
}
}
/// I/O PLL: Recommended clock for I/O peripherals
pub struct IoPll;
impl ClockSource<crl_apb::RegisterBlock> for IoPll {
#[inline]
fn pll_ctrl_regs(
slcr: &mut crl_apb::RegisterBlock,
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
(
&mut slcr.io_pll_ctrl,
&mut slcr.io_pll_cfg,
&mut slcr.io_pll_frac_cfg,
)
}
#[inline]
fn pll_locked() -> bool {
let slcr = crl_apb::RegisterBlock::slcr();
slcr.pll_status.read().io_pll_lock()
}
fn name() -> &'static str {
&"IO_PLL"
}
}
/// RPU PLL: Recommended clock for RPUs and LPD interconnect
pub struct RpuPll;
impl ClockSource<crl_apb::RegisterBlock> for RpuPll {
#[inline]
fn pll_ctrl_regs(
slcr: &mut crl_apb::RegisterBlock,
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
(
&mut slcr.io_pll_ctrl,
&mut slcr.io_pll_cfg,
&mut slcr.io_pll_frac_cfg,
)
}
#[inline]
fn pll_locked() -> bool {
let slcr = crl_apb::RegisterBlock::slcr();
slcr.pll_status.read().rpu_pll_lock()
}
fn name() -> &'static str {
&"RPU_PLL"
}
}

View File

@ -0,0 +1,9 @@
#![no_std]
extern crate alloc;
/// Re-export so that dependents can always use the same version
pub use libboard_zynq::smoltcp;
pub mod slcr;
pub mod clocks;

View File

@ -0,0 +1,29 @@
///! Type definitions for re-use across SLCR blocks
use libregister::{register, register_bit, register_bits};
pub trait SlcrRegisterBlock {
fn unlocked<F: FnMut(&mut Self) -> R, R>(f: F) -> R;
}
register!(wprot, WProt, RW, u32);
register_bit!(wprot, active, 0);
register!(pll_ctrl, PllCtrl, RW, u32);
register_bits!(pll_ctrl, pll_post_src, u8, 24, 26);
register_bits!(pll_ctrl, pll_pre_src, u8, 20, 22);
register_bit!(pll_ctrl, pll_div2, 16);
register_bits!(pll_ctrl, pll_fdiv, u8, 8, 14);
register_bit!(pll_ctrl, pll_bypass_force, 3);
register_bit!(pll_ctrl, pll_reset, 0);
register!(pll_cfg, PllCfg, RW, u32);
register_bits!(pll_cfg, lock_dly, u8, 25, 31);
register_bits!(pll_cfg, lock_cnt, u16, 13, 22);
register_bits!(pll_cfg, lfhf, u8, 10, 11);
register_bits!(pll_cfg, pll_cp, u8, 5, 8);
register_bits!(pll_cfg, pll_res, u8, 0, 3);
register!(pll_frac_cfg, PllFracCfg, RW, u32);
register_bit!(pll_frac_cfg, enabled, 31);
register_bits!(pll_frac_cfg, data, u16, 0, 15);

View File

@ -0,0 +1,100 @@
///! FPD clock and reset control
use volatile_register::{RO, RW, WO};
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
RegisterW,
};
use super::common::{SlcrRegisterBlock, WProt, PllCfg, PllCtrl, PllFracCfg};
#[repr(u8)]
pub enum ApuClkSource {
ApuPll = 0b00,
DdrPll = 0b10,
VideoPll = 0b11,
}
#[repr(C)]
pub struct RegisterBlock {
// CRF_APB
pub err_ctrl: RW<u32>,
pub ir_status: RW<u32>, // todo: WTC LSB
pub ir_mask: RO<u32>,
pub ir_enable: WO<u32>,
pub ir_disable: WO<u32>,
pub crf_wprot: WProt,
pub apu_pll_ctrl: PllCtrl,
pub apu_pll_cfg: PllCfg,
pub apu_pll_frac_cfg: PllFracCfg,
pub ddr_pll_ctrl: PllCtrl,
pub ddr_pll_cfg: PllCfg,
pub ddr_pll_frac_cfg: PllFracCfg,
pub video_pll_ctrl: PllCtrl,
pub video_pll_cfg: PllCfg,
pub video_pll_frac_cfg: PllFracCfg,
pub pll_status: PllStatus,
pub apu_pll_to_lpd_ctrl: PllToLpdCtrl,
pub ddr_pll_to_lpd_ctrl: PllToLpdCtrl,
pub video_pll_to_lpd_ctrl: PllToLpdCtrl,
reserved1: [u32; 3],
pub apu_clk_ctrl: ApuClkCtrl,
pub dbg_trace_clk_ctrl: RW<u32>,
pub dbg_fpd_clk_ctrl: RW<u32>,
reserved2: [u32; 1],
pub dp_video_clk_ctrl: RW<u32>,
pub dp_audio_clk_ctrl: RW<u32>,
reserved3: [u32; 1],
pub dp_sys_clk_ctrl: RW<u32>,
pub ddr_clk_ctrl: DdrClkCtrl,
pub gpu_clk_ctrl: RW<u32>,
reserved4: [u32; 6],
pub sata_clk_ctrl: RW<u32>,
reserved5: [u32; 4],
pub pcie_clk_ctrl: RW<u32>,
pub fpd_dma_clk_ctrl: RW<u32>,
pub dp_dma_clk_ctrl: RW<u32>,
pub topsw_main_clk_ctrl: RW<u32>,
pub topsw_lsbus_clk_ctrl: RW<u32>,
reserved6: [u32; 8],
pub dbg_tstmp_clk_ctrl: RW<u32>,
reserved7: [u32; 1],
pub rst_fpd_top: RW<u32>,
pub rst_fpd_apu: RW<u32>,
pub rst_ddr_ss: RW<u32>,
}
register_at!(RegisterBlock, 0xFD1A_0000, slcr);
impl SlcrRegisterBlock for RegisterBlock {
fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
let mut self_ = Self::slcr();
self_.crf_wprot.write(WProt::zeroed().active(false));
let r = f(&mut self_);
self_.crf_wprot.write(WProt::zeroed().active(true));
r
}
}
register!(pll_status, PllStatus, RO, u32);
register_bit!(pll_status, video_pll_stable, 5);
register_bit!(pll_status, ddr_pll_stable, 4);
register_bit!(pll_status, apu_pll_stable, 3);
register_bit!(pll_status, video_pll_lock, 2);
register_bit!(pll_status, ddr_pll_lock, 1);
register_bit!(pll_status, apu_pll_lock, 0);
register!(pll_to_lpd_ctrl, PllToLpdCtrl, RW, u32);
register_bits!(pll_to_lpd_ctrl, divisor0, u8, 8, 13);
register!(apu_clk_ctrl, ApuClkCtrl, RW, u32);
register_bit!(apu_clk_ctrl, clkact_half, 25);
register_bit!(apu_clk_ctrl, clkact_full, 24);
register_bits!(apu_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(apu_clk_ctrl, srcsel, u8, ApuClkSource, 0, 2);
register!(ddr_clk_ctrl, DdrClkCtrl, RW, u32);
register_bits!(ddr_clk_ctrl, divisor0, u8, 8, 13);
// 000: DDR PLL
// 001: Video PLL
register_bits!(ddr_clk_ctrl, srcsel, u8, 0, 2);

View File

@ -0,0 +1,315 @@
///! FPD clock and reset control
use volatile_register::{RO, RW, WO};
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
};
use super::common::{SlcrRegisterBlock, WProt, PllCfg, PllCtrl, PllFracCfg};
/// Clock source selection for IO-type devices
#[repr(u8)]
pub enum IoClkSource {
IoPll = 0b00,
RpuPll = 0b10,
DdrPllToLpd = 0b11,
}
/// Clock source selection for RPU and related (e.g. LPD interconnect) devices
#[repr(u8)]
pub enum RpuClkSource {
RpuPll = 0b00,
IoPll = 0b10,
DdrPllToLpd = 0b11,
}
#[repr(C)]
pub struct RegisterBlock {
pub err_ctrl: RW<u32>,
pub ir_status: RW<u32>, // todo: WTC LSB
pub ir_mask: RO<u32>,
pub ir_enable: WO<u32>,
pub ir_disable: WO<u32>,
reserved1: [u32; 2],
pub crl_wprot: WProt,
pub io_pll_ctrl: PllCtrl,
pub io_pll_cfg: PllCfg,
pub io_pll_frac_cfg: PllFracCfg,
reserved2: [u32; 1],
pub rpu_pll_ctrl: PllCtrl,
pub rpu_pll_cfg: PllCfg,
pub rpu_pll_frac_cfg: PllFracCfg,
reserved3: [u32; 1],
pub pll_status: PllStatus,
pub io_pll_to_fpd_ctrl: PllToFpdCtrl,
pub rpu_pll_to_fpd_ctrl: PllToFpdCtrl,
pub usb3_clk_ctrl: UsbClkCtrl,
pub gem0_clk_ctrl: GemClkCtrl,
pub gem1_clk_ctrl: GemClkCtrl,
pub gem2_clk_ctrl: GemClkCtrl,
pub gem3_clk_ctrl: GemClkCtrl,
pub usb0_bus_clk_ctrl: UsbClkCtrl,
pub usb1_bus_clk_ctrl: UsbClkCtrl,
pub qspi_clk_ctrl: QSpiClkCtrl,
pub sdio0_clk_ctrl: SdioClkCtrl,
pub sdio1_clk_ctrl: SdioClkCtrl,
pub uart0_clk_ctrl: UartClkCtrl,
pub uart1_clk_ctrl: UartClkCtrl,
pub spi0_clk_ctrl: SpiClkCtrl,
pub spi1_clk_ctrl: SpiClkCtrl,
pub can0_clk_ctrl: CanClkCtrl,
pub can1_clk_ctrl: CanClkCtrl,
reserved4: [u32; 1],
pub rpu_clk_ctrl: RpuClkCtrl,
reserved5: [u32; 2],
pub iou_switch_clk_ctrl: IouSwitchClkCtrl,
pub csu_clk_ctrl: CsuPllCtrl,
pub pcap_clk_ctrl: PcapClkCtrl,
pub lpd_switch_clk_ctrl: LpdSwitchClkCtrl,
pub lpd_lsbus_clk_ctrl: LpdLsbusClkCtrl,
pub dbg_lpd_clk_ctrl: DbgLpdClkCtrl,
pub nand_clk_ctrl: NandClkCtrl,
pub lpd_dma_clk_ctrl: LpdDmaClkCtrl,
reserved6: [u32; 1],
pub pl0_clk_ctrl: PlClkCtrl,
pub pl1_clk_ctrl: PlClkCtrl,
pub pl2_clk_ctrl: PlClkCtrl,
pub pl3_clk_ctrl: PlClkCtrl,
pub pl0_thr_ctrl: PlThrCtrl,
pub pl0_thr_cnt: PlThrCnt,
pub pl1_thr_ctrl: PlThrCtrl,
pub pl1_thr_cnt: PlThrCnt,
pub pl2_thr_ctrl: PlThrCtrl,
pub pl2_thr_cnt: PlThrCnt,
pub pl3_thr_ctrl: PlThrCtrl,
reserved7: [u32; 4],
pub pl3_thr_cnt: PlThrCnt,
pub gem_tsu_clk_ctrl: GemTsuClkCtrl,
pub dll_clk_ctrl: DllClkCtrl,
pub ps_sysmon_clk_ctrl: PsSysmonClkCtrl,
reserved8: [u32; 5],
pub i2c0_clk_ctrl: I2cClkCtrl,
pub i2c1_clk_ctrl: I2cClkCtrl,
pub timestamp_clk_ctrl: TimestampClkCtrl,
reserved9: [u32; 1],
pub safety_chk: RW<u32>,
reserved10: [u32; 3],
pub clkmon_status: RW<u32>,
pub clkmon_mask: RO<u32>,
pub clkmon_enable: WO<u32>,
pub clkmon_disable: WO<u32>,
pub clkmon_trigger: WO<u32>,
reserved11: [u32; 3],
pub chkr0_clka_upper: RW<u32>,
pub chkr0_clka_lower: RW<u32>,
pub chkr0_clkb_cnt: RW<u32>,
pub chkr0_ctrl: RW<u32>,
pub chkr1_clka_upper: RW<u32>,
pub chkr1_clka_lower: RW<u32>,
pub chkr1_clkb_cnt: RW<u32>,
pub chkr1_ctrl: RW<u32>,
pub chkr2_clka_upper: RW<u32>,
pub chkr2_clka_lower: RW<u32>,
pub chkr2_clkb_cnt: RW<u32>,
pub chkr2_ctrl: RW<u32>,
pub chkr3_clka_upper: RW<u32>,
pub chkr3_clka_lower: RW<u32>,
pub chkr3_clkb_cnt: RW<u32>,
pub chkr3_ctrl: RW<u32>,
pub chkr4_clka_upper: RW<u32>,
pub chkr4_clka_lower: RW<u32>,
pub chkr4_clkb_cnt: RW<u32>,
pub chkr4_ctrl: RW<u32>,
pub chkr5_clka_upper: RW<u32>,
pub chkr5_clka_lower: RW<u32>,
pub chkr5_clkb_cnt: RW<u32>,
pub chkr5_ctrl: RW<u32>,
pub chkr6_clka_upper: RW<u32>,
pub chkr6_clka_lower: RW<u32>,
pub chkr6_clkb_cnt: RW<u32>,
pub chkr6_ctrl: RW<u32>,
pub chkr7_clka_upper: RW<u32>,
pub chkr7_clka_lower: RW<u32>,
pub chkr7_clkb_cnt: RW<u32>,
pub chkr7_ctrl: RW<u32>,
reserved12: [u32; 8],
pub boot_mode_user: RW<u32>,
pub boot_mode: BootMode,
reserved13: [u32; 4],
pub reset_ctrl: RW<u32>,
pub blockonly_rst: RW<u32>, // todo: WTC LSB
pub reset_reason: RW<u32>, // todo: WTC 0:6
reserved14: [u32; 3],
pub gem_rst_ctrl: GemRstCtrl,
reserved15: [u32; 1],
pub peri_rst_ctrl: PeriRstCtrl,
pub rst_lpd_top: RstLpdTop,
pub rst_lpd_dbg: RW<u32>,
reserved16: [u32; 3],
pub boot_pin_ctrl: RW<u32>, // todo: RO 4:7
reserved17: [u32; 7],
pub bank3_drive0: RW<u32>,
pub bank3_drive1: RW<u32>,
pub bank3_input_ctrl: RW<u32>,
pub bank3_pull_ctrl: RW<u32>,
pub bank3_pull_enable: RW<u32>,
pub bank3_slew_ctrl: RW<u32>,
pub bank3_status: RO<u32>,
}
register_at!(RegisterBlock, 0xFF5E_0000, slcr);
impl SlcrRegisterBlock for RegisterBlock {
// Dummy definition (CRL_APB has no WProt) for consistency with CRF_APB
fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
let mut self_ = Self::slcr();
f(&mut self_)
}
}
register!(pll_status, PllStatus, RO, u32);
register_bit!(pll_status, rpu_pll_stable, 4);
register_bit!(pll_status, io_pll_stable, 3);
register_bit!(pll_status, rpu_pll_lock, 1);
register_bit!(pll_status, io_pll_lock, 0);
register!(pll_to_fpd_ctrl, PllToFpdCtrl, RW, u32);
register_bits!(pll_to_fpd_ctrl, divisor0, u8, 8, 13);
register!(gem_clk_ctrl, GemClkCtrl, RW, u32);
register_bit!(gem_clk_ctrl, rx_clkact, 26);
register_bit!(gem_clk_ctrl, clkact, 25);
register_bits!(gem_clk_ctrl, divisor1, u8, 16, 21);
register_bits!(gem_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(gem_clk_ctrl, srcsel, u8, IoClkSource, 0, 2);
register!(usb_clk_ctrl, UsbClkCtrl, RW, u32);
register_bit!(usb_clk_ctrl, clkact, 25);
register_bits!(usb_clk_ctrl, divisor1, u8, 16, 21);
register_bits!(usb_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(usb_clk_ctrl, srcsel, u8, IoClkSource, 0, 2);
macro_rules! dual_div_clk_reg {
($mod_name: ident, $struct_name: ident, $srcsel_type: ident) => {
register!($mod_name, $struct_name, RW, u32);
register_bit!($mod_name, clkact, 24);
register_bits!($mod_name, divisor1, u8, 16, 21);
register_bits!($mod_name, divisor0, u8, 8, 13);
register_bits_typed!($mod_name, srcsel, u8, $srcsel_type, 0, 2);
};
}
dual_div_clk_reg!(qspi_clk_ctrl, QSpiClkCtrl, IoClkSource);
dual_div_clk_reg!(sdio_clk_ctrl, SdioClkCtrl, IoClkSource);
dual_div_clk_reg!(uart_clk_ctrl, UartClkCtrl, IoClkSource);
dual_div_clk_reg!(spi_clk_ctrl, SpiClkCtrl, IoClkSource);
dual_div_clk_reg!(can_clk_ctrl, CanClkCtrl, IoClkSource);
dual_div_clk_reg!(nand_clk_ctrl, NandClkCtrl, IoClkSource);
dual_div_clk_reg!(pl_clk_ctrl, PlClkCtrl, IoClkSource);
dual_div_clk_reg!(gem_tsu_clk_ctrl, GemTsuClkCtrl, IoClkSource);
dual_div_clk_reg!(ps_sysmon_clk_ctrl, PsSysmonClkCtrl, RpuClkSource);
dual_div_clk_reg!(i2c_clk_ctrl, I2cClkCtrl, IoClkSource);
register!(rpu_clk_ctrl, RpuClkCtrl, RW, u32);
register_bit!(rpu_clk_ctrl, clkact_core, 25);
register_bit!(rpu_clk_ctrl, clkact, 24);
register_bits!(rpu_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(rpu_clk_ctrl, srcsel, u8, RpuClkSource, 0, 2);
macro_rules! single_div_clk_reg {
// default to RpuClkSource
($mod_name: ident, $struct_name: ident, $srcsel_type: ident) => {
register!($mod_name, $struct_name, RW, u32);
register_bit!($mod_name, clkact, 24);
register_bits!($mod_name, divisor1, u8, 16, 21);
register_bits!($mod_name, divisor0, u8, 8, 13);
register_bits_typed!($mod_name, srcsel, u8, $srcsel_type, 0, 2);
};
}
single_div_clk_reg!(iou_switch_clk_ctrl, IouSwitchClkCtrl, RpuClkSource);
single_div_clk_reg!(csu_clk_ctrl, CsuPllCtrl, IoClkSource);
single_div_clk_reg!(pcap_clk_ctrl, PcapClkCtrl, IoClkSource);
single_div_clk_reg!(lpd_switch_clk_ctrl, LpdSwitchClkCtrl, RpuClkSource);
single_div_clk_reg!(lpd_lsbus_clk_ctrl, LpdLsbusClkCtrl, RpuClkSource);
single_div_clk_reg!(dbg_lpd_clk_ctrl, DbgLpdClkCtrl, RpuClkSource);
single_div_clk_reg!(lpd_dma_clk_ctrl, LpdDmaClkCtrl, RpuClkSource);
// todo: timestamp clk can also run directly from PS_REF_CLK (0b1xx)
single_div_clk_reg!(timestamp_clk_ctrl, TimestampClkCtrl, IoClkSource);
register!(pl_thr_ctrl, PlThrCtrl, RW, u32);
register_bits!(pl_thr_ctrl, curr_val, u16, 16, 31, RO);
register_bit!(pl_thr_ctrl, running, 15, RO);
register_bit!(pl_thr_ctrl, cpu_start, 1);
register_bit!(pl_thr_ctrl, cnt_rst, 0);
register!(pl_thr_cnt, PlThrCnt, RW, u32);
register_bits!(pl_thr_cnt, last_cnt, u16, 0, 15);
register!(dll_clk_ctrl, DllClkCtrl, RW, u32);
register_bits!(dll_clk_ctrl, srcsel, u8, 0, 2);
// boot mode pin values read after POR and "triplicated for security"
register!(boot_mode, BootMode, RO, u32);
register_bits!(boot_mode, boot_mode2, u8, 8, 11);
register_bits!(boot_mode, boot_mode1, u8, 4, 7);
register_bits!(boot_mode, boot_mode0, u8, 0, 3);
register!(gem_rst_ctrl, GemRstCtrl, RW, u32);
register_bit!(gem_rst_ctrl, gem3_rst, 3);
register_bit!(gem_rst_ctrl, gem2_rst, 2);
register_bit!(gem_rst_ctrl, gem1_rst, 1);
register_bit!(gem_rst_ctrl, gem0_rst, 0);
register!(peri_rst_ctrl, PeriRstCtrl, RW, u32);
register_bit!(peri_rst_ctrl, timestamp_rst, 20);
register_bit!(peri_rst_ctrl, iou_cc_rst, 19);
register_bit!(peri_rst_ctrl, gpio_rst, 18);
register_bit!(peri_rst_ctrl, lpd_dma_rst, 17);
register_bit!(peri_rst_ctrl, nand_rst, 16);
register_bit!(peri_rst_ctrl, swdt_rst, 15);
register_bit!(peri_rst_ctrl, ttc3_rst, 14);
register_bit!(peri_rst_ctrl, ttc2_rst, 13);
register_bit!(peri_rst_ctrl, ttc1_rst, 12);
register_bit!(peri_rst_ctrl, ttc0_rst, 11);
register_bit!(peri_rst_ctrl, i2c1_rst, 10);
register_bit!(peri_rst_ctrl, i2c0_rst, 9);
register_bit!(peri_rst_ctrl, can1_rst, 8);
register_bit!(peri_rst_ctrl, can0_rst, 7);
register_bit!(peri_rst_ctrl, sdio1_rst, 6);
register_bit!(peri_rst_ctrl, sdio0_rst, 5);
register_bit!(peri_rst_ctrl, spi1_rst, 4);
register_bit!(peri_rst_ctrl, spi0_rst, 3);
register_bit!(peri_rst_ctrl, uart1_rst, 2);
register_bit!(peri_rst_ctrl, uart0_rst, 1);
register_bit!(peri_rst_ctrl, qspi_rst, 0);
register!(rst_lpd_top, RstLpdTop, RW, u32);
register_bit!(rst_lpd_top, fpd_rst, 23);
register_bit!(rst_lpd_top, lpd_swdt_rst, 20);
register_bit!(rst_lpd_top, s_axi_lpd_rst, 19);
register_bit!(rst_lpd_top, sysmon_rst, 17);
register_bit!(rst_lpd_top, rtc_rst, 16);
register_bit!(rst_lpd_top, apm_rst, 15);
register_bit!(rst_lpd_top, ipi_rst, 14);
register_bit!(rst_lpd_top, usb1_apb_rst, 11);
register_bit!(rst_lpd_top, usb0_apb_rst, 10);
register_bit!(rst_lpd_top, usb1_hiber_rst, 9);
register_bit!(rst_lpd_top, usb0_hiber_rst, 8);
register_bit!(rst_lpd_top, usb1_core_rst, 7);
register_bit!(rst_lpd_top, usb0_core_rst, 6);
register_bit!(rst_lpd_top, rpu_pge_rst, 4);
register_bit!(rst_lpd_top, ocm_rst, 3);
register_bit!(rst_lpd_top, rpu_amba_rst, 2);
register_bit!(rst_lpd_top, rpu_core1_rst, 1);
register_bit!(rst_lpd_top, rpu_core0_rst, 0);

View File

@ -0,0 +1,124 @@
///! IOU SLCR for MIO pin configuration
use volatile_register::{RO, RW, WO};
use libregister::{
register, register_at,
register_bit, register_bits,
};
use super::common::SlcrRegisterBlock;
#[repr(C)]
pub struct RegisterBlock {
pub mio_pin: [MioPin; 78],
pub bank0_drive0: BankDriveCtrl,
pub bank0_drive1: BankDriveCtrl,
pub bank0_input_ctrl: BankInputCtrl,
pub bank0_pull_ctrl: BankPullCtrl,
pub bank0_pull_enable: BankPullEnable,
pub bank0_slew_ctrl: BankSlewCtrl,
pub bank0_status: BankStatus,
pub bank1_drive0: BankDriveCtrl,
pub bank1_drive1: BankDriveCtrl,
pub bank1_input_ctrl: BankInputCtrl,
pub bank1_pull_ctrl: BankPullCtrl,
pub bank1_pull_enable: BankPullEnable,
pub bank1_slew_ctrl: BankSlewCtrl,
pub bank1_status: BankStatus,
pub bank2_drive0: BankDriveCtrl,
pub bank2_drive1: BankDriveCtrl,
pub bank2_input_ctrl: BankInputCtrl,
pub bank2_pull_ctrl: BankPullCtrl,
pub bank2_pull_enable: BankPullEnable,
pub bank2_slew_ctrl: BankSlewCtrl,
pub bank2_status: BankStatus,
reserved1: [u32; 5],
pub mio_loopback: RW<u32>,
pub mio_mst_tri0: RW<u32>,
pub mio_mst_tri1: RW<u32>,
pub mio_mst_tri2: RW<u32>,
pub wdt_clk_sel: RW<u32>, // 0 = internal APB clock, 1 = external
pub can_mio_ctrl: RW<u32>,
pub gem_clk_ctrl: RW<u32>,
pub sdio_clk_ctrl: RW<u32>,
pub ctrl_reg_sd: RW<u32>,
pub sd_itap_dly: RW<u32>,
pub sd_otap_dly_sel: RW<u32>,
pub sd_cfg1: RW<u32>,
pub sd_cfg2: RW<u32>,
pub sd_cfg3: RW<u32>,
pub sd_init_preset: RW<u32>,
pub sd_speed_preset: RW<u32>,
pub sd_hspeed_preset: RW<u32>,
pub sd_sdr12_preset: RW<u32>,
pub sd_sdr25_preset: RW<u32>,
pub sd_sdr50_preset: RW<u32>,
reserved2: [u32; 1],
pub sd_sdr104_preset: RW<u32>,
pub sd_ddr50_preset: RW<u32>,
pub sd_max_cur_18: RW<u32>,
pub sd_max_cur_30: RW<u32>,
pub sd_max_cur_33: RW<u32>,
pub sd_dll_ctrl: RW<u32>,
pub sd_cdn_ctrl: RW<u32>,
pub gem_ctrl: RW<u32>,
reserved3: [u32; 7],
pub iou_ttc_apb_clk: RW<u32>,
reserved4: [u32; 3],
pub iou_tapdly_bypass: RW<u32>,
reserved5: [u32; 3],
pub iou_coherent_ctrl: RW<u32>,
pub video_pss_clk_sel: RW<u32>,
pub iou_interconnect_route: RW<u32>,
reserved6: [u32; 125],
pub ctrl: RW<u32>,
reserved7: [u32; 63],
pub isr: RW<u32>, // todo: WTC LSB
pub imr: RO<u32>,
pub ier: WO<u32>,
pub idr: WO<u32>,
pub itr: WO<u32>,
}
register_at!(RegisterBlock, 0xFF18_0000, slcr);
impl SlcrRegisterBlock for RegisterBlock {
// Dummy definition for consistency
fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
let mut self_ = Self::slcr();
f(&mut self_)
}
}
register!(mio_pin, MioPin, RW, u32);
register_bits!(mio_pin, l3_sel, u8, 5, 7);
register_bits!(mio_pin, l2_sel, u8, 3, 4);
register_bit!(mio_pin, l1_sel, 2);
register_bit!(mio_pin, l0_sel, 1);
register!(bank_drive_ctrl, BankDriveCtrl, RW, u32);
register_bits!(bank_drive_ctrl, drive, u32, 0, 25);
// 0 = CMOS, 1 = Schmitt
register!(bank_input_ctrl, BankInputCtrl, RW, u32);
register_bits!(bank_input_ctrl, schmitt, u32, 0, 25);
// 0 = down, 1 = up
register!(bank_pull_ctrl, BankPullCtrl, RW, u32);
register_bits!(bank_pull_ctrl, pull_up, u32, 0, 25);
register!(bank_pull_enable, BankPullEnable, RW, u32);
register_bits!(bank_pull_enable, pull_enable, u32, 0, 25);
// 0 = fast, 1 = slow
register!(bank_slew_ctrl, BankSlewCtrl, RW, u32);
register_bits!(bank_slew_ctrl, slow_slew, u32, 0, 25);
// 0 = 2.5 or 3.3V, 1 = 1.8V
register!(bank_status, BankStatus, RO, u32);
register_bit!(bank_status, voltage_mode, 0);
// todo: impl for MioPin (or RegisterBlock?) to make drive ctrl less obnoxious
// might as well toss in convenience functions for pull up/down, etc.

View File

@ -0,0 +1,15 @@
///! Register definitions for UltraScale+ System Level Control
pub mod common;
pub mod crf_apb;
// APU
// FPD_SLCR
// FPD_SLCR_SECURE
pub mod iou_slcr;
// IOU_SECURE_SLCR
// IOU_SCNTRS
// LPD_SLCR
// LPD_SLCR_SECURE
pub mod crl_apb;
// RPU
// CCI_GPV
// FPD_GPV

View File

@ -0,0 +1,7 @@
use libregister::register_at;
use libboard_zynq::uart::baud_rate_gen;
use libboard_zync::uart::regs::RegisterBlock;
register_at!(RegisterBlock, 0xFF000000, uart0);
register_at!(RegisterBlock, 0xFF010000, uart1); // note: PS_UART1 is not connected on ZCU111

View File

@ -290,6 +290,19 @@ macro_rules! register_bits {
}
}
);
($mod_name: ident, $(#[$outer:meta])* $name: ident, $type: ty, $bit_begin: expr, $bit_end: expr, RO) => (
impl $mod_name::Read {
#[allow(unused)]
#[inline]
$(#[$outer])*
pub fn $name(&self) -> $type {
use bit_field::BitField;
self.inner.get_bits($bit_begin..=$bit_end) as $type
}
}
);
}
/// Define a multi-bit field of a register, coerced to a certain type