Skip to content

Commit

Permalink
Documentation improvements (#172)
Browse files Browse the repository at this point in the history
* Documentation improvements
  • Loading branch information
fractasy authored Nov 26, 2024
1 parent 726c98e commit cd11b3d
Show file tree
Hide file tree
Showing 18 changed files with 612 additions and 602 deletions.
22 changes: 19 additions & 3 deletions core/src/elf2rom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,64 @@ use elf::{
};
use std::error::Error;

/// Executes the file conversion process
/// Executes the ROM transpilation process: from ELF to Zisk
pub fn elf2rom(elf_file: String) -> Result<ZiskRom, Box<dyn Error>> {
// Get all data from the ELF file copied to a memory buffer
let elf_file_path = std::path::PathBuf::from(elf_file.clone());
let file_data = std::fs::read(elf_file_path)?;

// Parse the ELF data
let elf_bytes = ElfBytes::<AnyEndian>::minimal_parse(file_data.as_slice())?;

// Create an empty ZiskRom instance
let mut rom: ZiskRom = ZiskRom { next_init_inst_addr: ROM_ENTRY, ..Default::default() };

// Iterate on the available section headers of the ELF parsed data
if let Some(section_headers) = elf_bytes.section_headers() {
for section_header in section_headers {
// Consider only the section headers that contain program data
if section_header.sh_type == SHT_PROGBITS {
// Get the program section data as a vector of bytes
let (data_u8, _) = elf_bytes.section_data(&section_header)?;
let mut data = data_u8.to_vec();

// Remove extra bytes if length is not 4-bytes aligned
while data.len() % 4 != 0 {
data.pop();
}

// Get the section data address
let addr = section_header.sh_addr;

// If the data contains instructions, parse them as RISC-V instructions and add them
// to the ROM instructions, at the specified program address
if (section_header.sh_flags & SHF_EXECINSTR as u64) != 0 {
add_zisk_code(&mut rom, addr, &data);
}

// If the data is a writable memory section, add it to the ROM memory using Zisk
// copy instructions
if (section_header.sh_flags & SHF_WRITE as u64) != 0 &&
addr >= RAM_ADDR &&
addr + data.len() as u64 <= RAM_ADDR + RAM_SIZE
{
add_zisk_init_data(&mut rom, addr, &data);
// Otherwise, add it to the ROM as RO data
} else {
rom.ro_data.push(RoData::new(addr, data.len(), data));
}
}
}
}

// Add the program setup, system call and program wrapup instructions
add_entry_exit_jmp(&mut rom, elf_bytes.ehdr.e_entry);

// Preprocess the ROM (experimental)
// Split the ROM instructions based on their address in order to get a better performance when
// searching for the corresponding intruction to the pc program address
let mut max_rom_entry = 0;
let mut max_rom_instructions = 0;

let mut min_rom_na_unstructions = u64::MAX;
let mut max_rom_na_unstructions = 0;
for instruction in &rom.insts {
Expand Down Expand Up @@ -115,7 +130,8 @@ pub fn elf2rom(elf_file: String) -> Result<ZiskRom, Box<dyn Error>> {
Ok(rom)
}

/// Executes the file conversion process, and saves result into a file
/// Executes the ELF file data transpilation process into a Zisk ROM, and saves the result into a
/// file. The file format can be JSON, PIL-based or binary.
pub fn elf2romfile(
elf_file: String,
rom_file: String,
Expand Down
19 changes: 18 additions & 1 deletion core/src/inst_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@ use crate::{Mem, ROM_ENTRY};

/// ZisK instruction context data container, storing the state of the execution
pub struct InstContext {
// Memory, including several read-only sections and one read-write section (input data)
// This memory is initialized before running the program with the input data, and modified by
// the program instructions during the execution. The RW data that has not been previously
// written is read as zero
pub mem: Mem,

// Current values of registers a, b, c, and flag
pub a: u64,
pub b: u64,
pub c: u64,
pub flag: bool,

// Value of sp register
pub sp: u64,

// Value of ROM program execution address, i.e. program counter (pc)
pub pc: u64,

// Current execution step: 0, 1, 2...
pub step: u64,

// End flag, set to true only by the last instruction to execute
pub end: bool,
}

Expand All @@ -18,7 +32,7 @@ impl InstContext {
/// RisK instruction context constructor
pub fn new() -> InstContext {
InstContext {
mem: Mem::new(),
mem: Mem::default(),
a: 0,
b: 0,
c: 0,
Expand All @@ -29,13 +43,16 @@ impl InstContext {
end: false,
}
}

/// Creates a human-readable string describing the instruction context, for debugging purposes
pub fn to_text(&self) -> String {
let s = format! {"a={:x} b={:x} c={:x} flag={} sp={} pc={} step={} end={}", self.a, self.b, self.c, self.flag, self.sp, self.pc, self.step, self.end};
s
}
}

impl Default for InstContext {
/// Default instruction context constructor
fn default() -> Self {
Self::new()
}
Expand Down
51 changes: 33 additions & 18 deletions core/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,38 @@ use crate::UART_ADDR;
use crate::MemSection;

/// Memory structure, containing several read sections and one single write section
#[derive(Default)]
pub struct Mem {
pub read_sections: Vec<MemSection>,
pub write_section: MemSection,
}

/// Default constructor for Mem structure
impl Default for Mem {
fn default() -> Self {
Self::new()
}
}

/// Memory structure implementation
impl Mem {
/// Memory structue constructor
pub fn new() -> Mem {
Mem { read_sections: Vec::new(), write_section: MemSection::new() }
}

/// Adds a read section to the memory structure
pub fn add_read_section(&mut self, start: u64, buffer: &[u8]) {
// Check that the start address is alligned to 8 bytes
if (start & 0x07) != 0 {
panic!(
"Mem::add_read_section() got a start address={:x} not alligned to 8 bytes",
start
);
}

// Calculate the end address
let end = start + buffer.len() as u64;

// Create a mem section with this data
let mut mem_section = MemSection { start, end, buffer: buffer.to_owned() };

// Add zero-value bytes until the end address is alligned to 8 bytes
while (mem_section.end) % 8 != 0 {
mem_section.buffer.push(0);
mem_section.end += 1;
}

// Push the new read section to the read sections list
self.read_sections.push(mem_section);

/*println!(
"Mem::add_read_section() start={:x}={} len={} end={:x}={}",
start,
Expand All @@ -45,12 +49,21 @@ impl Mem {
pub fn add_write_section(&mut self, start: u64, size: u64) {
//println!("Mem::add_write_section() start={} size={}", start, size);

// Check that the start address is alligned to 8 bytes
if (start & 0x07) != 0 {
panic!(
"Mem::add_write_section() got a start address={:x} not alligned to 8 bytes",
start
);
}

// Check the start address is not zero
if start == 0 {
panic!("Mem::add_write_section() got invalid start={}", start);
}

// Check the write section address has been set before this call
// Check the write section address has not been set before this call, since one only write
// section is allowed
if self.write_section.start != 0 {
panic!(
"Mem::add_write_section() only one write section allowed, write_section.start={}",
Expand All @@ -67,10 +80,11 @@ impl Mem {
self.write_section.buffer = mem;
}

/// Read a u64 value from the memory read sections, based on the provided address and width
/// Reads a 1, 2, 4 or 8 bytes value from the memory read sections, based on the provided
/// address and width
#[inline(always)]
pub fn read(&self, addr: u64, width: u64) -> u64 {
// First try to read in the write section
// First try to read from the write section
if (addr >= self.write_section.start) && (addr <= (self.write_section.end - width)) {
// Calculate the read position
let read_position: usize = (addr - self.write_section.start) as usize;
Expand All @@ -94,7 +108,8 @@ impl Mem {
return value;
}

// Search for the section that contains the address using binary search (dicothomic search)
// Search for the section that contains the address using binary search (dicothomic search).
// Read sections are ordered by start address to allow this search.
let section = if let Ok(section) = self.read_sections.binary_search_by(|section| {
if addr < section.start {
std::cmp::Ordering::Greater
Expand All @@ -109,7 +124,7 @@ impl Mem {
panic!("Mem::read() section not found for addr: {} with width: {}", addr, width);
};

// Calculate the read position
// Calculate the buffer relative read position
let read_position: usize = (addr - section.start) as usize;

// Read the requested data based on the provided width
Expand Down
18 changes: 2 additions & 16 deletions core/src/mem_section.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
/// Memory section data, including a buffer vector, and start and end addresses
/// Memory section data, including a buffer (a vector of bytes) and start and end addresses
#[derive(Default)]
pub struct MemSection {
pub start: u64,
pub end: u64,
pub buffer: Vec<u8>,
}

/// Default constructor for MemSection structure
impl Default for MemSection {
fn default() -> Self {
Self::new()
}
}

/// Memory section structure implementation
impl MemSection {
/// Memory section constructor
pub fn new() -> MemSection {
MemSection { start: 0, end: 0, buffer: Vec::new() }
}
}
Loading

0 comments on commit cd11b3d

Please sign in to comment.