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:
@@ -9,7 +9,7 @@ use log::error;
|
|||||||
use zynq7000_hal::{
|
use zynq7000_hal::{
|
||||||
BootMode,
|
BootMode,
|
||||||
clocks::pll::{PllConfig, configure_arm_pll, configure_ddr_pll, configure_io_pll},
|
clocks::pll::{PllConfig, configure_arm_pll, configure_ddr_pll, configure_io_pll},
|
||||||
ddr::configure_dci,
|
ddr::{calculate_dci_divisors, calibrate_iob_impedance_for_ddr3},
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
};
|
};
|
||||||
use zynq7000_rt as _;
|
use zynq7000_rt as _;
|
||||||
@@ -58,10 +58,11 @@ pub fn main() -> ! {
|
|||||||
PllConfig::new_from_target_clock(PS_CLK, DDR_CLK).unwrap(),
|
PllConfig::new_from_target_clock(PS_CLK, DDR_CLK).unwrap(),
|
||||||
);
|
);
|
||||||
// Safety: Only done once here during start-up.
|
// Safety: Only done once here during start-up.
|
||||||
let ddr_clk = unsafe {
|
let ddr_clks = unsafe {
|
||||||
zynq7000_hal::clocks::DdrClocks::new_with_2x_3x_init(DDR_CLK, u6::new(2), u6::new(3))
|
zynq7000_hal::clocks::DdrClocks::new_with_2x_3x_init(DDR_CLK, u6::new(2), u6::new(3))
|
||||||
};
|
};
|
||||||
configure_dci(&ddr_clk);
|
let dci_clk_cfg = calculate_dci_divisors(&ddr_clks);
|
||||||
|
calibrate_iob_impedance_for_ddr3(dci_clk_cfg, false);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
cortex_ar::asm::nop();
|
cortex_ar::asm::nop();
|
||||||
|
@@ -1,41 +1,201 @@
|
|||||||
use arbitrary_int::u6;
|
use arbitrary_int::{Number, u2, u3, u6};
|
||||||
use zynq7000::slcr::clocks::DciClkCtrl;
|
use zynq7000::slcr::{
|
||||||
|
clocks::DciClkCtrl,
|
||||||
|
ddriob::{DciType, DdriobConfig, InputType, OutputEnable},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{clocks::DdrClocks, time::Hertz};
|
use crate::{clocks::DdrClocks, time::Hertz};
|
||||||
|
|
||||||
const DCI_MAX_FREQ: Hertz = Hertz::from_raw(10_000_000);
|
const DCI_MAX_FREQ: Hertz = Hertz::from_raw(10_000_000);
|
||||||
|
|
||||||
pub fn calculate_dci_divisors(ddr_clk: Hertz) -> (u6, u6) {
|
// These values were extracted from the ps7_init files and are not documented in the TMR.
|
||||||
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
|
// 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;
|
let mut best_error = 0;
|
||||||
for divisor0 in 1..63 {
|
for divisor0 in 1..63 {
|
||||||
for divisor1 in 1..63 {
|
for divisor1 in 1..63 {
|
||||||
let current_div = (divisor0 as u32) * (divisor1 as u32);
|
let current_div = (divisor0 as u32) * (divisor1 as u32);
|
||||||
let error = current_div.abs_diff(target_div);
|
let error = current_div.abs_diff(target_div);
|
||||||
if best.is_none() || best_error > error {
|
if error < best_error {
|
||||||
best = Some((divisor0, divisor1));
|
config.div0 = u6::new(divisor0 as u8);
|
||||||
|
config.div1 = u6::new(divisor1 as u8);
|
||||||
best_error = error;
|
best_error = error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
best.map(|(div0, div1)| (u6::new(div0), u6::new(div1)))
|
config
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_dci(ddr_clk: &DdrClocks) {
|
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.
|
// Safety: Only done once here during start-up.
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::Slcr::with(|slcr| {
|
crate::Slcr::with(|slcr| {
|
||||||
slcr.clk_ctrl().write_dci_clk_ctrl(
|
slcr.clk_ctrl().write_dci_clk_ctrl(
|
||||||
DciClkCtrl::builder()
|
DciClkCtrl::builder()
|
||||||
.with_divisor_1(divisor1)
|
.with_divisor_1(cfg.div1)
|
||||||
.with_divisor_0(divisor0)
|
.with_divisor_0(cfg.div0)
|
||||||
.with_clk_act(true)
|
.with_clk_act(true)
|
||||||
.build(),
|
.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.
|
/// Returns a mutable reference to the SLCR MMIO block.
|
||||||
///
|
///
|
||||||
/// The MMIO block will not be unlocked. However, the registers can still be read.
|
/// The MMIO block will not be unlocked. However, the registers can still be read.
|
||||||
pub fn regs(&mut self) -> &mut MmioSlcr<'static> {
|
pub fn regs(&self) -> &MmioSlcr<'static> {
|
||||||
&mut self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify the SLCR register.
|
/// 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
|
/// [configure_spi_ref_clk] can be used to configure the SPI reference clock with the calculated
|
||||||
/// value.
|
/// value.
|
||||||
pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
|
pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
|
||||||
let mut slcr = unsafe { Slcr::steal() };
|
let 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();
|
||||||
let div = match spi_clk_ctrl.srcsel() {
|
let div = match spi_clk_ctrl.srcsel() {
|
||||||
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||||
clks.io_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
|
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) {
|
pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
|
||||||
let mut slcr = unsafe { Slcr::steal() };
|
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| {
|
slcr.modify(|regs| {
|
||||||
regs.clk_ctrl().modify_spi_clk_ctrl(|mut val| {
|
regs.clk_ctrl().modify_spi_clk_ctrl(|mut val| {
|
||||||
val.set_divisor(divisor);
|
val.set_divisor(divisor);
|
||||||
|
139
zynq7000/src/slcr/ddriob.rs
Normal file
139
zynq7000/src/slcr/ddriob.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
use arbitrary_int::{u2, u3, u4};
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u4, exhaustive = false)]
|
||||||
|
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)]
|
||||||
|
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)]
|
||||||
|
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)]
|
||||||
|
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)]
|
||||||
|
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);
|
@@ -12,44 +12,10 @@ const GPIOB_OFFSET: usize = 0xB00;
|
|||||||
const DDRIOB_OFFSET: usize = 0xB40;
|
const DDRIOB_OFFSET: usize = 0xB40;
|
||||||
|
|
||||||
pub mod clocks;
|
pub mod clocks;
|
||||||
|
pub mod ddriob;
|
||||||
pub mod mio;
|
pub mod mio;
|
||||||
pub mod reset;
|
pub mod reset;
|
||||||
|
|
||||||
#[derive(derive_mmio::Mmio)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct DdrIoB {
|
|
||||||
ddriob_addr0: u32,
|
|
||||||
ddriob_addr1: u32,
|
|
||||||
ddriob_data0: u32,
|
|
||||||
ddriob_data1: u32,
|
|
||||||
ddriob_diff0: u32,
|
|
||||||
ddriob_diff1: u32,
|
|
||||||
ddriob_clock: u32,
|
|
||||||
ddriob_drive_slew_addr: u32,
|
|
||||||
ddriob_drive_slew_data: u32,
|
|
||||||
ddriob_drive_slew_diff: u32,
|
|
||||||
ddriob_drive_slew_clock: u32,
|
|
||||||
ddriob_ddr_ctrl: u32,
|
|
||||||
ddriob_dci_ctrl: u32,
|
|
||||||
ddriob_dci_status: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DdrIoB {
|
|
||||||
/// Create a new handle to this peripheral.
|
|
||||||
///
|
|
||||||
/// Writing to this register requires unlocking the SLCR registers first.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// If you create multiple instances of this handle at the same time, you are responsible for
|
|
||||||
/// ensuring that there are no read-modify-write races on any of the registers.
|
|
||||||
pub unsafe fn new_mmio_fixed() -> MmioDdrIoB<'static> {
|
|
||||||
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + DDRIOB_OFFSET) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u3, exhaustive = false)]
|
#[bitbybit::bitenum(u3, exhaustive = false)]
|
||||||
pub enum VrefSel {
|
pub enum VrefSel {
|
||||||
Disabled = 0b000,
|
Disabled = 0b000,
|
||||||
@@ -213,7 +179,7 @@ pub struct Slcr {
|
|||||||
gpiob: GpiobRegisters,
|
gpiob: GpiobRegisters,
|
||||||
|
|
||||||
#[mmio(Inner)]
|
#[mmio(Inner)]
|
||||||
ddriob: DdrIoB,
|
ddriob: ddriob::DdrIoB,
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(core::mem::size_of::<Slcr>(), 0xB78);
|
static_assertions::const_assert_eq!(core::mem::size_of::<Slcr>(), 0xB78);
|
||||||
|
Reference in New Issue
Block a user