add bitstream prog function
Some checks failed
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
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

This commit is contained in:
2025-09-05 19:12:47 +02:00
parent 3a8b286675
commit 38e2109e52
7 changed files with 435 additions and 13 deletions

View File

@@ -8,23 +8,21 @@ use cortex_ar::asm::nop;
use embedded_io::Write as _; use embedded_io::Write as _;
use log::{error, info}; use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode}; use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000::{PsPeripherals, qspi::MmioQspi};
use zynq7000_hal::{ use zynq7000_hal::{
BootMode,
clocks::{ clocks::{
pll::{configure_arm_pll, configure_io_pll, PllConfig},
Clocks, Clocks,
pll::{PllConfig, configure_arm_pll, configure_io_pll},
}, },
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest}, ddr::{configure_ddr_for_ddr3, memtest, DdrClockSetupConfig},
gic::GicConfigurator, gic, gpio, l2_cache,
gpio::{GpioPins, mio},
l2_cache,
prelude::*, prelude::*,
qspi::{self, QSPI_START_ADDRESS}, qspi,
time::Hertz, time::Hertz,
uart::{ClockConfigRaw, Uart, UartConfig}, uart::{ClockConfigRaw, Uart, UartConfig},
BootMode,
}; };
use zynq7000_rt as _; use zynq7000_rt as _;
use zynq_boot_image::DestinationDevice;
use crate::ddr_cfg::{DDRC_CONFIG_ZEDBOARD_FULL_BUILDERS, DDRIOB_CONFIG_SET_ZEDBOARD}; use crate::ddr_cfg::{DDRC_CONFIG_ZEDBOARD_FULL_BUILDERS, DDRIOB_CONFIG_SET_ZEDBOARD};
@@ -87,7 +85,7 @@ pub fn main() -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLK).unwrap(); let clocks = Clocks::new_from_regs(PS_CLK).unwrap();
let gpio_pins = GpioPins::new(dp.gpio); let gpio_pins = gpio::GpioPins::new(dp.gpio);
let mio_pins = gpio_pins.mio; let mio_pins = gpio_pins.mio;
// Set up the UART, we are logging with it. // Set up the UART, we are logging with it.
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200) let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
@@ -110,7 +108,7 @@ pub fn main() -> ! {
}; };
// Set up the global interrupt controller. // Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd); let mut gic = gic::GicConfigurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts(); gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0(); gic.set_all_spi_interrupt_targets_cpu0();
gic.enable(); gic.enable();
@@ -218,6 +216,28 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
let mut name_buf: [u8; 256] = [0; 256]; let mut name_buf: [u8; 256] = [0; 256];
for image_header in boot_header.image_header_iterator().unwrap() { for image_header in boot_header.image_header_iterator().unwrap() {
let name = image_header.image_name(&mut name_buf).unwrap(); let name = image_header.image_name(&mut name_buf).unwrap();
for partition in image_header
.partition_header_iterator(boot_header_slice)
.unwrap()
{
let section_attrs = partition.section_attributes();
if let Ok(dest_dev) = section_attrs.destination_device() {
match dest_dev {
DestinationDevice::Pl => {
info!("Loading image '{name}' to PL (FPGA)..");
// TODO: Load the bitstream.
}
DestinationDevice::Ps => {
// TODO: Load the binary into DDR. Jump at lowest load address after all
// partitions were parsed.
}
_ => {
error!("Unsupported destination device {dest_dev:?}");
continue;
}
}
}
}
} }
loop { loop {

View File

@@ -335,7 +335,7 @@ pub struct PartitionHeader<'a> {
} }
#[bitbybit::bitenum(u2, exhaustive = false)] #[bitbybit::bitenum(u2, exhaustive = false)]
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
pub enum PartitionOwner { pub enum PartitionOwner {
Fsbl = 0, Fsbl = 0,
@@ -343,7 +343,7 @@ pub enum PartitionOwner {
} }
#[bitbybit::bitenum(u3, exhaustive = false)] #[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
pub enum ChecksumType { pub enum ChecksumType {
None = 0, None = 0,
@@ -351,7 +351,7 @@ pub enum ChecksumType {
} }
#[bitbybit::bitenum(u4, exhaustive = false)] #[bitbybit::bitenum(u4, exhaustive = false)]
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
pub enum DestinationDevice { pub enum DestinationDevice {
None = 0, None = 0,

View File

@@ -0,0 +1,63 @@
#[derive(Debug, thiserror::Error)]
#[error("unaligned address: {0}")]
pub struct UnalignedAddrError(usize);
/// Configures the bitstream using the PCAP interface in non-secure mode.
///
/// Blocking function which only returns when the bitstream configuration is complete.
pub fn configure_bitstream_non_secure(
init_pl: bool,
bitstream: &[u8],
) -> Result<(), UnalignedAddrError> {
if !(bitstream.as_ptr() as usize).is_multiple_of(64) {
return Err(UnalignedAddrError(bitstream.as_ptr() as usize));
}
if bitstream.is_empty() {
return Ok(());
}
let devcfg = unsafe { zynq7000::devcfg::DevCfg::new_mmio_fixed() };
devcfg.modify_control(|mut val| {
val.set_config_access_select(zynq7000::devcfg::PlConfigAccess::ConfigAccessPort);
val.set_access_port_select(zynq7000::devcfg::ConfigAccessPortSelect::Pcap);
val
});
devcfg.write_interrupt_status(zynq7000::devcfg::Interrupt::new_with_raw_value(0xFFFF_FFFF));
if init_pl {
devcfg.modify_control(|mut val| {
val.set_prog_b_bit(true);
val
});
devcfg.modify_control(|mut val| {
val.set_prog_b_bit(false);
val
});
while devcfg.read_status().pcfg_init() {}
devcfg.modify_control(|mut val| {
val.set_prog_b_bit(true);
val
});
devcfg.write_interrupt_status(
zynq7000::devcfg::Interrupt::ZERO.with_pl_programming_done(true),
);
}
while !devcfg.read_status().pcfg_init() {}
if !init_pl {
while devcfg.read_status().dma_command_queue_full() {}
}
devcfg.modify_misc_control(|mut val| {
val.set_loopback(false);
val
});
devcfg.modify_control(|mut val| {
val.set_pcap_rate_enable(false);
val
});
devcfg.write_dma_source_addr(bitstream.as_ptr() as u32);
devcfg.write_dma_dest_addr(0xFFFF_FFFF);
devcfg.write_dma_source_len(bitstream.len());
devcfg.write_dma_dest_len(bitstream.len());
while !devcfg.read_interrupt_status().dma_done() {}
// TODO: Check for errors.
while !devcfg.read_interrupt_status().pl_programming_done() {}
}

View File

@@ -30,6 +30,7 @@ pub mod l2_cache;
pub mod log; pub mod log;
pub mod prelude; pub mod prelude;
pub mod priv_tim; pub mod priv_tim;
pub mod devcfg;
pub mod qspi; pub mod qspi;
pub mod slcr; pub mod slcr;
pub mod spi; pub mod spi;

304
zynq7000/src/devcfg.rs Normal file
View File

@@ -0,0 +1,304 @@
use arbitrary_int::{u4, u5, u7};
pub const DEVCFG_BASE_ADDR: usize = 0xF8007000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PlConfigAccess {
/// Used for JTAG access
TapController = 0,
/// Used for PCAP or ICAP access.
ConfigAccessPort = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum ConfigAccessPortSelect {
/// Internal Configuration Access Port (ICAP), using PL or PS-based software.
Icap = 0,
/// Processor Configuration Access Port (PCAP), using PS-based software.
Pcap = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum TimerSelect {
_64kTimer = 0,
_4kTimer = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum AesEnable {
Disable = 0b000,
Enable = 0b111,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PsBootMode {
NonSecure = 0,
Secure = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum ArmDapEnable {
Enabled = 0b111,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Control {
#[bit(31, rw)]
force_reset: bool,
/// Program singal used to reset the PL. It acts at the PROG_B signal in the PL.
#[bit(30, rw)]
prog_b_bit: bool,
/// Called PCFG_POR_CNT_4K by Xilinx.
#[bit(29, rw)]
timer_select: TimerSelect,
/// Called XDCFG_CTRL_PCAP_PR_MASK by Xilinx.
#[bit(27, rw)]
access_port_select: ConfigAccessPortSelect,
#[bit(26, rw)]
config_access_select: PlConfigAccess,
#[bit(25, rw)]
pcap_rate_enable: bool,
#[bit(24, rw)]
multiboot_enable: bool,
#[bit(23, rw)]
jtag_chain_disable: bool,
#[bit(12, rw)]
pcfg_aes_fuse: bool,
#[bits(9..=11, rw)]
pcfg_aes_enable: Option<AesEnable>,
#[bit(8, rw)]
seu_enable: bool,
/// Read-only because this is set and locked by BootROM.
#[bit(7, r)]
ps_boot_mode: PsBootMode,
/// SPNIDEN
#[bit(6, rw)]
secure_non_invasive_debug_enable: bool,
/// SPIDEN
#[bit(5, rw)]
secure_invasive_debug_enable: bool,
/// NIDEN
#[bit(4, rw)]
non_invasive_debug_enable: bool,
/// DBGEN
#[bit(3, rw)]
invasive_debug_enable: bool,
#[bits(0..=2, rw)]
dap_enable: Option<ArmDapEnable>,
}
/// The bits in this register and read/write, set-only, which means that only a PS_POR_B reset
/// can clear the bits.
#[bitbybit::bitfield(u32, debug)]
pub struct Lock {
#[bit(4, rw)]
aes_fuse: bool,
#[bit(3, rw)]
aes: bool,
#[bit(2, rw)]
seu: bool,
/// Locks the SEC_EN bit. BootROM will set this bit.
#[bit(1, rw)]
sec: bool,
/// Locks SPNIDEN, SPIDEN, NIDEN, DBGEN and DAP_EN
#[bit(0, rw)]
debug: bool,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum EdgeConfig {
Falling = 0,
Rising = 1,
}
/// Related to the full level for reads, and the empty level for writes.
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum FifoThresholdConfig {
OneFourth = 0b00,
HalfEmpty = 0b01,
ThreeFourth = 0b10,
EmptyOrFull = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Config {
#[bits(10..=11, rw)]
read_fifo_threshhold: FifoThresholdConfig,
#[bits(8..=9, rw)]
write_fifo_threshold: FifoThresholdConfig,
#[bit(7, rw)]
read_data_active_clock_edge: EdgeConfig,
#[bit(6, rw)]
write_data_active_clock_edge: EdgeConfig,
#[bit(5, rw)]
disable_src_increment: bool,
#[bit(4, rw)]
disable_dst_incremenet: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Interrupt {
/// Tri-state PL IO during HIZ.
#[bit(31, rw)]
gts_usr_b: bool,
#[bit(30, rw)]
first_config_done: bool,
#[bit(29, rw)]
global_powerdown: bool,
/// Tri-state PL IO during configuration.
#[bit(28, rw)]
gts_cfg_b: bool,
/// PSS_CFG_RESET_B_INT
#[bit(27, rw)]
pl_config_reset: bool,
#[bit(23, rw)]
axi_write_timeout: bool,
#[bit(22, rw)]
axi_write_response_error: bool,
#[bit(21, rw)]
axi_read_timeout: bool,
#[bit(20, rw)]
axi_read_response_error: bool,
#[bit(18, rw)]
rx_overflow: bool,
#[bit(17, rw)]
tx_fifo_below_threshold: bool,
#[bit(16, rw)]
rx_fifo_above_threshold: bool,
#[bit(15, rw)]
dma_illegal_command: bool,
#[bit(14, rw)]
dma_queue_overflow: bool,
#[bit(13, rw)]
dma_done: bool,
#[bit(12, rw)]
dma_pcap_done: bool,
#[bit(11, rw)]
inconsistent_pcap_to_dma_transfer_len: bool,
#[bit(6, rw)]
hamc_error: bool,
#[bit(5, rw)]
seu_error: bool,
#[bit(4, rw)]
pl_power_loss_por_b_low: bool,
#[bit(3, rw)]
pl_config_controller_under_reset: bool,
#[bit(2, rw)]
pl_programming_done: bool,
#[bit(1, rw)]
positive_edge_pl_init: bool,
#[bit(0, rw)]
negative_edge_pl_init: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct MiscControl {
#[bits(28..=31, r)]
ps_version: u4,
#[bit(8, r)]
por_b_signal: bool,
#[bit(4, rw)]
loopback: bool,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum UnacknowledgedDmaTransfers {
None = 0b00,
One =0b01,
Two = 0b10,
ThreeOrMore = 0b11,
}
#[bitbybit::bitfield(u32, debug)]
pub struct Status {
#[bit(31, rw)]
dma_command_queue_full: bool,
#[bit(30, rw)]
dma_command_queue_empty: bool,
#[bits(28..=29, rw)]
unacknowledged_dma_transfers: UnacknowledgedDmaTransfers,
#[bits(20..=24, rw)]
rx_fifo_level: u5,
#[bits(12..=18, rw)]
tx_fifo_level: u7,
#[bit(11, rw)]
gts_usr_b: bool,
#[bit(10, rw)]
first_config_done: bool,
#[bit(9, rw)]
global_powerdown: bool,
#[bit(8, rw)]
gts_cfg_b: bool,
#[bit(7, rw)]
secure_lockdown: bool,
#[bit(6, rw)]
illegal_apb_access: bool,
/// Active low reset bit.
#[bit(5, rw)]
pl_reset_n: bool,
#[bit(4, rw)]
pcfg_init: bool,
#[bit(3, rw)]
efuse_bbram_aes_key_disabled: bool,
#[bit(2, rw)]
efuse_sec_enable: bool,
#[bit(1, rw)]
efuse_jtag_disabled: bool
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DevCfg {
control: Control,
lock: Lock,
config: Config,
/// Interrupt is cleared by writing to this register.
interrupt_status: Interrupt,
/// Bits can be set to one to mask the interrupts.
interrupt_mask: Interrupt,
status: Status,
dma_source_addr: u32,
dma_dest_addr: u32,
dma_source_len: u32,
dma_dest_len: u32,
_reserved0: u32,
multiboot_addr: u32,
_reserved1: u32,
unlock_control: u32,
_reserved2: [u32; 0x12],
misc_control: MiscControl,
_reserved3: [u32; 0x1F],
// Included here but not exposed to avoid providing multiple references to the same peripheral.
// Exposed in [crate::xadc].
_xadc: crate::xadc::XAdc,
}
static_assertions::const_assert_eq!(core::mem::size_of::<DevCfg>(), 0x11C);
impl DevCfg {
/// Create a new DevCfg MMIO instance for for device configuration peripheral at address
/// [DEVCFG_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub unsafe fn new_mmio_fixed() -> MmioDevCfg<'static> {
unsafe { Self::new_mmio_at(DEVCFG_BASE_ADDR) }
}
}

View File

@@ -27,6 +27,8 @@ pub mod l2_cache;
pub mod mpcore; pub mod mpcore;
pub mod priv_tim; pub mod priv_tim;
pub mod qspi; pub mod qspi;
pub mod devcfg;
pub mod xadc;
pub mod slcr; pub mod slcr;
pub mod spi; pub mod spi;
pub mod ttc; pub mod ttc;
@@ -58,6 +60,8 @@ pub struct PsPeripherals {
pub eth_0: eth::MmioEthernet<'static>, pub eth_0: eth::MmioEthernet<'static>,
pub eth_1: eth::MmioEthernet<'static>, pub eth_1: eth::MmioEthernet<'static>,
pub qspi: qspi::MmioQspi<'static>, pub qspi: qspi::MmioQspi<'static>,
pub devcfg: devcfg::MmioDevCfg<'static>,
pub xadc: xadc::MmioXAdc<'static>,
} }
impl PsPeripherals { impl PsPeripherals {
@@ -96,6 +100,7 @@ impl PsPeripherals {
eth_0: eth::Ethernet::new_mmio_fixed_0(), eth_0: eth::Ethernet::new_mmio_fixed_0(),
eth_1: eth::Ethernet::new_mmio_fixed_1(), eth_1: eth::Ethernet::new_mmio_fixed_1(),
qspi: qspi::Qspi::new_mmio_fixed(), qspi: qspi::Qspi::new_mmio_fixed(),
devcfg: devcfg::DevCfg::new_mmio_fixed(),
} }
} }
} }

29
zynq7000/src/xadc.rs Normal file
View File

@@ -0,0 +1,29 @@
pub const XADC_BASE_ADDR: usize = 0xF8007100;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct XAdc {
config: u32,
interrupt_status: u32,
interrupt_mask: u32,
misc_status: u32,
command_fifo: u32,
data_fifo: u32,
misc_control: u32,
}
impl XAdc {
/// Create a new XADC MMIO instance for for device configuration peripheral at address
/// [XADC_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub unsafe fn new_mmio_fixed() -> MmioXAdc<'static> {
unsafe { XAdc::new_mmio_at(XADC_BASE_ADDR) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<XAdc>(), 0x1C);