continue with 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:
@@ -17,6 +17,7 @@ embedded-io = "0.6"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
arbitrary-int = "1.3"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
@@ -2,15 +2,14 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use arbitrary_int::u6;
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use log::error;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::pll::{
|
||||
PllConfig, PllConfigCtorError, configure_arm_pll, configure_ddr_pll, configure_io_pll,
|
||||
},
|
||||
gpio::{Output, PinState, mio},
|
||||
clocks::pll::{PllConfig, configure_arm_pll, configure_ddr_pll, configure_io_pll},
|
||||
ddr::configure_dci,
|
||||
time::Hertz,
|
||||
};
|
||||
use zynq7000_rt as _;
|
||||
@@ -20,14 +19,15 @@ const PS_CLK: Hertz = Hertz::from_raw(33_333_333);
|
||||
|
||||
/// 1600 MHz.
|
||||
const ARM_CLK: Hertz = Hertz::from_raw(1_600_000_000);
|
||||
/// 1067 MHz.
|
||||
//const DDR_CLK: Hertz = Hertz::from_raw(1_067_000_000);
|
||||
/// 1000 MHz.
|
||||
const IO_CLK: Hertz = Hertz::from_raw(1_000_000_000);
|
||||
|
||||
/// DDR frequency for the MT41K128M16JT-125 device.
|
||||
const DDR_FREQUENCY: Hertz = Hertz::from_raw(533_333_333);
|
||||
|
||||
/// 1067 MHz.
|
||||
const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.raw());
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
@@ -42,10 +42,26 @@ pub fn main() -> ! {
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
// The unwraps are okay here, the provided clock frequencies are standard values also used
|
||||
// by other Xilinx tools.
|
||||
configure_arm_pll(boot_mode, PllConfig::new_from_target_clock(PS_CLK, ARM_CLK).unwrap());
|
||||
configure_io_pll(boot_mode, PllConfig::new_from_target_clock(PS_CLK, IO_CLK).unwrap());
|
||||
configure_arm_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(PS_CLK, ARM_CLK).unwrap(),
|
||||
);
|
||||
configure_io_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(PS_CLK, IO_CLK).unwrap(),
|
||||
);
|
||||
|
||||
configure_ddr_pll(boot_mode, PllConfig::new_from_target_clock(PS_CLK, 2 * DDR_FREQUENCY).unwrap());
|
||||
// Set the DDR PLL output frequency to an even multiple of the operating frequency,
|
||||
// as recommended by the DDR documentation.
|
||||
configure_ddr_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(PS_CLK, DDR_CLK).unwrap(),
|
||||
);
|
||||
// Safety: Only done once here during start-up.
|
||||
let ddr_clk = unsafe {
|
||||
zynq7000_hal::clocks::DdrClocks::new_with_2x_3x_init(DDR_CLK, u6::new(2), u6::new(3))
|
||||
};
|
||||
configure_dci(&ddr_clk);
|
||||
|
||||
loop {
|
||||
cortex_ar::asm::nop();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//! Clock module.
|
||||
use arbitrary_int::Number;
|
||||
use arbitrary_int::{Number, u6};
|
||||
|
||||
pub mod pll;
|
||||
|
||||
@@ -47,21 +47,67 @@ impl ArmClocks {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DdrClocks {
|
||||
/// DDR reference clock generated by the DDR PLL.
|
||||
ref_clk: Hertz,
|
||||
ddr_3x_clk: Hertz,
|
||||
ddr_2x_clk: Hertz,
|
||||
}
|
||||
|
||||
impl DdrClocks {
|
||||
/// Update the DDR 3x and 2x clocks in the SLCR.
|
||||
///
|
||||
/// Usually, the DDR PLL output clock will be set to an even multiple of the DDR operating
|
||||
/// frequency. In that case, the multiplicator should be used as the DDR 3x clock divisor.
|
||||
/// The DDR 2x clock divisor should be set so that the resulting clock is 2/3 of the DDR
|
||||
/// operating frequency.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This should only be called once during start-up. It accesses the SLCR register.
|
||||
pub unsafe fn configure_2x_3x_clk(ddr_3x_div: u6, ddr_2x_div: u6) {
|
||||
// Safety: The DDR clock structure is a singleton.
|
||||
unsafe {
|
||||
crate::slcr::Slcr::with(|slcr| {
|
||||
slcr.clk_ctrl().modify_ddr_clk_ctrl(|mut val| {
|
||||
val.set_div_3x_clk(ddr_3x_div);
|
||||
val.set_div_2x_clk(ddr_2x_div);
|
||||
val
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the DDR 3x and 2x clocks in the SLCR and creates a DDR clock information structure.
|
||||
///
|
||||
/// Usually, the DDR PLL output clock will be set to an even multiple of the DDR operating
|
||||
/// frequency. In that case, the multiplicator should be used as the DDR 3x clock divisor.
|
||||
/// The DDR 2x clock divisor should be set so that the resulting clock is 2/3 of the DDR
|
||||
/// operating frequency.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This should only be called once during start-up. It accesses the SLCR register.
|
||||
pub unsafe fn new_with_2x_3x_init(ref_clk: Hertz, ddr_3x_div: u6, ddr_2x_div: u6) -> Self {
|
||||
unsafe { Self::configure_2x_3x_clk(ddr_3x_div, ddr_2x_div) };
|
||||
Self {
|
||||
ref_clk,
|
||||
ddr_3x_clk: ref_clk / ddr_3x_div.as_u32(),
|
||||
ddr_2x_clk: ref_clk / ddr_2x_div.as_u32(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference clock provided by DDR PLL which is used to calculate all other clock frequencies.
|
||||
pub const fn ref_clk(&self) -> Hertz {
|
||||
self.ref_clk
|
||||
}
|
||||
|
||||
/// DDR 3x clock which is used by the DRAM and must be set to the operating frequency.
|
||||
pub fn ddr_3x_clk(&self) -> Hertz {
|
||||
self.ddr_3x_clk
|
||||
}
|
||||
|
||||
/// DDR 2x clock is used by the interconnect and is typically set to 2/3 of the operating
|
||||
/// frequency.
|
||||
pub fn ddr_2x_clk(&self) -> Hertz {
|
||||
self.ddr_2x_clk
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
use arbitrary_int::u6;
|
||||
use zynq7000::slcr::clocks::DciClkCtrl;
|
||||
|
||||
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());
|
||||
|
||||
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));
|
||||
best_error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
best.map(|(div0, div1)| (u6::new(div0), u6::new(div1)))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn configure_dci(ddr_clk: &DdrClocks) {
|
||||
let (divisor0, divisor1) = calculate_dci_divisors(ddr_clk.ref_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_clk_act(true)
|
||||
.build(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ use zynq7000::slcr::{BootModeRegister, BootPllConfig, LevelShifterReg};
|
||||
|
||||
pub mod cache;
|
||||
pub mod clocks;
|
||||
pub mod ddr;
|
||||
pub mod eth;
|
||||
pub mod gic;
|
||||
pub mod gpio;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use arbitrary_int::{u11, u12, u3, u4, u5, u6, u7};
|
||||
use arbitrary_int::{u2, u3, u4, u5, u6, u7, u11, u12};
|
||||
|
||||
pub const DDRC_BASE_ADDR: usize = 0xF800_6000;
|
||||
|
||||
@@ -39,6 +39,9 @@ pub struct DdrcControl {
|
||||
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,
|
||||
@@ -137,9 +140,38 @@ pub struct DramParamReg3 {
|
||||
#[bits(5..=7, rw)]
|
||||
t_rrd: u3,
|
||||
#[bits(2..=4, rw)]
|
||||
t_ccd: u3
|
||||
t_ccd: u3,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
pub struct DramParamReg4 {
|
||||
#[bit(27, rw)]
|
||||
mr_rdata_valid: bool,
|
||||
#[bit(26, rw)]
|
||||
mr_type: bool,
|
||||
#[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)]
|
||||
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,
|
||||
}
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct DdrController {
|
||||
@@ -149,13 +181,13 @@ pub struct DdrController {
|
||||
lpr_queue_ctrl: LprHprQueueControl,
|
||||
wr_reg: WriteQueueControl,
|
||||
dram_param_reg0: DramParamReg0,
|
||||
dram_param_reg1: u32,
|
||||
dram_param_reg2: u32,
|
||||
dram_param_reg3: u32,
|
||||
dram_param_reg4: u32,
|
||||
dram_init_param: u32,
|
||||
dram_emr_reg: u32,
|
||||
dram_emr_mr_reg: u32,
|
||||
dram_param_reg1: DramParamReg1,
|
||||
dram_param_reg2: DramParamReg2,
|
||||
dram_param_reg3: DramParamReg3,
|
||||
dram_param_reg4: DramParamReg4,
|
||||
dram_init_param: DramInitParam,
|
||||
dram_emr: u32,
|
||||
dram_emr_mr: u32,
|
||||
dram_burst8_rdwr: u32,
|
||||
dram_disable_dq: u32,
|
||||
dram_addr_map_bank: u32,
|
||||
@@ -164,7 +196,8 @@ pub struct DdrController {
|
||||
dram_odt_reg: u32,
|
||||
phy_debug_reg: u32,
|
||||
phy_cmd_timeout_rddata_cpt: u32,
|
||||
mode_status_reg: u32,
|
||||
#[mmio(PureRead)]
|
||||
mode_status: u32,
|
||||
dll_calib: u32,
|
||||
odt_delay_hold: u32,
|
||||
ctrl_reg1: u32,
|
||||
@@ -186,16 +219,28 @@ pub struct DdrController {
|
||||
dfi_timing: u32,
|
||||
_reserved2: [u32; 0x2],
|
||||
che_corr_control: u32,
|
||||
#[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: u32,
|
||||
che_ecc_corr_bit_mask_31_0: u32,
|
||||
@@ -221,44 +266,47 @@ pub struct DdrController {
|
||||
reg_64: u32,
|
||||
reg_65: u32,
|
||||
_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,
|
||||
phy_dll_status_0: u32,
|
||||
phy_dll_status_1: u32,
|
||||
phy_dll_status_2: u32,
|
||||
phy_dll_status_3: 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_0: u32,
|
||||
axi_priority_wr_port_1: u32,
|
||||
axi_priority_wr_port_2: u32,
|
||||
axi_priority_wr_port_3: u32,
|
||||
axi_priority_rd_port_0: u32,
|
||||
axi_priority_rd_port_1: u32,
|
||||
axi_priority_rd_port_2: u32,
|
||||
axi_priority_rd_port_3: u32,
|
||||
axi_priority_wr_port: [u32; 4],
|
||||
axi_priority_rd_port: [u32; 4],
|
||||
|
||||
_reserved15: [u32; 0x1B],
|
||||
|
||||
excl_access_cfg_0: u32,
|
||||
excl_access_cfg_1: u32,
|
||||
excl_access_cfg_2: u32,
|
||||
excl_access_cfg_3: u32,
|
||||
excl_access_cfg: [u32; 4],
|
||||
#[mmio(PureRead)]
|
||||
mode_reg_read: u32,
|
||||
lpddr_ctrl_0: u32,
|
||||
lpddr_ctrl_1: u32,
|
||||
|
@@ -17,6 +17,7 @@ extern crate std;
|
||||
|
||||
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
|
||||
|
||||
pub mod ddrc;
|
||||
pub mod eth;
|
||||
pub mod gic;
|
||||
pub mod gpio;
|
||||
@@ -28,7 +29,6 @@ pub mod slcr;
|
||||
pub mod spi;
|
||||
pub mod ttc;
|
||||
pub mod uart;
|
||||
pub mod ddrc;
|
||||
|
||||
static PERIPHERALS_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
|
@@ -169,7 +169,7 @@ pub struct DdrClkCtrl {
|
||||
ddr_3x_clk_act: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
pub struct DciClkCtrl {
|
||||
/// Second cascade divider. Reset value: 0x1E
|
||||
#[bits(20..=25, rw)]
|
||||
|
Reference in New Issue
Block a user