Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
906 changes: 906 additions & 0 deletions gateware/src/rs/bootinfo_gen/Cargo.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions gateware/src/rs/bootinfo_gen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "bootinfo_gen"
version = "0.1.0"
edition = "2021"

[dependencies]
tiliqua-lib = { path = "../lib", default-features = false }
tiliqua-manifest = { path = "../manifest" }
tiliqua-hal = { path = "../hal", default-features = false }
clap = { version = "4.0", features = ["derive"] }
serde_json = "1.0"
crc = "3.0"
postcard = { version = "1.1.3", features = ["use-crc"] }

[profile.release]
lto = true
opt-level = 3
73 changes: 73 additions & 0 deletions gateware/src/rs/bootinfo_gen/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// `bootinfo_gen` is used to serialize a manifest and 'fake' bootloader
// configuration (dynamic modeline, pixel clocks and so on) to a binary
// file, matching what the real bootloader would save in PSRAM at the
// `bootinfo` address before starting a user bitstream.
//
// At the moment, this is only used for simulating user bitstreams (so
// they don't crash if the `bootinfo` structure is missing!)

use clap::Parser;
use std::fs;
use std::path::PathBuf;
use tiliqua_lib::bootinfo::BootInfo;
use tiliqua_manifest::BitstreamManifest;
use tiliqua_hal::dma_framebuffer::DVIModeline;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Input manifest JSON file
#[arg(short, long)]
manifest: PathBuf,


// Below here are fields in BootInfo usually populated by the bootloader.
// For bootinfo_gen, they are passed on the command line for simulation
// purposes (so user bitstreams inherit the correct modeline)

/// Horizontal active pixels
#[arg(long)]
h_active: u16,

/// Vertical active pixels
#[arg(long)]
v_active: u16,

/// Fixed pixel clock in Hz
#[arg(long)]
fixed_pclk_hz: u32,

/// Output bootinfo binary file
#[arg(short, long)]
output: PathBuf,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();

let manifest_str = fs::read_to_string(&args.manifest)?;
println!("Manifest file content: {}", manifest_str);

let manifest: BitstreamManifest = serde_json::from_str(&manifest_str)?;
println!("Manifest parsed successfully");

let modeline = DVIModeline::default().maybe_override_fixed(
Some((args.h_active, args.v_active)),
args.fixed_pclk_hz
);

let bootinfo = BootInfo {
manifest,
modeline,
};

// Serialize to binary using postcard (WARN: exactly match bootloader procedure!)
let mut buffer = [0u8; 1024]; // BOOTINFO_MAX_SIZE
let crc_checker = crc::Crc::<u32>::new(&crc::CRC_32_BZIP2);
let digest = crc_checker.digest();
let serialized = postcard::to_slice_crc32(&bootinfo, &mut buffer, digest)?;
let serialized_len = serialized.len();
fs::write(&args.output, serialized)?;
println!("Generated bootinfo: {} bytes -> {:?}", serialized_len, args.output);
Ok(())
}
12 changes: 9 additions & 3 deletions gateware/src/rs/manifest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub enum RegionType {
pub struct MemoryRegion {
pub filename: String<16>,
pub region_type: RegionType,
pub spiflash_src: u32,
pub spiflash_src: Option<u32>,
pub psram_dst: Option<u32>,
pub size: u32,
pub crc: Option<u32>,
Expand Down Expand Up @@ -90,7 +90,11 @@ impl BitstreamManifest {
for (i, region) in self.regions.iter().enumerate() {
info!("\tmemory_region[{}] = {{", i);
info!("\t\tfilename: '{}'", region.filename);
info!("\t\tspiflash_src: {:#x}", region.spiflash_src);
if let Some(spiflash_src) = region.spiflash_src {
info!("\t\tspiflash_src: {:#x}", spiflash_src);
} else {
info!("\t\tspiflash_src: None");
}
if let Some(psram_dst) = region.psram_dst {
info!("\t\tpsram_dst: {:#x} (copyto)", psram_dst);
}
Expand Down Expand Up @@ -151,7 +155,9 @@ impl BitstreamManifest {
pub fn get_option_storage_window(&self) -> Option<core::ops::Range<u32>> {
for region in self.regions.iter() {
if region.region_type == RegionType::OptionStorage {
return Some(region.spiflash_src..(region.spiflash_src+region.size))
if let Some(spiflash_src) = region.spiflash_src {
return Some(spiflash_src..(spiflash_src+region.size));
}
}
}
None
Expand Down
11 changes: 11 additions & 0 deletions gateware/src/tb_cpp/sim_soc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ int main(int argc, char** argv) {
fin.read((char*)psram_driver.psram_data + PSRAM_FW_OFFSET, psram_driver.psram_size_bytes);
#endif

#ifdef BOOTINFO_BIN_PATH
// Load bootinfo at the calculated offset in PSRAM
std::ifstream bootinfo_fin(BOOTINFO_BIN_PATH, std::ios::in | std::ios::binary);
if (bootinfo_fin) {
bootinfo_fin.read((char*)psram_driver.psram_data + BOOTINFO_OFFSET, 1024);
printf("Loaded bootinfo to PSRAM offset 0x%x\n", BOOTINFO_OFFSET);
} else {
printf("Warning: Could not load bootinfo from %s\n", BOOTINFO_BIN_PATH);
}
#endif


while (contextp->time() < sim_time && !contextp->gotFinish()) {

Expand Down
2 changes: 1 addition & 1 deletion gateware/src/tiliqua/build/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def top_level_cli(

if args.action == CliAction.Simulate:
sim.simulate(fragment, sim_ports(fragment), sim_harness,
hw_platform, kwargs["clock_settings"], args.trace_fst)
hw_platform, kwargs["clock_settings"], args.trace_fst, archiver)
sys.exit(0)

if ila_supported and args.ila:
Expand Down
45 changes: 40 additions & 5 deletions gateware/src/tiliqua/build/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def soc_simulation_ports(fragment):
"dvi_b": (fragment.fb.simif.b, None),
}

def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=False):
def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=False, archiver=None):

build_dst = "build"
dst = f"{build_dst}/tiliqua_soc.v"
Expand All @@ -109,6 +109,38 @@ def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=Fals
ports=ports
))

# Check modeline requirement early
if hasattr(fragment, "fb") and fragment.fb.fixed_modeline is None:
raise ValueError("Simulation requires specifying a static video mode with `--modeline`")

has_soc = hasattr(fragment, "fw_location")

if archiver and has_soc:
# Generate fake `bootinfo` (expected by user bitstreams with an SoC, the
# bootloader creates and saves this at a defined position in PSRAM)

bootinfo_path = os.path.join(build_dst, "bootinfo.bin")
manifest_path = os.path.join(build_dst, "manifest.json")

manifest = archiver.write_manifest()
manifest.write_to_path(manifest_path)

print(f"Generating bootinfo for simulation: {h_active}x{v_active}@{dvi_clk_hz}Hz")
dvi_clk_hz = clock_settings.frequencies.dvi
h_active = fragment.fb.fixed_modeline.h_active
v_active = fragment.fb.fixed_modeline.v_active
subprocess.check_call([
"cargo", "run",
"--manifest-path", "src/rs/bootinfo_gen/Cargo.toml",
"--",
"--manifest", manifest_path,
"--h-active", str(h_active),
"--v-active", str(v_active),
"--fixed-pclk-hz", str(dvi_clk_hz),
"--output", bootinfo_path
], env=os.environ)
print(f"Generated bootinfo: {bootinfo_path}")

# Write all additional files added with platform.add_file()
# to build/ directory, so verilator build can find them.
for file in sim_platform.files:
Expand All @@ -118,8 +150,6 @@ def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=Fals
tracing_flags = ["--trace-fst", "--trace-structs"] if tracing else []

if hasattr(fragment, "fb"):
if fragment.fb.fixed_modeline is None:
raise ValueError("Simulation requires specifying a static video mode with `--modeline`")
dvi_clk_hz = clock_settings.frequencies.dvi
dvi_h_active = fragment.fb.fixed_modeline.h_active
dvi_v_active = fragment.fb.fixed_modeline.v_active
Expand All @@ -140,10 +170,15 @@ def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=Fals

firmware_cflags = []
bootinfo_cflags = []
if hasattr(fragment, "fw_location"):
if has_soc:
firmware_cflags += [
"-CFLAGS", f"-DFIRMWARE_BIN_PATH=\\\"{fragment.firmware_bin_path}\\\"",
]
bootinfo_offset = fragment.bootinfo_base - fragment.psram_base
bootinfo_cflags += [
"-CFLAGS", f"-DBOOTINFO_BIN_PATH=\\\"{bootinfo_path}\\\"",
"-CFLAGS", f"-DBOOTINFO_OFFSET={hex(bootinfo_offset)}",
]
match fragment.fw_location:
case FirmwareLocation.PSRAM:
firmware_cflags += [
Expand Down Expand Up @@ -189,7 +224,7 @@ def simulate(fragment, ports, harness, hw_platform, clock_settings, tracing=Fals
"-CFLAGS", f"-DSYNC_CLK_HZ={clock_sync_hz}",
"-CFLAGS", f"-DAUDIO_CLK_HZ={audio_clk_hz}",
"-CFLAGS", f"-DFAST_CLK_HZ={fast_clk_hz}",
] + video_cflags + psram_cflags + firmware_cflags + [
] + video_cflags + psram_cflags + firmware_cflags + bootinfo_cflags + [
harness,
f"{dst}",
] + [
Expand Down
15 changes: 12 additions & 3 deletions gateware/src/top/bootloader/fw/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,18 +280,27 @@ fn configure_external_pll(pll_config: &ExternalPLLConfig, pll: &mut Si5351Device
}

fn validate_and_copy_spiflash_region(region: &MemoryRegion) -> Result<(), BitstreamError> {
// Skip regions without spiflash_src (e.g. during simulation)
let spiflash_src = match region.spiflash_src {
Some(addr) => addr,
None => {
info!("Skip region '{}' (no spiflash_src)", region.filename);
return Ok(());
}
};

let spiflash_ptr = SPIFLASH_BASE as *mut u32;
let spiflash_offset_words = region.spiflash_src as isize / 4isize;
let spiflash_offset_words = spiflash_src as isize / 4isize;
let size_words = region.size as isize / 4isize + 1;

match region.region_type {
RegionType::Bitstream | RegionType::XipFirmware | RegionType::RamLoad => {
info!("Validate region '{}' at {:#x} (size: {} KiB) ...",
region.filename, SPIFLASH_BASE + region.spiflash_src as usize, region.size / 1024);
region.filename, SPIFLASH_BASE + spiflash_src as usize, region.size / 1024);
},
_ => {
info!("Skip region '{}' at {:#x} (size: {} KiB) ...",
region.filename, SPIFLASH_BASE + region.spiflash_src as usize, region.size / 1024);
region.filename, SPIFLASH_BASE + spiflash_src as usize, region.size / 1024);
return Ok(());
}
}
Expand Down