From e2c1bedd388eaf015befa05f7db58471012332ac Mon Sep 17 00:00:00 2001 From: Avimitin Date: Tue, 13 Aug 2024 16:57:46 +0800 Subject: [PATCH] [nix] refactor whole test attribute under emulator Signed-off-by: Avimitin --- .github/workflows/vcs.yml | 2 +- .github/workflows/verilator.yml | 2 +- difftest/run-emulator.nix | 79 ++++++++++ difftest/verilator.nix | 18 ++- nix/overlay.nix | 1 + nix/t1/default.nix | 24 +-- nix/t1/run-vcs-emulation.nix | 74 +++++++++ nix/t1/vcs.nix | 113 ++++++++------ script/ci/src/Main.scala | 5 +- t1rocketemu/default.nix | 68 ++++++-- t1rocketemu/emu.nix | 61 -------- t1rocketemu/nix/run-emulator.nix | 78 ++++++++++ t1rocketemu/nix/run-vcs-emulation.nix | 74 +++++++++ t1rocketemu/nix/vcs.nix | 78 ++++++++++ t1rocketemu/nix/verilated-c-lib.nix | 2 +- t1rocketemu/{ => nix}/verilator.nix | 64 ++++---- t1rocketemu/online_vcs/default.nix | 11 +- t1rocketemu/test_common/src/lib.rs | 6 +- t1rocketemu/vcs.nix | 0 tests/builder.nix | 12 +- tests/default.nix | 67 +++----- tests/make-emu-result.nix | 214 -------------------------- 22 files changed, 602 insertions(+), 451 deletions(-) create mode 100644 difftest/run-emulator.nix create mode 100644 nix/t1/run-vcs-emulation.nix delete mode 100644 t1rocketemu/emu.nix create mode 100644 t1rocketemu/nix/run-emulator.nix create mode 100644 t1rocketemu/nix/run-vcs-emulation.nix create mode 100644 t1rocketemu/nix/vcs.nix rename t1rocketemu/{ => nix}/verilator.nix (53%) delete mode 100644 t1rocketemu/vcs.nix delete mode 100644 tests/make-emu-result.nix diff --git a/.github/workflows/vcs.yml b/.github/workflows/vcs.yml index 17115a19d..1fdff27eb 100644 --- a/.github/workflows/vcs.yml +++ b/.github/workflows/vcs.yml @@ -45,7 +45,7 @@ jobs: nix build '.#t1.${{ matrix.config }}.ip.vcs-emu' --impure --no-link --cores 64 - name: "Build all testcases" run: | - nix build ".#t1.${{ matrix.config }}.ip.cases._all" --max-jobs auto --no-link --cores 64 + nix build ".#t1.${{ matrix.config }}.ip.vcs-emu.cases._all" --max-jobs auto --no-link --cores 64 gen-matrix: name: "Prepare for running testcases" diff --git a/.github/workflows/verilator.yml b/.github/workflows/verilator.yml index 4b3d88702..c2ba00d5b 100644 --- a/.github/workflows/verilator.yml +++ b/.github/workflows/verilator.yml @@ -34,7 +34,7 @@ jobs: - name: "Build all testcases" run: | # Build testcases with vlen 1024 and vlen 4096 - nix build ".#t1.${{ matrix.config }}.ip.cases._all" --max-jobs auto -L --no-link --cores 64 + nix build ".#t1.${{ matrix.config }}.ip.verilator-emu.cases._all" --max-jobs auto -L --no-link --cores 64 # In the future, we may choose Verdi for trace, and left verilator trace only for performance evaluation build-verilator-trace-emulators: diff --git a/difftest/run-emulator.nix b/difftest/run-emulator.nix new file mode 100644 index 000000000..baee3ec7a --- /dev/null +++ b/difftest/run-emulator.nix @@ -0,0 +1,79 @@ +{ lib, runCommand, zstd, jq }: +emulator: +testCase: + +runCommand "run-${emulator.name}-for-${testCase.pname}" +{ + name = "${testCase.pname}-emu-result" + (lib.optionalString emulator.enable-trace "-trace"); + nativeBuildInputs = [ zstd jq ]; +} '' + mkdir -p "$out" + + emuDriverArgsArray=( + "--elf-file" + "${testCase}/bin/${testCase.pname}.elf" + "--log-file" + "$out/emu.log" + "--log-level" + "ERROR" + ${lib.optionalString emulator.enable-trace "--wave-path"} + ${lib.optionalString emulator.enable-trace "$out/wave.fst"} + ) + emuDriverArgs="''${emuDriverArgsArray[@]}" + emuDriver="${emulator}/bin/online_drive" + + rtlEventOutPath="$out/${testCase.pname}-rtl-event.jsonl" + + echo "[nix] Running test case ${testCase.pname} with args $emuDriverArgs" + + export RUST_BACKTRACE=full + if ! "$emuDriver" $emuDriverArgs 2> "$rtlEventOutPath"; then + echo -e "\033[0;31m[nix]\033[0m: online driver run failed" + cat $rtlEventOutPath + echo -e "\033[0;31m[nix]\033[0m: Try rerun with '\033[0;34m$emuDriver $emuDriverArgs\033[0m'" + exit 1 + fi + + echo "[nix] online driver run done" + + if [ ! -r "$rtlEventOutPath" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: no $rtlEventOutPath found in output" + exit 1 + fi + + if ! jq --stream -c -e '.[]' "$rtlEventOutPath" >/dev/null 2>&1; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: invalid JSON file $rtlEventOutPath, showing original file:" + echo "--------------------------------------------" + cat $rtlEventOutPath + echo "--------------------------------------------" + exit 1 + fi + + set +e + "${emulator}/bin/offline" \ + --elf-file ${testCase}/bin/${testCase.pname}.elf \ + --log-file $rtlEventOutPath \ + --log-level ERROR &> $out/offline-check-journal + printf "$?" > $out/offline-check-status + if [ "$(cat $out/offline-check-status)" != "0" ]; then + echo "[nix] Offline check FAIL" + else + echo "[nix] Offline check PASS" + fi + set -e + + echo "[nix] compressing event log" + zstd $rtlEventOutPath -o $rtlEventOutPath.zstd + rm $rtlEventOutPath + + if [ -r perf.txt ]; then + mv perf.txt $out/ + fi + + ${lib.optionalString emulator.enable-trace '' + if [ ! -r "$out/wave.fst" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: waveform not found in output" + exit 1 + fi + ''} +'' diff --git a/difftest/verilator.nix b/difftest/verilator.nix index 9fb575a35..ddf0263bf 100644 --- a/difftest/verilator.nix +++ b/difftest/verilator.nix @@ -14,6 +14,8 @@ , verilated , cmake , clang-tools + +, rtlDesignMetadata }: let @@ -73,7 +75,9 @@ let clang-tools ]; }); - inherit libspike_interfaces; + + inherit libspike_interfaces rtlDesignMetadata; + inherit (verilated) enable-trace; # enable debug info for difftest itself and libspike withDebug = self.overrideAttrs (old: { @@ -84,6 +88,18 @@ let }; dontStrip = true; }); + + # Here is an curry function: + # + # * run-emulator.nix return type + # run-emulator :: { callPackage Args... } -> emulatorDerivation -> testCase -> runCommandDerivation + # + # * runEmulation attribute type: + # runEmulation :: testCase -> runCommandDerivation + # + runEmulation = (callPackage ./run-emulator.nix { }) self; + + cases = callPackage ../tests { emulator = self; }; }; }; in diff --git a/nix/overlay.nix b/nix/overlay.nix index 20b69d081..4042f54cd 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -125,4 +125,5 @@ rec { }; t1 = final.callPackage ./t1 { }; + t1-rocket = final.callPackage ../t1rocketemu { }; } diff --git a/nix/t1/default.nix b/nix/t1/default.nix index 33bf22df7..fe9f7bb7d 100644 --- a/nix/t1/default.nix +++ b/nix/t1/default.nix @@ -37,7 +37,7 @@ lib.makeScope newScope rocketv = self.callPackage ../../rocketemu { }; - t1rocketemu = self.callPackage ../../t1rocketemu { }; + t1rocket = self.callPackage ../../t1rocketemu { }; omreader-unwrapped = self.callPackage ./omreader.nix { }; submodules = self.callPackage ./submodules.nix { }; @@ -84,23 +84,14 @@ lib.makeScope newScope omGet = args: lib.fileContents (runCommand "get-${args}" { } '' ${emu-omreader}/bin/omreader ${args} > $out ''); - rtlDesignMetadata = { + rtlDesignMetadata = rec { march = omGet "march"; extensions = builtins.fromJSON (omGet "extensionsJson"); vlen = omGet "vlen"; dlen = omGet "dlen"; + xlen = if (lib.hasPrefix "rv32" march) then 32 else 64; }; - cases = innerSelf.callPackage ../../tests { - inherit (ip) verilator-emu verilator-emu-trace vcs-emu vcs-emu-trace rtlDesignMetadata; - }; - - # for the convenience to use x86 cases on non-x86 machines, avoiding the extra build time - cases-x86 = - if system == "x86-64-linux" - then self.cases - else pkgsX86.t1."${configName}".cases; - emu-elaborate = innerSelf.callPackage ./elaborate.nix { target = "ipemu"; }; emu-mlirbc = innerSelf.callPackage ./mlirbc.nix { elaborate = emu-elaborate; }; @@ -119,8 +110,9 @@ lib.makeScope newScope }; verilator-emu-rtl-verilated = innerSelf.callPackage ./verilated.nix { rtl = verilator-emu-rtl; stdenv = moldStdenv; }; verilator-emu-rtl-verilated-trace = innerSelf.callPackage ./verilated.nix { rtl = verilator-emu-rtl; stdenv = moldStdenv; enable-trace = true; }; - verilator-emu = innerSelf.callPackage ../../difftest/verilator.nix { verilated = verilator-emu-rtl-verilated; }; - verilator-emu-trace = innerSelf.callPackage ../../difftest/verilator.nix { verilated = verilator-emu-rtl-verilated-trace; }; + + verilator-emu = innerSelf.callPackage ../../difftest/verilator.nix { inherit rtlDesignMetadata; verilated = verilator-emu-rtl-verilated; }; + verilator-emu-trace = innerSelf.callPackage ../../difftest/verilator.nix { inherit rtlDesignMetadata; verilated = verilator-emu-rtl-verilated-trace; }; # T1 VCS Emulator vcs-emu-omreader = self.omreader-unwrapped.mkWrapper { mlirbc = emu-mlirbc; }; @@ -137,8 +129,8 @@ lib.makeScope newScope }; vcs-dpi-lib = innerSelf.callPackage ../../difftest/online_vcs { }; vcs-dpi-lib-trace = vcs-dpi-lib.override { enable-trace = true; }; - vcs-emu = innerSelf.callPackage ./vcs.nix { inherit vcs-dpi-lib; rtl = vcs-emu-rtl; }; - vcs-emu-trace = innerSelf.callPackage ./vcs.nix { vcs-dpi-lib = vcs-dpi-lib-trace; rtl = vcs-emu-rtl; }; + vcs-emu = innerSelf.callPackage ./vcs.nix { inherit vcs-dpi-lib rtlDesignMetadata; rtl = vcs-emu-rtl; }; + vcs-emu-trace = innerSelf.callPackage ./vcs.nix { inherit rtlDesignMetadata; vcs-dpi-lib = vcs-dpi-lib-trace; rtl = vcs-emu-rtl; }; }; subsystem = rec { diff --git a/nix/t1/run-vcs-emulation.nix b/nix/t1/run-vcs-emulation.nix new file mode 100644 index 000000000..c5c0cf9b8 --- /dev/null +++ b/nix/t1/run-vcs-emulation.nix @@ -0,0 +1,74 @@ +{ lib, runCommand, zstd, jq }: +emulator: +testCase: + +runCommand "run-${emulator.name}-for-${testCase.pname}" +{ + name = "${testCase.pname}-vcs-result" + (lib.optionalString emulator.enable-trace "-trace"); + nativeBuildInputs = [ zstd jq ]; + __noChroot = true; +} '' + mkdir -p "$out" + + emuDriverArgsArray=( + "--elf-file" + "${testCase}/bin/${testCase.pname}.elf" + ${lib.optionalString emulator.enable-trace "--wave-path"} + ${lib.optionalString emulator.enable-trace "${testCase.pname}.fsdb"} + ) + emuDriverArgs="''${emuDriverArgsArray[@]}" + emuDriver="${emulator}/bin/t1-vcs-simulator" + + rtlEventOutPath="$out/${testCase.pname}-rtl-event.jsonl" + + echo "[nix] Running VCS ${testCase.pname} with args $emuDriverArgs" + + export RUST_BACKTRACE=full + if ! "$emuDriver" $emuDriverArgs >/dev/null 2>"$rtlEventOutPath"; then + echo -e "\033[0;31m[nix]\033[0m: online driver run failed" + cat $rtlEventOutPath + echo -e "\033[0;31m[nix]\033[0m: Try rerun with '\033[0;34m$emuDriver $emuDriverArgs\033[0m'" + exit 1 + fi + + echo "[nix] VCS run done" + + if [ ! -r "$rtlEventOutPath" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: no $rtlEventOutPath found in output" + exit 1 + fi + + if ! jq --stream -c -e '.[]' "$rtlEventOutPath" >/dev/null 2>&1; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: invalid JSON file $rtlEventOutPath, showing original file:" + echo "--------------------------------------------" + cat $rtlEventOutPath + echo "--------------------------------------------" + exit 1 + fi + + set +e + "${emulator}/bin/offline" \ + --elf-file ${testCase}/bin/${testCase.pname}.elf \ + --log-file $rtlEventOutPath \ + --log-level ERROR &> $out/offline-check-journal + printf "$?" > $out/offline-check-status + if [ "$(cat $out/offline-check-status)" != "0" ]; then + echo "[nix] Offline check FAIL" + else + echo "[nix] Offline check PASS" + fi + set -e + + echo "[nix] compressing event log" + zstd $rtlEventOutPath -o $rtlEventOutPath.zstd + rm $rtlEventOutPath + + if [ -r perf.txt ]; then + mv perf.txt $out/ + fi + + ${lib.optionalString emulator.enable-trace '' + cp -v ${testCase.pname}.fsdb "$out" + cp -vr ${emulator}/lib/t1-vcs-simulator.daidir "$out" + ''} +'' diff --git a/nix/t1/vcs.nix b/nix/t1/vcs.nix index 3b862f21f..a3b5a3131 100644 --- a/nix/t1/vcs.nix +++ b/nix/t1/vcs.nix @@ -1,68 +1,85 @@ { lib +, callPackage , bash , stdenv , configName , rtl , vcs-dpi-lib , vcs-fhs-env +, rtlDesignMetadata }: -stdenv.mkDerivation { - name = "${configName}-vcs"; +let + self = stdenv.mkDerivation { + name = "${configName}-vcs"; - # require license - __noChroot = true; - dontPatchELF = true; + # require license + __noChroot = true; + dontPatchELF = true; - src = rtl; + src = rtl; - buildPhase = '' - runHook preBuild + buildPhase = '' + runHook preBuild - echo "[nix] running VCS" - fhsBash=${vcs-fhs-env}/bin/vcs-fhs-env - VERDI_HOME=$("$fhsBash" -c "printenv VERDI_HOME") - "$fhsBash" vcs \ - -sverilog \ - -full64 \ - -timescale=1ns/1ps \ - -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ - ${lib.optionalString vcs-dpi-lib.enable-trace '' - -debug_access+pp+dmptf+thread \ - -kdb=common_elab,hgldd_all''} \ - -file filelist.f \ - ${vcs-dpi-lib}/lib/libdpi.a \ - -o t1-vcs-simulator + echo "[nix] running VCS" + fhsBash=${vcs-fhs-env}/bin/vcs-fhs-env + VERDI_HOME=$("$fhsBash" -c "printenv VERDI_HOME") + "$fhsBash" vcs \ + -sverilog \ + -full64 \ + -timescale=1ns/1ps \ + -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ + ${lib.optionalString vcs-dpi-lib.enable-trace '' + -debug_access+pp+dmptf+thread \ + -kdb=common_elab,hgldd_all''} \ + -file filelist.f \ + ${vcs-dpi-lib}/lib/libdpi.a \ + -o t1-vcs-simulator - runHook postBuild - ''; + runHook postBuild + ''; - passthru = { - inherit (vcs-dpi-lib) enable-trace; - inherit vcs-fhs-env; - }; + passthru = { + inherit (vcs-dpi-lib) enable-trace; + inherit vcs-fhs-env rtlDesignMetadata; + + # Here is an curry function: + # + # * run-emulator.nix return type + # run-emulator :: { callPackage Args... } -> emulatorDerivation -> testCase -> runCommandDerivation + # + # * runEmulation attribute type: + # runEmulation :: testCase -> runCommandDerivation + # + runEmulation = (callPackage ./run-vcs-emulation.nix { }) self; - shellHook = '' - echo "[nix] entering fhs env" - ${vcs-fhs-env}/bin/vcs-fhs-env - ''; + cases = callPackage ../../tests { emulator = self; }; + }; - installPhase = '' - runHook preInstall + shellHook = '' + echo "[nix] entering fhs env" + ${vcs-fhs-env}/bin/vcs-fhs-env + ''; - mkdir -p $out/bin $out/lib - cp t1-vcs-simulator $out/lib - cp -r t1-vcs-simulator.daidir $out/lib + installPhase = '' + runHook preInstall - # We need to carefully handle string escape here, so don't use makeWrapper - tee $out/bin/t1-vcs-simulator < - s".#t1.$config.ip.cases.$caseName.emu-result.with-offline" - case "vcs" => s".#t1.$config.ip.cases.$caseName.emu-result.with-vcs" + s".#t1.$config.ip.cases.$caseName.emu-result.verilator-check" + case "vcs" => s".#t1.$config.ip.cases.$caseName.emu-result.vcs-check" + case "t1rocket" => s".#t1.t1rocketemu.cases.$caseName.emu-result.t1rocket-check" case _ => Logger.fatal(s"Invalid test type ${testType}") val testResultPath = try diff --git a/t1rocketemu/default.nix b/t1rocketemu/default.nix index bd63fc4cb..f74cf6046 100644 --- a/t1rocketemu/default.nix +++ b/t1rocketemu/default.nix @@ -1,18 +1,60 @@ { lib , newScope }: -lib.makeScope newScope (scope: { - mlirbc = scope.callPackage ./nix/mlirbc.nix { }; - rtl = scope.callPackage ./nix/rtl.nix { }; - verilated-c-lib = scope.callPackage ./nix/verilated-c-lib.nix { }; - emu = scope.callPackage ./emu.nix { }; - designConfig = with builtins; (fromJSON (readFile ./configs/default.json)).parameter; - cases = scope.callPackage ../tests { - configName = "t1rocket"; - t1rocket-emu = scope.emu; - rtlDesignMetadata = { - march = "rv32iafcv_zve32x_zvl1024b"; +{ + ip = lib.makeScope newScope (scope: { + mlirbc = scope.callPackage ./nix/mlirbc.nix { }; + + rtl = scope.callPackage ./nix/rtl.nix { }; + + verilated-c-lib = scope.callPackage ./nix/verilated-c-lib.nix { enable-trace = false; }; + verilated-c-lib-trace = scope.callPackage ./nix/verilated-c-lib.nix { }; + + verilator-emu = scope.callPackage ./nix/verilator.nix { }; + verilator-emu-trace = scope.callPackage ./nix/verilator.nix { verilated-c-lib = scope.verilated-c-lib-trace; }; + + vcs-dpi-lib = scope.callPackage ./online_vcs { }; + vcs-dpi-lib-trace = scope.vcs-dpi-lib.override { enable-trace = true; }; + + vcs-emu = scope.callPackage ./nix/vcs.nix { }; + vcs-emu-trace = scope.callPackage ./nix/vcs.nix { vcs-dpi-lib = scope.vcs-dpi-lib-trace; }; + + getVLen = ext: + let + val = builtins.tryEval + (lib.toInt + (lib.removeSuffix "b" + (lib.removePrefix "zvl" + (lib.toLower ext)))); + in + if val.success then + val.value + else + throw "Invalid vlen extension `${ext}` specify, expect Zvl{N}b"; + + # TODO: designConfig should be read from OM + designConfig = with builtins; (fromJSON (readFile ./configs/default.json)).parameter; + + # TODO: We should have a type define, to keep t1 and t1rocket feeds same `rtlDesignMetadata` data structure. + rtlDesignMetadata = rec { + # TODO: `march` and `dlen` should be read from OM + # + # Although the string is already hard-coded in lower case, the toLower function call here is to remind developer that, + # when we switch OM, we should always ensure the march input is lower case. + march = lib.toLower "rv32imafcv_zve32x_zvl1024b"; dlen = scope.designConfig.dLen; + xlen = if (lib.hasPrefix "rv32" march) then 32 else 64; + + # Find "Zvl{N}b" string in march and parse it to vlen. + # Extract earlier so that downstream derivation that relies on this value doesn't have to parse the string multiple times. + vlen = lib.pipe (march) [ + (lib.splitString "_") + (lib.filter (x: lib.hasPrefix "zvl" x)) + (lib.last) + (lib.removePrefix "zvl") + (lib.removeSuffix "b") + (lib.toInt) + ]; }; - }; -}) + }); +} diff --git a/t1rocketemu/emu.nix b/t1rocketemu/emu.nix deleted file mode 100644 index 50f14e2c9..000000000 --- a/t1rocketemu/emu.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ lib -, rustPlatform -, zlib -, libspike -, libspike_interfaces -, cmake -, verilator -, verilated-c-lib -}: -rustPlatform.buildRustPackage { - name = "t1rocketemu"; - - src = with lib.fileset; toSource { - root = ./.; - fileset = unions [ - ./test_common - ./spike_rs - ./offline - ./online_dpi - ./online_drive - ./online_vcs - ./Cargo.lock - ./Cargo.toml - ]; - }; - - buildInputs = [ - zlib - libspike_interfaces - verilated-c-lib - ]; - - nativeBuildInputs = [ - verilator - cmake - ]; - - # FIXME: can we hack this into derivations, so that we don't need to specify library dir explicitly? - env = - let - toLib = drv: "${drv}/lib"; - in - { - SPIKE_LIB_DIR = toLib libspike; - SPIKE_INTERFACES_LIB_DIR = toLib libspike_interfaces; - VERILATED_INC_DIR = "${verilated-c-lib}/include"; - VERILATED_LIB_DIR = "${verilated-c-lib}/lib"; - }; - - cargoLock = { - lockFile = ./Cargo.lock; - }; - - outputs = [ "out" "driver" "offline" ]; - - postInstall = '' - mkdir -p $driver/bin $offline/bin - ln -s $out/bin/driver $driver/bin/driver - ln -s $out/bin/offline $driver/bin/offline - ''; -} diff --git a/t1rocketemu/nix/run-emulator.nix b/t1rocketemu/nix/run-emulator.nix new file mode 100644 index 000000000..cd2fdfc3a --- /dev/null +++ b/t1rocketemu/nix/run-emulator.nix @@ -0,0 +1,78 @@ +{ lib, runCommand, zstd, jq }: +emulator: +testCase: + +runCommand ("${emulator.name}-${testCase.pname}-emu-result" + lib.optionalString emulator.enable-trace "-trace") +{ + nativeBuildInputs = [ zstd jq ]; +} '' + mkdir -p "$out" + + emuDriverArgsArray=( + "--elf-file" + "${testCase}/bin/${testCase.pname}.elf" + "--log-file" + "$out/emu.log" + "--log-level" + "ERROR" + ${lib.optionalString emulator.enable-trace "--wave-path"} + ${lib.optionalString emulator.enable-trace "$out/wave.fst"} + ) + emuDriverArgs="''${emuDriverArgsArray[@]}" + emuDriver="${emulator}/bin/online_drive" + + rtlEventOutPath="$out/${testCase.pname}-rtl-event.jsonl" + + echo "[nix] Running test case ${testCase.pname} with args $emuDriverArgs" + + export RUST_BACKTRACE=full + if ! "$emuDriver" $emuDriverArgs 2> "$rtlEventOutPath"; then + echo -e "\033[0;31m[nix]\033[0m: online driver run failed" + cat $rtlEventOutPath + echo -e "\033[0;31m[nix]\033[0m: Try rerun with '\033[0;34m$emuDriver $emuDriverArgs\033[0m'" + exit 1 + fi + + echo "[nix] t1rocket run done" + + if [ ! -r "$rtlEventOutPath" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: no $rtlEventOutPath found in output" + exit 1 + fi + + if ! jq --stream -c -e '.[]' "$rtlEventOutPath" >/dev/null 2>&1; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: invalid JSON file $rtlEventOutPath, showing original file:" + echo "--------------------------------------------" + cat $rtlEventOutPath + echo "--------------------------------------------" + exit 1 + fi + + set +e + "${emulator}/bin/offline" \ + --elf-file ${testCase}/bin/${testCase.pname}.elf \ + --log-file $rtlEventOutPath \ + --log-level ERROR &> $out/offline-check-journal + printf "$?" > $out/offline-check-status + if [ "$(cat $out/offline-check-status)" != "0" ]; then + echo "[nix] Offline check FAIL" + else + echo "[nix] Offline check PASS" + fi + set -e + + echo "[nix] compressing event log" + zstd $rtlEventOutPath -o $rtlEventOutPath.zstd + rm $rtlEventOutPath + + if [ -r perf.txt ]; then + mv perf.txt $out/ + fi + + ${lib.optionalString emulator.enable-trace '' + if [ ! -r "$out/wave.fst" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: waveform not found in output" + exit 1 + fi + ''} +'' diff --git a/t1rocketemu/nix/run-vcs-emulation.nix b/t1rocketemu/nix/run-vcs-emulation.nix new file mode 100644 index 000000000..c5c0cf9b8 --- /dev/null +++ b/t1rocketemu/nix/run-vcs-emulation.nix @@ -0,0 +1,74 @@ +{ lib, runCommand, zstd, jq }: +emulator: +testCase: + +runCommand "run-${emulator.name}-for-${testCase.pname}" +{ + name = "${testCase.pname}-vcs-result" + (lib.optionalString emulator.enable-trace "-trace"); + nativeBuildInputs = [ zstd jq ]; + __noChroot = true; +} '' + mkdir -p "$out" + + emuDriverArgsArray=( + "--elf-file" + "${testCase}/bin/${testCase.pname}.elf" + ${lib.optionalString emulator.enable-trace "--wave-path"} + ${lib.optionalString emulator.enable-trace "${testCase.pname}.fsdb"} + ) + emuDriverArgs="''${emuDriverArgsArray[@]}" + emuDriver="${emulator}/bin/t1-vcs-simulator" + + rtlEventOutPath="$out/${testCase.pname}-rtl-event.jsonl" + + echo "[nix] Running VCS ${testCase.pname} with args $emuDriverArgs" + + export RUST_BACKTRACE=full + if ! "$emuDriver" $emuDriverArgs >/dev/null 2>"$rtlEventOutPath"; then + echo -e "\033[0;31m[nix]\033[0m: online driver run failed" + cat $rtlEventOutPath + echo -e "\033[0;31m[nix]\033[0m: Try rerun with '\033[0;34m$emuDriver $emuDriverArgs\033[0m'" + exit 1 + fi + + echo "[nix] VCS run done" + + if [ ! -r "$rtlEventOutPath" ]; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: no $rtlEventOutPath found in output" + exit 1 + fi + + if ! jq --stream -c -e '.[]' "$rtlEventOutPath" >/dev/null 2>&1; then + echo -e "[nix] \033[0;31mInternal Error\033[0m: invalid JSON file $rtlEventOutPath, showing original file:" + echo "--------------------------------------------" + cat $rtlEventOutPath + echo "--------------------------------------------" + exit 1 + fi + + set +e + "${emulator}/bin/offline" \ + --elf-file ${testCase}/bin/${testCase.pname}.elf \ + --log-file $rtlEventOutPath \ + --log-level ERROR &> $out/offline-check-journal + printf "$?" > $out/offline-check-status + if [ "$(cat $out/offline-check-status)" != "0" ]; then + echo "[nix] Offline check FAIL" + else + echo "[nix] Offline check PASS" + fi + set -e + + echo "[nix] compressing event log" + zstd $rtlEventOutPath -o $rtlEventOutPath.zstd + rm $rtlEventOutPath + + if [ -r perf.txt ]; then + mv perf.txt $out/ + fi + + ${lib.optionalString emulator.enable-trace '' + cp -v ${testCase.pname}.fsdb "$out" + cp -vr ${emulator}/lib/t1-vcs-simulator.daidir "$out" + ''} +'' diff --git a/t1rocketemu/nix/vcs.nix b/t1rocketemu/nix/vcs.nix new file mode 100644 index 000000000..d44360983 --- /dev/null +++ b/t1rocketemu/nix/vcs.nix @@ -0,0 +1,78 @@ +{ lib +, bash +, stdenv +, rtl +, callPackage +, vcs-dpi-lib +, vcs-fhs-env +}: + +let + self = stdenv.mkDerivation { + name = "t1rocket-vcs"; + + # require license + __noChroot = true; + dontPatchELF = true; + + src = rtl; + + buildPhase = '' + runHook preBuild + + echo "[nix] running VCS" + fhsBash=${vcs-fhs-env}/bin/vcs-fhs-env + VERDI_HOME=$("$fhsBash" -c "printenv VERDI_HOME") + "$fhsBash" vcs \ + -sverilog \ + -full64 \ + -timescale=1ns/1ps \ + -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ + ${lib.optionalString vcs-dpi-lib.enable-trace '' + -debug_access+pp+dmptf+thread \ + -kdb=common_elab,hgldd_all''} \ + -file filelist.f \ + ${vcs-dpi-lib}/lib/libdpi.a \ + -o t1-vcs-simulator + + runHook postBuild + ''; + + passthru = { + inherit (vcs-dpi-lib) enable-trace; + inherit vcs-fhs-env; + + cases = callPackage ../../tests { + configName = "t1rocket"; + emulator = self; + }; + + runEmulation = (callPackage ./run-vcs-emulation.nix { }) self; + }; + + shellHook = '' + echo "[nix] entering fhs env" + ${vcs-fhs-env}/bin/vcs-fhs-env + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin $out/lib + cp t1-vcs-simulator $out/lib + cp -r t1-vcs-simulator.daidir $out/lib + + # We need to carefully handle string escape here, so don't use makeWrapper + tee $out/bin/t1-vcs-simulator < "$rtlEventOutPath"; then - echo "[nix] online driver run failed" - cat $rtlEventOutPath - echo "[nix] Rerun with command: '$emuDriver $emuDriverArgs'" - exit 1 - fi - - echo "[nix] online driver done" - - runHook postBuild - ''; - - doCheck = true; - checkPhase = '' - runHook preCheck - - if [ ! -r "$rtlEventOutPath" ]; then - echo -e "[nix] \033[0;31mInternal Error\033[0m: no $rtlEventOutPath found in output" - exit 1 - fi - - if ! jq --stream -c -e '.[]' "$rtlEventOutPath" >/dev/null 2>&1; then - echo -e "[nix] \033[0;31mInternal Error\033[0m: invalid JSON file $rtlEventOutPath, showing original file:" - echo "--------------------------------------------" - cat $rtlEventOutPath - echo "--------------------------------------------" - exit 1 - fi - - if [ -z "$postCheck" ]; then - set +e - mkdir -p "$out" - "${verilator-emu}/bin/offline" \ - --elf-file ${testCase}/bin/${testCase.pname}.elf \ - --log-file $rtlEventOutPath \ - --log-level ERROR &> $out/offline-check-journal - printf "$?" > $out/offline-check-status - set -e - fi - - runHook postCheck - ''; - - installPhase = '' - runHook preInstall - - echo "[nix] compressing event log" - zstd $rtlEventOutPath -o $rtlEventOutPath.zstd - rm $rtlEventOutPath - - if [ -r perf.txt ]; then - mv perf.txt $out/ - fi - - runHook postInstall - ''; - }; - - verilator-check-trace = lib.overrideDerivation verilator-check (old: { - name = old.name + "-with-trace"; - emuDriver = "${verilator-emu-trace}/bin/online_drive"; - emuDriverArgs = old.emuDriverArgs or [ ] ++ [ "--wave-path" "${placeholder "out"}/wave.fst" ]; - postCheck = '' - if [ ! -r "$out/wave.fst" ]; then - echo -e "[nix] \033[0;31mInternal Error\033[0m: waveform not found in output" - exit 1 - fi - ''; - }); - - vcs-check = lib.overrideDerivation verilator-check (old: { - name = old.name + "-with-vcs"; - __noChroot = true; - dontPatchELF = true; - - buildPhase = '' - runHook preBuild - - mkdir -p "$out" - echo "[nix] Running VCS for ${testCase.pname}" - - RUST_BACKTRACE=full "${vcs-emu}/bin/t1-vcs-simulator" \ - --elf-file ${testCase}/bin/${testCase.pname}.elf \ - 1> /dev/null \ - 2> $rtlEventOutPath - - echo "[nix] VCS emu done" - - runHook postBuild - ''; - - postCheck = '' - set +e - - "${vcs-emu}/bin/offline" \ - --elf-file ${testCase}/bin/${testCase.pname}.elf \ - --log-file $rtlEventOutPath \ - --log-level ERROR &> $out/offline-check-journal - printf "$?" > $out/offline-check-status - - set -e - ''; - }); - - vcs-trace-check = lib.overrideDerivation verilator-check (old: { - name = old.name + "-with-vcs-trace"; - __noChroot = true; - dontPatchELF = true; - buildPhase = '' - runHook preBuild - - mkdir -p "$out" - echo "[nix] Running VCS(TRACE) for ${testCase.pname}" - - RUST_BACKTRACE=full "${vcs-emu-trace}/bin/t1-vcs-simulator" \ - --elf-file ${testCase}/bin/${testCase.pname}.elf \ - --wave-path ${testCase.pname}.fsdb \ - 1> /dev/null \ - 2> $rtlEventOutPath - - echo "[nix] VCS emu done" - - runHook postBuild - ''; - - postCheck = '' - set +e - - echo "[nix] Checking VCS event log" - "${vcs-emu-trace}/bin/offline" \ - --elf-file ${testCase}/bin/${testCase.pname}.elf \ - --log-file $rtlEventOutPath \ - --log-level ERROR &> $out/offline-check-journal - printf "$?" > $out/offline-check-status - if [ "$(cat $out/offline-check-status)" == "0" ]; then - echo "[nix] VCS difftest PASS" - else - echo "[nix] VCS difftest FAIL" - fi - - set -e - ''; - - postInstall = '' - # VCS have weird behavior on file creation, it will report read-only filesystem on our output, - # while other tools can mutate file system correctly. - cp ${testCase.pname}.fsdb "$out" - cp -r ${vcs-emu-trace}/lib/t1-vcs-simulator.daidir "$out" - ''; - }); - - t1rocket-check = lib.overrideDerivation verilator-check (old: { - name = old.name + "-t1rocket"; - emuDriver = "${t1rocket-emu}/bin/online_drive"; - }); - - t1rocket-check-trace = lib.overrideDerivation t1rocket-check (old: { - name = old.name + "-with-trace"; - emuDriver = "${verilator-emu-trace}/bin/online_drive"; - emuDriverArgs = old.emuDriverArgs or [ ] ++ [ "--wave-path" "${placeholder "out"}/wave.fst" ]; - postCheck = '' - if [ ! -r "$out/wave.fst" ]; then - echo -e "[nix] \033[0;31mInternal Error\033[0m: waveform not found in output" - exit 1 - fi - ''; - }); -}