From ae09f5f361723d33e5d543c62a84ac60ae6f5517 Mon Sep 17 00:00:00 2001 From: Clo91eaf Date: Mon, 13 May 2024 21:59:08 +0800 Subject: [PATCH] [difftest] add spike wrapper to run elf with spike purely [difftest] print pc with hex format [difftest] use space instead of tab --- difftest/libspike_interfaces/CMakeLists.txt | 34 + difftest/libspike_interfaces/default.nix | 11 + .../spike_interfaces-config.cmake | 3 + .../libspike_interfaces/spike_interfaces.cc | 241 +++++++ .../libspike_interfaces/spike_interfaces.h | 63 ++ .../libspike_interfaces/spike_interfaces_c.h | 59 ++ difftest/offline_difftest/.gitignore | 1 + difftest/offline_difftest/.rustfmt.toml | 2 + difftest/offline_difftest/Cargo.lock | 606 ++++++++++++++++++ difftest/offline_difftest/Cargo.toml | 19 + difftest/offline_difftest/default.nix | 18 + difftest/offline_difftest/src/main.rs | 64 ++ difftest/offline_difftest/src/spike.rs | 148 +++++ .../src/spike/libspike_interfaces.rs | 207 ++++++ nix/overlay.nix | 1 + nix/t1/default.nix | 2 + 16 files changed, 1479 insertions(+) create mode 100644 difftest/libspike_interfaces/CMakeLists.txt create mode 100644 difftest/libspike_interfaces/default.nix create mode 100644 difftest/libspike_interfaces/spike_interfaces-config.cmake create mode 100644 difftest/libspike_interfaces/spike_interfaces.cc create mode 100644 difftest/libspike_interfaces/spike_interfaces.h create mode 100644 difftest/libspike_interfaces/spike_interfaces_c.h create mode 100644 difftest/offline_difftest/.gitignore create mode 100644 difftest/offline_difftest/.rustfmt.toml create mode 100644 difftest/offline_difftest/Cargo.lock create mode 100644 difftest/offline_difftest/Cargo.toml create mode 100644 difftest/offline_difftest/default.nix create mode 100644 difftest/offline_difftest/src/main.rs create mode 100644 difftest/offline_difftest/src/spike.rs create mode 100644 difftest/offline_difftest/src/spike/libspike_interfaces.rs diff --git a/difftest/libspike_interfaces/CMakeLists.txt b/difftest/libspike_interfaces/CMakeLists.txt new file mode 100644 index 000000000..b51535d56 --- /dev/null +++ b/difftest/libspike_interfaces/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.20) +project(spike_interfaces LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) + +find_package(libspike REQUIRED) + +add_library(${CMAKE_PROJECT_NAME} SHARED spike_interfaces.cc) + +target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC libspike) + +target_include_directories(${CMAKE_PROJECT_NAME} INTERFACE + $ + $ +) + +# just playing with CMake export, maybe not necessary +target_sources(${CMAKE_PROJECT_NAME} PUBLIC + FILE_SET HEADERS + FILES spike_interfaces.h spike_interfaces_c.h) + +install( + TARGETS ${CMAKE_PROJECT_NAME} + EXPORT ${CMAKE_PROJECT_NAME}_targets + PUBLIC_HEADER + FILE_SET HEADERS +) + +install( + EXPORT ${CMAKE_PROJECT_NAME}_targets + NAMESPACE ${CMAKE_PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CMAKE_PROJECT_NAME} +) + +install(FILES ${CMAKE_PROJECT_NAME}-config.cmake DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}) diff --git a/difftest/libspike_interfaces/default.nix b/difftest/libspike_interfaces/default.nix new file mode 100644 index 000000000..032a7d42b --- /dev/null +++ b/difftest/libspike_interfaces/default.nix @@ -0,0 +1,11 @@ +{ lib, stdenv, cmake, libspike }: + +stdenv.mkDerivation { + name = "libspike_interfaces"; + src = with lib.fileset; toSource { + root = ./.; + fileset = fileFilter (file: file.name != "default.nix") ./.; + }; + nativeBuildInputs = [ cmake ]; + propagatedBuildInputs = [ libspike ]; +} diff --git a/difftest/libspike_interfaces/spike_interfaces-config.cmake b/difftest/libspike_interfaces/spike_interfaces-config.cmake new file mode 100644 index 000000000..8a0867895 --- /dev/null +++ b/difftest/libspike_interfaces/spike_interfaces-config.cmake @@ -0,0 +1,3 @@ +include(CMakeFindDependencyMacro) +find_dependency(libspike 0.1.0) +include(${CMAKE_CURRENT_LIST_DIR}/libspike_interface_targets.cmake) diff --git a/difftest/libspike_interfaces/spike_interfaces.cc b/difftest/libspike_interfaces/spike_interfaces.cc new file mode 100644 index 000000000..514877bab --- /dev/null +++ b/difftest/libspike_interfaces/spike_interfaces.cc @@ -0,0 +1,241 @@ +#include "spike_interfaces.h" + +constexpr uint32_t CSR_MSIMEND = 0x7cc; + +cfg_t make_spike_cfg(const std::string& varch) { + cfg_t cfg; + cfg.initrd_bounds = std::make_pair((reg_t)0, (reg_t)0), + cfg.bootargs = nullptr; + cfg.isa = DEFAULT_ISA; + cfg.priv = DEFAULT_PRIV; + cfg.varch = varch.data(); + cfg.misaligned = false; + cfg.endianness = endianness_little; + cfg.pmpregions = 16; + cfg.pmpgranularity = 4; + cfg.mem_layout = std::vector(); + cfg.hartids = std::vector(); + cfg.explicit_hartids = false; + cfg.real_time_clint = false; + cfg.trigger_count = 4; + return cfg; +} + +Spike::Spike(const char* arch, const char* set, const char* lvl) + : sim(), + varch(arch), + isa(set, lvl), + cfg(make_spike_cfg(varch)), + proc( + /*isa*/ &isa, + /*cfg*/ &cfg, + /*sim*/ &sim, + /*id*/ 0, + /*halt on reset*/ true, + /*log_file_t*/ nullptr, + /*sout*/ std::cerr) { + auto& csrmap = proc.get_state()->csrmap; + csrmap[CSR_MSIMEND] = std::make_shared(&proc, CSR_MSIMEND, 1); + proc.enable_log_commits(); +} + +spike_t* spike_new(const char* arch, const char* set, const char* lvl) { + return new spike_t{new Spike(arch, set, lvl)}; +} + +const char* proc_disassemble(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto mmu = proc->p->get_mmu(); + auto disasm = proc->p->get_disassembler(); + auto fetch = mmu->load_insn(pc); + return strdup(disasm->disassemble(fetch.insn).c_str()); +} + +spike_processor_t* spike_get_proc(spike_t* spike) { + return new spike_processor_t{spike->s->get_proc()}; +} + +void proc_reset(spike_processor_t* proc) { + proc->p->reset(); +} + +spike_state_t* proc_get_state(spike_processor_t* proc) { + return new spike_state_t{proc->p->get_state()}; +} + +reg_t proc_func(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto mmu = proc->p->get_mmu(); + auto fetch = mmu->load_insn(pc); + return fetch.func(proc->p, fetch.insn, pc); +} + +reg_t proc_get_insn(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto mmu = proc->p->get_mmu(); + auto fetch = mmu->load_insn(pc); + return fetch.insn.bits(); +} + +uint8_t* proc_get_vreg_addr(spike_processor_t* proc) { + return &proc->p->VU.elt(0, 0); +} + +uint32_t extract_f32(freg_t f) { return (uint32_t)f.v[0]; } + +inline uint32_t clip(uint32_t binary, int a, int b) { + int nbits = b - a + 1; + uint32_t mask = nbits >= 32 ? (uint32_t)-1 : (1 << nbits) - 1; + return (binary >> a) & mask; +} + +uint64_t proc_get_rs(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto fetch = proc->p->get_mmu()->load_insn(pc); + return (uint64_t)fetch.insn.rs1() << 32 | (uint64_t)fetch.insn.rs2(); +} + +uint32_t proc_get_rd(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto fetch = proc->p->get_mmu()->load_insn(pc); + return fetch.insn.rd(); +} + +uint64_t proc_get_rs_bits(spike_processor_t* proc) { + auto state = proc->p->get_state(); + auto &xr = state->XPR; + auto &fr = state->FPR; + auto pc = state->pc; + auto inst_bits = proc_get_insn(proc); + + uint32_t opcode = clip(inst_bits, 0, 6); + uint32_t width = clip(inst_bits, 12, 14); // also funct3 + auto fetch = proc->p->get_mmu()->load_insn(pc); + uint32_t rs1_bits, rs2_bits; + bool is_fp_operands = opcode == 0b1010111 && (width == 0b101 /* OPFVF */); + if (is_fp_operands) { + rs1_bits = extract_f32(fr[fetch.insn.rs1()]); + rs2_bits = extract_f32(fr[fetch.insn.rs2()]); + } else { + rs1_bits = xr[fetch.insn.rs1()]; + rs2_bits = xr[fetch.insn.rs2()]; + } + + return (uint64_t)rs1_bits << 32 | (uint64_t)rs2_bits; +} + +uint64_t proc_vu_get_vtype(spike_processor_t* proc) { + return proc->p->VU.vtype->read(); +} + +uint32_t proc_vu_get_vxrm(spike_processor_t* proc) { + return proc->p->VU.vxrm->read(); +} + +uint32_t proc_vu_get_vnf(spike_processor_t* proc) { + auto pc = proc->p->get_state()->pc; + auto fetch = proc->p->get_mmu()->load_insn(pc); + return fetch.insn.v_nf(); +} + +bool proc_vu_get_vill(spike_processor_t* proc) { + return proc->p->VU.vill; +} + +bool proc_vu_get_vxsat(spike_processor_t* proc) { + return proc->p->VU.vxsat->read(); +} + +uint32_t proc_vu_get_vl(spike_processor_t* proc) { + return proc->p->VU.vl->read(); +} + +uint16_t proc_vu_get_vstart(spike_processor_t* proc) { + return proc->p->VU.vstart->read(); +} + +reg_t state_get_pc(spike_state_t* state) { + return state->s->pc; +} + +void state_clear(spike_state_t* state) { + state->s->log_reg_write.clear(); + state->s->log_mem_read.clear(); + state->s->log_mem_write.clear(); +} + +static void state_set_serialized(spike_state_t* state, bool serialized) { + state->s->serialized = serialized; +} + +uint64_t state_handle_pc(spike_state_t* state, uint64_t new_pc) { + if ((new_pc & 1) == 0) { + state_set_pc(state, new_pc); + } else { + switch (new_pc) { + case PC_SERIALIZE_BEFORE: + state_set_serialized(state, true); + break; + case PC_SERIALIZE_AFTER: + break; + default: + return -1; + } + } + return 0; +} + +void state_set_pc(spike_state_t* state, uint64_t pc) { + state->s->pc = pc; +} + +uint32_t state_get_mem_write_size(spike_state_t* state) { + return state->s->log_mem_write.size(); +} + +uint32_t state_get_mem_write_addr(spike_state_t* state, uint32_t index) { + return std::get<0>(state->s->log_mem_write[index]) & 0xffffffff; +} + +uint64_t state_get_mem_write_value(spike_state_t* state, uint32_t index) { + return std::get<1>(state->s->log_mem_write[index]); +} + +uint8_t state_get_mem_write_size_by_byte(spike_state_t* state, uint32_t index) { + return std::get<2>(state->s->log_mem_write[index]); +} + +uint32_t state_get_mem_read_size(spike_state_t* state) { + return state->s->log_mem_read.size(); +} + +uint32_t state_get_mem_read_addr(spike_state_t* state, uint32_t index) { + return std::get<0>(state->s->log_mem_read[index]) & 0xffffffff; +} + +uint8_t state_get_mem_read_size_by_byte(spike_state_t* state, uint32_t index) { + return std::get<2>(state->s->log_mem_read[index]); +} + +reg_t state_exit(spike_state_t* state) { + auto& csrmap = state->s->csrmap; + return csrmap[CSR_MSIMEND]->read(); +} + +void spike_register_callback(ffi_callback callback) { + ffi_addr_to_mem = callback; + + return; +} + +void spike_destruct(spike_t* spike) { + delete spike; +} + +void proc_destruct(spike_processor_t* proc) { + delete proc; +} + +void state_destruct(spike_state_t* state) { + delete state; +} diff --git a/difftest/libspike_interfaces/spike_interfaces.h b/difftest/libspike_interfaces/spike_interfaces.h new file mode 100644 index 000000000..fcaf46bc7 --- /dev/null +++ b/difftest/libspike_interfaces/spike_interfaces.h @@ -0,0 +1,63 @@ +#ifndef __SPIKE_INTERFCES_H__ +#define __SPIKE_INTERFCES_H__ + +#include "cfg.h" +#include "decode_macros.h" +#include "disasm.h" +#include "mmu.h" +#include "processor.h" +#include "simif.h" +#include "spike_interfaces_c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ffi_callback ffi_addr_to_mem; + +class sim_t : public simif_t { + public: + sim_t() {} + ~sim_t() {} + char* addr_to_mem(reg_t addr) override { return ffi_addr_to_mem(addr); } + bool mmio_load(reg_t addr, size_t len, uint8_t* bytes) override { throw std::logic_error("not implemented"); } + bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes) override { throw std::logic_error("not implemented"); } + virtual void proc_reset(unsigned id) override { } + virtual const char* get_symbol(uint64_t addr) override { throw std::logic_error("not implemented"); } + [[nodiscard]] const cfg_t& get_cfg() const override { throw std::logic_error("not implemented"); } + [[nodiscard]] const std::map& get_harts() + const override { throw std::logic_error("not implemented"); } +}; + +class Spike { + public: + Spike(const char* arch, const char* set, const char* lvl); + processor_t* get_proc() { return &proc; } + + private: + std::string varch; + cfg_t cfg; + sim_t sim; + isa_parser_t isa; + processor_t proc; +}; + +struct spike_t { + Spike* s; + ffi_callback ffi_addr_to_mem; +}; +struct spike_processor_t { + processor_t* p; +}; +struct spike_state_t { + state_t* s; +}; +struct spike_mmu_t { + mmu_t* m; +}; + +#ifdef __cplusplus +} +#endif + +#endif // __SPIKE_INTERFCES_H__ diff --git a/difftest/libspike_interfaces/spike_interfaces_c.h b/difftest/libspike_interfaces/spike_interfaces_c.h new file mode 100644 index 000000000..2c2a73049 --- /dev/null +++ b/difftest/libspike_interfaces/spike_interfaces_c.h @@ -0,0 +1,59 @@ +#ifndef __SPIKE_INTERFCES_C_H__ +#define __SPIKE_INTERFCES_C_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char* (*ffi_callback)(uint64_t); + +typedef struct spike_t spike_t; +typedef struct spike_processor_t spike_processor_t; +typedef struct spike_state_t spike_state_t; + +void spike_register_callback(ffi_callback callback); +spike_t* spike_new(const char* arch, const char* set, const char* lvl); +const char* proc_disassemble(spike_processor_t* proc); +void proc_reset(spike_processor_t* proc); +spike_processor_t* spike_get_proc(spike_t* spike); +spike_state_t* proc_get_state(spike_processor_t* proc); + +uint64_t proc_func(spike_processor_t* proc); +uint64_t proc_get_insn(spike_processor_t* proc); +uint8_t* proc_get_vreg_addr(spike_processor_t* proc); +uint64_t proc_get_rs(spike_processor_t* proc); +uint32_t proc_get_rd(spike_processor_t* proc); +uint64_t proc_get_rs_bits(spike_processor_t* proc); + +uint64_t proc_vu_get_vtype(spike_processor_t* proc); +uint32_t proc_vu_get_vxrm(spike_processor_t* proc); +uint32_t proc_vu_get_vnf(spike_processor_t* proc); +bool proc_vu_get_vill(spike_processor_t* proc); +bool proc_vu_get_vxsat(spike_processor_t* proc); +uint32_t proc_vu_get_vl(spike_processor_t* proc); +uint16_t proc_vu_get_vstart(spike_processor_t* proc); + +uint64_t state_get_pc(spike_state_t* state); +uint64_t state_handle_pc(spike_state_t* state, uint64_t new_pc); +void state_set_pc(spike_state_t* state, uint64_t pc); +uint32_t state_get_mem_write_size(spike_state_t* state); +uint32_t state_get_mem_write_addr(spike_state_t* state, uint32_t index); +uint64_t state_get_mem_write_value(spike_state_t* state, uint32_t index); +uint8_t state_get_mem_write_size_by_byte(spike_state_t* state, uint32_t index); +uint32_t state_get_mem_read_size(spike_state_t* state); +uint32_t state_get_mem_read_addr(spike_state_t* state, uint32_t index); +uint8_t state_get_mem_read_size_by_byte(spike_state_t* state, uint32_t index); +void state_clear(spike_state_t* state); + +void spike_destruct(spike_t* spike); +void proc_destruct(spike_processor_t* proc); +void state_destruct(spike_state_t* state); +uint64_t state_exit(spike_state_t* state); + +#ifdef __cplusplus +} +#endif + +#endif // __SPIKE_INTERFCES_C_H__ diff --git a/difftest/offline_difftest/.gitignore b/difftest/offline_difftest/.gitignore new file mode 100644 index 000000000..9f970225a --- /dev/null +++ b/difftest/offline_difftest/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/difftest/offline_difftest/.rustfmt.toml b/difftest/offline_difftest/.rustfmt.toml new file mode 100644 index 000000000..3f95bb108 --- /dev/null +++ b/difftest/offline_difftest/.rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = false +tab_spaces = 2 \ No newline at end of file diff --git a/difftest/offline_difftest/Cargo.lock b/difftest/offline_difftest/Cargo.lock new file mode 100644 index 000000000..ae1c104d3 --- /dev/null +++ b/difftest/offline_difftest/Cargo.lock @@ -0,0 +1,606 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "offline_difftest" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "lazy_static", + "libc", + "libloading", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", + "xmas-elf", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "syn" +version = "2.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" + +[[package]] +name = "xmas-elf" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42c49817e78342f7f30a181573d82ff55b88a35f86ccaf07fc64b3008f56d1c6" +dependencies = [ + "zero", +] + +[[package]] +name = "zero" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" diff --git a/difftest/offline_difftest/Cargo.toml b/difftest/offline_difftest/Cargo.toml new file mode 100644 index 000000000..eca0a08dd --- /dev/null +++ b/difftest/offline_difftest/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "offline_difftest" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.4.18", features = ["derive"] } +libc = "0.2.153" +libloading = "0.8.1" +xmas-elf = "0.9.1" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1.0.79" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +lazy_static = "1.4.0" + diff --git a/difftest/offline_difftest/default.nix b/difftest/offline_difftest/default.nix new file mode 100644 index 000000000..50442c48d --- /dev/null +++ b/difftest/offline_difftest/default.nix @@ -0,0 +1,18 @@ +{ lib +, libspike +, rustPlatform +, libspike_interfaces +, rtl +}: + +rustPlatform.buildRustPackage { + name = "offline_difftest"; + src = with lib.fileset; toSource { + root = ./.; + fileset = fileFilter (file: file.name != "default.nix") ./.; + }; + buildInputs = [ libspike libspike_interfaces ]; + cargoLock = { + lockFile = ./Cargo.lock; + }; +} diff --git a/difftest/offline_difftest/src/main.rs b/difftest/offline_difftest/src/main.rs new file mode 100644 index 000000000..9411bb6a9 --- /dev/null +++ b/difftest/offline_difftest/src/main.rs @@ -0,0 +1,64 @@ +mod spike; + +use clap::Parser; +use spike::SpikeHandle; +use std::path::Path; +use tracing::{info, Level}; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +/// A simple offline difftest tool +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Path to the ELF file + #[arg(short, long)] + elf_file: String, + + /// count start of instruction trace + #[arg(short, long, default_value = "0")] + start: u64, + + /// count step of instruction trace + #[arg(short, long, default_value = "100000")] + step: u64, +} + +fn main() -> anyhow::Result<()> { + let global_logger = FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .with_max_level(Level::TRACE) + .without_time() + .with_target(false) + .compact() + .finish(); + tracing::subscriber::set_global_default(global_logger) + .expect("internal error: fail to setup log subscriber"); + + let args = Args::parse(); + + // count the instruction + let mut count: u64 = 0; + + // if there is no log file, just run spike and quit + let spike = SpikeHandle::new(1usize << 32, Path::new(&args.elf_file)); + loop { + count += 1; + if count > args.start && count % args.step == 0 { + info!( + "count = {}, pc = {:#x}, inst = {}", + count, + spike.get_pc(), + spike.get_disasm() + ); + } + + match spike.exec() { + Ok(_) => {} + Err(e) => { + info!("total instrucions count = {}", count); + info!("Simulation quit with error/quit: {:?}", e); + return Ok(()); + } + } + } +} diff --git a/difftest/offline_difftest/src/spike.rs b/difftest/offline_difftest/src/spike.rs new file mode 100644 index 000000000..bfbc964f1 --- /dev/null +++ b/difftest/offline_difftest/src/spike.rs @@ -0,0 +1,148 @@ +use lazy_static::lazy_static; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::sync::Mutex; +use tracing::{info, trace}; +use xmas_elf::{ + header, + program::{ProgramHeader, Type}, + ElfFile, +}; + +mod libspike_interfaces; +use libspike_interfaces::*; + +// read the addr from spike memory +// caller should make sure the address is valid +#[no_mangle] +pub extern "C" fn rs_addr_to_mem(addr: u64) -> *mut u8 { + let addr = addr as usize; + let mut spike_mem = SPIKE_MEM.lock().unwrap(); + let spike_mut = spike_mem.as_mut().unwrap(); + &mut spike_mut.mem[addr] as *mut u8 +} + +pub struct SpikeMem { + pub mem: Vec, + pub size: usize, +} + +lazy_static! { + static ref SPIKE_MEM: Mutex>> = Mutex::new(None); +} + +fn init_memory(size: usize) { + let mut spike_mem = SPIKE_MEM.lock().unwrap(); + if spike_mem.is_none() { + info!("Creating SpikeMem with size: 0x{:x}", size); + *spike_mem = Some(Box::new(SpikeMem { + mem: vec![0; size], + size, + })); + } +} + +fn ld(addr: usize, len: usize, bytes: Vec) -> anyhow::Result<()> { + trace!("ld: addr: 0x{:x}, len: 0x{:x}", addr, len); + let mut spike_mem = SPIKE_MEM.lock().unwrap(); + let spike_ref = spike_mem.as_mut().unwrap(); + + assert!(addr + len <= spike_ref.size); + + let dst = &mut spike_ref.mem[addr..addr + len]; + for (i, byte) in bytes.iter().enumerate() { + dst[i] = *byte; + } + + Ok(()) +} + +fn load_elf(fname: &Path) -> anyhow::Result { + let mut file = File::open(fname).unwrap(); + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).unwrap(); + + let elf_file = ElfFile::new(&buffer).unwrap(); + + let header = elf_file.header; + assert_eq!(header.pt2.machine().as_machine(), header::Machine::RISC_V); + assert_eq!(header.pt1.class(), header::Class::ThirtyTwo); + + for ph in elf_file.program_iter() { + match ph { + ProgramHeader::Ph32(ph) => { + if ph.get_type() == Ok(Type::Load) { + let offset = ph.offset as usize; + let size = ph.file_size as usize; + let addr = ph.virtual_addr as usize; + + let slice = &buffer[offset..offset + size]; + ld(addr, size, slice.to_vec()).unwrap(); + } + } + _ => (), + } + } + + Ok(header.pt2.entry_point()) +} + +pub struct SpikeHandle { + spike: Spike, +} + +impl SpikeHandle { + pub fn new(size: usize, fname: &Path) -> Self { + // register the addr_to_mem callback + unsafe { spike_register_callback(rs_addr_to_mem) } + + // create a new spike memory instance + init_memory(size); + + // load the elf file + let entry_addr = load_elf(fname).unwrap(); + + // initialize spike + let arch = "vlen:1024,elen:32"; + let set = "rv32gcv"; + let lvl = "M"; + + let spike = Spike::new(arch, set, lvl); + + // initialize processor + let proc = spike.get_proc(); + let state = proc.get_state(); + proc.reset(); + state.set_pc(entry_addr); + + SpikeHandle { spike } + } + + // just execute one instruction for no-difftest + pub fn exec(&self) -> anyhow::Result<()> { + let spike = &self.spike; + let proc = spike.get_proc(); + let state = proc.get_state(); + + let new_pc = proc.func(); + + state.handle_pc(new_pc).unwrap(); + + let ret = state.exit(); + + if ret == 0 { + return Err(anyhow::anyhow!("simulation finished!")); + } + + Ok(()) + } + + pub fn get_pc(&self) -> u64 { + self.spike.get_proc().get_state().get_pc() + } + + pub fn get_disasm(&self) -> String { + format!("{:?}", self.spike.get_proc().disassemble()) + } +} diff --git a/difftest/offline_difftest/src/spike/libspike_interfaces.rs b/difftest/offline_difftest/src/spike/libspike_interfaces.rs new file mode 100644 index 000000000..84d0d225c --- /dev/null +++ b/difftest/offline_difftest/src/spike/libspike_interfaces.rs @@ -0,0 +1,207 @@ +use libc::c_char; +use std::ffi::{CStr, CString}; + +pub struct Spike { + spike: *mut (), +} + +impl Spike { + pub fn new(arch: &str, set: &str, lvl: &str) -> Self { + let arch = CString::new(arch).unwrap(); + let set = CString::new(set).unwrap(); + let lvl = CString::new(lvl).unwrap(); + let spike = unsafe { spike_new(arch.as_ptr(), set.as_ptr(), lvl.as_ptr()) }; + Spike { spike } + } + + pub fn get_proc(&self) -> Processor { + let processor = unsafe { spike_get_proc(self.spike as *mut ()) }; + Processor { processor } + } +} + +impl Drop for Spike { + fn drop(&mut self) { + unsafe { spike_destruct(self.spike) } + } +} + +pub struct Processor { + processor: *mut (), +} + +impl Processor { + pub fn disassemble(&self) -> std::borrow::Cow { + let bytes = unsafe { proc_disassemble(self.processor) }; + let c_str = unsafe { CStr::from_ptr(bytes as *mut c_char) }; + c_str.to_string_lossy() + } + + pub fn reset(&self) { + unsafe { proc_reset(self.processor) } + } + + pub fn get_state(&self) -> State { + let state = unsafe { proc_get_state(self.processor) }; + State { state } + } + + pub fn func(&self) -> u64 { + unsafe { proc_func(self.processor) } + } + + pub fn get_insn(&self) -> u64 { + unsafe { proc_get_insn(self.processor) } + } + + pub fn get_vreg_addr(&self) -> *mut u8 { + unsafe { proc_get_vreg_addr(self.processor) } + } + + pub fn get_rs(&self) -> (u32, u32) { + let rs: u64 = unsafe { proc_get_rs(self.processor) }; + ((rs >> 32) as u32, rs as u32) + } + + pub fn get_rd(&self) -> u32 { + unsafe { proc_get_rd(self.processor) } + } + + pub fn get_rs_bits(&self) -> (u32, u32) { + let rs_bits: u64 = unsafe { proc_get_rs_bits(self.processor) }; + ((rs_bits >> 32) as u32, rs_bits as u32) + } + + // vu + pub fn vu_get_vtype(&self) -> u64 { + unsafe { proc_vu_get_vtype(self.processor) } + } + + pub fn vu_get_vxrm(&self) -> u32 { + unsafe { proc_vu_get_vxrm(self.processor) } + } + + pub fn vu_get_vnf(&self) -> u32 { + unsafe { proc_vu_get_vnf(self.processor) } + } + + pub fn vu_get_vill(&self) -> bool { + unsafe { proc_vu_get_vill(self.processor) } + } + + pub fn vu_get_vxsat(&self) -> bool { + unsafe { proc_vu_get_vxsat(self.processor) } + } + + pub fn vu_get_vl(&self) -> u32 { + unsafe { proc_vu_get_vl(self.processor) } + } + + pub fn vu_get_vstart(&self) -> u16 { + unsafe { proc_vu_get_vstart(self.processor) } + } +} + +impl Drop for Processor { + fn drop(&mut self) { + unsafe { proc_destruct(self.processor) } + } +} + +pub struct State { + state: *mut (), +} + +impl State { + pub fn set_pc(&self, pc: u64) { + unsafe { state_set_pc(self.state, pc) } + } + + pub fn get_pc(&self) -> u64 { + unsafe { state_get_pc(self.state) } + } + + pub fn handle_pc(&self, pc: u64) -> anyhow::Result<()> { + match unsafe { state_handle_pc(self.state, pc) } { + 0 => Ok(()), + _ => Err(anyhow::anyhow!("Error handling pc")), + } + } + + pub fn get_mem_write_size(&self) -> u32 { + unsafe { state_get_mem_write_size(self.state) } + } + + pub fn get_mem_write(&self, index: u32) -> (u32, u64, u8) { + let addr = unsafe { state_get_mem_write_addr(self.state, index) }; + let value = unsafe { state_get_mem_write_value(self.state, index) }; + let size_by_byte = unsafe { state_get_mem_write_size_by_byte(self.state, index) }; + (addr, value, size_by_byte) + } + + pub fn get_mem_read_size(&self) -> u32 { + unsafe { state_get_mem_read_size(self.state) } + } + + pub fn get_mem_read(&self, index: u32) -> (u32, u8) { + let addr = unsafe { state_get_mem_read_addr(self.state, index) }; + let size_by_byte = unsafe { state_get_mem_read_size_by_byte(self.state, index) }; + (addr, size_by_byte) + } + + pub fn clear(&self) { + unsafe { state_clear(self.state) } + } + + pub fn exit(&self) -> u64 { + unsafe { state_exit(self.state) } + } +} + +impl Drop for State { + fn drop(&mut self) { + unsafe { state_destruct(self.state) } + } +} + +type FfiCallback = extern "C" fn(u64) -> *mut u8; + +#[link(name = "spike_interfaces")] +extern "C" { + pub fn spike_register_callback(callback: FfiCallback); + fn spike_new(arch: *const c_char, set: *const c_char, lvl: *const c_char) -> *mut (); + fn spike_get_proc(spike: *mut ()) -> *mut (); + fn spike_destruct(spike: *mut ()); + fn proc_disassemble(proc: *mut ()) -> *mut c_char; + fn proc_reset(proc: *mut ()); + fn proc_get_state(proc: *mut ()) -> *mut (); + fn proc_func(proc: *mut ()) -> u64; + fn proc_get_insn(proc: *mut ()) -> u64; + fn proc_get_vreg_addr(proc: *mut ()) -> *mut u8; + fn proc_get_rs(proc: *mut ()) -> u64; + fn proc_get_rd(proc: *mut ()) -> u32; + fn proc_get_rs_bits(proc: *mut ()) -> u64; + + fn proc_vu_get_vtype(proc: *mut ()) -> u64; + fn proc_vu_get_vxrm(proc: *mut ()) -> u32; + fn proc_vu_get_vnf(proc: *mut ()) -> u32; + fn proc_vu_get_vill(proc: *mut ()) -> bool; + fn proc_vu_get_vxsat(proc: *mut ()) -> bool; + fn proc_vu_get_vl(proc: *mut ()) -> u32; + fn proc_vu_get_vstart(proc: *mut ()) -> u16; + + fn proc_destruct(proc: *mut ()); + fn state_set_pc(state: *mut (), pc: u64); + fn state_get_pc(state: *mut ()) -> u64; + fn state_get_mem_write_size(state: *mut ()) -> u32; + fn state_get_mem_write_addr(state: *mut (), index: u32) -> u32; + fn state_get_mem_write_value(state: *mut (), index: u32) -> u64; + fn state_get_mem_write_size_by_byte(state: *mut (), index: u32) -> u8; + fn state_get_mem_read_size(state: *mut ()) -> u32; + fn state_get_mem_read_addr(state: *mut (), index: u32) -> u32; + fn state_get_mem_read_size_by_byte(state: *mut (), index: u32) -> u8; + fn state_handle_pc(state: *mut (), pc: u64) -> u64; + fn state_clear(state: *mut ()); + fn state_destruct(state: *mut ()); + fn state_exit(state: *mut ()) -> u64; +} diff --git a/nix/overlay.nix b/nix/overlay.nix index aab82d1ea..59d137c90 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -18,6 +18,7 @@ in espresso = final.callPackage ./pkgs/espresso.nix { }; dramsim3 = final.callPackage ./pkgs/dramsim3.nix { }; libspike = final.callPackage ./pkgs/libspike.nix { }; + libspike_interfaces = final.callPackage ../difftest/libspike_interfaces { }; buddy-mlir = final.callPackage ./pkgs/buddy-mlir.nix { }; fetchMillDeps = final.callPackage ./pkgs/mill-builder.nix { }; circt-full = final.callPackage ./pkgs/circt-full.nix { }; diff --git a/nix/t1/default.nix b/nix/t1/default.nix index b52ad2297..7e6f9986b 100644 --- a/nix/t1/default.nix +++ b/nix/t1/default.nix @@ -80,6 +80,8 @@ lib.makeScope newScope emu = innerSelf.callPackage ./ipemu.nix { rtl = ip.emu-rtl; stdenv = moldStdenv; }; emu-trace = innerSelf.callPackage ./ipemu.nix { rtl = emu-rtl; stdenv = moldStdenv; do-trace = true; }; + + difftest = innerSelf.callPackage ../../difftest/offline_difftest/default.nix { rtl = innerSelf.ip.emu-rtl; }; }; subsystem = rec {