1use std::fs;
6use std::path::PathBuf;
7use std::str::FromStr;
8use std::time::Duration;
9
10use anyhow::{Context, Result, ensure};
11use bindgen::sram_program::{
12 SRAM_MAGIC_SP_CRC_ERROR, SRAM_MAGIC_SP_CRC_SKIPPED, SRAM_MAGIC_SP_EXECUTION_DONE,
13};
14use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
15use clap::Args;
16use crc::Crc;
17use object::{Object, ObjectSection, ObjectSegment, SectionKind};
18use serde::{Deserialize, Serialize};
19use thiserror::Error;
20
21use ot_hal::top;
22use ot_hal::util::multibits::MultiBitBool4;
23
24use crate::impl_serializable_error;
25use crate::io::jtag::{Jtag, RiscvCsr, RiscvGpr, RiscvReg};
26use crate::util::parse_int::ParseInt;
27use crate::util::vmem::Vmem;
28
29#[derive(Debug, Args, Clone, Default)]
31pub struct SramProgramParams {
32 #[arg(long, default_value = None)]
34 pub elf: Option<PathBuf>,
35
36 #[arg(long, conflicts_with = "elf", default_value = None)]
38 pub vmem: Option<PathBuf>,
39
40 #[arg(long, value_parser = <u32 as ParseInt>::from_str, conflicts_with="elf", default_value = None)]
42 pub load_addr: Option<u32>,
43
44 #[arg(long)]
46 pub skip_crc: bool,
47}
48
49#[derive(Debug, Clone)]
51pub enum SramProgramFile {
52 Vmem { path: PathBuf, load_addr: u32 },
53 Elf(PathBuf),
54}
55
56impl SramProgramParams {
57 pub fn get_file(&self) -> SramProgramFile {
59 if let Some(path) = &self.vmem {
60 SramProgramFile::Vmem {
61 path: path.clone(),
62 load_addr: self
63 .load_addr
64 .expect("you must provide a load address for a VMEM file"),
65 }
66 } else {
67 SramProgramFile::Elf(
68 self.elf
69 .as_ref()
70 .expect("you must provide either an ELF file or a VMEM file")
71 .clone(),
72 )
73 }
74 }
75
76 pub fn load(&self, jtag: &mut dyn Jtag) -> Result<SramProgramInfo> {
77 load_sram_program(jtag, &self.get_file())
78 }
79
80 pub fn load_and_execute(
81 &self,
82 jtag: &mut dyn Jtag,
83 exec_mode: ExecutionMode,
84 ) -> Result<ExecutionResult> {
85 load_and_execute_sram_program(jtag, &self.get_file(), exec_mode, self.skip_crc)
86 }
87}
88
89pub enum ExecutionMode {
91 Jump,
93 JumpAndHalt,
95 JumpAndWait(Duration),
97}
98
99#[derive(Debug, Deserialize, Serialize)]
101pub enum ExecutionError {
102 Unknown,
104 CrcMismatch,
106}
107
108#[derive(Debug, Deserialize, Serialize)]
110pub enum ExecutionResult {
111 HaltedAtStart,
113 Executing,
115 ExecutionDone(u32),
119 ExecutionError(ExecutionError),
121}
122
123#[derive(Error, Debug, Deserialize, Serialize)]
125pub enum LoadSramProgramError {
126 #[error("SRAM ELF programs must be 32-bit binaries")]
127 Not32Bit,
128 #[error(
129 "SRAM program contains segments whose address or size is not a multiple of the word size"
130 )]
131 SegmentNotWordAligned,
132 #[error("SRAM program must be compiled with the `-nmagic` flag")]
133 NotCompiledWithNmagic,
134 #[error("SRAM program's segments must be consecutive")]
135 GapBetweenSegments,
136 #[error("Data readback from the SRAM mismatches from the data loaded")]
137 ReadbackMismatch,
138 #[error("SRAM program entry point is not contained in any text section")]
139 EntryPointNotFound,
140 #[error("Generic error {0}")]
141 Generic(String),
142}
143impl_serializable_error!(LoadSramProgramError);
144
145pub struct SramProgramInfo {
147 pub entry_point: u32,
149 pub crc32: u32,
151}
152
153pub fn load_vmem_sram_program(
155 jtag: &mut dyn Jtag,
156 vmem_filename: &PathBuf,
157 load_addr: u32,
158) -> Result<SramProgramInfo> {
159 log::info!("Loading VMEM file {}", vmem_filename.display());
160 let vmem_content = fs::read_to_string(vmem_filename)?;
161 let mut vmem = Vmem::from_str(&vmem_content)?;
162 vmem.merge_sections();
163 log::info!("Uploading program to SRAM at {:x}", load_addr);
164 let crc = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
165 let mut digest = crc.digest();
166 for section in vmem.sections() {
167 log::info!(
168 "Load {} words at address {:x}",
169 section.data.len(),
170 load_addr + section.addr
171 );
172 jtag.write_memory32(load_addr + section.addr, §ion.data)?;
173 let mut data8: Vec<u8> = vec![];
175 for elem in §ion.data {
176 data8.write_u32::<LittleEndian>(*elem).unwrap();
177 }
178 digest.update(&data8);
179 }
180 Ok(SramProgramInfo {
181 entry_point: load_addr,
182 crc32: digest.finalize(),
183 })
184}
185
186pub fn load_elf_sram_program(
188 jtag: &mut dyn Jtag,
189 elf_filename: &PathBuf,
190) -> Result<SramProgramInfo> {
191 log::info!("Loading ELF file {}", elf_filename.display());
192 let file_data = std::fs::read(elf_filename)
193 .with_context(|| format!("Could not read ELF file {}.", elf_filename.display()))?;
194 let file = object::File::parse(&*file_data)
195 .with_context(|| format!("Could not parse ELF file {}", elf_filename.display()))?;
196 ensure!(!file.is_64(), LoadSramProgramError::Not32Bit);
197 log::info!("Uploading program to SRAM");
198
199 let crc = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
234 let mut digest = crc.digest();
235 let mut last_address: Option<u32> = None;
236 for segment in file.segments() {
237 let address = segment.address();
238 let data = segment.data()?;
239
240 if data.is_empty() {
241 continue;
242 }
243
244 const WORD_SIZE: usize = std::mem::size_of::<u32>();
247 ensure!(
248 address % WORD_SIZE as u64 == 0 && data.len() % WORD_SIZE == 0,
249 LoadSramProgramError::SegmentNotWordAligned
250 );
251 ensure!(
252 segment.align() <= 256,
253 LoadSramProgramError::NotCompiledWithNmagic
254 );
255 if let Some(last_addr) = last_address {
257 let gap_size = address as i32 - last_addr as i32;
258 ensure!(gap_size == 0, LoadSramProgramError::GapBetweenSegments);
259 }
260 log::info!(
262 "Load segment: {} bytes at address {:x}",
263 data.len(),
264 address
265 );
266 let data32: Vec<u32> = data.chunks(4).map(LittleEndian::read_u32).collect();
267 jtag.write_memory32(address as u32, &data32)?;
268 digest.update(data);
269
270 last_address = Some((address + data.len() as u64) as u32);
271 }
272
273 let mut entry_found = false;
278 for section in file.sections() {
279 if section.kind() != SectionKind::Text {
280 continue;
281 }
282
283 if (section.address()..(section.address() + section.size())).contains(&file.entry()) {
285 entry_found = true;
286
287 let data32: Vec<u32> = section
288 .data()?
289 .chunks(4)
290 .map(LittleEndian::read_u32)
291 .collect();
292 println!("{:?}", data32);
293 let mut read_data32 = vec![0u32; data32.len()];
294 log::info!("Read back data to verify");
295 jtag.read_memory32(section.address() as u32, &mut read_data32)?;
296 ensure!(
297 data32 == read_data32,
298 LoadSramProgramError::ReadbackMismatch
299 );
300 }
301 }
302 ensure!(entry_found, LoadSramProgramError::EntryPointNotFound);
303
304 Ok(SramProgramInfo {
305 entry_point: file.entry() as u32,
306 crc32: digest.finalize(),
307 })
308}
309
310pub fn load_sram_program(jtag: &mut dyn Jtag, file: &SramProgramFile) -> Result<SramProgramInfo> {
312 match file {
313 SramProgramFile::Vmem { path, load_addr } => load_vmem_sram_program(jtag, path, *load_addr),
314 SramProgramFile::Elf(path) => load_elf_sram_program(jtag, path),
315 }
316}
317
318pub fn prepare_epmp(jtag: &mut dyn Jtag) -> Result<()> {
355 log::info!("Configure ePMP for SRAM execution.");
357 let pmpcfg3 = jtag.read_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG3))?;
358 log::info!("Old value of pmpcfg3: {:x}", pmpcfg3);
359 let pmpcfg3 = (pmpcfg3 & 0x00ffffffu32) | 0x9f000000;
361 log::info!("New value of pmpcfg3: {:x}", pmpcfg3);
362 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG3), pmpcfg3)?;
363 let base = top::SRAM_CTRL_MAIN_RAM_BASE_ADDR as u32;
366 let size = top::SRAM_CTRL_MAIN_RAM_SIZE_BYTES as u32;
367 assert!(size & (size - 1) == 0);
369 let pmpaddr15 = (base >> 2) | ((size - 1) >> 3);
370 log::info!("New value of pmpaddr15: {:x}", pmpaddr15);
371 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR15), pmpaddr15)?;
372
373 log::info!("Configure ePMP for MMIO access.");
375 let pmpcfg2 = jtag.read_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG2))?;
376 log::info!("Old value of pmpcfg2: {:x}", pmpcfg2);
377 let pmpcfg2 = (pmpcfg2 & 0x00ffffffu32) | 0x8f000000;
379 log::info!("New value of pmpcfg2: {:x}", pmpcfg2);
380 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG2), pmpcfg2)?;
381 #[cfg(feature = "egret")]
383 let base = top::TOP_EGRET_MMIO_BASE_ADDR as u32;
384 #[cfg(feature = "egret")]
385 let size = top::TOP_EGRET_MMIO_SIZE_BYTES as u32;
386 #[cfg(feature = "dragonfly")]
387 let base = top::TOP_DRAGONFLY_MMIO_BASE_ADDR as u32;
388 #[cfg(feature = "dragonfly")]
389 let size = top::TOP_DRAGONFLY_MMIO_SIZE_BYTES as u32;
390 assert!(size & (size - 1) == 0);
392 let pmpaddr10 = base >> 2;
393 let pmpaddr11 = (base + size) >> 2;
394 log::info!("New value of pmpaddr10: {:x}", pmpaddr10);
395 log::info!("New value of pmpaddr11: {:x}", pmpaddr11);
396 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR10), pmpaddr10)?;
397 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR11), pmpaddr11)?;
398
399 Ok(())
400}
401
402pub fn prepare_sram_ctrl(jtag: &mut dyn Jtag) -> Result<()> {
404 const SRAM_CTRL_EXEC_REG_OFFSET: u32 =
405 (top::SRAM_CTRL_MAIN_REGS_BASE_ADDR as u32) + ot_bindgen_dif::SRAM_CTRL_EXEC_REG_OFFSET;
406 log::info!("Enabling execution from SRAM.");
407 let mut sram_ctrl_exec = [0];
408 jtag.read_memory32(SRAM_CTRL_EXEC_REG_OFFSET, &mut sram_ctrl_exec)?;
409 log::info!("Old value of sram_exec_en: {:x}", sram_ctrl_exec[0]);
410 sram_ctrl_exec[0] = u8::from(MultiBitBool4::True) as u32;
411 jtag.write_memory32(SRAM_CTRL_EXEC_REG_OFFSET, &sram_ctrl_exec)?;
412 log::info!("New value of sram_exec_en: {:x}", sram_ctrl_exec[0]);
413 Ok(())
414}
415
416pub fn execute_sram_program(
418 jtag: &mut dyn Jtag,
419 prog_info: &SramProgramInfo,
420 exec_mode: ExecutionMode,
421 skip_crc: bool,
422) -> Result<ExecutionResult> {
423 prepare_epmp(jtag)?;
424 prepare_sram_ctrl(jtag)?;
425
426 let ret_addr = 0xdeadbeefu32;
429 log::info!("set RA to {:x}", ret_addr);
430 jtag.write_riscv_reg(&RiscvReg::Gpr(RiscvGpr::RA), ret_addr)?;
431
432 if skip_crc {
434 log::info!(
436 "skip CRC by setting A0 to {:x} (crc32)",
437 SRAM_MAGIC_SP_CRC_SKIPPED
438 );
439 jtag.write_riscv_reg(&RiscvReg::Gpr(RiscvGpr::A0), SRAM_MAGIC_SP_CRC_SKIPPED)?;
440 } else {
441 log::info!("set A0 to {:x} (crc32)", prog_info.crc32);
443 jtag.write_riscv_reg(&RiscvReg::Gpr(RiscvGpr::A0), prog_info.crc32)?;
444 }
445
446 match exec_mode {
448 ExecutionMode::Jump => {
449 log::info!("resume execution at {:x}", prog_info.entry_point);
450 jtag.resume_at(prog_info.entry_point)?;
451 Ok(ExecutionResult::Executing)
452 }
453 ExecutionMode::JumpAndHalt => {
454 log::info!("set DPC to {:x}", prog_info.entry_point);
455 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::DPC), prog_info.entry_point)?;
456 Ok(ExecutionResult::HaltedAtStart)
457 }
458 ExecutionMode::JumpAndWait(tmo) => {
459 log::info!("resume execution at {:x}", prog_info.entry_point);
460 jtag.resume_at(prog_info.entry_point)?;
461 log::info!("wait for execution to stop");
462 jtag.wait_halt(tmo)?;
463 jtag.halt()?;
464 let sp = jtag.read_riscv_reg(&RiscvReg::Gpr(RiscvGpr::SP))?;
467 log::info!("after timeout, sp = {:x}", sp);
468 match sp {
469 SRAM_MAGIC_SP_EXECUTION_DONE => Ok(ExecutionResult::ExecutionDone(sp)),
470 SRAM_MAGIC_SP_CRC_SKIPPED => Ok(ExecutionResult::ExecutionDone(sp)),
471 SRAM_MAGIC_SP_CRC_ERROR => {
472 Ok(ExecutionResult::ExecutionError(ExecutionError::CrcMismatch))
473 }
474 _ => Ok(ExecutionResult::ExecutionError(ExecutionError::Unknown)),
475 }
476 }
477 }
478}
479
480pub fn load_and_execute_sram_program(
482 jtag: &mut dyn Jtag,
483 file: &SramProgramFile,
484 exec_mode: ExecutionMode,
485 skip_crc: bool,
486) -> Result<ExecutionResult> {
487 let prog_info = load_sram_program(jtag, file)?;
488 execute_sram_program(jtag, &prog_info, exec_mode, skip_crc)
490}