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

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:
2025-08-01 14:32:08 +02:00
committed by Robin Mueller
parent 0cf5bf6885
commit 5d0f2837d1
166 changed files with 9496 additions and 979 deletions

View File

@@ -0,0 +1,219 @@
use std::fs::File;
use std::io::Write;
use std::process::Command;
use zynq7000_rt::mmu::ONE_MB;
pub use zynq7000_rt::mmu::segments::*;
macro_rules! write_l1_section {
($writer:expr, $offset:expr, $attr:expr) => {
writeln!(
$writer,
"L1Section::new_with_addr_and_attrs({:#010x}, {}),",
$offset, $attr
)
.unwrap();
};
}
fn main() {
let file_path = "mmu_table.rs";
let file = File::create(file_path).expect("Failed to create file");
let mut offset = 0;
let attr_ddr = stringify!(section_attrs::DDR);
let attr_unassigned = stringify!(section_attrs::UNASSIGNED_RESERVED);
let attr_fpga_slaves = stringify!(section_attrs::FPGA_SLAVES);
let attr_shared_dev = stringify!(section_attrs::SHAREABLE_DEVICE);
let attr_sram = stringify!(section_attrs::SRAM);
let attr_qspi = stringify!(section_attrs::QSPI_XIP);
let attr_ocm_high = stringify!(section_attrs::OCM_MAPPED_HIGH);
assert_eq!(
1 + DDR_FULL_ACCESSIBLE
+ FPGA_SLAVE
+ FPGA_SLAVE
+ UNASSIGNED_0
+ IO_PERIPHS
+ UNASSIGNED_1
+ NAND
+ NOR
+ SRAM
+ SEGMENTS_UNASSIGNED_2
+ AMBA_APB
+ UNASSIGNED_3
+ QSPI_XIP
+ UNASSIGNED_4
+ OCM_MAPPED_HIGH,
4096
);
let mut buf_writer = std::io::BufWriter::new(file);
writeln!(
buf_writer,
"//! This file was auto-generated by table-gen.rs"
)
.unwrap();
writeln!(buf_writer, "use crate::mmu::section_attrs;").unwrap();
writeln!(buf_writer, "use cortex_ar::mmu::L1Section;").unwrap();
writeln!(buf_writer, "use zynq7000_mmu::L1Table;").unwrap();
writeln!(buf_writer, "").unwrap();
writeln!(buf_writer, "/// MMU Level 1 Page table.").unwrap();
writeln!(buf_writer, "///").unwrap();
writeln!(
buf_writer,
"/// 4096 entries, each covering 1MB of the address space."
)
.unwrap();
writeln!(
buf_writer,
"pub static MMU_L1_PAGE_TABLE: L1Table = L1Table::new(["
)
.unwrap();
writeln!(
buf_writer,
"// First DDR segment, OCM memory (0x0000_0000 - 0x0010_0000)"
)
.unwrap();
write_l1_section!(buf_writer, offset, attr_ddr);
offset += ONE_MB;
writeln!(buf_writer, "// DDR memory (0x00100000 - 0x4000_0000)").unwrap();
for _ in 0..DDR_FULL_ACCESSIBLE {
write_l1_section!(buf_writer, offset, attr_ddr);
offset += ONE_MB;
}
writeln!(buf_writer, "// FPGA slave 0 (0x4000_0000 - 0x8000_0000)").unwrap();
for _ in 0..FPGA_SLAVE {
write_l1_section!(buf_writer, offset, attr_fpga_slaves);
offset += ONE_MB;
}
writeln!(buf_writer, "// FPGA slave 1 (0x8000_0000 - 0xC000_0000)").unwrap();
for _ in 0..FPGA_SLAVE {
write_l1_section!(buf_writer, offset, attr_fpga_slaves);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Unassigned/Reserved (0xC000_0000 - 0xE000_0000)"
)
.unwrap();
for _ in 0..UNASSIGNED_0 {
write_l1_section!(buf_writer, offset, attr_unassigned);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Segments IO peripherals (0xE000_0000 - 0xE030_0000)"
)
.unwrap();
for _ in 0..IO_PERIPHS {
write_l1_section!(buf_writer, offset, attr_shared_dev);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Unassigned/Reserved (0xE030_0000 - 0xE100_0000)"
)
.unwrap();
for _ in 0..UNASSIGNED_1 {
write_l1_section!(buf_writer, offset, attr_unassigned);
offset += ONE_MB;
}
writeln!(buf_writer, "// NAND (0xE100_0000 - 0xE200_0000)").unwrap();
for _ in 0..NAND {
write_l1_section!(buf_writer, offset, attr_shared_dev);
offset += ONE_MB;
}
writeln!(buf_writer, "// NOR (0xE200_0000 - 0xE400_0000)").unwrap();
for _ in 0..NOR {
write_l1_section!(buf_writer, offset, attr_shared_dev);
offset += ONE_MB;
}
writeln!(buf_writer, "// SRAM (0xE400_0000 - 0xE600_0000)").unwrap();
for _ in 0..SRAM {
write_l1_section!(buf_writer, offset, attr_sram);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Unassigned/Reserved (0xE600_0000 - 0xF800_0000)"
)
.unwrap();
for _ in 0..SEGMENTS_UNASSIGNED_2 {
write_l1_section!(buf_writer, offset, attr_unassigned);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// AMBA APB peripherals (0xF800_0000 - 0xF900_0000)"
)
.unwrap();
for _ in 0..AMBA_APB {
write_l1_section!(buf_writer, offset, attr_shared_dev);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Unassigned/Reserved (0xF900_0000 - 0xFC00_0000)"
)
.unwrap();
for _ in 0..UNASSIGNED_3 {
write_l1_section!(buf_writer, offset, attr_unassigned);
offset += ONE_MB;
}
writeln!(buf_writer, "// QSPI XIP (0xFC00_0000 - 0xFE00_0000)").unwrap();
for _ in 0..QSPI_XIP {
write_l1_section!(buf_writer, offset, attr_qspi);
offset += ONE_MB;
}
writeln!(
buf_writer,
"// Unassiged/Reserved (0xFE00_0000 - 0xFFF0_0000)"
)
.unwrap();
for _ in 0..UNASSIGNED_4 {
write_l1_section!(buf_writer, offset, attr_unassigned);
offset += ONE_MB;
}
writeln!(buf_writer, "// OCM High (0xFFF0_0000 - 0xFFFF_FFFF)").unwrap();
let mut offset_u64 = offset as u64;
for _ in 0..OCM_MAPPED_HIGH {
write_l1_section!(buf_writer, offset, attr_ocm_high);
offset_u64 += ONE_MB as u64;
}
// Check that the full 4 GB were covered (not too much, or less)
assert_eq!(offset_u64, 0x1_0000_0000 as u64);
writeln!(buf_writer, "]);").unwrap();
// Finish the file.
drop(buf_writer);
println!("Generated mmu_table.rs");
// Run rustfmt on the generated file
let output = Command::new("rustfmt")
.arg(file_path)
.output()
.expect("Failed to run rustfmt");
if !output.status.success() {
eprintln!("rustfmt failed: {:?}", output);
}
}