Skip to content

Commit

Permalink
Merge pull request #56 from 0xPolygonHermez/feature/small-emutrace
Browse files Browse the repository at this point in the history
Integrated new EmuTrace
  • Loading branch information
xavi-pinsach authored Aug 8, 2024
2 parents 3ee210f + 8d6d75c commit 84a2d30
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 174 deletions.
65 changes: 64 additions & 1 deletion core/src/elf2rom.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//use core::num;
use crate::{
zv2zisk::{add_entry_exit_jmp, add_zisk_code, add_zisk_init_data},
RoData, ZiskRom, RAM_ADDR, RAM_SIZE, ROM_ENTRY,
RoData, ZiskInst, ZiskRom, RAM_ADDR, RAM_SIZE, ROM_ADDR, ROM_ADDR_MAX, ROM_ENTRY,
};
use elf::{
abi::{SHF_EXECINSTR, SHF_WRITE, SHT_PROGBITS},
Expand Down Expand Up @@ -49,6 +49,69 @@ pub fn elf2rom(elf_file: String) -> Result<ZiskRom, Box<dyn Error>> {

add_entry_exit_jmp(&mut rom, elf_bytes.ehdr.e_entry);

// Preprocess the ROM (experimental)
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 {
let addr = *instruction.0;

if addr < ROM_ENTRY {
return Err(format!("Address out of range: {}", addr).into());
} else if addr < ROM_ADDR {
if addr % 4 != 0 {
// When an address is not 4 bytes aligned, it is considered a
// na_rom_instructions We are supposed to have only one non
// aligned instructions in > ROM_ADDRESS
min_rom_na_unstructions = std::cmp::min(min_rom_na_unstructions, addr);
max_rom_na_unstructions = std::cmp::max(max_rom_na_unstructions, addr);
} else {
max_rom_entry = std::cmp::max(max_rom_entry, addr);
}
} else if addr < ROM_ADDR_MAX {
if addr % 4 != 0 {
// When an address is not 4 bytes aligned, it is considered a
// na_rom_instructions We are supposed to have only one non
// aligned instructions in > ROM_ADDRESS
min_rom_na_unstructions = std::cmp::min(min_rom_na_unstructions, addr);
max_rom_na_unstructions = std::cmp::max(max_rom_na_unstructions, addr);
} else {
max_rom_instructions = max_rom_instructions.max(addr);
}
} else {
return Err(format!("Address out of range: {}", addr).into());
}
}

let num_rom_entry = (max_rom_entry - ROM_ENTRY) / 4 + 1;
let num_rom_instructions = (max_rom_instructions - ROM_ADDR) / 4 + 1;
let num_rom_na_instructions = if u64::MAX == min_rom_na_unstructions {
0
} else {
max_rom_na_unstructions - min_rom_na_unstructions + 1
};

rom.rom_entry_instructions = vec![ZiskInst::default(); num_rom_entry as usize];
rom.rom_instructions = vec![ZiskInst::default(); num_rom_instructions as usize];
rom.rom_na_instructions = vec![ZiskInst::default(); num_rom_na_instructions as usize];
rom.offset_rom_na_unstructions = min_rom_na_unstructions;

for instruction in &rom.insts {
let addr = *instruction.0;

if addr % 4 != 0 {
rom.rom_na_instructions[(addr - min_rom_na_unstructions) as usize] =
instruction.1.i.clone();
} else if addr < ROM_ADDR {
rom.rom_entry_instructions[((addr - ROM_ENTRY) >> 2) as usize] =
instruction.1.i.clone();
} else {
rom.rom_instructions[((addr - ROM_ADDR) >> 2) as usize] = instruction.1.i.clone();
}
}

Ok(rom)
}

Expand Down
14 changes: 5 additions & 9 deletions emulator/benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ fn bench_process_rom(c: &mut Criterion) {
c.bench_function("Process ROM", |b| {
// Convert the ELF file to ZisK ROM
let elf_file = "./benches/data/my.elf".to_string();
let mut rom: ZiskRom = {
let rom: ZiskRom = {
// Create an instance of the RISCV -> ZisK program converter
let rv2zk = Riscv2zisk::new(elf_file.clone(), String::new());

Expand Down Expand Up @@ -142,12 +142,8 @@ fn bench_process_rom(c: &mut Criterion) {
};

b.iter(|| {
let _ = ZiskEmulator::process_rom(
&mut rom,
&input,
&options,
None::<Box<dyn Fn(EmuTrace)>>,
);
let _ =
ZiskEmulator::process_rom(&rom, &input, &options, None::<Box<dyn Fn(EmuTrace)>>);
});
});

Expand All @@ -169,7 +165,7 @@ fn bench_process_rom_callback(c: &mut Criterion) {
//let elf_file =
// "../riscof/riscof_work/rv64i_m/A/src/amoxor.w-01.S/dut/my.elf".to_string();
let elf_file = "./benches/data/my.elf".to_string();
let mut rom: ZiskRom = {
let zisk_rom: ZiskRom = {
// Create an instance of the RISCV -> ZisK program converter
let rv2zk = Riscv2zisk::new(elf_file.clone(), String::new());

Expand Down Expand Up @@ -201,7 +197,7 @@ fn bench_process_rom_callback(c: &mut Criterion) {

b.iter(|| {
let _ = ZiskEmulator::process_rom(
&mut rom,
&zisk_rom,
&input,
&options,
Some(Box::new(dummy_callback)),
Expand Down
40 changes: 39 additions & 1 deletion emulator/src/emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,43 @@ impl<'a> Emu<'a> {
}
}

/// Store the 'c' register value based on the storage specified by the current instruction
#[inline(always)]
pub fn store_c_silent(&mut self, instruction: &ZiskInst) {
match instruction.store {
STORE_NONE => {}
STORE_MEM => {
let val: i64 = if instruction.store_ra {
self.ctx.pc as i64 + instruction.jmp_offset2
} else {
self.ctx.c as i64
};
let mut addr: i64 = instruction.store_offset;
if instruction.store_use_sp {
addr += self.ctx.sp as i64;
}
self.ctx.mem.write_silent(addr as u64, val as u64, 8);
}
STORE_IND => {
let val: i64 = if instruction.store_ra {
self.ctx.pc as i64 + instruction.jmp_offset2
} else {
self.ctx.c as i64
};
let mut addr = instruction.store_offset;
if instruction.store_use_sp {
addr += self.ctx.sp as i64;
}
addr += self.ctx.a as i64;
self.ctx.mem.write_silent(addr as u64, val as u64, instruction.ind_width);
}
_ => panic!(
"Emu::store_c_silent() Invalid store={} pc={}",
instruction.store, self.ctx.pc
),
}
}

/// Set SP, if specified by the current instruction
#[inline(always)]
pub fn set_sp(&mut self, instruction: &ZiskInst) {
Expand All @@ -143,6 +180,7 @@ impl<'a> Emu<'a> {

/// Performs one single step of the emulation
#[inline(always)]
#[allow(unused_variables)]
pub fn step(&mut self, options: &EmuOptions, callback: &Option<impl Fn(EmuTrace)>) {
let instruction = self.rom.get_instruction(self.ctx.pc);

Expand Down Expand Up @@ -428,7 +466,7 @@ impl<'a> Emu<'a> {
self.ctx.a = trace_step.a;
self.ctx.b = trace_step.b;
(self.ctx.c, self.ctx.flag) = (instruction.func)(self.ctx.a, self.ctx.b);
self.store_c(instruction);
self.store_c_silent(instruction);
self.set_sp(instruction);
self.set_pc(instruction);
self.ctx.end = instruction.end;
Expand Down
75 changes: 6 additions & 69 deletions emulator/src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
time::Instant,
};
use sysinfo::System;
use zisk_core::{Riscv2zisk, ZiskInst, ZiskRom, ROM_ADDR, ROM_ADDR_MAX, ROM_ENTRY};
use zisk_core::{Riscv2zisk, ZiskRom};

pub trait Emulator {
fn emulate(
Expand Down Expand Up @@ -73,7 +73,7 @@ impl ZiskEmulator {
return Err(ZiskEmulatorErr::Unknown(zisk_rom.err().unwrap().to_string()));
}

Self::process_rom(&mut zisk_rom.unwrap(), inputs, options, callback)
Self::process_rom(&zisk_rom.unwrap(), inputs, options, callback)
}

fn process_rom_file(
Expand All @@ -87,12 +87,12 @@ impl ZiskEmulator {
}

// TODO: load from file
let mut rom: ZiskRom = ZiskRom::new();
Self::process_rom(&mut rom, inputs, options, callback)
let rom: ZiskRom = ZiskRom::new();
Self::process_rom(&rom, inputs, options, callback)
}

pub fn process_rom(
rom: &mut ZiskRom,
rom: &ZiskRom,
inputs: &[u8],
options: &EmuOptions,
callback: Option<impl Fn(EmuTrace)>,
Expand All @@ -101,69 +101,6 @@ impl ZiskEmulator {
println!("process_rom() rom size={} inputs size={}", rom.insts.len(), inputs.len());
}

// Preprocess the ROM (experimental)
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 {
let addr = *instruction.0;

if addr < ROM_ENTRY {
return Err(ZiskEmulatorErr::AddressOutOfRange(addr));
} else if addr < ROM_ADDR {
if addr % 4 != 0 {
// When an address is not 4 bytes aligned, it is considered a
// na_rom_instructions We are supposed to have only one non
// aligned instructions in > ROM_ADDRESS
min_rom_na_unstructions = std::cmp::min(min_rom_na_unstructions, addr);
max_rom_na_unstructions = std::cmp::max(max_rom_na_unstructions, addr);
} else {
max_rom_entry = std::cmp::max(max_rom_entry, addr);
}
} else if addr < ROM_ADDR_MAX {
if addr % 4 != 0 {
// When an address is not 4 bytes aligned, it is considered a
// na_rom_instructions We are supposed to have only one non
// aligned instructions in > ROM_ADDRESS
min_rom_na_unstructions = std::cmp::min(min_rom_na_unstructions, addr);
max_rom_na_unstructions = std::cmp::max(max_rom_na_unstructions, addr);
} else {
max_rom_instructions = max_rom_instructions.max(addr);
}
} else {
return Err(ZiskEmulatorErr::AddressOutOfRange(addr));
}
}

let num_rom_entry = (max_rom_entry - ROM_ENTRY) / 4 + 1;
let num_rom_instructions = (max_rom_instructions - ROM_ADDR) / 4 + 1;
let num_rom_na_instructions = if u64::MAX == min_rom_na_unstructions {
0
} else {
max_rom_na_unstructions - min_rom_na_unstructions + 1
};

rom.rom_entry_instructions = vec![ZiskInst::default(); num_rom_entry as usize];
rom.rom_instructions = vec![ZiskInst::default(); num_rom_instructions as usize];
rom.rom_na_instructions = vec![ZiskInst::default(); num_rom_na_instructions as usize];
rom.offset_rom_na_unstructions = min_rom_na_unstructions;

for instruction in &rom.insts {
let addr = *instruction.0;

if addr % 4 != 0 {
rom.rom_na_instructions[(addr - min_rom_na_unstructions) as usize] =
instruction.1.i.clone();
} else if addr < ROM_ADDR {
rom.rom_entry_instructions[((addr - ROM_ENTRY) >> 2) as usize] =
instruction.1.i.clone();
} else {
rom.rom_instructions[((addr - ROM_ADDR) >> 2) as usize] = instruction.1.i.clone();
}
}

// Create a emulator instance with this rom and inputs
let mut emu = Emu::new(rom);
let start = Instant::now();
Expand Down Expand Up @@ -219,7 +156,7 @@ impl ZiskEmulator {
}

pub fn process_slice<F: AbstractField>(
rom: &mut ZiskRom,
rom: &ZiskRom,
trace: &EmuTrace,
) -> Result<EmuFullTrace<F>, ZiskEmulatorErr> {
// Create a emulator instance with this rom
Expand Down
21 changes: 14 additions & 7 deletions emulator/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ impl Mem {
/// Write a u64 value to the memory write section, based on the provided address and width
#[inline(always)]
pub fn write(&mut self, addr: u64, val: u64, width: u64) {
// Call write_silent to perform the real work
self.write_silent(addr, val, width);

// Log to console bytes written to UART address
if (addr == UART_ADDR) && (width == 1) {
print!("{}", String::from(val as u8 as char));
}
}

/// Write a u64 value to the memory write section, based on the provided address and width
#[inline(always)]
pub fn write_silent(&mut self, addr: u64, val: u64, width: u64) {
//println!("Mem::write() addr={:x}={} width={} value={:x}={}", addr, addr, width, val,
// val);

Expand All @@ -135,7 +147,7 @@ impl Mem {

// Check that the address and width fall into this section address range
if (addr < section.start) || ((addr + width) > section.end) {
panic!("Mem::write() invalid addr={}", addr);
panic!("Mem::write_silent() invalid addr={}", addr);
}

// Calculate the write position
Expand All @@ -150,12 +162,7 @@ impl Mem {
.copy_from_slice(&(val as u32).to_le_bytes()),
8 => section.buffer[write_position..write_position + 8]
.copy_from_slice(&val.to_le_bytes()),
_ => panic!("Mem::write() invalid width={}", width),
}

// Log to console bytes written to UART address
if (addr == UART_ADDR) && (width == 1) {
print!("{}", String::from(val as u8 as char));
_ => panic!("Mem::write_silent() invalid width={}", width),
}
}
}
Loading

0 comments on commit 84a2d30

Please sign in to comment.