continue fsbl
Some checks failed
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
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
Some checks failed
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
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
This commit is contained in:
@@ -1,41 +1,201 @@
|
||||
use arbitrary_int::u6;
|
||||
use zynq7000::slcr::clocks::DciClkCtrl;
|
||||
use arbitrary_int::{Number, u2, u3, u6};
|
||||
use zynq7000::slcr::{
|
||||
clocks::DciClkCtrl,
|
||||
ddriob::{DciType, DdriobConfig, InputType, OutputEnable},
|
||||
};
|
||||
|
||||
use crate::{clocks::DdrClocks, time::Hertz};
|
||||
|
||||
const DCI_MAX_FREQ: Hertz = Hertz::from_raw(10_000_000);
|
||||
|
||||
pub fn calculate_dci_divisors(ddr_clk: Hertz) -> (u6, u6) {
|
||||
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
|
||||
// These values were extracted from the ps7_init files and are not documented in the TMR.
|
||||
// zynq-rs uses the same values.. I assume they are constant.
|
||||
|
||||
pub const DRIVE_SLEW_ADDR_CFG: u32 = 0x0018_c61c;
|
||||
pub const DRIVE_SLEW_DATA_CFG: u32 = 0x00f9_861c;
|
||||
pub const DRIVE_SLEW_DIFF_CFG: u32 = 0x00f9_861c;
|
||||
pub const DRIVE_SLEW_CLOCK_CFG: u32 = 0x00f9_861c;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DciClkConfig {
|
||||
div0: u6,
|
||||
div1: u6,
|
||||
}
|
||||
|
||||
pub fn calculate_dci_divisors(ddr_clks: &DdrClocks) -> DciClkConfig {
|
||||
calculate_dci_divisors_with_ddr_clk(ddr_clks.ref_clk())
|
||||
}
|
||||
|
||||
pub fn calculate_dci_divisors_with_ddr_clk(ddr_clk: Hertz) -> DciClkConfig {
|
||||
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
|
||||
let mut config = DciClkConfig {
|
||||
div0: u6::new(u6::MAX.value() as u8),
|
||||
div1: u6::new(u6::MAX.value() as u8),
|
||||
};
|
||||
|
||||
let mut best = None;
|
||||
let mut best_error = 0;
|
||||
for divisor0 in 1..63 {
|
||||
for divisor1 in 1..63 {
|
||||
let current_div = (divisor0 as u32) * (divisor1 as u32);
|
||||
let error = current_div.abs_diff(target_div);
|
||||
if best.is_none() || best_error > error {
|
||||
best = Some((divisor0, divisor1));
|
||||
if error < best_error {
|
||||
config.div0 = u6::new(divisor0 as u8);
|
||||
config.div1 = u6::new(divisor1 as u8);
|
||||
best_error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
best.map(|(div0, div1)| (u6::new(div0), u6::new(div1)))
|
||||
.unwrap()
|
||||
config
|
||||
}
|
||||
|
||||
pub fn configure_dci(ddr_clk: &DdrClocks) {
|
||||
let (divisor0, divisor1) = calculate_dci_divisors(ddr_clk.ref_clk());
|
||||
let cfg = calculate_dci_divisors(ddr_clk);
|
||||
// Safety: Only done once here during start-up.
|
||||
unsafe {
|
||||
crate::Slcr::with(|slcr| {
|
||||
slcr.clk_ctrl().write_dci_clk_ctrl(
|
||||
DciClkCtrl::builder()
|
||||
.with_divisor_1(divisor1)
|
||||
.with_divisor_0(divisor0)
|
||||
.with_divisor_1(cfg.div1)
|
||||
.with_divisor_0(cfg.div0)
|
||||
.with_clk_act(true)
|
||||
.build(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrates the IOB impedance for DDR3 memory according to to TRM p.325, DDR IOB Impedance
|
||||
/// calibration.
|
||||
///
|
||||
/// This function will also enable the DCI clock with the provided clock configuration.
|
||||
/// You can use [calculate_dci_divisors] to calculate the divisor values for the given DDR clock,
|
||||
/// or you can hardcode the values if they are fixed.
|
||||
pub fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_for_done: bool) {
|
||||
calibrate_iob_impedance(
|
||||
dci_clk_cfg,
|
||||
u3::new(0),
|
||||
u2::new(0),
|
||||
u3::new(0b001),
|
||||
u3::new(0),
|
||||
u2::new(0),
|
||||
poll_for_done,
|
||||
);
|
||||
}
|
||||
|
||||
/// Calibrates the IOB impedance according to to TRM p.325, DDR IOB Impedance calibration.
|
||||
///
|
||||
/// This function will also enable the DCI clock with the provided clock configuration.
|
||||
/// You can use [calculate_dci_divisors] to calculate the divisor values for the given DDR clock,
|
||||
/// or you can hardcode the values if they are fixed.
|
||||
pub fn calibrate_iob_impedance(
|
||||
dci_clk_cfg: DciClkConfig,
|
||||
pref_opt2: u3,
|
||||
pref_opt1: u2,
|
||||
nref_opt4: u3,
|
||||
nref_opt2: u3,
|
||||
nref_opt1: u2,
|
||||
poll_for_done: bool,
|
||||
) {
|
||||
let mut slcr = unsafe { crate::slcr::Slcr::steal() };
|
||||
slcr.modify(|slcr| {
|
||||
slcr.clk_ctrl().write_dci_clk_ctrl(
|
||||
DciClkCtrl::builder()
|
||||
.with_divisor_1(dci_clk_cfg.div1)
|
||||
.with_divisor_0(dci_clk_cfg.div0)
|
||||
.with_clk_act(true)
|
||||
.build(),
|
||||
);
|
||||
let mut ddriob = slcr.ddriob();
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(true);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(false);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(true);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_pref_opt2(pref_opt2);
|
||||
val.set_pref_opt1(pref_opt1);
|
||||
val.set_nref_opt4(nref_opt4);
|
||||
val.set_nref_opt2(nref_opt2);
|
||||
val.set_nref_opt1(nref_opt1);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_update_control(false);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_enable(true);
|
||||
val
|
||||
});
|
||||
if poll_for_done {
|
||||
while !slcr.ddriob().read_dci_status().done() {
|
||||
// Wait for the DDR IOB impedance calibration to complete.
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub struct DdriobConfigSet {
|
||||
pub addr0: DdriobConfig,
|
||||
pub addr1: DdriobConfig,
|
||||
pub data0: DdriobConfig,
|
||||
pub data1: DdriobConfig,
|
||||
pub diff0: DdriobConfig,
|
||||
pub diff1: DdriobConfig,
|
||||
pub clock: DdriobConfig,
|
||||
}
|
||||
|
||||
/// TODO: We could pull out the values from the ps7 init file, convert them into rust constants,
|
||||
/// and take a configuration set passed here.
|
||||
pub fn configure_iob_for_ddr3() {
|
||||
let mut slcr = unsafe { crate::slcr::Slcr::steal() };
|
||||
slcr.modify(|slcr| {
|
||||
let mut ddriob = slcr.ddriob();
|
||||
let mut addr_and_clk_cfg = DdriobConfig::new_with_raw_value(0);
|
||||
addr_and_clk_cfg.set_output_enable(OutputEnable::OBuf);
|
||||
ddriob.write_ddriob_addr0(addr_and_clk_cfg);
|
||||
ddriob.write_ddriob_addr1(addr_and_clk_cfg);
|
||||
|
||||
let data_config = DdriobConfig::builder()
|
||||
.with_pullup_enable(false)
|
||||
.with_output_enable(OutputEnable::OBuf)
|
||||
.with_term_disable_mode(false)
|
||||
.with_ibuf_disable_mode(false)
|
||||
.with_dci_type(DciType::DciTermination)
|
||||
.with_termination_enable(true)
|
||||
.with_dci_update_enable(false)
|
||||
.with_inp_type(InputType::VRefBasedDifferentialReceiverForSstlHstl)
|
||||
.build();
|
||||
ddriob.write_ddriob_data0(data_config);
|
||||
ddriob.write_ddriob_data1(data_config);
|
||||
let diff_config = DdriobConfig::builder()
|
||||
.with_pullup_enable(false)
|
||||
.with_output_enable(OutputEnable::OBuf)
|
||||
.with_term_disable_mode(false)
|
||||
.with_ibuf_disable_mode(false)
|
||||
.with_dci_type(DciType::DciTermination)
|
||||
.with_termination_enable(true)
|
||||
.with_dci_update_enable(false)
|
||||
.with_inp_type(InputType::DifferentialInputReceiver)
|
||||
.build();
|
||||
ddriob.write_ddriob_diff0(diff_config);
|
||||
ddriob.write_ddriob_diff1(diff_config);
|
||||
|
||||
ddriob.write_ddriob_clock(addr_and_clk_cfg);
|
||||
|
||||
// These values were extracted from the ps7_init files and are not documented in the TMR.
|
||||
// zynq-rs uses the same values.. I assume they are constant.
|
||||
ddriob.write_ddriob_drive_slew_addr(DRIVE_SLEW_ADDR_CFG);
|
||||
ddriob.write_ddriob_drive_slew_data(DRIVE_SLEW_DATA_CFG);
|
||||
ddriob.write_ddriob_drive_slew_diff(DRIVE_SLEW_DIFF_CFG);
|
||||
ddriob.write_ddriob_drive_slew_clock(DRIVE_SLEW_CLOCK_CFG);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ impl Slcr {
|
||||
/// Returns a mutable reference to the SLCR MMIO block.
|
||||
///
|
||||
/// The MMIO block will not be unlocked. However, the registers can still be read.
|
||||
pub fn regs(&mut self) -> &mut MmioSlcr<'static> {
|
||||
&mut self.0
|
||||
pub fn regs(&self) -> &MmioSlcr<'static> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Modify the SLCR register.
|
||||
|
||||
@@ -1141,8 +1141,8 @@ pub fn reset(id: SpiId) {
|
||||
/// [configure_spi_ref_clk] can be used to configure the SPI reference clock with the calculated
|
||||
/// value.
|
||||
pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
|
||||
let mut slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl().read_spi_clk_ctrl();
|
||||
let slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
|
||||
let div = match spi_clk_ctrl.srcsel() {
|
||||
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||
clks.io_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
|
||||
@@ -1163,7 +1163,7 @@ pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6
|
||||
|
||||
pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
|
||||
let mut slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl().read_spi_clk_ctrl();
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
|
||||
slcr.modify(|regs| {
|
||||
regs.clk_ctrl().modify_spi_clk_ctrl(|mut val| {
|
||||
val.set_divisor(divisor);
|
||||
|
||||
Reference in New Issue
Block a user