diff --git a/npc/csrc/Flow/config.cpp b/npc/csrc/Flow/config.cpp index 4d8338b..fd49141 100644 --- a/npc/csrc/Flow/config.cpp +++ b/npc/csrc/Flow/config.cpp @@ -7,18 +7,16 @@ void Config::cli_parse(int argc, char **argv) { ->check(CLI::ExistingFile); app.add_flag("!--no-bin", memory_file_binary, "Memory file is in text format"); - app.add_flag("--trace", do_trace, "Enable tracing"); - app.add_option("--wav", wavefile, "output .vcd file path") - ->check([=](const std::string &) { - if (!do_trace) - throw CLI::ValidationError( - "dependency", "You must turn on trace before specify wave file"); - return std::string(); - }); + app.add_option("--wav", wavefile, "output .vcd file path"); app.add_option("-t", max_sim_time, "Max simulation timestep"); app.add_option("--diff-lib", lib_ref, "Dynamic library file of difftest reference") ->check(CLI::ExistingFile); + app.add_flag("--mtrace", do_mtrace, "Enable memory tracing"); + app.add_option( + "--mtrace-range", mtrace_ranges, + "Specify memory tracing range (default: 0x80000000-0x8fffffff)") + ->delimiter(','); try { app.parse(argc, argv); diff --git a/npc/csrc/Flow/config.hpp b/npc/csrc/Flow/config.hpp deleted file mode 100644 index 9f5873c..0000000 --- a/npc/csrc/Flow/config.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _NPC_CONFIG_H_ -#define _NPC_CONFIG_H_ -#include -#include -#include -#include - -struct Config { - std::filesystem::path memory_file; - uint64_t max_sim_time = 1000; - bool memory_file_binary = {true}; - bool do_trace{false}; - std::filesystem::path wavefile; - std::filesystem::path lib_ref; - void cli_parse(int argc, char **argv); -}; - -extern Config config; - -#endif \ No newline at end of file diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index ea71189..7e962d5 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,29 +1,35 @@ #include "VFlow___024root.h" -#include "config.hpp" -#include "disasm.hpp" -#include "vl_wrapper.hpp" -#include "vpi_user.h" -#include "vpi_wrapper.hpp" #include +#include #include #include +#include #include #include #include #include #include #include +#include +#include +#include using VlModule = VlModuleInterfaceCommon; using Registers = _RegistersVPI; // SDB::SDB sdb_dut; using CPUState = CPUStateBase; +bool g_skip_memcheck = false; CPUState npc_cpu; +VlModule *top; +Registers *regs; +vpiHandle pc = nullptr; + extern "C" { void *pmem_get() { - static auto pmem = new Memory(config.memory_file, - config.memory_file_binary); + static auto pmem = + new Memory(config.memory_file, config.memory_file_binary, + std::move(config.mtrace_ranges)); return pmem; } @@ -32,20 +38,20 @@ int pmem_read(int raddr) { auto mem = static_cast *>(pmem); // TODO: Do memory difftest at memory read and write to diagnose at a finer // granularity + if (config.do_mtrace) + mem->trace(raddr, true, regs->get_pc()); return mem->read(raddr); } void pmem_write(int waddr, int wdata, char wmask) { void *pmem = pmem_get(); auto mem = static_cast *>(pmem); + if (config.do_mtrace) + mem->trace((std::size_t)waddr, false, regs->get_pc(), wdata); return mem->write((std::size_t)waddr, wdata, wmask); } } -VlModule *top; -Registers *regs; -vpiHandle pc = nullptr; - namespace NPC { void npc_memcpy(paddr_t addr, void *buf, size_t sz, bool direction) { if (direction == TRM_FROM_MACHINE) { @@ -78,10 +84,15 @@ void npc_exec(uint64_t n) { } } +void npc_atexit(void) { + delete top; + delete regs; +} + void npc_init(int port) { - // top = std::make_unique(config.do_trace, config.wavefile); - top = new VlModule{config.do_trace, config.wavefile}; + top = new VlModule{config.wavefile}; regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out"); + atexit(npc_atexit); top->reset_eval(10); } @@ -115,15 +126,19 @@ word_t reg_str2val(const char *name, bool *success) { int main(int argc, char **argv, char **env) { config.cli_parse(argc, argv); - /* -- Difftest -- */ - std::filesystem::path ref{config.lib_ref}; - RefTrmInterface ref_interface{ref}; - DifftestTrmInterface diff_interface{NPC::npc_interface, ref_interface, - pmem_get(), 128}; - SDB::SDB sdb_diff{diff_interface}; + if (config.max_sim_time > 1) { + NPC::npc_interface.exec(config.max_sim_time / 2); + } else { + /* -- Difftest -- */ + std::filesystem::path ref{config.lib_ref}; + RefTrmInterface ref_interface{ref}; + DifftestTrmInterface diff_interface{NPC::npc_interface, ref_interface, + pmem_get(), 1024}; + SDB::SDB sdb_diff{diff_interface}; - int t = 8; - sdb_diff.main_loop(); + int t = 8; + sdb_diff.main_loop(); + } return 0; } diff --git a/npc/csrc/Flow/vl_wrapper.hpp b/npc/csrc/Flow/vl_wrapper.hpp deleted file mode 100644 index 4d42b55..0000000 --- a/npc/csrc/Flow/vl_wrapper.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _NPC_TRACER_H_ -#define _NPC_TRACER_H_ -#include -#include - -template class Tracer { - std::shared_ptr top; - std::unique_ptr m_trace; - uint64_t cycle = 0; - -public: - Tracer(T *top, std::filesystem::path wavefile) { - top = top; - Verilated::traceEverOn(true); - m_trace = std::make_unique(); - top->trace(m_trace.get(), 5); - m_trace->open(wavefile.c_str()); - } - ~Tracer() { m_trace->close(); } - - /** - * Dump signals to waveform file. Must be called once after every top->eval() - * call. - */ - void update() { m_trace->dump(cycle++); } -}; - -template class VlModuleInterfaceCommon : public T { - uint64_t sim_time = 0; - uint64_t posedge_cnt = 0; - std::unique_ptr> tracer; - -public: - VlModuleInterfaceCommon(bool do_trace, - std::filesystem::path wavefile = "waveform.vcd") { - if (do_trace) - tracer = std::make_unique>(this, wavefile); - } - void eval() { - if (this->is_posedge()) { - posedge_cnt++; - } - T::clock = !T::clock; - sim_time++; - T::eval(); - if (tracer) - tracer->update(); - } - void eval(int n) { - for (int i = 0; i < n; i++) { - this->eval(); - } - } - void reset_eval(int n) { - this->reset = 1; - this->eval(n); - this->reset = 0; - } - bool is_posedge() { - // Will be posedge when eval is called - return T::clock == 0; - } -}; - -#endif diff --git a/npc/include/components.hpp b/npc/include/components.hpp index 4f8c592..15cea34 100644 --- a/npc/include/components.hpp +++ b/npc/include/components.hpp @@ -1,15 +1,19 @@ #ifndef _NPC_COMPONENTS_H_ #define _NPC_COMPONENTS_H_ +#include "types.h" #include +#include #include #include #include #include #include #include +#include #include #include +#include template class _RegistersBase { std::array regs; @@ -29,9 +33,14 @@ public: template class Memory { std::size_t addr_to_index(std::size_t addr) { - if (addr < 0x80000000) { + extern bool g_skip_memcheck; + if (g_skip_memcheck) { return 0; } + if (addr < 0x80000000 || addr > 0x87ffffff) { + std::cerr << std::hex << "ACCESS " << addr << std::dec << std::endl; + throw std::runtime_error("Invalid memory access"); + } // Linear mapping return (addr >> 2) - 0x20000000; } @@ -45,7 +54,10 @@ template class Memory { public: std::array mem; - Memory(std::filesystem::path filepath, bool is_binary = true) { + std::vector> trace_ranges; + Memory(std::filesystem::path filepath, bool is_binary, + std::vector> &&trace_ranges) + : trace_ranges(std::move(trace_ranges)) { if (!std::filesystem::exists(filepath)) throw std::runtime_error("Memory file not found"); if (is_binary) { @@ -82,5 +94,22 @@ public: void *guest_to_host(std::size_t addr) { return mem.data() + addr_to_index(addr); } + void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) { + for (auto &r : trace_ranges) { + if (r[0] <= addr && r[1] >= addr) { + std::stringstream os; + os << std::hex; + if (pc != 0) + os << "0x" << pc << " "; + if (is_read) + os << "[R] "; + else + os << "[W] " << value << " -> "; + os << "0x" << addr << std::dec << std::endl; + std::cout << os.rdbuf(); + break; + } + } + } }; #endif diff --git a/npc/include/config.hpp b/npc/include/config.hpp index 9f5873c..de536b6 100644 --- a/npc/include/config.hpp +++ b/npc/include/config.hpp @@ -3,13 +3,17 @@ #include #include #include +#include #include +#include struct Config { std::filesystem::path memory_file; - uint64_t max_sim_time = 1000; + uint64_t max_sim_time = 0; bool memory_file_binary = {true}; - bool do_trace{false}; + bool do_mtrace{false}; + std::vector> mtrace_ranges{ + {0x80000000, 0x8ffffffff}}; std::filesystem::path wavefile; std::filesystem::path lib_ref; void cli_parse(int argc, char **argv); diff --git a/npc/include/trm_difftest.hpp b/npc/include/trm_difftest.hpp index dd2a93d..ef8bde0 100644 --- a/npc/include/trm_difftest.hpp +++ b/npc/include/trm_difftest.hpp @@ -15,7 +15,6 @@ #include #include #include -Disassembler d{"riscv32-linux-pc-gnu"}; using paddr_t = uint32_t; struct DifftestTrmInterface : public TrmInterface { diff --git a/npc/include/trm_interface.hpp b/npc/include/trm_interface.hpp index 716745d..5acb8d6 100644 --- a/npc/include/trm_interface.hpp +++ b/npc/include/trm_interface.hpp @@ -1,5 +1,6 @@ #ifndef _NPC_TRM_INTERFACE_HEADER_FILE_ #define _NPC_TRM_INTERFACE_HEADER_FILE_ +#include #include #include #include @@ -7,6 +8,8 @@ #include #include +extern Disassembler d; + template struct CPUStateBase { R reg[nr_reg] = {0}; word_t pc = 0x80000000; diff --git a/npc/include/vl_wrapper.hpp b/npc/include/vl_wrapper.hpp index b84d745..61414c4 100644 --- a/npc/include/vl_wrapper.hpp +++ b/npc/include/vl_wrapper.hpp @@ -1,5 +1,6 @@ #ifndef _NPC_TRACER_H_ #define _NPC_TRACER_H_ +#include "components.hpp" #include #include @@ -19,7 +20,8 @@ public: ~Tracer() { m_trace->close(); } /** - * @brief: Dump signals to waveform file. Must be called once after every top->eval() call. + * Dump signals to waveform file. Must be called once after every top->eval() + * call. */ void update() { m_trace->dump(cycle++); } }; @@ -30,9 +32,8 @@ template class VlModuleInterfaceCommon : public T { std::unique_ptr> tracer; public: - VlModuleInterfaceCommon(bool do_trace, - std::filesystem::path wavefile = "waveform.vcd") { - if (do_trace) + VlModuleInterfaceCommon(std::filesystem::path wavefile) { + if (!wavefile.empty()) tracer = std::make_unique>(this, wavefile); } void eval() { @@ -51,9 +52,12 @@ public: } } void reset_eval(int n) { + extern bool g_skip_memcheck; + g_skip_memcheck = true; this->reset = 1; this->eval(n); this->reset = 0; + g_skip_memcheck = false; } bool is_posedge() { // Will be posedge when eval is called diff --git a/npc/csrc/Flow/vpi_wrapper.hpp b/npc/include/vpi_wrapper.hpp similarity index 100% rename from npc/csrc/Flow/vpi_wrapper.hpp rename to npc/include/vpi_wrapper.hpp diff --git a/npc/utils/sdb/include/sdb.hpp b/npc/utils/sdb/include/sdb.hpp index f3e1ab0..964412a 100644 --- a/npc/utils/sdb/include/sdb.hpp +++ b/npc/utils/sdb/include/sdb.hpp @@ -37,11 +37,13 @@ private: Handler{{"si", "step-instruction"}, &SDBHandlers::cmd_step}, Handler{{"info-r"}, &SDBHandlers::cmd_info_registers}, Handler{{"p", "print"}, &SDBHandlers::cmd_print}, + Handler{{"disas", "disassemble"}, &SDBHandlers::cmd_disassemble}, }; int cmd_continue(const cr::Console::Arguments &input); int cmd_step(const std::vector &input); int cmd_info_registers(const std::vector &input); int cmd_print(const std::vector &input); + int cmd_disassemble(const std::vector &input); int exec_catch(uint64_t); public: diff --git a/npc/utils/sdb/sdb.cpp b/npc/utils/sdb/sdb.cpp index d38ea02..55d6d41 100644 --- a/npc/utils/sdb/sdb.cpp +++ b/npc/utils/sdb/sdb.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -18,6 +19,8 @@ std::ostream &operator<<(std::ostream &os, const TrmInterface &d) { return os; }; +Disassembler d{"riscv32-linux-pc-gnu"}; + namespace SDB { int SDBHandlers::exec_catch(uint64_t n) { @@ -83,6 +86,21 @@ int SDBHandlers::cmd_print(const std::vector &input) { return SDB_SUCCESS; } +int SDBHandlers::cmd_disassemble(const std::vector &input) { + word_t buf[2]; + word_t addr = parse_expr(input[1].c_str()); + this->funcs.memcpy(addr, &buf, sizeof(word_t), TRM_FROM_MACHINE); + // TODO: Difftest only + std::cout << "dut: \n" + << d.disassemble(addr, (uint8_t *)&buf[0], sizeof(word_t)) + << std::endl + << "ref: \n" + << d.disassemble(addr, (uint8_t *)&buf[0], sizeof(word_t)) + << std::endl; + ; + return SDB_SUCCESS; +} + void SDBHandlers::register_handlers(cr::Console *c) { for (auto &h : this->all_handlers) { for (auto &name : h.names) {