diff --git a/difftest/Cargo.lock b/difftest/Cargo.lock index 91669b161..ea7978d74 100644 --- a/difftest/Cargo.lock +++ b/difftest/Cargo.lock @@ -173,9 +173,9 @@ dependencies = [ "dpi_common", "elf", "hex", - "png", "spike_rs", "svdpi", + "t1devices", "tracing", ] @@ -508,6 +508,25 @@ dependencies = [ "xmas-elf", ] +[[package]] +name = "t1devices" +version = "0.1.0" +dependencies = [ + "anyhow", + "png", + "tracing", +] + +[[package]] +name = "t1emu" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "elf", + "t1devices", +] + [[package]] name = "thread_local" version = "1.1.8" diff --git a/difftest/Cargo.toml b/difftest/Cargo.toml index 6cfdf2339..ef992f867 100644 --- a/difftest/Cargo.toml +++ b/difftest/Cargo.toml @@ -6,6 +6,8 @@ members = [ "dpi_t1emu", "dpi_t1rocketemu", "dpi_common", + "t1devices", + "t1emu", ] exclude = [ "spike_interfaces" diff --git a/difftest/default.nix b/difftest/default.nix index c411ec525..7edb72db5 100644 --- a/difftest/default.nix +++ b/difftest/default.nix @@ -10,7 +10,7 @@ }: assert let - available = [ "dpi_t1emu" "dpi_t1rocketemu" "t1-sim-checker" ]; + available = [ "dpi_t1emu" "dpi_t1rocketemu" "t1-sim-checker" "t1emu" ]; in lib.assertMsg (lib.elem moduleType available) "moduleType is not in ${lib.concatStringsSep ", " available}"; @@ -23,6 +23,8 @@ let ./dpi_t1emu ./dpi_t1rocketemu ./t1-sim-checker + ./t1devices + ./t1emu ./Cargo.lock ./Cargo.toml ./.rustfmt.toml diff --git a/difftest/dpi_t1rocketemu/Cargo.toml b/difftest/dpi_t1rocketemu/Cargo.toml index 179684382..3867abd4a 100644 --- a/difftest/dpi_t1rocketemu/Cargo.toml +++ b/difftest/dpi_t1rocketemu/Cargo.toml @@ -10,12 +10,12 @@ crate-type = ["staticlib", "cdylib"] [dependencies] dpi_common = { path = "../dpi_common" } +t1devices = { path = "../t1devices" } spike_rs = { path = "../spike_rs" } tracing = { workspace = true } svdpi = { workspace = true } anyhow = { workspace = true } hex = "0.4.3" elf = "0.7.4" -png = "0.17.14" [features] diff --git a/difftest/dpi_t1rocketemu/src/drive.rs b/difftest/dpi_t1rocketemu/src/drive.rs index 7a73f292e..1f22a852f 100644 --- a/difftest/dpi_t1rocketemu/src/drive.rs +++ b/difftest/dpi_t1rocketemu/src/drive.rs @@ -1,8 +1,8 @@ use crate::get_t; -use crate::interconnect::simctrl::ExitFlagRef; -use crate::interconnect::{create_emu_addrspace, AddressSpace}; use dpi_common::util::MetaConfig; use svdpi::SvScope; +use t1devices::simctrl::ExitFlagRef; +use t1devices::{create_emu_addrspace, AddressSpace}; use anyhow::Context; use elf::{ @@ -59,7 +59,7 @@ pub(crate) struct Driver { impl Driver { pub(crate) fn new(scope: SvScope, args: &OnlineArgs) -> Self { - let (mut addr_space, exit_flag) = create_emu_addrspace(); + let (mut addr_space, exit_flag) = create_emu_addrspace(get_t); // pass e_entry to rocket let (e_entry, _fn_sym_tab) = Self::load_elf(Path::new(&args.elf_file), &mut addr_space).expect("fail creating simulator"); diff --git a/difftest/dpi_t1rocketemu/src/lib.rs b/difftest/dpi_t1rocketemu/src/lib.rs index 9ce1dea36..a1f931342 100644 --- a/difftest/dpi_t1rocketemu/src/lib.rs +++ b/difftest/dpi_t1rocketemu/src/lib.rs @@ -1,6 +1,5 @@ mod dpi; mod drive; -mod interconnect; // keep in sync with TestBench.verbatimModule // the value is measured in simulation time unit diff --git a/difftest/spike_interfaces/CMakeLists.txt b/difftest/spike_interfaces/CMakeLists.txt index fe5272891..ae896be85 100644 --- a/difftest/spike_interfaces/CMakeLists.txt +++ b/difftest/spike_interfaces/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(libspike REQUIRED) -add_library(${CMAKE_PROJECT_NAME} STATIC spike_interfaces.cc) +add_library(${CMAKE_PROJECT_NAME} STATIC spike_interfaces.cc t1emu.cc) target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC libspike) diff --git a/difftest/spike_interfaces/t1emu.cc b/difftest/spike_interfaces/t1emu.cc new file mode 100644 index 000000000..7a76ed37d --- /dev/null +++ b/difftest/spike_interfaces/t1emu.cc @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "cfg.h" +#include "decode_macros.h" +#include "disasm.h" +#include "mmu.h" +#include "processor.h" +#include "simif.h" + +static_assert(std::is_same_v); + +struct t1emu_memory_vtable_t { + uint8_t* (*addr_to_mem)(void* memory, reg_t addr); + int (*mmio_load)(void* memory, reg_t addr, size_t len, uint8_t* bytes); + int (*mmio_store)(void* memory, reg_t addr, size_t len, const uint8_t* bytes); +}; + +class t1emu_sim_t: public simif_t { + void* m_memory; + t1emu_memory_vtable_t m_vtable; + + cfg_t m_cfg; + isa_parser_t m_isa_parser; + processor_t m_proc; + +public: + t1emu_sim_t( + void* memory, + t1emu_memory_vtable_t const* vtable, + cfg_t cfg, + size_t vlen + ): + m_memory(memory), + m_vtable(*vtable), + m_cfg(std::move(cfg)), + m_isa_parser(m_cfg.isa, m_cfg.priv), + m_proc( + &m_isa_parser, + &m_cfg, + this, + 0, + true, + nullptr, + std::cerr + ) + { + m_proc.VU.lane_num = vlen / 32; + m_proc.VU.lane_granularity = 32; + } + + char* addr_to_mem(reg_t addr) override { + return (char*)m_vtable.addr_to_mem(m_memory, addr); + } + + bool mmio_fetch(reg_t addr, size_t len, uint8_t *bytes) override { + // TODO: currently inst fetch is disallowed on mmio + return false; + } + + bool mmio_load(reg_t addr, size_t len, uint8_t *bytes) override { + return (bool)m_vtable.mmio_load(m_memory, addr, len, bytes); + } + + bool mmio_store(reg_t addr, size_t len, const uint8_t *bytes) override { + return (bool)m_vtable.mmio_store(m_memory, addr, len, bytes); + } + + virtual void proc_reset(unsigned id) override { + // do nothing + } + + virtual const char* get_symbol(uint64_t addr) override { + throw std::logic_error("t1emu_sim_t::get_symbol not implemented"); + } + + const cfg_t& get_cfg() const override { + return m_cfg; + } + + const std::map & + get_harts() const override { + throw std::logic_error("t1emu_sim_t::get_harts not implemented"); + } + + void reset_with_pc(reg_t new_pc) { + m_proc.reset(); + m_proc.check_pc_alignment(new_pc); + m_proc.get_state()->pc = new_pc; + } + + void step_one() { + reg_t pc = m_proc.get_state()->pc; + mmu_t* mmu = m_proc.get_mmu(); + state_t* state = m_proc.get_state(); + + try { + insn_fetch_t fetch = mmu->load_insn(pc); + reg_t new_pc = fetch.func(&m_proc, fetch.insn, pc); + printf("pc=%08lx, new_pc=%08lx\n", pc, new_pc); + if ((new_pc & 1) == 0) { + state->pc = new_pc; + } else { + switch (new_pc) { + case PC_SERIALIZE_BEFORE: state->serialized = true; break; + case PC_SERIALIZE_AFTER: break; + default: throw std::logic_error("invalid PC after fetch.func"); + } + } + } catch (trap_t &trap) { + std::cerr << "Error: spike trapped with " << trap.name() + << " (tval=" << std::uppercase << std::setfill('0') + << std::setw(8) << std::hex << trap.get_tval() + << ", tval2=" << std::setw(8) << std::hex << trap.get_tval2() + << ", tinst=" << std::setw(8) << std::hex << trap.get_tinst() + << ")" << std::endl; + throw; + } catch (std::exception& e) { + std::cerr << e.what() << std::endl; + throw; + } + } +}; + +extern "C" { + t1emu_sim_t* t1emu_create( + void* memory, + t1emu_memory_vtable_t const* vtable, + const char* isa_set, + size_t vlen + ) { + cfg_t cfg; + cfg.isa = strdup(isa_set); + cfg.priv = "M"; + + return new t1emu_sim_t(memory, vtable, cfg, vlen); + } + void t1emu_destroy(t1emu_sim_t* emu) { + delete emu; + } + void t1emu_reset_with_pc(t1emu_sim_t* emu, reg_t new_pc) { + emu->reset_with_pc(new_pc); + } + void t1emu_step_one(t1emu_sim_t* emu) { + emu->step_one(); + } +} diff --git a/difftest/t1devices/Cargo.toml b/difftest/t1devices/Cargo.toml new file mode 100644 index 000000000..121d47bb3 --- /dev/null +++ b/difftest/t1devices/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "t1devices" +edition = "2021" +version.workspace = true + +[dependencies] +anyhow = { workspace = true } +tracing = { workspace = true } +png = "0.17.14" diff --git a/difftest/dpi_t1rocketemu/src/interconnect/framebuffer.rs b/difftest/t1devices/src/framebuffer.rs similarity index 99% rename from difftest/dpi_t1rocketemu/src/interconnect/framebuffer.rs rename to difftest/t1devices/src/framebuffer.rs index 876055314..eb2215375 100644 --- a/difftest/dpi_t1rocketemu/src/interconnect/framebuffer.rs +++ b/difftest/t1devices/src/framebuffer.rs @@ -1,6 +1,6 @@ use std::{env, fs::File, io::BufWriter, path::PathBuf}; -use crate::interconnect::memcpy_mask; +use crate::memcpy_mask; use super::{AddrInfo, Device}; diff --git a/difftest/dpi_t1rocketemu/src/interconnect.rs b/difftest/t1devices/src/lib.rs similarity index 89% rename from difftest/dpi_t1rocketemu/src/interconnect.rs rename to difftest/t1devices/src/lib.rs index 91f29892a..58f9b14ed 100644 --- a/difftest/dpi_t1rocketemu/src/interconnect.rs +++ b/difftest/t1devices/src/lib.rs @@ -1,7 +1,7 @@ use std::any::Any; use framebuffer::FrameBuffer; -use simctrl::{ExitFlagRef, SimCtrl}; +use simctrl::{ExitFlagRef, PfnGetCycle, SimCtrl}; pub mod framebuffer; pub mod simctrl; @@ -38,6 +38,12 @@ pub trait Device: Any + Send + Sync { // NOTE: even if the device does not support partial write, // it shall check mask and behave as a full write when mask is all active fn mem_write_masked(&mut self, addr: AddrInfo, data: &[u8], mask: &[bool]); + + // TODO: add more comments + // spike simif_t::addr_to_mem. Should only be implemented by RegularMemory + fn addr_to_mem(&mut self, offset: u32) -> Option<&mut u8> { + None + } } impl DeviceExt for T {} @@ -119,6 +125,10 @@ impl Device for RegularMemory { let mem_data = &mut self.data[addr.as_range()]; memcpy_mask(mem_data, data, mask); } + + fn addr_to_mem(&mut self, offset: u32) -> Option<&mut u8> { + self.data.get_mut(offset as usize) + } } fn memcpy_mask(dst: &mut [u8], src: &[u8], mask: &[bool]) { @@ -139,6 +149,12 @@ pub struct AddressSpace { } impl AddressSpace { + pub fn addr_to_mem(&mut self, addr: u32) -> Option<&mut u8> { + let device_idx = self.find_device_idx(addr, 1)?; + let dev_entry = &mut self.devices[device_idx]; + dev_entry.device.addr_to_mem(addr - dev_entry.base_and_size.0) + } + pub fn read_mem(&mut self, addr: u32, len: u32, data: &mut [u8]) { assert_eq!(len as usize, data.len()); let Some(device_idx) = self.find_device_idx(addr, len) else { @@ -196,7 +212,7 @@ impl AddressSpace { /// - 0x0400_0000 - 0x0600_0000 : framebuffer /// - 0x1000_0000 - 0x1000_1000 : simctrl /// - 0x2000_0000 - 0xc000_0000 : sram -pub fn create_emu_addrspace() -> (AddressSpace, ExitFlagRef) { +pub fn create_emu_addrspace(get_cycle: PfnGetCycle) -> (AddressSpace, ExitFlagRef) { const SRAM_BASE: u32 = 0x2000_0000; const SRAM_SIZE: u32 = 0xa000_0000; @@ -210,7 +226,7 @@ pub fn create_emu_addrspace() -> (AddressSpace, ExitFlagRef) { let devices = vec![ RegularMemory::with_size(SRAM_SIZE).with_addr(SRAM_BASE, SRAM_SIZE), FrameBuffer::new().with_addr(DISPLAY_BASE, DISPLAY_SIZE), - SimCtrl::new(exit_flag.clone()).with_addr(SIMCTRL_BASE, SIMCTRL_SIZE), + SimCtrl::new(exit_flag.clone(), get_cycle).with_addr(SIMCTRL_BASE, SIMCTRL_SIZE), ]; (AddressSpace { devices }, exit_flag) } diff --git a/difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs b/difftest/t1devices/src/simctrl.rs similarity index 89% rename from difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs rename to difftest/t1devices/src/simctrl.rs index 21f94ad71..28b69ee52 100644 --- a/difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs +++ b/difftest/t1devices/src/simctrl.rs @@ -9,8 +9,6 @@ use std::{ use tracing::{info, warn}; -use crate::get_t; - use super::RegDevice; #[derive(Default, Debug, Clone)] @@ -32,6 +30,8 @@ impl ExitFlagRef { pub const EXIT_CODE: u32 = 0xdead_beef; +pub type PfnGetCycle = fn() -> u64; + /// Reg map: /// - 0x0000 : WO, write EXIT_CODE to mark simulation finish /// - 0x0010 : WO, uart write register @@ -43,16 +43,17 @@ pub const EXIT_CODE: u32 = 0xdead_beef; pub struct SimCtrl { exit_flag: ExitFlagRef, event_file: File, + get_cycle: PfnGetCycle, } impl SimCtrl { - pub fn new(exit_flag: ExitFlagRef) -> Self { + pub fn new(exit_flag: ExitFlagRef, get_cycle: PfnGetCycle) -> Self { let event_file = File::create("mmio-event.jsonl").unwrap(); - SimCtrl { exit_flag, event_file } + SimCtrl { exit_flag, event_file, get_cycle } } fn append_event(&mut self, event: &str, value: u32) { - let cycle = get_t(); + let cycle = (self.get_cycle)(); let buf = format!("{{\"cycle\": {cycle}, \"event\": \"{event}\", \"value\": {value}}}\n"); self.event_file.write_all(buf.as_bytes()).unwrap(); diff --git a/difftest/t1emu/Cargo.toml b/difftest/t1emu/Cargo.toml new file mode 100644 index 000000000..7d75a492b --- /dev/null +++ b/difftest/t1emu/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "t1emu" +edition = "2021" +version.workspace = true + +[dependencies] +t1devices = { path = "../t1devices" } +anyhow.workspace = true +clap.workspace = true +elf = "0.7.4" diff --git a/difftest/t1emu/build.rs b/difftest/t1emu/build.rs new file mode 100644 index 000000000..90ce35da9 --- /dev/null +++ b/difftest/t1emu/build.rs @@ -0,0 +1,25 @@ +use std::env; + +fn main() { + println!( + "cargo::rustc-link-search=native={}", + env::var("SPIKE_LIB_DIR").expect("SPIKE_LIB_DIR should be set") + ); + println!( + "cargo::rustc-link-search=native={}", + env::var("SPIKE_INTERFACES_LIB_DIR").expect("SPIKE_INTERFACES_LIB_DIR should be set") + ); + + println!("cargo::rerun-if-env-changed=SPIKE_LIB_DIR"); + println!("cargo::rerun-if-env-changed=SPIKE_INTERFACES_LIB_DIR"); + + println!("cargo::rustc-link-lib=static=spike_interfaces"); + + println!("cargo::rustc-link-lib=static=riscv"); + println!("cargo::rustc-link-lib=static=softfloat"); + println!("cargo::rustc-link-lib=static=disasm"); + println!("cargo::rustc-link-lib=static=fesvr"); + println!("cargo::rustc-link-lib=static=fdt"); + + println!("cargo::rustc-link-lib=stdc++"); +} diff --git a/difftest/t1emu/src/main.rs b/difftest/t1emu/src/main.rs new file mode 100644 index 000000000..a6f6e074f --- /dev/null +++ b/difftest/t1emu/src/main.rs @@ -0,0 +1,113 @@ +use std::{ + fs::File, + os::unix::fs::FileExt as _, + path::{Path, PathBuf}, +}; + +use anyhow::Context as _; +use clap::Parser; +use elf::{ + abi::{EM_RISCV, ET_EXEC, PT_LOAD}, + endian::LittleEndian, + ElfStream, +}; +use spike::{Spike, SpikeMemory}; +use t1devices::AddressSpace; + +mod spike; + +#[derive(Parser)] +pub struct CliArgs { + /// Path to the ELF file + pub elf_file: PathBuf, + + /// ISA config + #[arg(long)] + pub isa: String, + + /// vlen config + #[arg(long)] + pub vlen: u32, +} + +fn main() -> anyhow::Result<()> { + let args = CliArgs::parse(); + let (memory, exit_flag) = t1devices::create_emu_addrspace(fake_get_cycle); + let mut memory = Memory::new(memory); + let elf_entry = memory.load_elf(&args.elf_file)?; + let mut emu = Spike::new(&args.isa, args.vlen as usize, memory); + dbg!(elf_entry); + emu.reset_with_pc(elf_entry); + while !exit_flag.is_finish() { + emu.step_one(); + } + Ok(()) +} + +fn fake_get_cycle() -> u64 { + 0 +} + +struct Memory { + mem: t1devices::AddressSpace, +} + +impl Memory { + pub fn new(mem: AddressSpace) -> Self { + Memory { mem } + } + + pub fn load_elf(&mut self, path: &Path) -> anyhow::Result { + let mem = &mut self.mem; + let file = File::open(path).with_context(|| "reading ELF file")?; + let mut elf: ElfStream = + ElfStream::open_stream(&file).with_context(|| "parsing ELF file")?; + + if elf.ehdr.e_machine != EM_RISCV { + anyhow::bail!("ELF is not in RISC-V"); + } + + if elf.ehdr.e_type != ET_EXEC { + anyhow::bail!("ELF is not an executable"); + } + + if elf.ehdr.e_phnum == 0 { + anyhow::bail!("ELF has zero size program header"); + } + + let mut load_buffer = Vec::new(); + elf.segments().iter().filter(|phdr| phdr.p_type == PT_LOAD).for_each(|phdr| { + let vaddr: usize = phdr.p_vaddr.try_into().expect("fail converting vaddr(u64) to usize"); + let filesz: usize = phdr.p_filesz.try_into().expect("fail converting p_filesz(u64) to usize"); + + // Load file start from offset into given mem slice + // The `offset` of the read_at method is relative to the start of the file and thus independent from the current cursor. + load_buffer.resize(filesz, 0u8); + file.read_at(load_buffer.as_mut_slice(), phdr.p_offset).unwrap_or_else(|err| { + panic!( + "fail reading ELF into mem with vaddr={}, filesz={}, offset={}. Error detail: {}", + vaddr, filesz, phdr.p_offset, err + ) + }); + mem.write_mem(vaddr as u32, load_buffer.len() as u32, &load_buffer); + }); + + Ok(elf.ehdr.e_entry) + } +} + +impl SpikeMemory for Memory { + fn addr_to_mem(&mut self, addr: u64) -> Option<&mut u8> { + self.mem.addr_to_mem(addr.try_into().unwrap()) + } + + fn mmio_load(&mut self, addr: u64, data: &mut [u8]) -> bool { + self.mem.read_mem(addr.try_into().unwrap(), data.len() as u32, data); + true + } + + fn mmio_store(&mut self, addr: u64, data: &[u8]) -> bool { + self.mem.write_mem(addr.try_into().unwrap(), data.len() as u32, data); + true + } +} diff --git a/difftest/t1emu/src/spike.rs b/difftest/t1emu/src/spike.rs new file mode 100644 index 000000000..315faee8f --- /dev/null +++ b/difftest/t1emu/src/spike.rs @@ -0,0 +1,125 @@ +use std::{ + ffi::{c_char, c_int, c_void, CString}, + ptr, slice, +}; + +#[repr(C)] +pub struct MemoryVTable { + addr_to_mem: unsafe extern "C" fn(memory: *mut c_void, addr: u64) -> *mut u8, + mmio_load: + unsafe extern "C" fn(memory: *mut c_void, addr: u64, len: usize, bytes: *mut u8) -> c_int, + mmio_store: + unsafe extern "C" fn(memory: *mut c_void, addr: u64, len: usize, bytes: *const u8) -> c_int, +} + +extern "C" { + fn t1emu_create( + memory: *mut c_void, + vtable: *const MemoryVTable, + isa_set: *const c_char, + vlen: usize, + ) -> *mut c_void; + fn t1emu_destroy(emu: *mut c_void); + fn t1emu_reset_with_pc(emu: *mut c_void, new_pc: u64); + fn t1emu_step_one(emu: *mut c_void); +} + +pub trait SpikeMemory: 'static { + fn addr_to_mem(&mut self, addr: u64) -> Option<&mut u8>; + fn mmio_load(&mut self, addr: u64, data: &mut [u8]) -> bool; + fn mmio_store(&mut self, addr: u64, data: &[u8]) -> bool; + + unsafe extern "C" fn _addr_to_mem(memory: *mut c_void, addr: u64) -> *mut u8 + where + Self: Sized, + { + let memory = unsafe { &mut *(memory as *mut Self) }; + match memory.addr_to_mem(addr) { + Some(v) => v, + None => ptr::null_mut(), + } + } + + unsafe extern "C" fn _mmio_load( + memory: *mut c_void, + addr: u64, + len: usize, + bytes: *mut u8, + ) -> c_int + where + Self: Sized, + { + let memory = unsafe { &mut *(memory as *mut Self) }; + let data = unsafe { slice::from_raw_parts_mut(bytes, len) }; + memory.mmio_load(addr, data) as c_int + } + unsafe extern "C" fn _mmio_store( + memory: *mut c_void, + addr: u64, + len: usize, + bytes: *const u8, + ) -> c_int + where + Self: Sized, + { + let memory = unsafe { &mut *(memory as *mut Self) }; + let data = unsafe { slice::from_raw_parts(bytes, len) }; + memory.mmio_store(addr, data) as c_int + } + + fn _make_vtable(&mut self) -> (*mut c_void, &'static MemoryVTable) + where + Self: Sized, + { + let vtable = &const { + MemoryVTable { + addr_to_mem: Self::_addr_to_mem, + mmio_load: Self::_mmio_load, + mmio_store: Self::_mmio_store, + } + }; + (self as *mut Self as *mut c_void, vtable) + } +} + +pub struct Spike { + spike: *mut c_void, + + // TODO: use raw pointer if supported + // `memory` is semantically mut borrowed by `spike`, + // thus using a Box actually breaks Rust's alias rules. + // However, raw fat pointers are unstable. + memory: Box, +} + +impl Drop for Spike { + fn drop(&mut self) { + unsafe { + t1emu_destroy(self.spike); + } + } +} + +impl Spike { + pub fn new(isa_set: &str, vlen: usize, memory: M) -> Self { + assert!(vlen > 0 && vlen % 32 == 0); + let isa_set = CString::new(isa_set).unwrap(); + let mut memory = Box::new(memory); + let (_memory, _vtable) = M::_make_vtable(&mut *memory); + let spike = unsafe { t1emu_create(_memory, _vtable, isa_set.as_ptr(), vlen) }; + + Spike { spike, memory } + } + + pub fn reset_with_pc(&mut self, new_pc: u64) { + unsafe { + t1emu_reset_with_pc(self.spike, new_pc); + } + } + + pub fn step_one(&mut self) { + unsafe { + t1emu_step_one(self.spike); + } + } +} diff --git a/nix/t1/default.nix b/nix/t1/default.nix index 9462f0d82..623388f39 100644 --- a/nix/t1/default.nix +++ b/nix/t1/default.nix @@ -60,6 +60,11 @@ lib.makeScope newScope outputName = "t1-sim-checker"; moduleType = "t1-sim-checker"; }; + femu = t1Scope.callPackage ../../difftest { } { + outputName = "t1emu"; + moduleType = "t1emu"; + }; + # --------------------------------------------------------------------------------- # Lowering utilities # ---------------------------------------------------------------------------------