continue fsbl

This commit is contained in:
2025-07-27 15:39:12 +02:00
parent 76bc8e11e1
commit 54cc78acac
22 changed files with 578 additions and 74 deletions

View File

@@ -8,7 +8,7 @@ members = [
"examples/simple",
"examples/embassy",
"examples/zedboard",
"zynq-mmu",
"zynq-mmu", "fsbl",
]
exclude = ["experiments"]

View File

@@ -137,7 +137,7 @@ async fn main(_spawner: Spawner) -> ! {
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
}
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));

View File

@@ -75,7 +75,7 @@ async fn main(spawner: Spawner) -> ! {
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);

View File

@@ -91,7 +91,7 @@ async fn main(_spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));

View File

@@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));

View File

@@ -79,7 +79,7 @@ pub fn main() -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {boot_mode:?}");
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);

View File

@@ -64,22 +64,22 @@ pub fn main() -> ! {
#[zynq7000_rt::irq]
pub fn irq_handler() {}
#[unsafe(no_mangle)]
pub extern "C" fn _abort_handler() {
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _undefined_handler() {
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _prefetch_handler() {
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}

View File

@@ -250,7 +250,7 @@ async fn main(spawner: Spawner) -> ! {
// Safety: We are not multi-threaded yet.
unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core(uart, LOG_LEVEL, false) };
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
static ETH_RX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_RX_SLOTS]> =

View File

@@ -93,7 +93,7 @@ async fn main(_spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let pin_sel = match I2C_ADDR_SEL {

View File

@@ -96,7 +96,7 @@ async fn main(spawner: Spawner) -> ! {
.unwrap();
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
if DEBUG_SPI_CLK_CONFIG {

View File

@@ -160,7 +160,7 @@ async fn main(_spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));

View File

@@ -254,7 +254,7 @@ async fn main(spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);

View File

@@ -79,7 +79,7 @@ async fn main(_spawner: Spawner) -> ! {
)
};
let boot_mode = BootMode::new();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(200));

24
fsbl/Cargo.toml Normal file
View File

@@ -0,0 +1,24 @@
[package]
name = "fsbl"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Rust First Stage Bootloader for the Zynq7000 SoC"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
embedded-io = "0.6"
embedded-hal = "1"
fugit = "0.3"
log = "0.4"
[profile.release]
codegen-units = 1
debug = true
lto = true

24
fsbl/memory.x Normal file
View File

@@ -0,0 +1,24 @@
MEMORY
{
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
to configuring the page at address 0x400_0000 accordingly */
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
}
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}

77
fsbl/src/main.rs Normal file
View File

@@ -0,0 +1,77 @@
//! Simple FSBL.
#![no_std]
#![no_main]
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},
time::Hertz,
};
use zynq7000_rt as _;
// PS clock input frequency.
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);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
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_ddr_pll(boot_mode, PllConfig::new_from_target_clock(PS_CLK, DDR_CLK).unwrap());
loop {
cortex_ar::asm::nop();
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}

View File

@@ -1,8 +1,7 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 63 MB for now, should be plenty for a bare-metal app.
Leave 1 MB of memory which will be configured as uncached device memory by the MPU. This is
Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M

View File

@@ -1,6 +1,8 @@
//! Clock module.
use arbitrary_int::Number;
pub mod pll;
use zynq7000::slcr::{
ClockControl,
clocks::{
@@ -201,9 +203,9 @@ impl Clocks {
pub fn new_from_regs(ps_clk_freq: Hertz) -> Result<Self, ClockReadError> {
let mut clk_regs = unsafe { ClockControl::new_mmio_fixed() };
let arm_pll_cfg = clk_regs.read_arm_pll();
let io_pll_cfg = clk_regs.read_io_pll();
let ddr_pll_cfg = clk_regs.read_ddr_pll();
let arm_pll_cfg = clk_regs.read_arm_pll_ctrl();
let io_pll_cfg = clk_regs.read_io_pll_ctrl();
let ddr_pll_cfg = clk_regs.read_ddr_pll_ctrl();
if arm_pll_cfg.fdiv().as_u32() == 0
|| io_pll_cfg.fdiv().as_u32() == 0

View File

@@ -0,0 +1,356 @@
use core::sync::atomic::AtomicBool;
use arbitrary_int::{u4, u7, u10};
use crate::{BootMode, time::Hertz};
/// Minimal value based on Zynq-7000 TRM Table 25-6, p.744
pub const PLL_MUL_MIN: u32 = 13;
/// Maximum value based on Zynq-7000 TRM Table 25-6, p.744
pub const PLL_MUL_MAX: u32 = 66;
static ARM_PLL_INIT: AtomicBool = AtomicBool::new(false);
static IO_PLL_INIT: AtomicBool = AtomicBool::new(false);
static DDR_PLL_INIT: AtomicBool = AtomicBool::new(false);
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
#[error("pll muliplier value {0} is out of range ({PLL_MUL_MIN}..={PLL_MUL_MAX})")]
pub struct MulOutOfRangeError(pub u32);
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PllConfigCtorError {
#[error("invalid input")]
InvalidInput,
#[error("pll multiplier out of range: {0}")]
MulOutOfRange(#[from] MulOutOfRangeError),
}
pub struct PllConfig {
fdiv: u7,
charge_pump: u4,
loop_resistor: u4,
lock_count: u10,
}
impl PllConfig {
pub fn new_from_target_clock(
ps_clk: Hertz,
target_clk: Hertz,
) -> Result<Self, PllConfigCtorError> {
if ps_clk.raw() == 0 {
return Err(PllConfigCtorError::InvalidInput);
}
let mul = target_clk / ps_clk;
Self::new(mul).map_err(PllConfigCtorError::MulOutOfRange)
}
/// Create a new PLL configuration based on the multiplier value.
///
/// These configuration values are based on the Zynq-7000 TRM Table 25-6, p.744.
pub fn new(pll_mul: u32) -> Result<Self, MulOutOfRangeError> {
if !(PLL_MUL_MIN..=PLL_MUL_MAX).contains(&pll_mul) {
return Err(MulOutOfRangeError(pll_mul));
}
Ok(match pll_mul {
13 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(6),
u10::new(750),
),
14 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(6),
u10::new(700),
),
15 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(6),
u10::new(650),
),
16 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(10),
u10::new(625),
),
17 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(10),
u10::new(575),
),
18 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(10),
u10::new(550),
),
19 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(10),
u10::new(525),
),
20 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(500),
),
21 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(475),
),
22 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(450),
),
23 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(425),
),
24..=25 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(400),
),
26 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(375),
),
27..=28 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(350),
),
29..=30 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(12),
u10::new(325),
),
31..=33 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(2),
u10::new(300),
),
34..=36 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(2),
u10::new(275),
),
37..=40 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(2),
u10::new(250),
),
41..=47 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(3),
u4::new(12),
u10::new(250),
),
48..=66 => Self::new_raw(
u7::new(pll_mul as u8),
u4::new(2),
u4::new(4),
u10::new(250),
),
_ => {
unreachable!()
}
})
}
/// Create a new PLL configuration with raw values.
///
/// It is recommended to use [Self::new] instead, which creates a configuration
/// based on a look-up table provided in the Zynq-7000 TRM.
pub fn new_raw(fdiv: u7, charge_pump: u4, loop_resistor: u4, lock_count: u10) -> Self {
Self {
fdiv,
charge_pump,
loop_resistor,
lock_count,
}
}
}
/// This function configures the ARM PLL based on the provided [PllConfig].
pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the IO PLL based on the provided [PllConfig].
pub fn configure_io_pll(boot_mode: BootMode, pll_config: PllConfig) {
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the DDR PLL based on the provided [PllConfig].
pub fn configure_ddr_pll(boot_mode: BootMode, pll_config: PllConfig) {
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the ARM PLL basejjon the provided [PllConfig].
///
/// # Safety
///
/// This function should only be called once during system initialization, for example in the
/// first-stage bootloader (FSBL).
pub unsafe fn configure_arm_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
unsafe {
crate::slcr::Slcr::with(|slcr| {
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_arm_pll_ctrl();
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_arm_pll_cfg();
configure_pll_unchecked(
boot_mode,
pll_config,
PllType::Arm,
slcr,
pll_ctrl_reg,
pll_cfg_reg,
);
});
}
}
/// This function configures the IO PLL based on the provided [PllConfig].
///
/// # Safety
///
/// This function should only be called once during system initialization, for example in the
/// first-stage bootloader (FSBL).
pub unsafe fn configure_io_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
unsafe {
crate::slcr::Slcr::with(|slcr| {
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_io_pll_ctrl();
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_io_pll_cfg();
configure_pll_unchecked(
boot_mode,
pll_config,
PllType::Io,
slcr,
pll_ctrl_reg,
pll_cfg_reg,
);
});
}
}
/// This function configures the DDR PLL based on the provided [PllConfig].
///
/// # Safety
///
/// This function should only be called once during system initialization, for example in the
/// first-stage bootloader (FSBL).
pub unsafe fn configure_ddr_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
unsafe {
crate::slcr::Slcr::with(|slcr| {
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_ddr_pll_ctrl();
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_ddr_pll_cfg();
configure_pll_unchecked(
boot_mode,
pll_config,
PllType::Ddr,
slcr,
pll_ctrl_reg,
pll_cfg_reg,
);
});
}
}
enum PllType {
Io,
Ddr,
Arm,
}
impl PllType {
pub const fn bit_offset_pll_locked(&self) -> usize {
match self {
PllType::Io => 2,
PllType::Ddr => 1,
PllType::Arm => 0,
}
}
}
unsafe fn configure_pll_unchecked(
boot_mode: BootMode,
cfg: PllConfig,
pll_type: PllType,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
pll_ctrl_reg: *mut zynq7000::slcr::clocks::PllCtrl,
pll_cfg_reg: *mut zynq7000::slcr::clocks::PllCfg,
) {
// Step 1: Program the multiplier and other PLL configuration parameters.
// New values will only be consumed once the PLL is reset.
let mut pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
pll_ctrl.set_fdiv(cfg.fdiv);
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
let mut pll_cfg = unsafe { core::ptr::read_volatile(pll_cfg_reg) };
pll_cfg.set_charge_pump(cfg.charge_pump);
pll_cfg.set_loop_resistor(cfg.loop_resistor);
pll_cfg.set_lock_count(cfg.lock_count);
unsafe { core::ptr::write_volatile(pll_cfg_reg, pll_cfg) };
// Step 2: Force the PLL into bypass mode. If the PLL bypass mode pin is tied high,
// the PLLs need to be enabled. According to the TRM, this is done by setting the
// PLL_BYPASS_QUAL bit to 0, which de-asserts the reset to the Arm PLL.
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
if boot_mode.pll_config() == zynq7000::slcr::BootPllConfig::Bypassed {
pll_ctrl.set_bypass_qual(false);
}
pll_ctrl.set_bypass_force(true);
pll_ctrl.set_pwrdwn(false);
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
// Step 3: Reset the PLL. This also loads the new configuration.
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
pll_ctrl.set_reset(true);
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
pll_ctrl.set_reset(false);
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
while ((slcr.clk_ctrl().read_pll_status().raw_value() >> pll_type.bit_offset_pll_locked())
& 0b1)
!= 1
{
cortex_ar::asm::nop();
}
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
pll_ctrl.set_bypass_force(false);
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
}

View File

@@ -10,7 +10,7 @@
#![no_std]
use slcr::Slcr;
use zynq7000::slcr::LevelShifterReg;
use zynq7000::slcr::{BootModeRegister, BootPllConfig, LevelShifterReg};
pub mod cache;
pub mod clocks;
@@ -40,36 +40,26 @@ pub enum BootDevice {
}
#[derive(Debug, Copy, Clone)]
pub enum BootPllConfig {
Enabled,
Bypassed,
}
#[derive(Debug)]
pub struct BootMode {
boot_mode: Option<BootDevice>,
pll_config: BootPllConfig,
}
impl BootMode {
#[allow(clippy::new_without_default)]
/// Create a new boot mode information structure by reading the boot mode register from the
/// fixed SLCR block.
pub fn new() -> Self {
pub fn new_from_regs() -> Self {
// Safety: Only read a read-only register here.
Self::new_with_raw_reg(
unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() }
.read_boot_mode()
.raw_value(),
)
Self::new_with_reg(unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() }.read_boot_mode())
}
fn new_with_raw_reg(raw_register: u32) -> Self {
let msb_three_bits = (raw_register >> 1) & 0b111;
fn new_with_reg(boot_mode_reg: BootModeRegister) -> Self {
let boot_dev = boot_mode_reg.boot_mode();
let msb_three_bits = (boot_dev.value() >> 1) & 0b111;
let boot_mode = match msb_three_bits {
0b000 => {
if raw_register & 0b1 == 0 {
if boot_dev.value() & 0b1 == 0 {
Some(BootDevice::JtagCascaded)
} else {
Some(BootDevice::JtagIndependent)
@@ -81,21 +71,17 @@ impl BootMode {
0b110 => Some(BootDevice::SdCard),
_ => None,
};
let pll_config = if (raw_register >> 4) & 0b1 == 0 {
BootPllConfig::Enabled
} else {
BootPllConfig::Bypassed
};
Self {
boot_mode,
pll_config,
pll_config: boot_mode_reg.pll_config(),
}
}
pub fn boot_device(&self) -> Option<BootDevice> {
pub const fn boot_device(&self) -> Option<BootDevice> {
self.boot_mode
}
pub const fn pll_enable(&self) -> BootPllConfig {
pub const fn pll_config(&self) -> BootPllConfig {
self.pll_config
}
}

View File

@@ -4,18 +4,12 @@
use super::{CLOCK_CONTROL_OFFSET, SLCR_BASE_ADDR};
use arbitrary_int::{u4, u6, u7, u10};
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum BypassForce {
EnabledOrSetByBootMode = 0b0,
Bypassed = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum BypassQual {
BypassForceBit = 0b0,
BootModeFourthBit = 0b1,
pub enum Bypass {
NotBypassed = 0b00,
/// This is the default reset value.
PinStrapSettings = 0b01,
Bypassed = 0b10,
BypassedRegardlessOfPinStrapping = 0b11,
}
#[bitbybit::bitfield(u32)]
@@ -29,10 +23,10 @@ pub struct PllCtrl {
fdiv: u7,
/// Select source for the ARM PLL bypass control
#[bit(4, rw)]
bypass_force: BypassForce,
bypass_force: bool,
/// Select source for the ARM PLL bypass control
#[bit(3, rw)]
bypass_qual: BypassQual,
bypass_qual: bool,
// Power-down control
#[bit(1, rw)]
pwrdwn: bool,
@@ -41,6 +35,40 @@ pub struct PllCtrl {
reset: bool,
}
impl PllCtrl {
#[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)]
#[derive(Debug)]
pub struct PllCfg {
@@ -48,26 +76,26 @@ pub struct PllCfg {
lock_count: u10,
/// Charge Pump control
#[bits(8..=11, rw)]
pll_cp: u4,
charge_pump: u4,
/// Loop resistor control
#[bits(4..=7, rw)]
pll_res: u4,
loop_resistor: u4,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct PllStatus {
#[bit(5)]
#[bit(5, r)]
io_pll_stable: bool,
#[bit(4)]
#[bit(4, r)]
ddr_pll_stable: bool,
#[bit(3)]
#[bit(3, r)]
arm_pll_stable: bool,
#[bit(2)]
#[bit(2, r)]
io_pll_lock: bool,
#[bit(1)]
#[bit(1, r)]
drr_pll_lock: bool,
#[bit(0)]
#[bit(0, r)]
arm_pll_lock: bool,
}
@@ -341,9 +369,9 @@ pub struct AperClkCtrl {
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct ClockControl {
arm_pll: PllCtrl,
ddr_pll: PllCtrl,
io_pll: PllCtrl,
arm_pll_ctrl: PllCtrl,
ddr_pll_ctrl: PllCtrl,
io_pll_ctrl: PllCtrl,
pll_status: PllStatus,
arm_pll_cfg: PllCfg,
ddr_pll_cfg: PllCfg,

View File

@@ -93,11 +93,19 @@ impl GpiobRegisters {
}
}
#[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_bypass: bool,
pll_config: BootPllConfig,
#[bits(0..=3, r)]
boot_mode: u4,
}