diff --git a/npc/cmake/ChiselBuild.cmake b/npc/cmake/ChiselBuild.cmake index 789b476..f446790 100644 --- a/npc/cmake/ChiselBuild.cmake +++ b/npc/cmake/ChiselBuild.cmake @@ -1,20 +1,19 @@ -# -- Add an always run target to generate verilog files with sbt/bloop, -# as we don't know if the result files will be different from cmake -# NOTE: Must reconfigure if we add new files in SCALA_CORE directory +# -- Add an always run target to generate verilog files with sbt/bloop, as we +# don't know if the result files will be different from cmake NOTE: Must +# reconfigure if we add new files in SCALA_CORE directory file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/main/resources/*") message(STATUS "Found scala source file: ${SCALA_CORE_SOURCES}") -set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} ${SCALA_CORE}/build.sbt) +set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} + ${SCALA_CORE}/build.sbt) if(BUILD_USE_BLOOP) + message(STATUS "Building core using bloop") set(CHISEL_TARGET bloop_${TOPMODULE}) set(CHISEL_TEST_TARGET bloop_${TOPMODULE}_test) # Export sbt build config to bloop if(NOT EXISTS ${SCALA_CORE}/.bloop) - execute_process( - COMMAND sbt bloopInstall - WORKING_DIRECTORY ${SCALA_CORE} - ) + execute_process(COMMAND sbt bloopInstall WORKING_DIRECTORY ${SCALA_CORE}) endif() string(REPLACE " " ";" CHISEL_EMIT_ARGS_LIST ${CHISEL_EMIT_ARGS}) list(TRANSFORM CHISEL_EMIT_ARGS_LIST PREPEND "--args;") @@ -24,14 +23,11 @@ if(BUILD_USE_BLOOP) WORKING_DIRECTORY ${SCALA_CORE} DEPENDS ${CHISEL_DEPENDENCY} COMMAND_EXPAND_LISTS - COMMENT "Run bloop from CMake" - ) -# add_test( -# NAME bloop_${TOPMODULE}_test -# COMMAND bloop test -# WORKING_DIRECTORY ${SCALA_CORE} -# ) + COMMENT "Run bloop from CMake") + # add_test( NAME bloop_${TOPMODULE}_test COMMAND bloop test WORKING_DIRECTORY + # ${SCALA_CORE} ) else() + message(STATUS "Building core using sbt") set(CHISEL_TARGET sbt_${TOPMODULE}) set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test) add_custom_command( @@ -40,19 +36,16 @@ else() WORKING_DIRECTORY ${SCALA_CORE} DEPENDS ${CHISEL_DEPENDENCY} VERBATIM - COMMENT "Run sbt from CMake" - ) + COMMENT "Run sbt from CMake") add_test( NAME sbt_${TOPMODULE}_test COMMAND sbt test - WORKING_DIRECTORY ${SCALA_CORE} - ) + WORKING_DIRECTORY ${SCALA_CORE}) endif() if(NOT EXISTS ${CHISEL_OUTPUT_TOPMODULE}) - # Probably cold build, generate verilog at configure time to produce top module file - execute_process( - COMMAND sbt "run ${CHISEL_EMIT_ARGS}" - WORKING_DIRECTORY ${SCALA_CORE} - ) + # Probably cold build, generate verilog at configure time to produce top + # module file + execute_process(COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + WORKING_DIRECTORY ${SCALA_CORE}) endif() diff --git a/npc/core/src/main/scala/components/ALU.scala b/npc/core/src/main/scala/components/ALU.scala index 44e9c14..0c1f0ae 100644 --- a/npc/core/src/main/scala/components/ALU.scala +++ b/npc/core/src/main/scala/components/ALU.scala @@ -6,7 +6,8 @@ import shapeless.{HNil, ::} class ALUControlInterface extends Bundle { object OpSelect extends ChiselEnum { - val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, aOpSrl, aOpSra = Value + val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, + aOpSrl, aOpSra = Value } object SrcASelect extends ChiselEnum { val aSrcARs1, aSrcAPc, aSrcAZero = Value @@ -54,19 +55,21 @@ class ALU[T <: UInt](tpe: T) extends Module { import control.OpSelect._ - out.result := MuxLookup(control.op, 0.U)(Seq( - aOpAdd -> add, - aOpSub -> sub, - aOpNot -> not, - aOpAnd -> and, - aOpOr -> or, - aOpXor -> xor, - aOpSlt -> slt, - aOpSltu -> sltu, - aOpSll -> sll, - aOpSrl -> srl, - aOpSra -> sra.asUInt - )) + out.result := MuxLookup(control.op, 0.U)( + Seq( + aOpAdd -> add, + aOpSub -> sub, + aOpNot -> not, + aOpAnd -> and, + aOpOr -> or, + aOpXor -> xor, + aOpSlt -> slt, + aOpSltu -> sltu, + aOpSll -> sll, + aOpSrl -> srl, + aOpSra -> sra.asUInt + ) + ) } object ALU { diff --git a/npc/core/src/main/scala/components/Mem.scala b/npc/core/src/main/scala/components/Mem.scala index 24f9927..a9d3e22 100644 --- a/npc/core/src/main/scala/components/Mem.scala +++ b/npc/core/src/main/scala/components/Mem.scala @@ -1,13 +1,15 @@ package flow.components import chisel3._ +import chisel3.experimental.noPrefix import chisel3.util.HasBlackBoxPath import chisel3.util.HasBlackBoxResource import chisel3.util.log2Ceil -import chisel3.experimental.noPrefix -import scala.collection.SeqMap import flow.components -import shapeless.{HNil, ::} +import shapeless.:: +import shapeless.HNil + +import scala.collection.SeqMap class RamControlInterface(addrWidth: Int) extends Bundle { val valid = Input(Bool()) diff --git a/npc/csrc/Flow/config.cpp b/npc/csrc/Flow/config.cpp index 035fe24..e8e224b 100644 --- a/npc/csrc/Flow/config.cpp +++ b/npc/csrc/Flow/config.cpp @@ -11,9 +11,6 @@ void Config::cli_parse(int argc, char **argv) { "Memory file is in text format"); app.add_option("--wav", wavefile, "output .vcd file path"); app.add_flag("-i", interactive, "Launch sdb for interactive session"); - app.add_option("--diff-lib", lib_ref, - "Dynamic library file of difftest reference") - ->check(CLI::ExistingFile); try { app.parse(argc, argv); diff --git a/npc/include/components.hpp b/npc/include/components.hpp index cfd05f1..887ded0 100644 --- a/npc/include/components.hpp +++ b/npc/include/components.hpp @@ -14,32 +14,48 @@ #include template class _RegistersBase { - std::array regs; - T pc; std::shared_ptr logger = spdlog::stdout_color_mt("registers"); - virtual T fetch_pc() const; - virtual T fetch_reg(std::size_t id) const; + virtual T fetch_pc() const = 0; + virtual T fetch_reg(std::size_t id) const = 0; public: 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); - } - } }; template class Memory { - paddr_t pmem_start, pmem_end; - public: std::array mem; - // TODO: Read memory file before init and use memcpy to initialize memory. + Memory(std::filesystem::path filepath, bool is_binary, paddr_t pmem_start, paddr_t pmem_end) : pmem_start(pmem_start), pmem_end(pmem_end) { + read_memory(filepath, is_binary); + } + + const word_t &operator[](std::size_t addr) { return this->read(addr); } + + void transfer(paddr_t addr, uint8_t data[], size_t len, bool is_write) { + if (is_write) { + // memcpy(guest_to_host(addr), data, len); + size_t offset = (addr - pmem_start); + std::copy(data, data + len, &mem[offset]); + } else { + // memcpy(data, guest_to_host(addr), len); + size_t offset = (addr - pmem_start); + std::copy(&mem[offset], &mem[offset + len], data); + } + } + + bool in_pmem(paddr_t addr) const { + return addr >= pmem_start && addr <= pmem_end; + } + +private: + paddr_t pmem_start, pmem_end; + + void read_memory(std::filesystem::path filepath, bool is_binary) { if (!std::filesystem::exists(filepath)) throw std::runtime_error("Memory file not found"); if (is_binary) { @@ -55,30 +71,12 @@ public: } } } - const word_t &operator[](std::size_t addr) { return this->read(addr); } - void transfer(paddr_t addr, uint8_t data[], size_t len, bool is_write) { - if (is_write) { - // memcpy(guest_to_host(addr), data, len); - size_t offset = (addr - pmem_start); - std::copy(data, data + len, &mem[offset]); - } else { - // memcpy(data, guest_to_host(addr), len); - size_t offset = (addr - pmem_start); - std::copy(&mem[offset], &mem[offset + len], data); - } - } - bool in_pmem(paddr_t addr) const { - return addr >= pmem_start && addr <= pmem_end; - } }; template class MemoryMap { - std::unique_ptr ram; - std::unique_ptr devices; - std::shared_ptr logger = spdlog::stdout_color_mt("mmap"); - public: - MemoryMap(std::unique_ptr &&ram, std::unique_ptr &&devices) + MemoryMap(std::unique_ptr &&ram, + std::unique_ptr &&devices) noexcept : ram(std::move(ram)), devices(std::move(devices)) {} void write(paddr_t waddr, word_t wdata, char wmask) { @@ -122,5 +120,10 @@ public: void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) { logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr)); } + +private: + std::unique_ptr ram; + std::unique_ptr devices; + std::shared_ptr logger = spdlog::stdout_color_mt("mmap"); }; #endif diff --git a/npc/include/config.hpp b/npc/include/config.hpp index 8c12f31..8d6bf33 100644 --- a/npc/include/config.hpp +++ b/npc/include/config.hpp @@ -12,7 +12,6 @@ struct Config { bool interactive{false}; bool memory_file_binary = {true}; 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 deleted file mode 100644 index ef8bde0..0000000 --- a/npc/include/trm_difftest.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef _DIFFTEST_DIFFTEST_H_ -#define _DIFFTEST_DIFFTEST_H_ -#include "disasm.hpp" -#include "types.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using paddr_t = uint32_t; -struct DifftestTrmInterface : public TrmInterface { - TrmInterface &dut; - TrmInterface &ref; - - DifftestTrmInterface(TrmInterface &dut, TrmInterface &ref, void *mem, - size_t mem_size) - : dut(dut), ref(ref) { - init = [this, mem, mem_size](int n) { - this->ref.init(n); - this->dut.init(n); - paddr_t reset_vector = 0x80000000; - this->ref.memcpy(reset_vector, mem, mem_size, TRM_TO_MACHINE); - this->dut.memcpy(reset_vector, mem, mem_size, TRM_TO_MACHINE); - fetch_state(); - }; - exec = [this](uint64_t n) { - bool enable_disasm = true; - if (n > 30) { - enable_disasm = false; - } - - while (n--) { - word_t pc = this->ref.at("pc"); - word_t inst = this->ref.at(pc); - if (enable_disasm) - std::cout << d.disassemble(pc, (uint8_t *)&inst, WORD_BYTES) - << std::endl; - if (inst == 1048691) { - // ebreak - throw TrmRuntimeException(TrmRuntimeException::EBREAK, "ebreak"); - } - this->ref.exec(1); - this->dut.exec(1); - this->ref.fetch_state(); - this->dut.fetch_state(); - if (*(CPUState *)this->ref.cpu_state != - *(CPUState *)this->dut.cpu_state) { - throw TrmRuntimeException(TrmRuntimeException::DIFFTEST_FAILED, - "Difftest failed"); - } - } - }; - - // NOTE: Different from normal Trm, we copy 2 * sizeof(CPUState) to/from p, - // which represents ref_state and dut state - regcpy = [this](void *p, bool direction) { - // this->ref.regcpy(p, direction); - // this->dut.regcpy(p, direction); - }; - - memcpy = [this](paddr_t paddr, void *p, size_t n, bool direction) { - this->dut.memcpy(paddr, p, n, direction); - this->ref.memcpy(paddr, (uint8_t *)p + n, n, direction); - }; - } - - word_t at(std::string name) const override { - if (name.empty()) { - throw std::runtime_error("Empty register name"); - } else if (name[0] == 'r') { - std::cout << name.substr(1) << std::endl; - this->ref.at(name.substr(1)); - } else if (name[0] == 'd') { - this->dut.at(name.substr(1)); - } else { - throw std::runtime_error("Register name provided to difftest interface " - "must start with r or d."); - } - return 0; - } - - word_t at(paddr_t addr) const override { - std::cout << ref.at(addr) << "\t" << dut.at(addr) << std::endl; - return dut.at(addr); - } - - void print(std::ostream &os) const override { - os << "REF state:\n" - << *(CPUState *)ref.cpu_state << "DUT state:\n" - << *(CPUState *)dut.cpu_state << std::endl; - } -}; -#endif \ No newline at end of file diff --git a/npc/include/trm_interface.hpp b/npc/include/trm_interface.hpp deleted file mode 100644 index 5410820..0000000 --- a/npc/include/trm_interface.hpp +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef _NPC_TRM_INTERFACE_HEADER_FILE_ -#define _NPC_TRM_INTERFACE_HEADER_FILE_ -#include -#include -#include -#include -#include -#include -#include - -extern Disassembler d; - -template struct CPUStateBase { - R reg[nr_reg] = {0}; - word_t pc = 0x80000000; - - static const std::map inline regs_by_name = - riscv32_regs_by_name; - CPUStateBase() { - for (int i = 0; i < nr_reg; i++) - reg[i] = 0; - } - - bool operator==(const CPUStateBase &other) const { - if (pc != other.pc) - return false; - for (int i = 0; i < nr_reg; ++i) { - if (reg[i] != other.reg[i]) - return false; - } - return true; - } - - bool operator!=(const CPUStateBase &other) const { - return !(*this == other); // Reuse the == operator for != implementation - } - - /* This does not update the register!!! */ - R at(std::string name) { - // FIXME: Using this to get pc seems broken - return name == "pc" ? pc : reg[regs_by_name.at(name)]; - } - - uint32_t reg_str2val(const char *name, bool *success) { - try { - *success = true; - return this->at(name); - } catch (std::runtime_error) { - *success = false; - return 0; - } - } -}; - -template -std::ostream &operator<<(std::ostream &os, const CPUStateBase &cpu) { - os << "PC: " << std::hex << cpu.pc << std::endl; - for (int i = 0; i < nr_reg; i++) { - os << "reg " << std::dec << std::setw(2) << i << ":" << std::hex - << std::setw(10) << cpu.reg[i]; - if (i % 4 == 3) { - os << std::endl; - } else { - os << " | "; - } - } - return os; -} - -using CPUState = CPUStateBase; - -enum { TRM_FROM_MACHINE, TRM_TO_MACHINE }; - -class TrmInterface { -protected: - using memcpy_t = void (*)(paddr_t, void *, size_t, bool); - using regcpy_t = void (*)(void *, bool); - using exec_t = void (*)(uint64_t); - using init_t = void (*)(int); - std::function regcpy; - -public: - std::function exec; - std::function init; - // TODO: paddr_t can probably changed to (void *)? - std::function memcpy; - // Managed by callee - void *cpu_state; - - TrmInterface() {} - TrmInterface(memcpy_t f_memcpy, regcpy_t f_regcpy, exec_t f_exec, - init_t f_init, void *cpu_state) - : memcpy(f_memcpy), regcpy(f_regcpy), exec(f_exec), init(f_init), - cpu_state(cpu_state) {} - - void fetch_state() { this->regcpy(cpu_state, TRM_FROM_MACHINE); } - void push_state() { this->regcpy(cpu_state, TRM_TO_MACHINE); } - virtual word_t at(std::string) const = 0; - virtual word_t at(word_t addr) const = 0; - virtual void print(std::ostream &os) const = 0; -}; - -class TrmRuntimeException : public std::exception { -private: - const char *msg_; - int code_; - -public: - enum { EBREAK, DIFFTEST_FAILED }; - TrmRuntimeException(int code, const char *message) - : code_(code), msg_(message) {} - - virtual const char *what() const throw() { return msg_; } - - int error_code() const { return code_; } -}; - -struct RefTrmInterface : TrmInterface { - RefTrmInterface(std::filesystem::path lib_file) { - void *handle = dlopen(lib_file.c_str(), RTLD_LAZY); - if (handle == nullptr) { - throw std::runtime_error("Failed to open diff library file"); - }; - memcpy = (memcpy_t)dlsym(handle, "difftest_memcpy"); - if (handle == nullptr) { - throw std::runtime_error("Failed to find `difftest_memcpy`"); - }; - regcpy = (regcpy_t)dlsym(handle, "difftest_regcpy"); - if (handle == nullptr) { - throw std::runtime_error("Failed to find `difftest_regcpy`"); - }; - exec = (exec_t)dlsym(handle, "difftest_exec"); - if (handle == nullptr) { - throw std::runtime_error("Failed to find `difftest_exec`"); - }; - init = (init_t)dlsym(handle, "difftest_init"); - if (handle == nullptr) { - throw std::runtime_error("Failed to find `difftest_init`"); - }; - cpu_state = new CPUState{}; - } - - ~RefTrmInterface() { delete (CPUState *)cpu_state; } - - word_t at(std::string name) const override { - return ((CPUState *)cpu_state)->at(name); - } - - 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 { os << *(CPUState *)cpu_state; } -}; - -#endif diff --git a/npc/include/types.h b/npc/include/types.h index fcc14e4..f34f168 100644 --- a/npc/include/types.h +++ b/npc/include/types.h @@ -4,8 +4,7 @@ extern "C" { #endif #include -#include -#include +#include typedef uint32_t word_t; typedef int32_t sword_t; @@ -15,10 +14,8 @@ static const sword_t SWORD_T_MIN = INT32_MIN; #define WORD_BYTES 4 #define REG_COUNT 32 -#define FMT_WORD "0x%08x" typedef uint32_t vaddr_t; typedef uint32_t paddr_t; -#define FMT_ADDR "0x%08x" typedef uint16_t ioaddr_t; struct Breakpoint { @@ -31,18 +28,8 @@ struct Breakpoint { #endif #ifdef __cplusplus -#include -#include #include -const std::map riscv32_regs_by_name{ - {"$0", 0}, {"ra", 1}, {"sp", 2}, {"gp", 3}, {"tp", 4}, {"t0", 5}, - {"t1", 6}, {"t2", 7}, {"s0", 8}, {"s1", 9}, {"a0", 10}, {"a1", 11}, - {"a2", 12}, {"a3", 13}, {"a4", 14}, {"a5", 15}, {"a6", 16}, {"a7", 17}, - {"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, {"s7", 23}, - {"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, {"t3", 28}, {"t4", 29}, - {"t5", 30}, {"t6", 31}}; - struct DbgState { std::vector *bp; }; diff --git a/npc/include/vpi_wrapper.hpp b/npc/include/vpi_wrapper.hpp index 25f26fb..22b1876 100644 --- a/npc/include/vpi_wrapper.hpp +++ b/npc/include/vpi_wrapper.hpp @@ -23,6 +23,11 @@ class _RegistersVPI : public _RegistersBase { public: _RegistersVPI(const std::string regs_prefix, const std::string pcname) { + init_handlers(regs_prefix, pcname); + } + +private: + void init_handlers(const std::string regs_prefix, const std::string pcname) { for (int i = 0; i < nr; i++) { std::string regname = regs_prefix + std::to_string(i); vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), nullptr);