npc: split gdbstub api into seperate file

This commit is contained in:
xinyangli 2024-08-01 18:53:17 +08:00
parent fed4ac225d
commit 85d7840804
Signed by: xin
SSH key fingerprint: SHA256:qZ/tzd8lYRtUFSrfBDBMcUqV4GHKxqeqRA3huItgvbk
15 changed files with 287 additions and 270 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
**/.gdbinit
!*/ !*/
difftest/ difftest/
!/nemu/* !/nemu/*

View file

@ -93,7 +93,7 @@
devShells.npc = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } { devShells.npc = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
inherit (self.checks.${system}.pre-commit-check) shellHook; inherit (self.checks.${system}.pre-commit-check) shellHook;
CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin";
NPC_IMAGES_DIR="${self.packages.${system}.am-kernels-npc}/share/am-kernels"; NPC_IMAGES_PATH = "${self.packages.${system}.rv32Cross.am-kernels-npc}/share/am-kernels";
packages = with pkgs; [ packages = with pkgs; [
clang-tools clang-tools
cmake cmake
@ -109,6 +109,7 @@
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
cmake cmake
ninja
sbt sbt
nvboard nvboard
nixpkgs-circt162.legacyPackages.${system}.circt nixpkgs-circt162.legacyPackages.${system}.circt
@ -117,10 +118,11 @@
flex flex
bison bison
verilator verilator
self.packages.${system}.am-kernels-npc self.packages.${system}.rv32Cross.am-kernels-npc
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
spdlog
nvboard nvboard
openssl openssl
libllvm libllvm
@ -128,10 +130,6 @@
readline readline
mini-gdbstub mini-gdbstub
] ++ self.checks.${system}.pre-commit-check.enabledPackages; ] ++ self.checks.${system}.pre-commit-check.enabledPackages;
cmakeFlags = [
"-DDIFFTEST_LIB:string=${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so"
];
}; };
} }
); );

View file

@ -41,6 +41,7 @@ if(BUILD_SIM_NVBOARD_TARGET)
find_package(SDL2_image REQUIRED) find_package(SDL2_image REQUIRED)
endif() endif()
find_package(CLI11 CONFIG REQUIRED) find_package(CLI11 CONFIG REQUIRED)
find_package(spdlog REQUIRED)
option(ENABLE_SDB "Enable simple debugger" OFF) option(ENABLE_SDB "Enable simple debugger" OFF)
@ -58,9 +59,6 @@ set(CHISEL_OUTPUT_VERILATOR_CONF ${CHISEL_OUTPUT_DIR}/conf.vlt)
set(CHISEL_OUTPUT_TOPMODULE ${CHISEL_OUTPUT_DIR}/${TOPMODULE}.sv) set(CHISEL_OUTPUT_TOPMODULE ${CHISEL_OUTPUT_DIR}/${TOPMODULE}.sv)
set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_DIR}") set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_DIR}")
# -- Find difftest binaries
file(GLOB_RECURSE DIFFTEST_BINARY_FILES "${DIFFTEST_RESOURCE_DIR}/*.bin")
# -- Build NVBoard executable # -- Build NVBoard executable
if(BUILD_SIM_NVBOARD_TARGET) if(BUILD_SIM_NVBOARD_TARGET)
add_subdirectory(csrc_nvboard) add_subdirectory(csrc_nvboard)

View file

@ -1,6 +1,6 @@
include(ChiselBuild) include(ChiselBuild)
add_executable(V${TOPMODULE} config.cpp main.cpp) add_executable(V${TOPMODULE} config.cpp gdbstub_wrapper.cpp main.cpp)
target_link_libraries(V${TOPMODULE} PRIVATE devices gdbstub) target_link_libraries(V${TOPMODULE} PRIVATE devices gdbstub spdlog::spdlog)
target_include_directories(V${TOPMODULE} PRIVATE ${CMAKE_SOURCE_DIR}/include) target_include_directories(V${TOPMODULE} PRIVATE ${CMAKE_SOURCE_DIR}/include)
verilate( verilate(
@ -28,8 +28,8 @@ foreach(DIFFTEST_BINARY_FILE IN LISTS DIFFTEST_BINARY_FILES)
unset(FILENAME) unset(FILENAME)
endforeach() endforeach()
add_library(${TOPMODULE} SHARED config.cpp main.cpp) add_library(${TOPMODULE} SHARED config.cpp gdbstub_wrapper.cpp)
target_link_libraries(${TOPMODULE} PRIVATE devices gdbstub) target_link_libraries(${TOPMODULE} PRIVATE devices gdbstub spdlog::spdlog)
target_include_directories(${TOPMODULE} PRIVATE ${CMAKE_SOURCE_DIR}/include) target_include_directories(${TOPMODULE} PRIVATE ${CMAKE_SOURCE_DIR}/include)
set_property(TARGET PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_options(${TOPMODULE} PRIVATE -Wl,-E) target_link_options(${TOPMODULE} PRIVATE -Wl,-E)

View file

@ -2,8 +2,10 @@
void Config::cli_parse(int argc, char **argv) { void Config::cli_parse(int argc, char **argv) {
CLI::App app; CLI::App app;
app.add_option("-l,--listen", gdbsocket,
"Listen to debugger at this address");
app.add_flag("-g", do_debug, "Listen for gdb");
app.add_option("-m,--memory", memory_file, "Content of memory") app.add_option("-m,--memory", memory_file, "Content of memory")
->required()
->check(CLI::ExistingFile); ->check(CLI::ExistingFile);
app.add_flag("!--no-bin", memory_file_binary, app.add_flag("!--no-bin", memory_file_binary,
"Memory file is in text format"); "Memory file is in text format");
@ -12,11 +14,6 @@ void Config::cli_parse(int argc, char **argv) {
app.add_option("--diff-lib", lib_ref, app.add_option("--diff-lib", lib_ref,
"Dynamic library file of difftest reference") "Dynamic library file of difftest reference")
->check(CLI::ExistingFile); ->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 { try {
app.parse(argc, argv); app.parse(argc, argv);

View file

@ -0,0 +1,196 @@
extern "C" {
#include <gdbstub.h>
}
#include "components.hpp"
#include <VFlow.h>
#include <config.hpp>
#include <cstdint>
#include <cstdlib>
#include <devices.hpp>
#include <types.h>
#include <vl_wrapper.hpp>
#include <vpi_user.h>
#include <vpi_wrapper.hpp>
using Registers = _RegistersVPI<uint32_t, 32>;
using VlModule = VlModuleInterfaceCommon<VFlow, Registers>;
// SDB::SDB<NPC::npc_interface> sdb_dut;
bool g_skip_memcheck = false;
VlModule *top;
Registers *regs;
vpiHandle pc = nullptr;
const size_t PMEM_START = 0x80000000;
const size_t PMEM_END = 0x87ffffff;
extern "C" {
/* === Memory Access === */
using MMap = MemoryMap<Memory<128 * 1024>, Devices::DeviceMap>;
void *pmem_get() {
static Devices::DeviceMap devices{
new Devices::Serial(0x10000000, 0x1000),
new Devices::RTC(0x10001000, 0x1000),
};
static auto pmem = new MemoryMap<Memory<128 * 1024>, Devices::DeviceMap>(
std::make_unique<Memory<128 * 1024>>(config.memory_file, true, PMEM_START,
PMEM_END),
std::make_unique<Devices::DeviceMap>(devices));
return pmem;
}
int pmem_read(int raddr) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem);
// TODO: Do memory difftest at memory read and write to diagnose at a finer
// granularity
mem->trace(raddr, true, regs->get_pc());
if (g_skip_memcheck)
return mem->read(PMEM_START);
return mem->read(raddr);
}
void pmem_write(int waddr, int wdata, char wmask) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem);
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<MMap *>(pmem);
return mmap->copy_to(addr, (uint8_t *)val, len);
}
int npc_write_mem(void *args, size_t addr, size_t len, void *val) {
void *pmem = pmem_get();
auto mmap = static_cast<MMap *>(pmem);
return mmap->copy_from(addr, (uint8_t *)val, len);
}
int npc_read_reg(void *args, int regno, size_t *value) {
if (regno == 32)
*value = regs->get_pc();
else
*value = (*regs)[regno];
return 0;
}
int npc_write_reg(void *args, int regno, size_t value) { return 1; }
inline void breakpoint_to_action(const Breakpoint *bp, gdb_action_t *res) {
if (bp == nullptr) {
res->reason = gdb_action_t::ACT_NONE;
return;
}
switch (bp->type) {
case BP_SOFTWARE:
res->reason = gdb_action_t::ACT_BREAKPOINT;
break;
case BP_ACCESS:
res->reason = gdb_action_t::ACT_WATCH;
break;
case BP_WRITE:
res->reason = gdb_action_t::ACT_WWATCH;
break;
case BP_READ:
res->reason = gdb_action_t::ACT_RWATCH;
break;
}
res->data = bp->addr;
}
void npc_cont(void *args, gdb_action_t *res) {
DbgState *dbg = (DbgState *)args;
const Breakpoint *stopped_at = nullptr;
stopped_at = top->cont(*dbg->bp);
breakpoint_to_action(stopped_at, res);
}
void npc_stepi(void *args, gdb_action_t *res) {
DbgState *dbg = (DbgState *)args;
const Breakpoint *stopped_at = nullptr;
stopped_at = top->stepi(*dbg->bp);
breakpoint_to_action(stopped_at, res);
}
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;
}
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_on_interrupt(void *args) { ; }
void npc_init(void *args) {
DbgState *dbg = (DbgState *)args;
void *mem = pmem_get();
dbg->bp = new std::vector<Breakpoint>;
top = new VlModule;
regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out");
top->setup(config.wavefile, regs);
top->reset_eval(10);
}
bool npc_do_difftest = true;
static gdbstub_t gdbstub_priv;
arch_info_t npc_isa_arch_info{
.target_desc = strdup(TARGET_RV32), .reg_num = 32, .reg_byte = 4};
size_t npc_dbg_state_size = sizeof(DbgState);
} // extern "C"
int gdbstub_loop() {
DbgState dbg;
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};
if (!config.do_debug) {
gdb_action_t res;
npc_init(&dbg);
npc_cont(&dbg, &res);
return !(res.reason == gdb_action_t::ACT_SHUTDOWN);
}
if (!gdbstub_init(&gdbstub_priv, &npc_gdbstub_ops,
(arch_info_t)npc_isa_arch_info, NULL,
config.gdbsocket.c_str())) {
return EINVAL;
}
npc_init(&dbg);
bool success = gdbstub_run(&gdbstub_priv, &dbg);
// gdbstub_close(&gdbstub_priv);
return !success;
}

View file

@ -1,180 +1,13 @@
extern "C" {
#include <gdbstub.h>
}
// #include "VFlow___024root.h"
#include "components.hpp"
#include <VFlow.h>
#include <config.hpp> #include <config.hpp>
#include <cstdint> #include <spdlog/cfg/env.h>
#include <cstdlib> #include <spdlog/spdlog.h>
#include <devices.hpp>
#include <memory>
#include <types.h> #include <types.h>
#include <vector>
#include <vl_wrapper.hpp>
#include <vpi_user.h>
#include <vpi_wrapper.hpp>
using Registers = _RegistersVPI<uint32_t, 32>; int gdbstub_loop();
using VlModule = VlModuleInterfaceCommon<VFlow, Registers>;
// SDB::SDB<NPC::npc_interface> sdb_dut;
bool g_skip_memcheck = false;
VlModule *top;
Registers *regs;
vpiHandle pc = nullptr;
const size_t PMEM_START = 0x80000000;
const size_t PMEM_END = 0x87ffffff;
struct DbgState {
std::vector<Breakpoint> *bp;
};
extern "C" {
/* === Memory Access === */
using MMap = MemoryMap<Memory<128 * 1024>, Devices::DeviceMap>;
void *pmem_get() {
static Devices::DeviceMap devices{
new Devices::Serial(0x10000000, 0x1000),
new Devices::RTC(0x10001000, 0x1000),
};
static auto pmem = new MemoryMap<Memory<128 * 1024>, Devices::DeviceMap>(
std::make_unique<Memory<128 * 1024>>(
"/nix/store/"
"nv2c00y24qwz1jihfbaip9n1lskbzyb3-am-kernel-riscv32-none-elf-2024-07-"
"10/share/am-kernels/add.bin",
config.memory_file_binary, PMEM_START, PMEM_END),
std::make_unique<Devices::DeviceMap>(devices), config.mtrace_ranges);
return pmem;
}
int pmem_read(int raddr) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(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());
if (g_skip_memcheck)
return mem->read(PMEM_START);
return mem->read(raddr);
}
void pmem_write(int waddr, int wdata, char wmask) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(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);
}
/* === For gdbstub === */
int npc_read_mem(void *args, size_t addr, size_t len, void *val) {
void *pmem = pmem_get();
auto mmap = static_cast<MMap *>(pmem);
mmap->copy_to(addr, (uint8_t *)val, len);
return 0;
}
int npc_write_mem(void *args, size_t addr, size_t len, void *val) {
void *pmem = pmem_get();
auto mmap = static_cast<MMap *>(pmem);
mmap->copy_from(addr, (uint8_t *)val, len);
return 0;
}
int npc_read_reg(void *args, int regno, size_t *value) {
if (regno == 32)
*value = regs->get_pc();
else
*value = (*regs)[regno];
return 0;
}
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;
}
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_on_interrupt(void *args) { ; }
void npc_init(void *args) {
DbgState *dbg = (DbgState *)args;
dbg->bp = new std::vector<Breakpoint>;
top = new VlModule;
regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out");
top->setup(config.wavefile, regs);
top->reset_eval(10);
}
static gdbstub_t gdbstub_priv;
arch_info_t isa_arch_info = {
.target_desc = strdup(TARGET_RV32), .reg_num = 32, .reg_byte = 4};
size_t argsize = sizeof(DbgState);
} // extern "C"
int gdbstub_loop() {
DbgState dbg;
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};
if (!gdbstub_init(&gdbstub_priv, &npc_gdbstub_ops, (arch_info_t)isa_arch_info,
strdup("/tmp/gdbstub-npc.sock"))) {
return EINVAL;
}
npc_init(&dbg);
bool success = gdbstub_run(&gdbstub_priv, &dbg);
// gdbstub_close(&gdbstub_priv);
return !success;
}
int main(int argc, char **argv, char **env) { int main(int argc, char **argv, char **env) {
spdlog::cfg::load_env_levels();
config.cli_parse(argc, argv); config.cli_parse(argc, argv);
spdlog::debug("Configuration parsed");
return gdbstub_loop(); return gdbstub_loop();
} }

View file

@ -1,3 +1,4 @@
add_library(devices serial.cpp rtc.cpp) add_library(devices serial.cpp rtc.cpp)
target_include_directories(devices PUBLIC include) target_include_directories(devices PUBLIC include)
target_link_libraries(devices PRIVATE spdlog::spdlog)

View file

@ -1,14 +1,12 @@
#include <array>
#include <cassert>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <initializer_list> #include <initializer_list>
#include <iostream> #include <iostream>
#include <iterator>
#include <map> #include <map>
#include <memory> #include <memory>
#include <unordered_map> #include <spdlog/logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <utility> #include <utility>
namespace Devices { namespace Devices {
@ -55,6 +53,7 @@ public:
class DeviceMap { class DeviceMap {
std::map<uint64_t, Device *> addr_to_device; std::map<uint64_t, Device *> addr_to_device;
std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("devicemap");
public: public:
DeviceMap(std::initializer_list<Device *> devices) { DeviceMap(std::initializer_list<Device *> devices) {
@ -66,8 +65,8 @@ public:
auto it = addr_to_device.upper_bound(addr); auto it = addr_to_device.upper_bound(addr);
if (it == addr_to_device.begin() || if (it == addr_to_device.begin() ||
(--it)->second->addr + it->second->len <= addr) { (--it)->second->addr + it->second->len <= addr) {
std::cerr << "Use of a unintialized device at memory addr: 0x" << std::hex logger->error("Accessed an unintialized device at memory addr: 0x{:x}",
<< addr << std::dec << std::endl; addr);
return false; return false;
} }
auto &device = it->second; auto &device = it->second;

View file

@ -52,7 +52,6 @@ int main(int argc, char **argv, char **env) {
if (keycode != top->io_ps2_data) { if (keycode != top->io_ps2_data) {
keycode = top->io_ps2_data; keycode = top->io_ps2_data;
printf("%d\n", keycode); printf("%d\n", keycode);
}
}); });
} }
delete top; delete top;

View file

@ -3,23 +3,21 @@
#define _NPC_COMPONENTS_H_ #define _NPC_COMPONENTS_H_
#include "types.h" #include "types.h"
#include <array> #include <array>
#include <cmath>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <exception>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <map> #include <spdlog/sinks/stdout_color_sinks.h>
#include <memory> #include <spdlog/spdlog.h>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector>
template <typename T, std::size_t nr> class _RegistersBase { template <typename T, std::size_t nr> class _RegistersBase {
std::array<T, nr> regs; std::array<T, nr> regs;
T pc; T pc;
std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("registers");
virtual T fetch_pc() const; virtual T fetch_pc() const;
virtual T fetch_reg(std::size_t id) const; virtual T fetch_reg(std::size_t id) const;
@ -33,12 +31,6 @@ public:
} }
}; };
// class MemoryFile {
// std::filesystem::path filepath;
// public:
// };
template <std::size_t n> class Memory { template <std::size_t n> class Memory {
paddr_t pmem_start, pmem_end; paddr_t pmem_start, pmem_end;
@ -83,15 +75,13 @@ public:
template <typename Mem, typename DevMap> class MemoryMap { template <typename Mem, typename DevMap> class MemoryMap {
std::unique_ptr<Mem> ram; std::unique_ptr<Mem> ram;
std::unique_ptr<DevMap> devices; std::unique_ptr<DevMap> devices;
const std::vector<std::array<uint64_t, 2>> &trace_ranges; std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("mmap");
public: public:
MemoryMap(std::unique_ptr<Mem> &&ram, std::unique_ptr<DevMap> &&devices, MemoryMap(std::unique_ptr<Mem> &&ram, std::unique_ptr<DevMap> &&devices)
const std::vector<std::array<uint64_t, 2>> &trace_ranges) : ram(std::move(ram)), devices(std::move(devices)) {}
: ram(std::move(ram)), devices(std::move(devices)),
trace_ranges(trace_ranges) {}
void write(paddr_t waddr, word_t wdata, char wmask) { void write(paddr_t waddr, word_t wdata, char wmask) {
// printf("waddr: 0x%x\n", waddr);
size_t len = (wmask & 1) + ((wmask & 2) >> 1) + ((wmask & 4) >> 2) + size_t len = (wmask & 1) + ((wmask & 2) >> 1) + ((wmask & 4) >> 2) +
((wmask & 8) >> 3); ((wmask & 8) >> 3);
if (ram->in_pmem(waddr)) { if (ram->in_pmem(waddr)) {
@ -99,47 +89,38 @@ public:
} else if (devices->handle(waddr, (uint8_t *)&wdata, len, true)) { } else if (devices->handle(waddr, (uint8_t *)&wdata, len, true)) {
} }
} }
word_t read(paddr_t raddr) const { word_t read(paddr_t raddr) const {
word_t res = 0; word_t res = 0;
// printf("raddr: 0x%x, in_pmem: %d\n", raddr, ram->in_pmem(raddr));
if (ram->in_pmem(raddr)) { if (ram->in_pmem(raddr)) {
ram->transfer(raddr, (uint8_t *)&res, 4, false); ram->transfer(raddr, (uint8_t *)&res, 4, false);
} else if (devices->handle(raddr, (uint8_t *)&res, 4, false)) { } else if (devices->handle(raddr, (uint8_t *)&res, 4, false)) {
} }
return res; return res;
} }
void copy_to(paddr_t addr, uint8_t *buf, size_t len) const {
int copy_to(paddr_t addr, uint8_t *buf, size_t len) const {
if (ram->in_pmem(addr)) { if (ram->in_pmem(addr)) {
ram->transfer(addr, buf, len, false); ram->transfer(addr, buf, len, false);
return 0;
} else { } else {
std::cerr << "0x" << std::hex << addr << " not in pmem" << std::endl; return EINVAL;
} }
} }
void copy_from(paddr_t addr, uint8_t *buf, size_t len) {
int copy_from(paddr_t addr, uint8_t *buf, size_t len) {
if (ram->in_pmem(addr)) { if (ram->in_pmem(addr)) {
ram->transfer(addr, buf, len, true); ram->transfer(addr, buf, len, true);
return 0;
} else { } else {
std::cerr << "0x" << std::hex << addr << " not in pmem" << std::endl; return EINVAL;
} }
} }
void *get_pmem() { return ram->mem.data(); } void *get_pmem() { return ram->mem.data(); }
void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) { void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) {
for (auto &r : trace_ranges) { logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr));
if (r[0] <= addr && r[1] >= addr) {
std::stringstream os;
if (pc != 0)
os << "0x" << std::hex << pc << " ";
if (is_read)
os << "[R] "
<< "0x" << addr << ": 0x" << this->read(addr);
else
os << "[W] " << value << " -> "
<< "0x" << addr;
os << std::dec << std::endl;
std::cout << os.rdbuf();
break;
}
}
} }
}; };
#endif #endif

View file

@ -3,17 +3,14 @@
#include <CLI/App.hpp> #include <CLI/App.hpp>
#include <CLI/CLI.hpp> #include <CLI/CLI.hpp>
#include <CLI/Validators.hpp> #include <CLI/Validators.hpp>
#include <cstddef>
#include <filesystem> #include <filesystem>
#include <utility>
struct Config { struct Config {
std::filesystem::path memory_file; std::filesystem::path memory_file;
std::string gdbsocket = "gdbstub-npc.sock";
bool do_debug{false};
bool interactive{false}; bool interactive{false};
bool memory_file_binary = {true}; bool memory_file_binary = {true};
bool do_mtrace{false};
std::vector<std::array<std::size_t, 2>> mtrace_ranges{
{0x80000000, 0x8ffffffff}};
std::filesystem::path wavefile; std::filesystem::path wavefile;
std::filesystem::path lib_ref; std::filesystem::path lib_ref;
void cli_parse(int argc, char **argv); void cli_parse(int argc, char **argv);

View file

@ -1,6 +1,11 @@
#ifndef _NPC_TYPES_H__ #ifndef _NPC_TYPES_H__
#define _NPC_TYPES_H__ #define _NPC_TYPES_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <gdbstub.h>
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h>
typedef uint32_t word_t; typedef uint32_t word_t;
typedef int32_t sword_t; typedef int32_t sword_t;
@ -16,9 +21,19 @@ typedef uint32_t paddr_t;
#define FMT_ADDR "0x%08x" #define FMT_ADDR "0x%08x"
typedef uint16_t ioaddr_t; typedef uint16_t ioaddr_t;
struct Breakpoint {
size_t addr;
bp_type_t type;
};
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus #ifdef __cplusplus
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
const std::map<std::string, int> riscv32_regs_by_name{ const std::map<std::string, int> riscv32_regs_by_name{
{"$0", 0}, {"ra", 1}, {"sp", 2}, {"gp", 3}, {"tp", 4}, {"t0", 5}, {"$0", 0}, {"ra", 1}, {"sp", 2}, {"gp", 3}, {"tp", 4}, {"t0", 5},
@ -27,12 +42,10 @@ const std::map<std::string, int> riscv32_regs_by_name{
{"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, {"s7", 23}, {"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, {"s7", 23},
{"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, {"t3", 28}, {"t4", 29}, {"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, {"t3", 28}, {"t4", 29},
{"t5", 30}, {"t6", 31}}; {"t5", 30}, {"t6", 31}};
#endif
#include <gdbstub.h> struct DbgState {
struct Breakpoint { std::vector<Breakpoint> *bp;
size_t addr;
bp_type_t type;
}; };
#endif
#endif #endif

View file

@ -2,6 +2,7 @@
#define _NPC_TRACER_H_ #define _NPC_TRACER_H_
#include "components.hpp" #include "components.hpp"
#include "types.h" #include "types.h"
#include "verilated.h"
#include <filesystem> #include <filesystem>
#include <gdbstub.h> #include <gdbstub.h>
#include <sys/types.h> #include <sys/types.h>
@ -47,7 +48,7 @@ public:
registers = r; registers = r;
} }
void eval() { void eval(void) {
if (this->is_posedge()) { if (this->is_posedge()) {
posedge_cnt++; posedge_cnt++;
} }
@ -57,35 +58,34 @@ public:
if (tracer) if (tracer)
tracer->update(); tracer->update();
} }
void eval(int n) {
for (int i = 0; i < n; i++) {
this->eval();
}
}
gdb_action_t eval(const std::vector<Breakpoint> &breakpoints) { const Breakpoint *stepi(const std::vector<Breakpoint> &breakpoints) {
gdb_action_t res; this->eval();
do {
this->eval(); this->eval();
size_t pc = registers->get_pc(); size_t pc = registers->get_pc();
for (const auto &bp : breakpoints) { for (const auto &bp : breakpoints) {
if (pc == bp.addr) { if (pc == bp.addr) {
res.data = bp.addr; return &bp;
switch (bp.type) {
default:
res.reason = gdb_action_t::ACT_BREAKPOINT;
} }
}
return nullptr;
}
const Breakpoint *cont(const std::vector<Breakpoint> &breakpoints) {
const Breakpoint *res = nullptr;
do {
res = stepi(breakpoints);
} while (res == nullptr);
return res; return res;
} }
}
} while (true);
}
void reset_eval(int n) { void reset_eval(int n) {
extern bool g_skip_memcheck; extern bool g_skip_memcheck;
g_skip_memcheck = true; g_skip_memcheck = true;
this->reset = 1; this->reset = 1;
this->eval(n); do {
this->eval();
} while (--n);
this->reset = 0; this->reset = 0;
g_skip_memcheck = false; g_skip_memcheck = false;
} }

View file

@ -1,12 +1,16 @@
#ifndef _NPC_VPI_WRAPPER_H_ #ifndef _NPC_VPI_WRAPPER_H_
#define _NPC_VPI_WRAPPER_H_ #define _NPC_VPI_WRAPPER_H_
#include <components.hpp> #include <components.hpp>
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <vpi_user.h> #include <vpi_user.h>
template <typename T, std::size_t nr> template <typename T, std::size_t nr>
class _RegistersVPI : public _RegistersBase<T, nr> { class _RegistersVPI : public _RegistersBase<T, nr> {
std::array<vpiHandle, nr> reg_handles; std::array<vpiHandle, nr> reg_handles;
vpiHandle pc_handle; vpiHandle pc_handle;
T vpi_get(vpiHandle vh) const { T vpi_get(vpiHandle vh) const {
s_vpi_value v; s_vpi_value v;
v.format = vpiIntVal; v.format = vpiIntVal;