Introduce Rust FSBL
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
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 PR introduces some major features while also changing the project structure to be more flexible for multiple platforms (e.g. host tooling). It also includes a lot of bugfixes, renamings for consistency purposes and dependency updates. Added features: 1. Pure Rust FSBL for the Zedboard. This first variant is simplistic. It is currently only capable of QSPI boot. It searches for a bitstream and ELF file inside the boot binary, flashes them and jumps to them. 2. QSPI flasher for the Zedboard. 3. DDR, QSPI, DEVC, private CPU timer and PLL configuration modules 3. Tooling to auto-generate board specific DDR and DDRIOB config parameters from the vendor provided ps7init.tcl file Changed project structure: 1. All target specific project are inside a dedicated workspace inside the `zynq` folder now. 2. All tool intended to be run on a host are inside a `tools` workspace 3. All other common projects are at the project root Major bugfixes: 1. SPI module: CPOL was not configured properly 2. Logger flush implementation was empty, implemented properly now.
This commit is contained in:
280
zynq/zynq7000/src/qspi.rs
Normal file
280
zynq/zynq7000/src/qspi.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use arbitrary_int::{u2, u3};
|
||||
|
||||
pub use crate::{SpiClockPhase, SpiClockPolarity};
|
||||
|
||||
const QSPI_BASE_ADDR: usize = 0xE000D000;
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InterfaceMode {
|
||||
LegacySpi = 0,
|
||||
FlashMemoryInterface = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Endianness {
|
||||
Little = 0,
|
||||
Big = 1,
|
||||
}
|
||||
|
||||
/// Baud rate divisor register values.
|
||||
#[bitbybit::bitenum(u3, exhaustive = true)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BaudRateDivisor {
|
||||
_2 = 0b000,
|
||||
_4 = 0b001,
|
||||
_8 = 0b010,
|
||||
_16 = 0b011,
|
||||
_32 = 0b100,
|
||||
_64 = 0b101,
|
||||
_128 = 0b110,
|
||||
_256 = 0b111,
|
||||
}
|
||||
|
||||
impl BaudRateDivisor {
|
||||
/// Actual divisor value.
|
||||
pub fn divisor(&self) -> usize {
|
||||
match self {
|
||||
BaudRateDivisor::_2 => 2,
|
||||
BaudRateDivisor::_4 => 4,
|
||||
BaudRateDivisor::_8 => 8,
|
||||
BaudRateDivisor::_16 => 16,
|
||||
BaudRateDivisor::_32 => 32,
|
||||
BaudRateDivisor::_64 => 64,
|
||||
BaudRateDivisor::_128 => 128,
|
||||
BaudRateDivisor::_256 => 256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use bitbybit debug support as soon as support for write fields has been implemented.
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
#[bit(31, rw)]
|
||||
interface_mode: InterfaceMode,
|
||||
#[bit(26, rw)]
|
||||
edianness: Endianness,
|
||||
#[bit(19, rw)]
|
||||
holdb_dr: bool,
|
||||
#[bit(16, w)]
|
||||
manual_start_command: bool,
|
||||
#[bit(15, rw)]
|
||||
manual_start_enable: bool,
|
||||
#[bit(14, rw)]
|
||||
manual_cs: bool,
|
||||
/// Directly drives the chip select line when CS is driven manually (bit 14 is set)
|
||||
#[bit(10, rw)]
|
||||
peripheral_chip_select: bool,
|
||||
/// The only valid value is 0b11 (32 bits)
|
||||
#[bits(6..=7, rw)]
|
||||
fifo_width: u2,
|
||||
#[bits(3..=5, rw)]
|
||||
baud_rate_div: BaudRateDivisor,
|
||||
#[bit(2, rw)]
|
||||
clock_phase: SpiClockPhase,
|
||||
#[bit(1, rw)]
|
||||
clock_polarity: SpiClockPolarity,
|
||||
/// Must be set to 1 before using QSPI, 0 is a reserved value.
|
||||
#[bit(0, rw)]
|
||||
mode_select: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug)]
|
||||
pub struct InterruptStatus {
|
||||
/// Write-to-clear bit.
|
||||
#[bit(6, rw)]
|
||||
tx_underflow: bool,
|
||||
#[bit(5, r)]
|
||||
rx_full: bool,
|
||||
#[bit(4, r)]
|
||||
rx_above_threshold: bool,
|
||||
#[bit(3, r)]
|
||||
tx_full: bool,
|
||||
#[bit(2, r)]
|
||||
tx_below_threshold: bool,
|
||||
/// Write-to-clear bit.
|
||||
#[bit(0, rw)]
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
pub struct InterruptControl {
|
||||
#[bit(6, w)]
|
||||
tx_underflow: bool,
|
||||
#[bit(5, w)]
|
||||
rx_full: bool,
|
||||
#[bit(4, w)]
|
||||
rx_not_empty: bool,
|
||||
#[bit(3, w)]
|
||||
tx_full: bool,
|
||||
#[bit(2, w)]
|
||||
tx_not_full: bool,
|
||||
#[bit(0, w)]
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug)]
|
||||
pub struct InterruptMask {
|
||||
#[bit(6, r)]
|
||||
tx_underflow: bool,
|
||||
#[bit(5, r)]
|
||||
rx_full: bool,
|
||||
#[bit(4, r)]
|
||||
rx_not_empty: bool,
|
||||
#[bit(3, r)]
|
||||
tx_full: bool,
|
||||
#[bit(2, r)]
|
||||
tx_not_full: bool,
|
||||
#[bit(0, r)]
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0, debug)]
|
||||
pub struct SpiEnable {
|
||||
#[bit(0, rw)]
|
||||
enable: bool,
|
||||
}
|
||||
|
||||
/// All the delays are in SPI reference block or external clock cycles.
|
||||
#[bitbybit::bitfield(u32, debug)]
|
||||
pub struct Delay {
|
||||
/// Length of the master mode chip select output de-asserts between words when CPHA = 0.
|
||||
#[bits(24..=31, rw)]
|
||||
deassert: u8,
|
||||
/// Delay between one chip select being de-activated and another being activated.
|
||||
#[bits(16..=23, rw)]
|
||||
between: u8,
|
||||
/// Length between last bit of current word and first bit of next word.
|
||||
#[bits(8..=15, rw)]
|
||||
after: u8,
|
||||
/// Delay between setting chip select low and first bit transfer.
|
||||
#[bits(0..=7, rw)]
|
||||
init: u8,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
pub struct Gpio {
|
||||
/// Active low write-protect bit.
|
||||
#[bit(0, rw)]
|
||||
write_protect_n: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0, debug)]
|
||||
pub struct LoopbackMasterClockDelay {
|
||||
/// Use internal loopback master clock for read data capturing when the baud rate divisor
|
||||
/// is 2.
|
||||
#[bit(5, rw)]
|
||||
use_loopback: bool,
|
||||
#[bits(3..=4,rw)]
|
||||
delay_1: u2,
|
||||
#[bits(0..=2 ,rw)]
|
||||
delay_0: u3,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u8, exhaustive = false)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InstructionCode {
|
||||
Read = 0x03,
|
||||
FastRead = 0x0B,
|
||||
FastReadDualOutput = 0x3B,
|
||||
FastReadQuadOutput = 0x6B,
|
||||
FastReadDualIo = 0xBB,
|
||||
FastReadQuadIo = 0xEB,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0, debug)]
|
||||
pub struct LinearQspiConfig {
|
||||
#[bit(31, rw)]
|
||||
enable_linear_mode: bool,
|
||||
#[bit(30, rw)]
|
||||
both_memories: bool,
|
||||
/// Only has a meaning is bit 30 is set (both memories).
|
||||
#[bit(29, rw)]
|
||||
separate_memory_bus: bool,
|
||||
/// Upper memory page, if set. Only has a meaning if bit 30 is set and bit 29 / bit 31 are
|
||||
/// cleared.
|
||||
///
|
||||
/// In LQSPI mode, address bit 25 will indicate the lower (0) or upper (1) page.
|
||||
/// In IO mode, this bit selects the lower or upper memory.
|
||||
#[bit(28, rw)]
|
||||
upper_memory_page: bool,
|
||||
#[bit(25, rw)]
|
||||
mode_enable: bool,
|
||||
#[bit(24, rw)]
|
||||
mode_on: bool,
|
||||
#[bits(16..=23, rw)]
|
||||
mode_bits: u8,
|
||||
#[bits(8..=10, rw)]
|
||||
num_dummy_bytes: u3,
|
||||
#[bits(0..=7, rw)]
|
||||
instruction_code: Option<InstructionCode>,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, debug)]
|
||||
pub struct LinearQspiStatus {
|
||||
#[bit(2, rw)]
|
||||
data_fsm_error: bool,
|
||||
#[bit(1, rw)]
|
||||
axi_write_command_received: bool,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct Qspi {
|
||||
config: Config,
|
||||
interrupt_status: InterruptStatus,
|
||||
#[mmio(Write)]
|
||||
interrupt_enable: InterruptControl,
|
||||
#[mmio(Write)]
|
||||
interrupt_disable: InterruptControl,
|
||||
#[mmio(PureRead)]
|
||||
interupt_mask: InterruptMask,
|
||||
spi_enable: SpiEnable,
|
||||
delay: Delay,
|
||||
/// Transmits 1-byte command and 3-byte data OR 4-byte data.
|
||||
#[mmio(Write)]
|
||||
tx_data_00: u32,
|
||||
#[mmio(PureRead)]
|
||||
rx_data: u32,
|
||||
slave_idle_count: u32,
|
||||
/// Defines the level at which the TX FIFO not full interrupt is generated.
|
||||
tx_fifo_threshold: u32,
|
||||
/// Defines the level at which the RX FIFO not empty interrupt is generated.
|
||||
rx_fifo_threshold: u32,
|
||||
gpio: Gpio,
|
||||
_reserved0: u32,
|
||||
loopback_master_clock_delay: LoopbackMasterClockDelay,
|
||||
_reserved1: [u32; 0x11],
|
||||
/// Transmits 1-byte command.
|
||||
#[mmio(Write)]
|
||||
tx_data_01: u32,
|
||||
/// Transmits 1-byte command and 1-byte data.
|
||||
#[mmio(Write)]
|
||||
tx_data_10: u32,
|
||||
/// Transmits 1-byte command and 2-byte data.
|
||||
#[mmio(Write)]
|
||||
tx_data_11: u32,
|
||||
_reserved2: [u32; 0x5],
|
||||
linear_qspi_config: LinearQspiConfig,
|
||||
linear_qspi_status: LinearQspiStatus,
|
||||
_reserved3: [u32; 0x15],
|
||||
/// Module ID value with reset value 0x1090101.
|
||||
module_id: u32,
|
||||
}
|
||||
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<Qspi>(), 0x100);
|
||||
|
||||
impl Qspi {
|
||||
/// Create a new QSPI MMIO instance for for QSPI controller at address [QSPI_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 const unsafe fn new_mmio_fixed() -> MmioQspi<'static> {
|
||||
unsafe { Self::new_mmio_at(QSPI_BASE_ADDR) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user