diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3ba9598 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +ReflowComments: false diff --git a/.gitea/workflows/npc-test.yml b/.gitea/workflows/npc-test.yml new file mode 100644 index 0000000..a5652e4 --- /dev/null +++ b/.gitea/workflows/npc-test.yml @@ -0,0 +1,45 @@ +name: Run CTests within npc +on: [push] + +jobs: + npc-test: + runs-on: nix + steps: + - uses: https://github.com/cachix/cachix-action@v14 + with: + name: ysyx + authToken: '${{ secrets.CACHIX_SIGNING_KEY }}' + - uses: actions/checkout@v4 + with: + submodules: true + - name: Cache develop environment + id: cache-nix-develop + uses: actions/cache@v4 + with: + path: /nix/store + key: nix-develop-${{ hashFiles('flake.*') }} + - name: Fetch nix store + if: steps.cache-nix-develop.outputs.cache-hit != 'true' + run: nix develop .#npc + - name: Use develop environment + uses: https://git.xinyang.life/xin/nix-develop@main + with: + arguments: .#npc + - name: Cache sbt dependencies + id: cache-sbt-dependency + uses: actions/cache@v4 + with: + path: npc/core + key: core-${{ hashFiles('npc/core/build.sbt') }} + - name: Fetch sbt dependencies + if: steps.cache-sbt-dependency.outputs.cache-hit != 'true' + run: | + cd npc/core + sbt update + - name: Run difftests + run: | + mkdir -p npc/build + cd npc/build + cmake $cmakeFlags ../ + make -j8 + ctest -V diff --git a/.gitignore b/.gitignore index 44a51ce..7a06b53 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ /nvboard **/.cache **/result +/.pre-commit-config.yaml diff --git a/flake.lock b/flake.lock index 36b9a39..247c2b9 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,45 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1709237383, @@ -34,10 +89,89 @@ "type": "github" } }, + "nixpkgs-circt162": { + "locked": { + "lastModified": 1705645507, + "narHash": "sha256-tX3vipIAmNDBA8WNWG4oY4KyTfnm2YieTHO2BhG8ISA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nur-xin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1710242657, + "narHash": "sha256-tgaILHKZH6vC8P8N8NBMNQ/XIpTY72zMojlGLPLPLZk=", + "ref": "refs/heads/master", + "rev": "6a499c8371c6a14d11a8c2fcc9f233e7b8e688a0", + "revCount": 148, + "type": "git", + "url": "https://git.xinyang.life/xin/nur.git" + }, + "original": { + "type": "git", + "url": "https://git.xinyang.life/xin/nur.git" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "nixpkgs-circt162": "nixpkgs-circt162", + "nur-xin": "nur-xin", + "pre-commit-hooks": "pre-commit-hooks" } }, "systems": { @@ -54,6 +188,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 3a07d79..c5db1e6 100644 --- a/flake.nix +++ b/flake.nix @@ -1,13 +1,30 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; flake-utils.url = "github:numtide/flake-utils"; + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nur-xin = { + url = "git+https://git.xinyang.life/xin/nur.git"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, ... }@inputs: with inputs; flake-utils.lib.eachDefaultSystem (system: let - pkgs = nixpkgs.legacyPackages.${system}; + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + overlays = [ + (self: super: { + nvboard = nur-xin.legacyPackages.${system}.nvboard; + }) + ]; + }; crossPkgs = import nixpkgs { localSystem = system; crossSystem = { @@ -20,14 +37,27 @@ }; in { + checks = { + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + trim-trailing-whitespace.enable = true; + clang-format = { + enable = true; + types_or = pkgs.lib.mkForce [ "c" "c++" ]; + }; + }; + }; + }; packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; + packages.nemu-lib = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; defconfig = "riscv32-lib_defconfig"; }; packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { pname = "am-kernels-cmake"; version = "2024.02.18"; - src = ./am-kernels; + src = ./am-kernels; nativeBuildInputs = [ pkgs.cmake @@ -44,7 +74,7 @@ self.packages.${system}.abstract-machine ]; }; - + devShells.nemu = pkgs.mkShell { packages = with pkgs; [ clang-tools @@ -53,6 +83,44 @@ inputsFrom = [ self.packages.${system}.nemu ]; + IMAGES_PATH = "${self.packages.${system}.am-kernels}/share/binary"; + }; + + devShells.npc = with pkgs; mkShell { + inherit (self.checks.${system}.pre-commit-check) shellHook; + CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; + packages = [ + clang-tools + cmake + ninja + coursier + espresso + bloop + + gdb + jre + + gtkwave + ]; + + nativeBuildInputs = [ + cmake + sbt + nvboard + nixpkgs-circt162.legacyPackages.${system}.circt + yosys + cli11 + ]; + + buildInputs = [ + verilator + nvboard + openssl + ] ++ self.checks.${system}.pre-commit-check.enabledPackages; + + cmakeFlags = [ + "-DDIFFTEST_LIB:string=${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so" + ]; }; } ); diff --git a/nemu/.clang-format b/nemu/.clang-format deleted file mode 100644 index 0515c02..0000000 --- a/nemu/.clang-format +++ /dev/null @@ -1,3 +0,0 @@ ---- -Language: Cpp -BasedOnStyle: LLVM \ No newline at end of file diff --git a/nemu/Kconfig b/nemu/Kconfig index 2b316a5..85074bd 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -126,6 +126,26 @@ endmenu menu "Testing and Debugging" +choice + prompt "Choose log level" + default LOG_TRACE +config LOG_TRACE + bool "trace" +config LOG_INFO + bool "info" +config LOG_WARNING + bool "warning" +config LOG_ERROR + bool "error" +endchoice + +config LOG_LEVEL + int + default 1 if LOG_ERROR + default 2 if LOG_WARNING + default 3 if LOG_INFO + default 4 if LOG_TRACE + default 0 config TRACE bool "Enable tracer" diff --git a/nemu/configs/riscv32-lib_defconfig b/nemu/configs/riscv32-lib_defconfig new file mode 100644 index 0000000..15d8758 --- /dev/null +++ b/nemu/configs/riscv32-lib_defconfig @@ -0,0 +1,72 @@ +# +# Automatically generated file; DO NOT EDIT. +# NEMU Configuration Menu +# +# CONFIG_ISA_x86 is not set +# CONFIG_ISA_mips32 is not set +CONFIG_ISA_riscv=y +# CONFIG_ISA_loongarch32r is not set +CONFIG_ISA="riscv32" + +# +# ISA-dependent Options for riscv +# +# CONFIG_RV64 is not set +# CONFIG_RVE is not set +# end of ISA-dependent Options for riscv + +CONFIG_ENGINE_INTERPRETER=y +CONFIG_ENGINE="interpreter" +CONFIG_MODE_SYSTEM=y +# CONFIG_TARGET_NATIVE_ELF is not set +CONFIG_TARGET_SHARE=y +# CONFIG_TARGET_AM is not set + +# +# Build Options +# +CONFIG_CC_GCC=y +# CONFIG_CC_GPP is not set +# CONFIG_CC_CLANG is not set +CONFIG_CC="gcc" +# CONFIG_CC_O0 is not set +# CONFIG_CC_O1 is not set +CONFIG_CC_O2=y +# CONFIG_CC_O3 is not set +CONFIG_CC_OPT="-O2" +CONFIG_CC_LTO=y +CONFIG_CC_DEBUG=y +# CONFIG_CC_ASAN is not set +# end of Build Options + +# +# Testing and Debugging +# +CONFIG_LOG_TRACE=y +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARNING is not set +# CONFIG_LOG_ERROR is not set +CONFIG_LOG_LEVEL=4 +# CONFIG_TRACE is not set +CONFIG_DIFFTEST_REF_PATH="none" +CONFIG_DIFFTEST_REF_NAME="none" +# end of Testing and Debugging + +# +# Memory Configuration +# +CONFIG_MBASE=0x80000000 +CONFIG_MSIZE=0x8000000 +CONFIG_PC_RESET_OFFSET=0 +# CONFIG_PMEM_MALLOC is not set +CONFIG_PMEM_GARRAY=y +CONFIG_MEM_RANDOM=y +# end of Memory Configuration + +# +# Miscellaneous +# +CONFIG_TIMER_GETTIMEOFDAY=y +# CONFIG_TIMER_CLOCK_GETTIME is not set +CONFIG_RT_CHECK=y +# end of Miscellaneous diff --git a/nemu/default.nix b/nemu/default.nix index d3d5a70..052b4a7 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -2,7 +2,8 @@ lib, stdenv, am-kernels, - dtc + dtc, + defconfig ? "alldefconfig", }: stdenv.mkDerivation rec { @@ -31,27 +32,27 @@ stdenv.mkDerivation rec { configurePhase = '' export NEMU_HOME=$(pwd) - make alldefconfig + make ${defconfig} ''; buildPhase = '' make ''; - doCheck = true; - checkPhase = '' + doCheck = (defconfig == "alldefconfig"); + checkPhase = if doCheck then '' export IMAGES_PATH=${am-kernels}/share/binary make test - ''; + '' else ""; installPhase = '' mkdir -p $out/bin + mkdir -p $out/lib make PREFIX=$out install ''; shellHook = '' export NEMU_HOME=$(pwd) - export IMAGES_PATH=${am-kernels}/share/binary ''; meta = with lib; { diff --git a/nemu/include/debug.h b/nemu/include/debug.h index 329c64a..c0f4952 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -16,41 +16,59 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ +#include #include #include -#include IFDEF(CONFIG_ITRACE, void log_itrace_print()); -#define Trace(format, ...) \ - _Log("[TRACE] " format "\n", ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 4) +#define Trace(format, ...) _Log("[TRACE] " format "\n", ##__VA_ARGS__) +#else +#define Trace(format, ...) +#endif -#define Log(format, ...) \ - _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 3) +#define Log(format, ...) \ + _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", __FILE__, \ + __LINE__, __func__, ##__VA_ARGS__) +#else +#define Log(format, ...) +#endif -#define Warning(format, ...) \ - _Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 2) +#define Warning(format, ...) \ + _Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#else +#define Warning(format, ...) +#endif -#define Error(format, ...) \ - _Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 1) +#define Error(format, ...) \ + _Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", __FILE__, \ + __LINE__, __func__, ##__VA_ARGS__) +#else +#define Error(format, ...) +#endif -#define Assert(cond, format, ...) \ - do { \ - if (!(cond)) { \ - MUXDEF(CONFIG_TARGET_AM, printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__), \ - (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__))); \ - IFNDEF(CONFIG_TARGET_AM, extern FILE* log_fp; fflush(log_fp)); \ - IFDEF(CONFIG_ITRACE, log_itrace_print()); \ - extern void assert_fail_msg(); \ - assert_fail_msg(); \ - assert(cond); \ - } \ +#define Assert(cond, format, ...) \ + do { \ + if (!(cond)) { \ + MUXDEF( \ + CONFIG_TARGET_AM, \ + printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ##__VA_ARGS__), \ + (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", \ + ##__VA_ARGS__))); \ + IFNDEF(CONFIG_TARGET_AM, extern FILE *log_fp; fflush(log_fp)); \ + IFDEF(CONFIG_ITRACE, log_itrace_print()); \ + extern void assert_fail_msg(); \ + assert_fail_msg(); \ + assert(cond); \ + } \ } while (0) -#define panic(format, ...) Assert(0, format, ## __VA_ARGS__) +#define panic(format, ...) Assert(0, format, ##__VA_ARGS__) #define TODO() panic("please implement me") diff --git a/nemu/scripts/build.mk b/nemu/scripts/build.mk index f050197..a3b71d2 100644 --- a/nemu/scripts/build.mk +++ b/nemu/scripts/build.mk @@ -76,8 +76,13 @@ $(BINARY):: $(OBJS) $(ARCHIVES) @$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS) install: $(BINARY) +ifeq ($(SHARE),1) + @mkdir -p $(PREFIX)/lib + @cp $(BINARY) $(PREFIX)/lib/ +else @mkdir -p $(PREFIX)/bin @cp $(BINARY) $(PREFIX)/bin/ +endif clean: -rm -rf $(BUILD_DIR) diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index d2b1f98..097c02f 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -83,7 +83,7 @@ static void execute(uint64_t n) { g_nr_guest_inst ++; trace_and_difftest(&s, cpu.pc); if (wp_eval_all()) { - puts(logbuf[logbuf_rear]); + IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); break; } if (nemu_state.state != NEMU_RUNNING) break; @@ -132,7 +132,7 @@ void cpu_exec(uint64_t n) { ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), nemu_state.halt_pc); if(nemu_state.halt_ret != 0) { - log_itrace_print(); + IFDEF(CONFIG_ITRACE, log_itrace_print()); } } // fall through case NEMU_QUIT: statistic(); diff --git a/nemu/src/cpu/difftest/ref.c b/nemu/src/cpu/difftest/ref.c index ef89a61..7d59b4a 100644 --- a/nemu/src/cpu/difftest/ref.c +++ b/nemu/src/cpu/difftest/ref.c @@ -13,25 +13,34 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include +#include "types.h" #include +#include #include +#include #include -__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) { - assert(0); +__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, + bool direction) { + if (direction == DIFFTEST_TO_REF) { + memcpy(guest_to_host(addr), buf, n); + } else { + assert(0); + } } __EXPORT void difftest_regcpy(void *dut, bool direction) { - assert(0); + // assert(0); + if (direction == DIFFTEST_TO_DUT) + memcpy(dut, &cpu, sizeof(CPU_state)); + else + memcpy(&cpu, dut, sizeof(CPU_state)); } -__EXPORT void difftest_exec(uint64_t n) { - assert(0); -} +__EXPORT void difftest_exec(uint64_t n) { cpu_exec(n); } __EXPORT void difftest_raise_intr(word_t NO) { - assert(0); + // assert(0); } __EXPORT void difftest_init(int port) { diff --git a/npc/.envrc b/npc/.envrc index 3550a30..3f3af37 100644 --- a/npc/.envrc +++ b/npc/.envrc @@ -1 +1 @@ -use flake +use flake ..#npc diff --git a/npc/.gitignore b/npc/.gitignore index fd7efb6..e0793d8 100644 --- a/npc/.gitignore +++ b/npc/.gitignore @@ -15,3 +15,4 @@ hs_err_pid* .vscode/ .direnv/ compile_commands.json + diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index db9d0a1..78dbfe5 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -1,91 +1,69 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.26) project(flow) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0144 NEW) -execute_process( - COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "configure(npc)" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. -) +include(CMakeDependentOption) +include(CTest) +enable_testing() +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -find_package(SDL2 REQUIRED) -find_package(SDL2_image REQUIRED) +# -- Build options +option(BUILD_USE_BLOOP "Whether to use bloop to speed up elaborate" ON) +option(BUILD_SIM_TARGET "Whether to build verilator simulation binary" ON) +cmake_dependent_option(BUILD_SIM_NVBOARD_TARGET "Whether to build nvboard target" OFF "BUILD_SIM_TARGET" OFF) +option(ENABLE_YSYX_GIT_TRACKER "Ysyx tracker support" OFF) +set(TOPMODULE "Flow" CACHE STRING "Topmodule name in chisel") +set(DIFFTEST_LIB "" CACHE STRING "Dynamic library file used as difftest reference") -find_package(verilator REQUIRED) +# -- Ysyx tracker, configure +if(ENABLE_YSYX_GIT_TRACKER) + execute_process( + COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "configure(npc)" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. + ) +endif() + +# -- Check dependencies +if(BUILD_SIM_TARGET) + find_package(verilator REQUIRED) +endif() +if(BUILD_SIM_NVBOARD_TARGET) + find_package(SDL2 REQUIRED) + find_package(SDL2_image REQUIRED) +endif() +find_package(CLI11 CONFIG REQUIRED) find_library(NVBOARD_LIBRARY NAMES nvboard) find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h) -set(TOPMODULES "Flow") +# FIXME: all scala source file are tracked here, cause all files to rebuild +# after a source update. +set(SCALA_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") +set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") -foreach(TOPMODULE IN LISTS TOPMODULES) - - # FIXME: all scala source file are tracked here, cause all files to rebuild - # after a source update. - set(SCALA_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") - set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") - file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") - file(GLOB_RECURSE SCALA_CORE_TEST_SOURCES "${SCALA_CORE}/src/test/scala/*.scala") +# Verilog files are generted in CHISEL_OUTPUT_TMP_DIR and copy to +# CHISEL_OUTPUT_DIR if content changes +set(CHISEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc) +set(CHISEL_OUTPUT_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc_tmp) - # Configure time verilog source generation for verilator - execute_process( - COMMAND sbt "runMain circt.stage.ChiselMain --target-dir ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc --module ${CHISEL_MODULE_CLASS} --target verilog" - WORKING_DIRECTORY ${SCALA_CORE} - ) +set(CHISEL_OUTPUT_VERILATOR_CONF ${CHISEL_OUTPUT_DIR}/conf.vlt) +set(CHISEL_OUTPUT_TOPMODULE ${CHISEL_OUTPUT_DIR}/${TOPMODULE}.sv) +set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_TMP_DIR}") - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v - COMMAND sbt "runMain circt.stage.ChiselMain --target-dir ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc --module ${CHISEL_MODULE_CLASS} --target verilog" - WORKING_DIRECTORY ${SCALA_CORE} - DEPENDS ${SCALA_CORE_SOURCES} - ) +# -- Build NVBoard executable +if(BUILD_SIM_NVBOARD_TARGET) + add_subdirectory(csrc_nvboard) +endif() - add_custom_target( - ChiselBuild_${TOPMODULE} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v - ) +# -- Build Verilator executable and add to test +include_directories(include) - # -- Build NVBoard executable +add_subdirectory(csrc) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp - COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp - DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc - ) - - unset(SOURCES) - file(GLOB_RECURSE SOURCES csrc_nvboard/${TOPMODULE}/*.cpp) - add_executable(V${TOPMODULE}_nvboard ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp) - - verilate(V${TOPMODULE}_nvboard TRACE THREADS - TOP_MODULE ${TOPMODULE} - PREFIX V${TOPMODULE} - SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v) - - add_dependencies(V${TOPMODULE}_nvboard ChiselBuild_${TOPMODULE}) - target_include_directories(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS}) - target_link_libraries(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_LIBRARY} SDL2::SDL2 SDL2_image::SDL2_image) - - install(TARGETS V${TOPMODULE}_nvboard) - - # -- Build Verilator executable and add to test - - unset(SOURCES) - file(GLOB_RECURSE SOURCES csrc/${TOPMODULE}/*.cpp) - add_executable(V${TOPMODULE} ${SOURCES}) - - verilate(V${TOPMODULE} TRACE COVERAGE THREADS - TOP_MODULE ${TOPMODULE} - PREFIX V${TOPMODULE} - SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v) - - add_dependencies(V${TOPMODULE} ChiselBuild_${TOPMODULE}) - - enable_testing() - add_test(NAME V${TOPMODULE} COMMAND V${TOPMODULE}) - - # -- Add build tracking +# -- Add build tracking +if(ENABLE_YSYX_GIT_TRACKER) add_custom_command( TARGET V${TOPMODULE}_nvboard PRE_BUILD COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "build_${CMAKE_PROJECT_NAME}_V${TOPMODULE}_nvboard" @@ -97,5 +75,4 @@ foreach(TOPMODULE IN LISTS TOPMODULES) COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "build_${CMAKE_PROJECT_NAME}_V${TOPMODULE}" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. ) - -endforeach() +endif() diff --git a/npc/cmake/ChiselBuild.cmake b/npc/cmake/ChiselBuild.cmake new file mode 100644 index 0000000..aa830f7 --- /dev/null +++ b/npc/cmake/ChiselBuild.cmake @@ -0,0 +1,62 @@ +# -- 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/*") +set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} ${SCALA_CORE}/build.sbt) + +if(BUILD_USE_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} + ) + endif() + string(REPLACE " " ";" CHISEL_EMIT_ARGS_LIST ${CHISEL_EMIT_ARGS}) + list(TRANSFORM CHISEL_EMIT_ARGS_LIST PREPEND "--args;") + add_custom_command( + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND bloop run root ${CHISEL_EMIT_ARGS_LIST} + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + 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} +# ) +else() + set(CHISEL_TARGET sbt_${TOPMODULE}) + set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test) + add_custom_command( + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + WORKING_DIRECTORY ${SCALA_CORE} + # DEPENDS ${CHISEL_DEPENDENCY} test.scala + VERBATIM + COMMENT "Run sbt from CMake" + ) + add_test( + NAME sbt_${TOPMODULE}_test + COMMAND sbt test + WORKING_DIRECTORY ${SCALA_CORE} + ) +endif() + +if(NOT EXISTS ${CHISEL_OUTPUT_DIR}) + # 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} + ) + execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + ) +endif() diff --git a/npc/core/build.sbt b/npc/core/build.sbt index e04b603..3d0f4f8 100644 --- a/npc/core/build.sbt +++ b/npc/core/build.sbt @@ -3,6 +3,7 @@ ThisBuild / version := "0.1.0" val chiselVersion = "6.2.0" +val circeVersion = "0.14.1" lazy val root = (project in file(".")) .settings( @@ -10,8 +11,13 @@ lazy val root = (project in file(".")) libraryDependencies ++= Seq( "org.chipsalliance" %% "chisel" % chiselVersion, "edu.berkeley.cs" %% "chiseltest" % "6.0.0" % "test", - "com.chuusai" %% "shapeless" % "2.3.3" - ), + "com.chuusai" %% "shapeless" % "2.3.3", + "com.github.scopt" %% "scopt" % "4.1.0", + ) ++ Seq( + "io.circe" %% "circe-core", + "io.circe" %% "circe-generic", + "io.circe" %% "circe-parser" + ).map(_ % circeVersion), scalacOptions ++= Seq( "-language:reflectiveCalls", "-deprecation", @@ -20,4 +26,4 @@ lazy val root = (project in file(".")) "-Ymacro-annotations", ), addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full), - ) + ) \ No newline at end of file diff --git a/npc/core/src/main/resources/RamDpi.v b/npc/core/src/main/resources/RamDpi.v new file mode 100644 index 0000000..d6e3c98 --- /dev/null +++ b/npc/core/src/main/resources/RamDpi.v @@ -0,0 +1,24 @@ +import "DPI-C" function int pmem_read(input int addr); +import "DPI-C" function void pmem_write(input int waddr, input int wdata, input byte wmask); + +module RamDpi ( + input writeEnable, + input valid, + input [31:0] writeAddr, + input [31:0] writeData, + input [3:0] writeMask, + input reg [31:0] readAddr, + output reg [31:0] readData +); + always @(*) begin + if (valid) begin // 有读写请求时 + readData = pmem_read(readAddr); + if (writeEnable) begin // 有写请求时 + pmem_write(writeAddr, writeData, { 4'h0, writeMask }); + end + end + else begin + readData = 0; + end + end +endmodule \ No newline at end of file diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/RegisterFile.scala deleted file mode 100644 index 509ceaa..0000000 --- a/npc/core/src/main/scala/RegisterFile.scala +++ /dev/null @@ -1,86 +0,0 @@ -package flow.components - -import chisel3._ -import chisel3.util.log2Ceil -import chisel3.util.UIntToOH -import chisel3.util.MuxLookup -import shapeless.{ HNil, :: } - -class RegControl extends Bundle { - object WriteSelect extends ChiselEnum { - val rAluOut, rMemOut = Value - } - - val writeEnable = Input(Bool()) - val writeSelect = Input(WriteSelect()) - - type CtrlTypes = Bool :: WriteSelect.Type :: HNil - def ctrlBindPorts: CtrlTypes = { - writeEnable :: writeSelect :: HNil - } -} - -class RegFileData[T <: Data](size:Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { - val write = new Bundle { - val addr = Input(UInt(size.W)) - val data = Vec(numWritePorts, Input(tpe)) - } - val read = Vec(numReadPorts, new Bundle { - val rs = Input(UInt(size.W)) - val src = Output(tpe) - }) -} - -class RegFileInterface[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { - val control = new RegControl - // val data = new RegFileData(size, tpe, numReadPorts, numWritePorts) - val in = new Bundle { - val writeAddr = Input(UInt(size.W)) - val writeData = Input(Vec(numWritePorts, tpe)) - val rs = Input(Vec(numReadPorts, UInt(size.W))) - } - val out = new Bundle { - val src = Output(Vec(numReadPorts, tpe)) - } -} - -class RegisterFileCore[T <: Data](size: Int, tpe: T, numReadPorts: Int) extends Module { - require(numReadPorts >= 0) - val writePort = IO(new Bundle { - val enable = Input(Bool()) - val addr = Input(UInt(log2Ceil(size).W)) - val data = Input(tpe) - }) - val readPorts = IO(Vec(numReadPorts, new Bundle { - val addr = Input(UInt(log2Ceil(size).W)) - val data = Output(tpe) - })) - - val regFile = RegInit(VecInit(Seq.fill(size)(0.U(tpe.getWidth.W)))) - val writeAddrOH = UIntToOH(writePort.addr) - for ((reg, i) <- regFile.zipWithIndex.tail) { - reg := Mux(writeAddrOH(i) && writePort.enable, writePort.data, reg) - } - regFile(0) := 0.U - - for (readPort <- readPorts) { - readPort.data := regFile(readPort.addr) - } - dontTouch(regFile) -} - -object RegisterFile { - def apply[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int): RegFileInterface[T] = { - val core = Module(new RegisterFileCore(size, tpe, numReadPorts)) - val _out = Wire(new RegFileInterface(size, tpe, numReadPorts, numWritePorts)) - val clock = core.clock - for (i <- 0 until numReadPorts) { - core.readPorts(i).addr := _out.in.rs(i) - _out.out.src(i) := core.readPorts(i).data - } - core.writePort.addr := _out.in.writeAddr - core.writePort.data := _out.in.writeData(_out.control.writeSelect.asUInt) - core.writePort.enable := _out.control.writeEnable - _out - } -} diff --git a/npc/core/src/main/scala/ALU.scala b/npc/core/src/main/scala/components/ALU.scala similarity index 96% rename from npc/core/src/main/scala/ALU.scala rename to npc/core/src/main/scala/components/ALU.scala index 70f0fea..9a0b0f9 100644 --- a/npc/core/src/main/scala/ALU.scala +++ b/npc/core/src/main/scala/components/ALU.scala @@ -9,7 +9,7 @@ class ALUControlInterface extends Bundle { val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpEq, aOpNop = Value } object SrcSelect extends ChiselEnum { - val aSrcRs1, aSrcImm = Value + val aSrcRs2, aSrcImm = Value } val op = Input(OpSelect()) val src = Input(SrcSelect()) @@ -33,7 +33,7 @@ class ALU[T <: UInt](tpe: T) extends Module { val a = in.a(control.src.asUInt) // val adder_b = (Fill(tpe.getWidth, io.op(0)) ^ io.b) + io.op(0) // take (-b) if sub - val add = a + in.b + val add = a + in.b val sub = a - in.b val and = a & in.b val not = ~a diff --git a/npc/core/src/main/scala/components/Mem.scala b/npc/core/src/main/scala/components/Mem.scala new file mode 100644 index 0000000..6634edb --- /dev/null +++ b/npc/core/src/main/scala/components/Mem.scala @@ -0,0 +1,24 @@ +package flow.components + +import chisel3._ +import chisel3.util.HasBlackBoxPath +import chisel3.util.HasBlackBoxResource +import chisel3.util.log2Ceil +import chisel3.experimental.noPrefix +import scala.collection.SeqMap +import javax.swing.plaf.synth.SynthRadioButtonMenuItemUI + +class RamInterface[T <: Data](tpe: T, addrWidth: Int) extends Bundle { + val valid = Input(Bool()) + val writeEnable = Input(Bool()) + val writeAddr = Input(UInt(addrWidth.W)) + val writeData = Input(tpe) + val writeMask = Input(UInt((addrWidth / 8).W)) + val readAddr = Input(UInt(addrWidth.W)) + val readData = Output(tpe) +} + +class RamDpi extends BlackBox with HasBlackBoxResource { + val io = IO(new RamInterface(UInt(32.W), 32)) + addResource("/RamDpi.v") +} diff --git a/npc/core/src/main/scala/ProgramCounter.scala b/npc/core/src/main/scala/components/ProgramCounter.scala similarity index 100% rename from npc/core/src/main/scala/ProgramCounter.scala rename to npc/core/src/main/scala/components/ProgramCounter.scala diff --git a/npc/core/src/main/scala/components/RegisterFile.scala b/npc/core/src/main/scala/components/RegisterFile.scala new file mode 100644 index 0000000..9b50375 --- /dev/null +++ b/npc/core/src/main/scala/components/RegisterFile.scala @@ -0,0 +1,52 @@ +package flow.components + +import chisel3._ +import chisel3.util.log2Ceil +import chisel3.util.UIntToOH +import chisel3.util.MuxLookup +import chisel3.experimental.Trace._ +import shapeless.{ HNil, :: } + +class RegControl extends Bundle { + object WriteSelect extends ChiselEnum { + val rAluOut, rMemOut = Value + } + + val writeEnable = Input(Bool()) + val writeSelect = Input(WriteSelect()) + + type CtrlTypes = Bool :: WriteSelect.Type :: HNil + def ctrlBindPorts: CtrlTypes = { + writeEnable :: writeSelect :: HNil + } + traceName(writeEnable) +} + +class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) extends Module { + require(numReadPorts >= 0) + val control = IO(new RegControl) + val dataAddrWidth = log2Ceil(regCount).W + val in = IO(new Bundle { + val writeAddr = Input(UInt(dataAddrWidth)) + val writeData = Input(Vec(control.WriteSelect.all.length, tpe)) + val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth))) + }) + val out = IO(new Bundle { + val src = Output(Vec(numReadPorts, tpe)) + }) + + val regResetValue = 0.U(tpe.getWidth.W) + val regFile = RegInit(VecInit(Seq.fill(regCount)(regResetValue))) + val writeAddrOH = UIntToOH(in.writeAddr) + + for ((reg, i) <- regFile.zipWithIndex.tail) { + reg := Mux(writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable, in.writeData(control.writeSelect.asUInt), reg) + } + regFile(0) := 0.U + + for (port <- 0 until numReadPorts) { + out.src(port) := regFile(in.rs(port).asUInt) + } + traceName(regFile) + dontTouch(regFile) +} diff --git a/npc/core/src/main/scala/top/ArgParse.scala b/npc/core/src/main/scala/top/ArgParse.scala new file mode 100644 index 0000000..b430a70 --- /dev/null +++ b/npc/core/src/main/scala/top/ArgParse.scala @@ -0,0 +1,47 @@ +package flow + +import scopt.{ OParser, DefaultOEffectSetup } +import java.io.File + +case class CliOptions( + targetDir: File = new File("."), + configFile: Option[File] = None, + argsFile: Option[File] = None, + verilatorConfigFileOut: File = new File("conf.vlt"), +) { + val builder = OParser.builder[CliOptions] + val parser = { + import builder._ + OParser.sequence( + programName("flow"), + help("help"), + opt[Option[File]]('c', "config") + .action((x, c) => c.copy(configFile = x)) + .text("JSON Configuration file for generation"), + opt[Option[File]]("args-file") + .action((x, c) => c.copy(argsFile = x)) + .text("Passing file content as args when emit"), + opt[File]('o', "target-dir") + .action((x, c) => c.copy(targetDir = x)) + .text("Output files relative to this path"), + opt[File]("out-verilator-conf") + .action((x, c) => c.copy(verilatorConfigFileOut = x)) + .text("Options needed when simulating with verilator") + ) + } + + def parse(args: Array[String]): CliOptions = { + OParser.runParser(parser, args, CliOptions()) match { + case (result, effects) => + OParser.runEffects(effects, new DefaultOEffectSetup { + // ignore terminate + override def terminate(exitState: Either[String, Unit]): Unit = () + }) + + result match { + case Some(cliOptions: CliOptions) => { return cliOptions } + case _ => { throw new IllegalArgumentException("Wrong command line argument") } + } + } + } +} diff --git a/npc/core/src/main/scala/top/Config.scala b/npc/core/src/main/scala/top/Config.scala new file mode 100644 index 0000000..aa58107 --- /dev/null +++ b/npc/core/src/main/scala/top/Config.scala @@ -0,0 +1,16 @@ +package flow + +import io.circe.generic.JsonCodec + +// Which group of signals to trace +@JsonCodec case class TraceConfig ( + enable: Boolean = false, + registers: Array[Int] = Array(), + mem: Array[(Int, Int)] = Array(), +) + +@JsonCodec case class Config( + // Whether to enable Difftest + enableDifftest: Boolean = true, + traceConfig: TraceConfig = TraceConfig(), +) diff --git a/npc/core/src/main/scala/Main.scala b/npc/core/src/main/scala/top/FlowMain.scala similarity index 78% rename from npc/core/src/main/scala/Main.scala rename to npc/core/src/main/scala/top/FlowMain.scala index 1ede7e1..73df9ed 100644 --- a/npc/core/src/main/scala/Main.scala +++ b/npc/core/src/main/scala/top/FlowMain.scala @@ -5,16 +5,17 @@ import chisel3._ import chisel3.util.{MuxLookup, Fill, Decoupled, Counter, Queue, Reverse} import chisel3.util.{SRAM} import chisel3.util.experimental.decode.{decoder, TruthTable} -import chisel3.stage.ChiselOption import chisel3.util.log2Ceil import chisel3.util.BitPat import chisel3.util.Enum -import chisel3.experimental.prefix +import chisel3.experimental.Trace._ import shapeless.{HNil, ::} import shapeless.HList import shapeless.ops.coproduct.Prepend import chisel3.util.{ BinaryMemoryFile, HexMemoryFile } +import chisel3.experimental.Trace + object RV32Inst { private val bp = BitPat val addi = this.bp("b???????_?????_?????_000_?????_00100_11") @@ -73,27 +74,20 @@ class Control(width: Int) extends Module { }) } -import flow.components.{RegisterFile, RegFileInterface, ProgramCounter, ALU} +import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi} import chisel3.util.experimental.loadMemoryFromFileInline class Flow extends Module { val dataType = UInt(32.W) - - val ram = SRAM( - size = 1024, - tpe = dataType, - numReadPorts = 2, - numWritePorts = 1, - numReadwritePorts = 0, - memoryFile = HexMemoryFile("./resource/addi.txt") - ) + val ram = Module(new RamDpi) val control = Module(new Control(32)) - val reg = RegisterFile(32, dataType, 2, 2) + val reg = Module(new RegisterFile(dataType, 32, 2)) val pc = Module(new ProgramCounter(dataType)) val alu = Module(new ALU(dataType)) - ram.readPorts(0).enable := true.B - ram.readPorts(0).address := pc.out - 0x80000000L.U - val inst = ram.readPorts(0).data + ram.io.readAddr := pc.out + val inst = ram.io.readData + + dontTouch(reg.control.writeEnable) import control.pc.SrcSelect._ @@ -107,23 +101,26 @@ class Flow extends Module { import control.reg.WriteSelect._ reg.in.writeData(rAluOut.litValue.toInt) := alu.out.result + // TODO: Read address in load command goes here - ram.readPorts(1).enable := false.B - ram.readPorts(1).address := 0.U - reg.in.writeData(rMemOut.litValue.toInt) := ram.readPorts(1).data + reg.in.writeData(rMemOut.litValue.toInt) := DontCare + reg.in.writeAddr := inst(11, 7) - reg.in.rs(0) := inst(19, 15) - reg.in.rs(1) := inst(24, 20) + reg.in.rs(0) := inst(19, 15) // rs1 + reg.in.rs(1) := inst(24, 20) // rs2 // TODO: Memory write goes here - ram.writePorts(0).address := 1.U - ram.writePorts(0).data := 1.U - ram.writePorts(0).enable := false.B + ram.io.writeAddr := DontCare + ram.io.writeData := DontCare + ram.io.writeMask := DontCare + ram.io.writeEnable := false.B + ram.io.valid := true.B import control.alu.SrcSelect._ - alu.in.a(aSrcRs1.litValue.toInt) := reg.out.src(0) + alu.in.a(aSrcRs2.litValue.toInt) := reg.out.src(1) alu.in.a(aSrcImm.litValue.toInt) := inst(31, 20) - alu.in.b := reg.out.src(1) + alu.in.b := reg.out.src(0) + Trace.traceName(pc.out); dontTouch(control.out) } diff --git a/npc/core/src/main/scala/top/Main.scala b/npc/core/src/main/scala/top/Main.scala new file mode 100644 index 0000000..3c02ff3 --- /dev/null +++ b/npc/core/src/main/scala/top/Main.scala @@ -0,0 +1,72 @@ +package flow + +import flow._ + +import chisel3._ +import chisel3.experimental.Trace._ +import chisel3.stage.{ChiselGeneratorAnnotation, DesignAnnotation} +import chisel3.util.experimental.InlineInstance +import circt.stage.ChiselStage +import firrtl.AnnotationSeq +import firrtl.annotations.TargetToken.{Instance, OfModule, Ref} +import java.io.PrintWriter +import scala.io.Source +import java.io.File + + +// TODO: Generate verilator config file + +object VerilogMain extends App { + val opt = CliOptions().parse(args) + val topName = "Flow" + + val config: Config = opt.configFile match { + case Some(f) => { + val source = Source.fromFile(f) + val jsonString = source.mkString + source.close() + io.circe.parser.decode[Config](jsonString) match { + case Right(x) => x + case Left(e) => throw e + } + } + case None => Config(traceConfig = TraceConfig(enable = true)) + } + + val annos = (new ChiselStage).execute( + Array("--target-dir", opt.targetDir.toString, "--target", "systemverilog", "--split-verilog"), + Seq( + + ) ++ (if(config.traceConfig.enable) Seq(ChiselGeneratorAnnotation(() => new Flow)) else Seq()) + ) + + if(config.traceConfig.enable) { + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Flow] + + val verilatorConfigSeq = finalTargetMap(annos) + .values + .flatten + .map(ct => + s"""public_flat_rd -module "${ + ct.tokens.collectFirst { case OfModule(m) => m }.get + }" -var "${ct.tokens.collectFirst { case Ref(r) => r }.get}"""") + finalTargetMap(annos) + .values + .flatten + .foreach( + ct => println(s"""TOP.${ct.circuit}.${ct.path.map { case (Instance(i), _) => i }.mkString(".")}.${ct.tokens.collectFirst { + case Ref(r) => r + }.get}""") + ) + + val verilatorConfigWriter = new PrintWriter(new File(opt.targetDir, opt.verilatorConfigFileOut.toString())) + verilatorConfigWriter.write("`verilator_config\n") + try { + for(ct <- verilatorConfigSeq) { + verilatorConfigWriter.println(ct) + } + } finally { + verilatorConfigWriter.close() + } + } +} \ No newline at end of file diff --git a/npc/core/src/main/scala/utils/DPI.scala b/npc/core/src/main/scala/utils/DPI.scala new file mode 100644 index 0000000..2c83be5 --- /dev/null +++ b/npc/core/src/main/scala/utils/DPI.scala @@ -0,0 +1,11 @@ +package flow.utils + +import chisel3._ +import chisel3.util.HasBlackBoxResource + +// class DiffTester extends BlackBox with HasBlackBoxResource { +// val io = IO(new Bundle { +// val regs = +// }) +// addResource("difftest.v"); +// } diff --git a/npc/core/src/main/scala/Keyboard.scala b/npc/core/src/main/scala/utils/Keyboard.scala similarity index 100% rename from npc/core/src/main/scala/Keyboard.scala rename to npc/core/src/main/scala/utils/Keyboard.scala diff --git a/npc/core/src/main/scala/SegControllerGenerator.scala b/npc/core/src/main/scala/utils/SegControllerGenerator.scala similarity index 100% rename from npc/core/src/main/scala/SegControllerGenerator.scala rename to npc/core/src/main/scala/utils/SegControllerGenerator.scala diff --git a/npc/csrc/CMakeLists.txt b/npc/csrc/CMakeLists.txt new file mode 100644 index 0000000..3f645a7 --- /dev/null +++ b/npc/csrc/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(${TOPMODULE}) \ No newline at end of file diff --git a/npc/csrc/Flow/CMakeLists.txt b/npc/csrc/Flow/CMakeLists.txt new file mode 100644 index 0000000..606fd73 --- /dev/null +++ b/npc/csrc/Flow/CMakeLists.txt @@ -0,0 +1,17 @@ +include(ChiselBuild) +add_executable(V${TOPMODULE} config.cpp main.cpp) + +verilate(V${TOPMODULE} TRACE COVERAGE THREADS + TOP_MODULE ${TOPMODULE} + PREFIX V${TOPMODULE} + SOURCES ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF} + INCLUDE_DIRS ${CHISEL_OUTPUT_DIR} + VERILATOR_ARGS + "--vpi" # Enable VPI +) + +add_test( + NAME V${TOPMODULE} + COMMAND V${TOPMODULE} + --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt + --diff-lib ${DIFFTEST_LIB}) diff --git a/npc/csrc/Flow/components.hpp b/npc/csrc/Flow/components.hpp new file mode 100644 index 0000000..a84309a --- /dev/null +++ b/npc/csrc/Flow/components.hpp @@ -0,0 +1,110 @@ + +#ifndef _NPC_COMPONENTS_H_ +#define _NPC_COMPONENTS_H_ +#include "vpi_user.h" +#include +#include +#include +#include +#include +#include + +template class _RegistersBase { + std::array regs; + T pc; + virtual T fetch_pc(); + virtual T fetch_reg(std::size_t id); + +public: + T operator[](size_t id) { return fetch_reg(id); } + T get_pc() { return fetch_pc(); } + void update() { + for (int i = 0; i < regs.size(); i++) { + regs[i] = fetch_reg(i); + } + } +}; + +template +class _RegistersVPI : public _RegistersBase { + std::array reg_handles; + vpiHandle pc_handle; + T vpi_get(vpiHandle vh) { + 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]); } + +public: + _RegistersVPI(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); + if (vh == nullptr) { + std::cerr << "vpiHandle " << regname.c_str() << " not found" + << std::endl; + exit(EXIT_FAILURE); + } + reg_handles[i] = vh; + } + pc_handle = vpi_handle_by_name((PLI_BYTE8 *)pcname.c_str(), nullptr); + } +}; + +template class Memory { + std::size_t addr_to_index(std::size_t addr) { + if (addr < 0x80000000) { + return 0; + } + // Linear mapping + return (addr >> 2) - 0x20000000; + } + uint32_t expand_bits(uint8_t bits) { + uint32_t x = bits; + x = (x | (x << 7) | (x << 14) | (x << 21)) & 0x01010101; + x = x * 0xFF; + // printf("expand: %hhx->%x\n", bits, x); + return x; + } + +public: + std::array mem; + Memory(std::filesystem::path filepath, bool is_binary = true) { + assert(std::filesystem::exists(filepath)); + if (is_binary) { + std::ifstream file(filepath, std::ios::binary); + char *pmem = reinterpret_cast(mem.data()); + file.read(pmem, mem.size() * sizeof(mem[0])); + } else { + std::string line; + std::ifstream file(filepath); + int i = 0; + while (std::getline(file, line)) { + mem[i++] = std::stoul(line, 0, 16); + } + } + } + const T &operator[](std::size_t addr) { return this->read(addr); } + /** + * Always reads and returns 4 bytes from the address raddr & ~0x3u. + */ + T read(int raddr) { + // printf("raddr: 0x%x\n", raddr); + return mem[addr_to_index((uint32_t)raddr)]; + } + /** + * Always writes to the 4 bytes at the address `waddr` & ~0x3u. + * Each bit in `wmask` represents a mask for one byte in wdata. + * For example, wmask = 0x3 means only the lowest 2 bytes are written, + * and the other bytes in memory remain unchanged. + */ + void write(int waddr, T wdata, char wmask) { + // printf("waddr: 0x%x\n", waddr); + mem[addr_to_index((uint32_t)waddr)] = expand_bits(wmask) & wdata; + } +}; +#endif diff --git a/npc/csrc/Flow/config.cpp b/npc/csrc/Flow/config.cpp new file mode 100644 index 0000000..4d8338b --- /dev/null +++ b/npc/csrc/Flow/config.cpp @@ -0,0 +1,30 @@ +#include "config.hpp" + +void Config::cli_parse(int argc, char **argv) { + CLI::App app; + app.add_option("-m,--memory", memory_file, "Content of memory") + ->required() + ->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("-t", max_sim_time, "Max simulation timestep"); + app.add_option("--diff-lib", lib_ref, + "Dynamic library file of difftest reference") + ->check(CLI::ExistingFile); + + try { + app.parse(argc, argv); + } catch (const CLI::ParseError &e) { + exit((app).exit(e)); + } +} + +Config config; diff --git a/npc/csrc/Flow/config.hpp b/npc/csrc/Flow/config.hpp new file mode 100644 index 0000000..9f5873c --- /dev/null +++ b/npc/csrc/Flow/config.hpp @@ -0,0 +1,20 @@ +#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 c708610..50eeeeb 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,43 +1,85 @@ -#include -#include -#include -#include -#include +#include "components.hpp" +#include "config.hpp" +#include "vl_wrapper.hpp" +#include "vpi_user.h" #include -#define MAX_SIM_TIME 100 -#define VERILATOR_TRACE +#include +#include + +using VlModule = VlModuleInterfaceCommon; +using Registers = _RegistersVPI; + +extern "C" { +void *pmem_get() { + static auto pmem = new Memory(config.memory_file, + config.memory_file_binary); + return pmem; +} + +int pmem_read(int raddr) { + void *pmem = pmem_get(); + auto mem = static_cast *>(pmem); + // TODO: Do memory difftest at memory read and write to diagnose at a finer + // granularity + return mem->read(raddr); +} + +void pmem_write(int waddr, int wdata, char wmask) { + void *pmem = pmem_get(); + auto mem = static_cast *>(pmem); + return mem->write((std::size_t)waddr, wdata, wmask); +} +} + +VlModule *top; +Registers *regs; +using CPUState = CPUStateBase; +vpiHandle pc = nullptr; +void difftest_memcpy(paddr_t, void *, size_t, bool){}; + +void difftest_regcpy(void *p, bool direction) { + + if (direction == DIFFTEST_FROM_REF) { + ((CPUState *)p)->pc = regs->get_pc(); + for (int i = 0; i < 32; i++) { + ((CPUState *)p)->reg[i] = (*regs)[i]; + } + } +} + +void difftest_exec(uint64_t n) { + while (n--) { + for (int i = 0; i < 2; i++) { + if (top->is_posedge()) { + // Posedge + regs->update(); + } + top->eval(); + } + } +} +void difftest_init(int port) { + // top = std::make_unique(config.do_trace, config.wavefile); + top = new VlModule{config.do_trace, config.wavefile}; + regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out"); + top->reset_eval(10); +} int main(int argc, char **argv, char **env) { - int sim_time = 0; - Verilated::commandArgs(argc, argv); + config.cli_parse(argc, argv); - VFlow *top = new VFlow; + /* -- Difftest -- */ + std::filesystem::path ref{config.lib_ref}; + DifftestInterface dut_interface = DifftestInterface{ + &difftest_memcpy, &difftest_regcpy, &difftest_exec, &difftest_init}; + DifftestInterface ref_interface = DifftestInterface{ref}; - Verilated::traceEverOn(true); - VerilatedVcdC *m_trace = new VerilatedVcdC; -#ifdef VERILATOR_TRACE - top->trace(m_trace, 5); - m_trace->open("waveform.vcd"); -#endif - for (sim_time = 0; sim_time < 10; sim_time++) { - top->eval(); - top->clock = !top->clock; - top->reset = 1; -#ifdef VERILATOR_TRACE - m_trace->dump(sim_time); -#endif - } - top->reset = 0; - for (sim_time = 10; sim_time < MAX_SIM_TIME; sim_time++) { - top->eval(); - top->clock = !top->clock; -#ifdef VERILATOR_TRACE - m_trace->dump(sim_time); -#endif - } -#ifdef VERILATOR_TRACE - m_trace->close(); -#endif - delete top; - exit(EXIT_SUCCESS); + Difftest> diff{dut_interface, ref_interface, + pmem_get(), 128}; + int t = 8; + while (t--) { + diff.step(1); + } + + return 0; } diff --git a/npc/csrc/Flow/vl_wrapper.hpp b/npc/csrc/Flow/vl_wrapper.hpp new file mode 100644 index 0000000..4d42b55 --- /dev/null +++ b/npc/csrc/Flow/vl_wrapper.hpp @@ -0,0 +1,65 @@ +#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/csrc_nvboard/Flow/CMakeLists.txt b/npc/csrc_nvboard/Flow/CMakeLists.txt new file mode 100644 index 0000000..82143bc --- /dev/null +++ b/npc/csrc_nvboard/Flow/CMakeLists.txt @@ -0,0 +1,23 @@ +include(ChiselBuild) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc +) + +add_executable(V${TOPMODULE}_nvboard + ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + main.cpp +) + +verilate(V${TOPMODULE}_nvboard TRACE THREADS + TOP_MODULE ${TOPMODULE} + PREFIX V${TOPMODULE} + SOURCES ${CHISEL_OUTPUT_TOPMODULE} + INCLUDE_DIRS ${CHISEL_OUTPUT_DIR}) + +target_include_directories(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS}) +target_link_libraries(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_LIBRARY} SDL2::SDL2 SDL2_image::SDL2_image) + +install(TARGETS V${TOPMODULE}_nvboard) \ No newline at end of file diff --git a/npc/flake.lock b/npc/flake.lock deleted file mode 100644 index 76de6c7..0000000 --- a/npc/flake.lock +++ /dev/null @@ -1,99 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1709961763, - "narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-circt162": { - "locked": { - "lastModified": 1705645507, - "narHash": "sha256-tX3vipIAmNDBA8WNWG4oY4KyTfnm2YieTHO2BhG8ISA=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", - "type": "github" - } - }, - "nur-xin": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1707020873, - "narHash": "sha256-+dNltc7tjgTIyle/I/5siQ5IvPwu+R5Uf6e24CmjLNk=", - "ref": "refs/heads/master", - "rev": "8142717e7154dbaadee0679f0224fe75cebb1735", - "revCount": 147, - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - }, - "original": { - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "nixpkgs-circt162": "nixpkgs-circt162", - "nur-xin": "nur-xin" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/npc/flake.nix b/npc/flake.nix deleted file mode 100644 index 15b910f..0000000 --- a/npc/flake.nix +++ /dev/null @@ -1,116 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; - flake-utils.url = "github:numtide/flake-utils"; - nur-xin = { - url = "git+https://git.xinyang.life/xin/nur.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, ... }@inputs: with inputs; - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }// - { nur.xin = nur-xin.legacyPackages.${system}; }; - in - { - devShells.default = with pkgs; mkShell { - CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; - packages = [ - clang-tools - # rnix-lsp - coursier - espresso - - gdb - jre - - gtkwave - ]; - - inputsFrom = [ self.packages.${system}.default ]; - }; - packages.default = with pkgs; clangStdenv.mkDerivation { - name = "npc"; - version = "0.0.1"; - src = ./.; - nativeBuildInputs = [ - cmake - sbt - nur.xin.nvboard - nixpkgs-circt162.legacyPackages.${system}.circt - yosys - ]; - buildInputs = [ - verilator - nur.xin.nvboard - ]; - - NEMU_HOME="/home/xin/repo/ysyx-workbench/nemu"; - }; - - # This version (1.43.0) of circt does not exist in nixpkgs - # and Chisel 5.1.0 specifically build against it, so here we are. - # Ref: https://github.com/NixOS/nixpkgs/blob/b6465c8/pkgs/development/compilers/circt/default.nix - packages.circt = - with pkgs; - let - pythonEnv = python3.withPackages (ps: [ ps.psutil ]); - in - stdenv.mkDerivation rec { - pname = "circt"; - version = "1.43.0"; - src = fetchFromGitHub { - owner = "llvm"; - repo = "circt"; - rev = "firtool-${version}"; - sha256 = "sha256-RkjigboswLkLgLkgOGahQLIygCkC3Q9rbVw3LqIzREY="; - fetchSubmodules = true; - }; - - requiredSystemFeatures = [ "big-parallel" ]; - - nativeBuildInputs = [ cmake ninja git pythonEnv ]; - - cmakeDir = "../llvm/llvm"; - cmakeFlags = [ - "-DLLVM_ENABLE_BINDINGS=OFF" - "-DLLVM_ENABLE_OCAMLDOC=OFF" - "-DLLVM_BUILD_EXAMPLES=OFF" - "-DLLVM_OPTIMIZED_TABLEGEN=ON" - "-DLLVM_ENABLE_PROJECTS=mlir" - "-DLLVM_EXTERNAL_PROJECTS=circt" - "-DLLVM_EXTERNAL_CIRCT_SOURCE_DIR=.." - "-DCIRCT_LLHD_SIM_ENABLED=OFF" - ]; - - LIT_FILTER_OUT = if stdenv.cc.isClang then "CIRCT :: Target/ExportSystemC/.*\.mlir" else null; - - preConfigure = '' - find ./test -name '*.mlir' -exec sed -i 's|/usr/bin/env|${coreutils}/bin/env|g' {} \; - ''; - - installPhase = '' - runHook preInstall - mkdir -p $out/bin - mv bin/{{fir,hls}tool,circt-{as,dis,lsp-server,opt,reduce,translate}} $out/bin - runHook postInstall - ''; - - doCheck = true; - checkTarget = "check-circt check-circt-integration"; - - meta = { - description = "Circuit IR compilers and tools"; - homepage = "https://circt.org/"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ sharzy ]; - platforms = lib.platforms.all; - }; - }; - } - ); -} - diff --git a/npc/include/difftest.hpp b/npc/include/difftest.hpp new file mode 100644 index 0000000..dd690b9 --- /dev/null +++ b/npc/include/difftest.hpp @@ -0,0 +1,126 @@ +#ifndef _DIFFTEST_DIFFTEST_H_ +#define _DIFFTEST_DIFFTEST_H_ +#include +#include +#include +#include +#include +#include +#include +#include + +using paddr_t = uint32_t; +enum { DIFFTEST_FROM_REF, DIFFTEST_TO_REF }; + +struct DifftestInterface { + 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 memcpy; + std::function regcpy; + std::function exec; + std::function init; + + DifftestInterface(memcpy_t memcpy, regcpy_t regcpy, exec_t exec, init_t init) + : memcpy(memcpy), regcpy(regcpy), exec(exec), init(init){}; + + // using fs = std::filesystem::path; + DifftestInterface(std::filesystem::path lib_file) { + void *handle = dlopen(lib_file.c_str(), RTLD_LAZY); + assert(handle != nullptr); + memcpy = (memcpy_t)dlsym(handle, "difftest_memcpy"); + assert(memcpy); + regcpy = (regcpy_t)dlsym(handle, "difftest_regcpy"); + assert(regcpy); + exec = (exec_t)dlsym(handle, "difftest_exec"); + assert(exec); + init = (init_t)dlsym(handle, "difftest_init"); + assert(init); + } +}; + +template class Difftest { + const DifftestInterface &ref; + std::unique_ptr ref_state; + const DifftestInterface &dut; + std::unique_ptr dut_state; + +public: + Difftest(const DifftestInterface &dut, const DifftestInterface &ref, + void *mem, size_t n, std::unique_ptr ref_state = nullptr, + std::unique_ptr dut_state = nullptr) + : ref(ref), dut(dut), ref_state(std::move(ref_state)), + dut_state(std::move(dut_state)) { + if (ref_state == nullptr) + this->ref_state = std::make_unique(); + if (dut_state == nullptr) + this->dut_state = std::make_unique(); + ref.init(0); + dut.init(0); + fetch_state(); + paddr_t reset_vector = 0x80000000; + ref.memcpy(reset_vector, mem, n, DIFFTEST_TO_REF); + dut.memcpy(reset_vector, mem, n, DIFFTEST_TO_REF); + }; + + void fetch_state() { + ref.regcpy(ref_state.get(), DIFFTEST_FROM_REF); + dut.regcpy(dut_state.get(), DIFFTEST_FROM_REF); + } + + void step(uint64_t n) { + ref.exec(n); + dut.exec(n); + fetch_state(); + if (*ref_state != *dut_state) { + std::cout << *this; + exit(EXIT_FAILURE); + } + } + + friend std::ostream &operator<<(std::ostream &os, const Difftest &d) { + os << "REF state:\n" + << *d.ref_state << "DUT state:\n" + << *d.dut_state << std::endl; + return os; + } +}; + +template struct CPUStateBase { + R reg[nr_reg] = {0}; + paddr_t pc = 0x80000000; + 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 + } +}; + +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; +} + +#endif \ No newline at end of file diff --git a/npc/resource/addi.txt b/npc/resource/addi.txt index 8eb11e4..6fb3e74 100644 --- a/npc/resource/addi.txt +++ b/npc/resource/addi.txt @@ -1,10 +1,17 @@ -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113