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::{SRAM_MAGIC_SP_CRC_ERROR, SRAM_MAGIC_SP_EXECUTION_DONE};
12use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
13use clap::Args;
14use crc::Crc;
15use object::{Object, ObjectSection, ObjectSegment, SectionKind};
16use serde::{Deserialize, Serialize};
17use thiserror::Error;
18
19use crate::chip::boolean::MultiBitBool4;
20use crate::impl_serializable_error;
21use crate::io::jtag::{Jtag, RiscvCsr, RiscvGpr, RiscvReg};
22use crate::util::parse_int::ParseInt;
23use crate::util::vmem::Vmem;
24
25use top_earlgrey::top_earlgrey;
26
27#[derive(Debug, Args, Clone, Default)]
29pub struct SramProgramParams {
30 #[arg(long, default_value = None)]
32 pub elf: Option<PathBuf>,
33
34 #[arg(long, conflicts_with = "elf", default_value = None)]
36 pub vmem: Option<PathBuf>,
37
38 #[arg(long, value_parser = <u32 as ParseInt>::from_str, conflicts_with="elf", default_value = None)]
40 pub load_addr: Option<u32>,
41}
42
43#[derive(Debug, Clone)]
45pub enum SramProgramFile {
46 Vmem { path: PathBuf, load_addr: u32 },
47 Elf(PathBuf),
48}
49
50impl SramProgramParams {
51 pub fn get_file(&self) -> SramProgramFile {
53 if let Some(path) = &self.vmem {
54 SramProgramFile::Vmem {
55 path: path.clone(),
56 load_addr: self
57 .load_addr
58 .expect("you must provide a load address for a VMEM file"),
59 }
60 } else {
61 SramProgramFile::Elf(
62 self.elf
63 .as_ref()
64 .expect("you must provide either an ELF file or a VMEM file")
65 .clone(),
66 )
67 }
68 }
69
70 pub fn load(&self, jtag: &mut dyn Jtag) -> Result<SramProgramInfo> {
71 load_sram_program(jtag, &self.get_file())
72 }
73
74 pub fn load_and_execute(
75 &self,
76 jtag: &mut dyn Jtag,
77 exec_mode: ExecutionMode,
78 ) -> Result<ExecutionResult> {
79 load_and_execute_sram_program(jtag, &self.get_file(), exec_mode)
80 }
81}
82
83pub enum ExecutionMode {
85 Jump,
87 JumpAndHalt,
89 JumpAndWait(Duration),
91}
92
93#[derive(Debug, Deserialize, Serialize)]
95pub enum ExecutionError {
96 Unknown,
98 CrcMismatch,
100}
101
102#[derive(Debug, Deserialize, Serialize)]
104pub enum ExecutionResult {
105 HaltedAtStart,
107 Executing,
109 ExecutionDone(u32),
113 ExecutionError(ExecutionError),
115}
116
117#[derive(Error, Debug, Deserialize, Serialize)]
119pub enum LoadSramProgramError {
120 #[error("SRAM ELF programs must be 32-bit binaries")]
121 Not32Bit,
122 #[error(
123 "SRAM program contains segments whose address or size is not a multiple of the word size"
124 )]
125 SegmentNotWordAligned,
126 #[error("SRAM program must be compiled with the `-nmagic` flag")]
127 NotCompiledWithNmagic,
128 #[error("SRAM program's segments must be consecutive")]
129 GapBetweenSegments,
130 #[error("Data readback from the SRAM mismatches from the data loaded")]
131 ReadbackMismatch,
132 #[error("SRAM program entry point is not contained in any text section")]
133 EntryPointNotFound,
134 #[error("Generic error {0}")]
135 Generic(String),
136}
137impl_serializable_error!(LoadSramProgramError);
138
139pub struct SramProgramInfo {
141 pub entry_point: u32,
143 pub crc32: u32,
145}
146
147pub fn load_vmem_sram_program(
149 jtag: &mut dyn Jtag,
150 vmem_filename: &PathBuf,
151 load_addr: u32,
152) -> Result<SramProgramInfo> {
153 log::info!("Loading VMEM file {}", vmem_filename.display());
154 let vmem_content = fs::read_to_string(vmem_filename)?;
155 let mut vmem = Vmem::from_str(&vmem_content)?;
156 vmem.merge_sections();
157 log::info!("Uploading program to SRAM at {:x}", load_addr);
158 let crc = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
159 let mut digest = crc.digest();
160 for section in vmem.sections() {
161 log::info!(
162 "Load {} words at address {:x}",
163 section.data.len(),
164 load_addr + section.addr
165 );
166 jtag.write_memory32(load_addr + section.addr, §ion.data)?;
167 let mut data8: Vec<u8> = vec![];
169 for elem in §ion.data {
170 data8.write_u32::<LittleEndian>(*elem).unwrap();
171 }
172 digest.update(&data8);
173 }
174 Ok(SramProgramInfo {
175 entry_point: load_addr,
176 crc32: digest.finalize(),
177 })
178}
179
180pub fn load_elf_sram_program(
182 jtag: &mut dyn Jtag,
183 elf_filename: &PathBuf,
184) -> Result<SramProgramInfo> {
185 log::info!("Loading ELF file {}", elf_filename.display());
186 let file_data = std::fs::read(elf_filename)
187 .with_context(|| format!("Could not read ELF file {}.", elf_filename.display()))?;
188 let file = object::File::parse(&*file_data)
189 .with_context(|| format!("Could not parse ELF file {}", elf_filename.display()))?;
190 ensure!(!file.is_64(), LoadSramProgramError::Not32Bit);
191 log::info!("Uploading program to SRAM");
192
193 let crc = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
228 let mut digest = crc.digest();
229 let mut last_address: Option<u32> = None;
230 for segment in file.segments() {
231 let address = segment.address();
232 let data = segment.data()?;
233
234 if data.is_empty() {
235 continue;
236 }
237
238 const WORD_SIZE: usize = std::mem::size_of::<u32>();
241 ensure!(
242 address % WORD_SIZE as u64 == 0 && data.len() % WORD_SIZE == 0,
243 LoadSramProgramError::SegmentNotWordAligned
244 );
245 ensure!(
246 segment.align() <= 256,
247 LoadSramProgramError::NotCompiledWithNmagic
248 );
249 if let Some(last_addr) = last_address {
251 let gap_size = address as i32 - last_addr as i32;
252 ensure!(gap_size == 0, LoadSramProgramError::GapBetweenSegments);
253 }
254 log::info!(
256 "Load segment: {} bytes at address {:x}",
257 data.len(),
258 address
259 );
260 let data32: Vec<u32> = data.chunks(4).map(LittleEndian::read_u32).collect();
261 jtag.write_memory32(address as u32, &data32)?;
262 digest.update(data);
263
264 last_address = Some((address + data.len() as u64) as u32);
265 }
266
267 let mut entry_found = false;
272 for section in file.sections() {
273 if section.kind() != SectionKind::Text {
274 continue;
275 }
276
277 if (section.address()..(section.address() + section.size())).contains(&file.entry()) {
279 entry_found = true;
280
281 let data32: Vec<u32> = section
282 .data()?
283 .chunks(4)
284 .map(LittleEndian::read_u32)
285 .collect();
286 let mut read_data32 = vec![0u32; data32.len()];
287 log::info!("Read back data to verify");
288 jtag.read_memory32(section.address() as u32, &mut read_data32)?;
289 ensure!(
290 data32 == read_data32,
291 LoadSramProgramError::ReadbackMismatch
292 );
293 }
294 }
295 ensure!(entry_found, LoadSramProgramError::EntryPointNotFound);
296
297 Ok(SramProgramInfo {
298 entry_point: file.entry() as u32,
299 crc32: digest.finalize(),
300 })
301}
302
303pub fn load_sram_program(jtag: &mut dyn Jtag, file: &SramProgramFile) -> Result<SramProgramInfo> {
305 match file {
306 SramProgramFile::Vmem { path, load_addr } => load_vmem_sram_program(jtag, path, *load_addr),
307 SramProgramFile::Elf(path) => load_elf_sram_program(jtag, path),
308 }
309}
310
311pub fn prepare_epmp(jtag: &mut dyn Jtag) -> Result<()> {
348 log::info!("Configure ePMP for SRAM execution.");
350 let pmpcfg3 = jtag.read_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG3))?;
351 log::info!("Old value of pmpcfg3: {:x}", pmpcfg3);
352 let pmpcfg3 = (pmpcfg3 & 0x00ffffffu32) | 0x9f000000;
354 log::info!("New value of pmpcfg3: {:x}", pmpcfg3);
355 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG3), pmpcfg3)?;
356 let base = top_earlgrey::SRAM_CTRL_MAIN_RAM_BASE_ADDR as u32;
359 let size = top_earlgrey::SRAM_CTRL_MAIN_RAM_SIZE_BYTES as u32;
360 assert!(size & (size - 1) == 0);
362 let pmpaddr15 = (base >> 2) | ((size - 1) >> 3);
363 log::info!("New value of pmpaddr15: {:x}", pmpaddr15);
364 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR15), pmpaddr15)?;
365
366 log::info!("Configure ePMP for MMIO access.");
368 let pmpcfg2 = jtag.read_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG2))?;
369 log::info!("Old value of pmpcfg2: {:x}", pmpcfg2);
370 let pmpcfg2 = (pmpcfg2 & 0x00ffffffu32) | 0x8f000000;
372 log::info!("New value of pmpcfg2: {:x}", pmpcfg2);
373 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPCFG2), pmpcfg2)?;
374 let base = top_earlgrey::TOP_EARLGREY_MMIO_BASE_ADDR as u32;
376 let size = top_earlgrey::TOP_EARLGREY_MMIO_SIZE_BYTES as u32;
377 assert!(size & (size - 1) == 0);
379 let pmpaddr10 = base >> 2;
380 let pmpaddr11 = (base + size) >> 2;
381 log::info!("New value of pmpaddr10: {:x}", pmpaddr10);
382 log::info!("New value of pmpaddr11: {:x}", pmpaddr11);
383 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR10), pmpaddr10)?;
384 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::PMPADDR11), pmpaddr11)?;
385
386 Ok(())
387}
388
389pub fn prepare_sram_ctrl(jtag: &mut dyn Jtag) -> Result<()> {
391 const SRAM_CTRL_EXEC_REG_OFFSET: u32 = (top_earlgrey::SRAM_CTRL_MAIN_REGS_BASE_ADDR as u32)
392 + bindgen::dif::SRAM_CTRL_EXEC_REG_OFFSET;
393 log::info!("Enabling execution from SRAM.");
394 let mut sram_ctrl_exec = [0];
395 jtag.read_memory32(SRAM_CTRL_EXEC_REG_OFFSET, &mut sram_ctrl_exec)?;
396 log::info!("Old value of sram_exec_en: {:x}", sram_ctrl_exec[0]);
397 sram_ctrl_exec[0] = u8::from(MultiBitBool4::True) as u32;
398 jtag.write_memory32(SRAM_CTRL_EXEC_REG_OFFSET, &sram_ctrl_exec)?;
399 log::info!("New value of sram_exec_en: {:x}", sram_ctrl_exec[0]);
400 Ok(())
401}
402
403pub fn execute_sram_program(
405 jtag: &mut dyn Jtag,
406 prog_info: &SramProgramInfo,
407 exec_mode: ExecutionMode,
408) -> Result<ExecutionResult> {
409 prepare_epmp(jtag)?;
410 prepare_sram_ctrl(jtag)?;
411 let ret_addr = 0xdeadbeefu32;
414 log::info!("set RA to {:x}", ret_addr);
415 jtag.write_riscv_reg(&RiscvReg::Gpr(RiscvGpr::RA), ret_addr)?;
416 log::info!("set A0 to {:x} (crc32)", prog_info.crc32);
418 jtag.write_riscv_reg(&RiscvReg::Gpr(RiscvGpr::A0), prog_info.crc32)?;
419 match exec_mode {
421 ExecutionMode::Jump => {
422 log::info!("resume execution at {:x}", prog_info.entry_point);
423 jtag.resume_at(prog_info.entry_point)?;
424 Ok(ExecutionResult::Executing)
425 }
426 ExecutionMode::JumpAndHalt => {
427 log::info!("set DPC to {:x}", prog_info.entry_point);
428 jtag.write_riscv_reg(&RiscvReg::Csr(RiscvCsr::DPC), prog_info.entry_point)?;
429 Ok(ExecutionResult::HaltedAtStart)
430 }
431 ExecutionMode::JumpAndWait(tmo) => {
432 log::info!("resume execution at {:x}", prog_info.entry_point);
433 jtag.resume_at(prog_info.entry_point)?;
434 log::info!("wait for execution to stop");
435 jtag.wait_halt(tmo)?;
436 jtag.halt()?;
437 let sp = jtag.read_riscv_reg(&RiscvReg::Gpr(RiscvGpr::SP))?;
440 match sp {
441 SRAM_MAGIC_SP_EXECUTION_DONE => {
442 let a0 = jtag.read_riscv_reg(&RiscvReg::Gpr(RiscvGpr::A0))?;
443 Ok(ExecutionResult::ExecutionDone(a0))
444 }
445 SRAM_MAGIC_SP_CRC_ERROR => {
446 Ok(ExecutionResult::ExecutionError(ExecutionError::CrcMismatch))
447 }
448 _ => Ok(ExecutionResult::ExecutionError(ExecutionError::Unknown)),
449 }
450 }
451 }
452}
453
454pub fn load_and_execute_sram_program(
456 jtag: &mut dyn Jtag,
457 file: &SramProgramFile,
458 exec_mode: ExecutionMode,
459) -> Result<ExecutionResult> {
460 let prog_info = load_sram_program(jtag, file)?;
461 execute_sram_program(jtag, &prog_info, exec_mode)
462}