diff --git a/npc/.envrc b/npc/.envrc index 3f3af37..beaa935 100644 --- a/npc/.envrc +++ b/npc/.envrc @@ -1 +1 @@ -use flake ..#npc +use flake ".?submodules=1#npc" diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index 035d603..4b4424e 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -42,7 +42,7 @@ if(BUILD_SIM_NVBOARD_TARGET) endif() find_package(CLI11 CONFIG REQUIRED) -option(ENABLE_SDB "Enable simple debugger" ON) +option(ENABLE_SDB "Enable simple debugger" OFF) find_library(NVBOARD_LIBRARY NAMES nvboard) find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h) diff --git a/npc/csrc/Flow/CMakeLists.txt b/npc/csrc/Flow/CMakeLists.txt index 5ea7fce..f945f7a 100644 --- a/npc/csrc/Flow/CMakeLists.txt +++ b/npc/csrc/Flow/CMakeLists.txt @@ -1,6 +1,6 @@ include(ChiselBuild) add_executable(V${TOPMODULE} config.cpp main.cpp) -target_link_libraries(V${TOPMODULE} PRIVATE sdb devices) +target_link_libraries(V${TOPMODULE} PRIVATE devices gdbstub) target_include_directories(V${TOPMODULE} PRIVATE ${CMAKE_SOURCE_DIR}/include) verilate( diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index 324156f..591ebc8 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,30 +1,25 @@ +extern "C" { +#include +} #include "VFlow___024root.h" #include "components.hpp" #include -#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include #include +#include #include #include #include -using VlModule = VlModuleInterfaceCommon; using Registers = _RegistersVPI; +using VlModule = VlModuleInterfaceCommon; // SDB::SDB sdb_dut; -using CPUState = CPUStateBase; bool g_skip_memcheck = false; -CPUState npc_cpu; VlModule *top; Registers *regs; vpiHandle pc = nullptr; @@ -32,7 +27,13 @@ vpiHandle pc = nullptr; const size_t PMEM_START = 0x80000000; const size_t PMEM_END = 0x87ffffff; +struct DbgState { + std::vector bp; +}; + extern "C" { + +/* === Memory Access === */ using MMap = MemoryMap, Devices::DeviceMap>; void *pmem_get() { static Devices::DeviceMap devices{ @@ -65,127 +66,99 @@ void pmem_write(int waddr, int wdata, char wmask) { mem->trace((std::size_t)waddr, false, regs->get_pc(), wdata); return mem->write((std::size_t)waddr, wdata, wmask); } + +/* === For gdbstub === */ + +int npc_read_mem(void *args, size_t addr, size_t len, void *val) { + void *pmem = pmem_get(); + auto mmap = static_cast(pmem); + mmap->copy_to(addr, (uint8_t *)val, len); + return 0; } -namespace NPC { -void npc_memcpy(paddr_t addr, void *buf, size_t sz, bool direction) { - if (direction == TRM_FROM_MACHINE) { - static_cast(pmem_get())->copy_to(addr, (uint8_t *)buf, sz); - } -}; +int npc_write_mem(void *args, size_t addr, size_t len, void *val) { + void *pmem = pmem_get(); + auto mmap = static_cast(pmem); + mmap->copy_from(addr, (uint8_t *)val, len); + return 0; +} -void npc_regcpy(void *p, bool direction) { +int npc_read_reg(void *args, int regno, size_t *value) { + if (regno == 32) + *value = regs->get_pc(); + else + *value = (*regs)[regno]; + return 0; +} - if (direction == TRM_FROM_MACHINE) { - ((CPUState *)p)->pc = regs->get_pc(); - for (int i = 0; i < 32; i++) { - ((CPUState *)p)->reg[i] = (*regs)[i]; +int npc_write_reg(void *args, int regno, size_t value) { return 1; } + +void npc_cont(void *args, gdb_action_t *res) { + DbgState *dbg = (DbgState *)args; + *res = top->eval(dbg->bp); +} + +void npc_stepi(void *args, gdb_action_t *res) { + DbgState *dbg = (DbgState *)args; + *res = top->eval(dbg->bp); +} + +bool npc_set_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg = (DbgState *)args; + for (const auto &bp : dbg->bp) { + if (bp.addr == addr && bp.type == type) { + return true; } } + dbg->bp.push_back({.addr = addr, .type = type}); + return true; } -void npc_exec(uint64_t n) { - while (n--) { - for (int i = 0; i < 2; i++) { - if (top->is_posedge()) { - // Posedge - regs->update(); - } - top->eval(); +bool npc_del_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg = (DbgState *)args; + for (auto it = dbg->bp.begin(); it != dbg->bp.end(); it++) { + if (it->addr == addr && it->type == type) { + std::swap(*it, *dbg->bp.rbegin()); + dbg->bp.pop_back(); + return true; } } + return false; } -void npc_atexit(void) { - delete top; - delete regs; -} +static target_ops npc_gdbstub_ops = {.cont = npc_cont, + .stepi = npc_stepi, + .read_reg = npc_read_reg, + .write_reg = npc_write_reg, + .read_mem = npc_read_mem, + .write_mem = npc_write_mem, + .set_bp = npc_set_bp, + .del_bp = npc_del_bp, + .on_interrupt = NULL}; -void npc_init(int port) { - top = new VlModule{config.wavefile}; - regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out"); - atexit(npc_atexit); - top->reset_eval(10); -} +static gdbstub_t gdbstub_priv; +static DbgState dbg; +arch_info_t isa_arch_info = { + .target_desc = strdup(TARGET_RV32), .reg_num = 33, .reg_byte = 4}; -class DutTrmInterface : public TrmInterface { -public: - DutTrmInterface(memcpy_t f_memcpy, regcpy_t f_regcpy, exec_t f_exec, - init_t f_init, void *cpu_state) - : TrmInterface{f_memcpy, f_regcpy, f_exec, f_init, cpu_state} {} - word_t at(std::string name) const override { - return ((CPUState *)cpu_state)->at(name); +int gdbstub_loop() { + if (!gdbstub_init(&gdbstub_priv, &npc_gdbstub_ops, (arch_info_t)isa_arch_info, + strdup("127.0.0.1:1234"))) { + return EINVAL; } - - word_t at(paddr_t addr) const override { - word_t buf; - this->memcpy(addr, &buf, sizeof(word_t), TRM_FROM_MACHINE); - return buf; - } - void print(std::ostream &os) const override { - this->regcpy(cpu_state, TRM_FROM_MACHINE); - os << *(CPUState *)cpu_state << std::endl; - } -}; - -DutTrmInterface npc_interface = - DutTrmInterface{&npc_memcpy, &npc_regcpy, &npc_exec, &npc_init, &npc_cpu}; -}; // namespace NPC - -extern "C" { -word_t reg_str2val(const char *name, bool *success) { - return npc_cpu.reg_str2val(name, success); -} + bool success = gdbstub_run(&gdbstub_priv, &dbg); + gdbstub_close(&gdbstub_priv); + return !success; } +} // extern "C" int main(int argc, char **argv, char **env) { config.cli_parse(argc, argv); - if (config.lib_ref.empty()) { - if (config.interactive) { - SDB::SDB sdb_npc{NPC::npc_interface}; - sdb_npc.main_loop(); - return 0; - } else { - NPC::npc_interface.init(0); - while (true) { - word_t inst = NPC::npc_interface.at(regs->get_pc()); - if (inst == 1048691) { - return 0; - } - NPC::npc_interface.exec(1); - } - } - } + top = new VlModule; + regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out"); + top->setup(config.wavefile, regs); + top->reset_eval(10); - /* -- Difftest -- */ - std::filesystem::path ref{config.lib_ref}; - RefTrmInterface ref_interface{ref}; - DifftestTrmInterface diff_interface{ - NPC::npc_interface, ref_interface, - static_cast(pmem_get())->get_pmem(), 128 * 1024}; - if (config.interactive) { - SDB::SDB sdb_diff{diff_interface}; - sdb_diff.main_loop(); - return 0; - } - - try { - diff_interface.init(0); - diff_interface.exec(-1); - } catch (TrmRuntimeException &e) { - switch (e.error_code()) { - case TrmRuntimeException::EBREAK: - return 0; - case TrmRuntimeException::DIFFTEST_FAILED: - std::cout << "Difftest Failed" << std::endl; - diff_interface.print(std::cout); - return 1; - default: - std::cerr << "Unknown error happened" << std::endl; - return 1; - } - } - - return 0; + return gdbstub_loop(); } diff --git a/npc/include/components.hpp b/npc/include/components.hpp index ca86156..bf16895 100644 --- a/npc/include/components.hpp +++ b/npc/include/components.hpp @@ -20,12 +20,12 @@ template class _RegistersBase { std::array regs; T pc; - virtual T fetch_pc(); - virtual T fetch_reg(std::size_t id); + virtual T fetch_pc() const; + virtual T fetch_reg(std::size_t id) const; public: - T operator[](size_t id) { return fetch_reg(id); } - T get_pc() { return fetch_pc(); } + T operator[](size_t id) const { return fetch_reg(id); } + T get_pc() const { return fetch_pc(); } void update() { for (int i = 0; i < regs.size(); i++) { regs[i] = fetch_reg(i); @@ -112,14 +112,14 @@ public: if (ram->in_pmem(addr)) { ram->transfer(addr, buf, len, false); } else { - std::cerr << "Not in pmem" << std::endl; + std::cerr << "0x" << std::hex << addr << " not in pmem" << std::endl; } } - void copy_from(paddr_t addr, const uint8_t *buf, size_t len) { + void copy_from(paddr_t addr, uint8_t *buf, size_t len) { if (ram->in_pmem(addr)) { ram->transfer(addr, buf, len, true); } else { - std::cerr << "Not in pmem" << std::endl; + std::cerr << "0x" << std::hex << addr << " not in pmem" << std::endl; } } void *get_pmem() { return ram->mem.data(); } diff --git a/npc/include/types.h b/npc/include/types.h index 6f78cc1..e991d8a 100644 --- a/npc/include/types.h +++ b/npc/include/types.h @@ -29,4 +29,10 @@ const std::map riscv32_regs_by_name{ {"t5", 30}, {"t6", 31}}; #endif -#endif \ No newline at end of file +#include +struct Breakpoint { + size_t addr; + bp_type_t type; +}; + +#endif diff --git a/npc/include/vl_wrapper.hpp b/npc/include/vl_wrapper.hpp index 61414c4..02a6ac5 100644 --- a/npc/include/vl_wrapper.hpp +++ b/npc/include/vl_wrapper.hpp @@ -1,7 +1,10 @@ #ifndef _NPC_TRACER_H_ #define _NPC_TRACER_H_ #include "components.hpp" +#include "types.h" #include +#include +#include #include template class Tracer { @@ -26,16 +29,24 @@ public: void update() { m_trace->dump(cycle++); } }; -template class VlModuleInterfaceCommon : public T { +template class VlModuleInterfaceCommon : public T { uint64_t sim_time = 0; uint64_t posedge_cnt = 0; std::unique_ptr> tracer; public: - VlModuleInterfaceCommon(std::filesystem::path wavefile) { + const R *registers; + VlModuleInterfaceCommon() { + tracer = nullptr; + registers = nullptr; + } + + void setup(std::filesystem::path wavefile, const R *r) { if (!wavefile.empty()) tracer = std::make_unique>(this, wavefile); + registers = r; } + void eval() { if (this->is_posedge()) { posedge_cnt++; @@ -51,6 +62,25 @@ public: this->eval(); } } + + gdb_action_t eval(const std::vector &breakpoints) { + gdb_action_t res; + do { + this->eval(); + size_t pc = registers->get_pc(); + for (const auto &bp : breakpoints) { + if (pc == bp.addr) { + res.data = bp.addr; + switch (bp.type) { + default: + res.reason = gdb_action_t::ACT_BREAKPOINT; + } + return res; + } + } + } while (true); + } + void reset_eval(int n) { extern bool g_skip_memcheck; g_skip_memcheck = true; diff --git a/npc/include/vpi_wrapper.hpp b/npc/include/vpi_wrapper.hpp index a1dd089..4aaaeb8 100644 --- a/npc/include/vpi_wrapper.hpp +++ b/npc/include/vpi_wrapper.hpp @@ -7,14 +7,14 @@ template class _RegistersVPI : public _RegistersBase { std::array reg_handles; vpiHandle pc_handle; - T vpi_get(vpiHandle vh) { + T vpi_get(vpiHandle vh) const { s_vpi_value v; v.format = vpiIntVal; vpi_get_value(vh, &v); return v.value.integer; } - T fetch_pc(void) { return vpi_get(pc_handle); } - T fetch_reg(std::size_t id) { return vpi_get(reg_handles[id]); } + T fetch_pc(void) const { return vpi_get(pc_handle); } + T fetch_reg(std::size_t id) const { return vpi_get(reg_handles[id]); } public: _RegistersVPI(const std::string regs_prefix,