From 38c413d44833e344dbf10d3eb102eab1946ca9d7 Mon Sep 17 00:00:00 2001 From: Avimitin Date: Thu, 1 Aug 2024 01:19:21 +0800 Subject: [PATCH] [nix] refactor test case * cases is now under the ip scope * replace isFp detect with features abstract layer, so that tests can have more extensible way to declare their required emulator type. Signed-off-by: Avimitin --- .github/workflows/vcs.yml | 2 +- .github/workflows/verilator.yml | 4 +- nix/t1/default.nix | 36 +++++++---- script/ci/src/Main.scala | 12 ++-- tests/asm/default.nix | 4 +- tests/asm/fpsmoke/features-required.json | 1 + tests/asm/fpsmoke/isFp | 0 tests/builder.nix | 36 +++++------ tests/codegen/default.nix | 38 +++++++----- tests/codegen/zvbb.txt | 16 +++++ tests/default.nix | 62 ++++++++++++++----- .../features-required.json | 1 + tests/intrinsic/linear_normalization/isFp | 0 .../intrinsic/softmax/features-required.json | 1 + tests/intrinsic/softmax/isFp | 0 tests/mlir/default.nix | 8 +-- .../mandelbrot/features-required.json | 1 + tests/rvv_bench/mandelbrot/isFp | 0 18 files changed, 148 insertions(+), 74 deletions(-) create mode 100644 tests/asm/fpsmoke/features-required.json delete mode 100644 tests/asm/fpsmoke/isFp create mode 100644 tests/codegen/zvbb.txt create mode 100644 tests/intrinsic/linear_normalization/features-required.json delete mode 100644 tests/intrinsic/linear_normalization/isFp create mode 100644 tests/intrinsic/softmax/features-required.json delete mode 100644 tests/intrinsic/softmax/isFp create mode 100644 tests/rvv_bench/mandelbrot/features-required.json delete mode 100644 tests/rvv_bench/mandelbrot/isFp diff --git a/.github/workflows/vcs.yml b/.github/workflows/vcs.yml index 82b9dcf761..708cac92b9 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 }}.cases.all" --max-jobs auto --no-link --cores 64 + nix build ".#t1.${{ matrix.config }}.ip.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 d839cef209..339cccc435 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 }}.cases.all" --max-jobs auto -L --no-link --cores 64 + nix build ".#t1.${{ matrix.config }}.ip.cases.all" --max-jobs auto -L --no-link --cores 64 gen-matrix: name: "Prepare for running testcases" @@ -91,4 +91,4 @@ jobs: run: | nix run ".#ci-helper" -- postCI --failed-tests-file-path ./failed-tests.md --cycle-update-file-path ./cycle-update.md cat ./failed-tests.md >> $GITHUB_STEP_SUMMARY - cat ./cycle-update.md >> $GITHUB_STEP_SUMMARY \ No newline at end of file + cat ./cycle-update.md >> $GITHUB_STEP_SUMMARY diff --git a/nix/t1/default.nix b/nix/t1/default.nix index 106ac1059f..088cd98ae8 100644 --- a/nix/t1/default.nix +++ b/nix/t1/default.nix @@ -3,6 +3,7 @@ , stdenv , useMoldLinker , newScope +, runCommand , pkgsX86 }: @@ -51,16 +52,6 @@ lib.makeScope newScope elaborateConfigJson = configPath; elaborateConfig = builtins.fromJSON (lib.readFile configPath); - cases = innerSelf.callPackage ../../tests { - inherit (ip) verilator-emu verilator-emu-trace vcs-emu vcs-emu-trace; - }; - - # 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; - ip = rec { recurseForDerivations = true; @@ -80,8 +71,31 @@ lib.makeScope newScope "--lowering-options=verifLabels,omitVersionComment,emittedLineLength=240,locationInfoStyle=none" ]; }; - omreader = self.omreader-unwrapped.mkWrapper { inherit mlirbc; }; + om = innerSelf.callPackage ./om.nix { inherit mlirbc; }; + omreader = self.omreader-unwrapped.mkWrapper { inherit mlirbc; }; + + emu-om = innerSelf.callPackage ./om.nix { mlirbc = emu-mlirbc; }; + emu-omreader = self.omreader-unwrapped.mkWrapper { mlirbc = emu-mlirbc; }; + omGet = args: lib.fileContents (runCommand "get-${args}" { } '' + ${emu-omreader}/bin/omreader ${args} > $out + ''); + rtlDesignMetadata = { + march = omGet "march"; + extensions = builtins.fromJSON (omGet "extensionsJson"); + vlen = omGet "vlen"; + dlen = omGet "dlen"; + }; + + 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; }; diff --git a/script/ci/src/Main.scala b/script/ci/src/Main.scala index 8564ce6b2c..50add27c36 100644 --- a/script/ci/src/Main.scala +++ b/script/ci/src/Main.scala @@ -162,8 +162,8 @@ object Main: val testAttr = testType.toLowerCase() match case "verilator" => - s".#t1.$config.cases.$caseName.emu-result.with-offline" - case "vcs" => s".#t1.$config.cases.$caseName.emu-result.with-vcs" + s".#t1.$config.ip.cases.$caseName.emu-result.with-offline" + case "vcs" => s".#t1.$config.ip.cases.$caseName.emu-result.with-vcs" case _ => Logger.fatal(s"Invalid test type ${testType}") val testResultPath = try @@ -186,7 +186,7 @@ object Main: os.read(testResultPath / "offline-check-status").trim() == "0" if !testSuccess then Logger.error(s"Offline check FAILED for $caseName ($config)") - allFailedTest :+ s"t1.$config.cases.$caseName" + allFailedTest :+ s"t1.$config.ip.cases.$caseName" else Logger.info(s"Offline check PASS for $caseName ($config)") allFailedTest @@ -268,8 +268,8 @@ object Main: Logger.info("Fetching CI results") val resultAttr = emuType.toLowerCase() match case "verilator" => - s".#t1.$config.cases._allEmuResult" - case "vcs" => s".#t1.$config.cases._allVCSEmuResult" + s".#t1.$config.ip.cases._allEmuResult" + case "vcs" => s".#t1.$config.ip.cases._allVCSEmuResult" case _ => Logger.fatal(s"Invalid test type ${emuType}") val emuResultPath = os.Path(nixResolvePath( resultAttr, @@ -359,7 +359,7 @@ object Main: import scala.util.chaining._ val testPlans: Seq[String] = emulatorConfigs.flatMap: configName => - val allCasesPath = nixResolvePath(s".#t1.$configName.cases.all") + val allCasesPath = nixResolvePath(s".#t1.$configName.ip.cases.all") os.walk(os.Path(allCasesPath) / "configs") .filter: path => path.ext == "json" diff --git a/tests/asm/default.nix b/tests/asm/default.nix index 4bd751df85..debd78f5ca 100644 --- a/tests/asm/default.nix +++ b/tests/asm/default.nix @@ -3,6 +3,7 @@ , makeBuilder , findAndBuild , t1main +, getTestRequiredFeatures }: let @@ -13,6 +14,7 @@ let src = sourcePath; + featuresRequired = getTestRequiredFeatures sourcePath; isFp = lib.pathExists (lib.path.append sourcePath "isFp"); buildPhase = '' @@ -29,5 +31,5 @@ let meta.description = "test case '${caseName}', written in C assembly"; }; in - findAndBuild ./. build +findAndBuild ./. build diff --git a/tests/asm/fpsmoke/features-required.json b/tests/asm/fpsmoke/features-required.json new file mode 100644 index 0000000000..892f81d203 --- /dev/null +++ b/tests/asm/fpsmoke/features-required.json @@ -0,0 +1 @@ +["zve32f"] diff --git a/tests/asm/fpsmoke/isFp b/tests/asm/fpsmoke/isFp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/builder.nix b/tests/builder.nix index 7016042aaa..f250a8b13f 100644 --- a/tests/builder.nix +++ b/tests/builder.nix @@ -2,9 +2,7 @@ { stdenv , lib , jq -, elaborateConfig -, isFp -, vLen +, rtlDesignMetadata , makeEmuResult }: @@ -28,22 +26,17 @@ let CC = "${stdenv.targetPlatform.config}-cc"; - NIX_CFLAGS_COMPILE = - let - march = (if isFp then "rv32gc_zve32f" else "rv32gc_zve32x") - + "_zvl${toString (lib.min 1024 vLen)}b"; - in - [ - "-mabi=ilp32f" - "-march=${march}" - "-mno-relax" - "-static" - "-mcmodel=medany" - "-fvisibility=hidden" - "-fno-PIC" - "-g" - "-O3" - ]; + NIX_CFLAGS_COMPILE = [ + "-mabi=ilp32f" + "-march=${rtlDesignMetadata.march}" + "-mno-relax" + "-static" + "-mcmodel=medany" + "-fvisibility=hidden" + "-fno-PIC" + "-g" + "-O3" + ]; installPhase = '' runHook preInstall @@ -63,7 +56,10 @@ let dontFixup = true; - passthru.emu-result = makeEmuResult caseDrv; + passthru = { + inherit rtlDesignMetadata; + emu-result = makeEmuResult caseDrv; + }; } // overrides); in diff --git a/tests/codegen/default.nix b/tests/codegen/default.nix index e771efddb4..4e309bac5b 100644 --- a/tests/codegen/default.nix +++ b/tests/codegen/default.nix @@ -2,16 +2,25 @@ , linkerScript , rvv-codegen , makeBuilder -, xLen -, vLen -, isFp + # Instead of testing feature is supported on TOP level, + # codegen case are always generated with supported code. +, currentFeatures }: let builder = makeBuilder { casePrefix = "codegen"; }; makeCaseName = lib.replaceStrings [ "." ] [ "_" ]; + extraValueFromFeatures = pattern: + lib.last + (lib.splitString ":" + (lib.head + (lib.filter + (lib.hasPrefix pattern) + currentFeatures))); + vlen = extraValueFromFeatures "vlen"; + xlen = extraValueFromFeatures "xlen"; - build = { rawCaseName, isFp }: + build = { rawCaseName, passthru }: builder rec { caseName = makeCaseName rawCaseName; @@ -22,14 +31,14 @@ let dontUnpack = true; - inherit isFp; + inherit passthru; buildPhase = '' runHook preBuild ${rvv-codegen}/bin/single \ - -VLEN "${toString vLen}" \ - -XLEN "${toString xLen}" \ + -VLEN "${vlen}" \ + -XLEN "${xlen}" \ -repeat 16 \ -testfloat3level 2 \ -configfile ${rvv-codegen}/configs/${rawCaseName}.toml \ @@ -50,7 +59,7 @@ let meta.description = "test case '${caseName}' generated by codegen"; }; - buildTestsFromFile = file: { isFp ? false }: + buildTestsFromFile = file: passthru: with lib; let rawCaseNames = lib.splitString "\n" (lib.fileContents file); @@ -59,17 +68,18 @@ let (map (rawCaseName: nameValuePair (makeCaseName rawCaseName) - (build { inherit rawCaseName isFp; }) + (build { inherit rawCaseName passthru; }) ) rawCaseNames)); commonTests = buildTestsFromFile ./common.txt { }; - fpTests = buildTestsFromFile ./fp.txt { isFp = true; }; - + fpTests = buildTestsFromFile ./fp.txt { featuresRequired = [ "zve32f" ]; }; + zvbbTests = buildTestsFromFile ./zvbb.txt { featuresRequired = [ "zvbb" ]; }; + hasFeature = feat: lib.any (f: feat == f) currentFeatures; in lib.recurseIntoAttrs ( - if isFp - then commonTests // fpTests - else commonTests + commonTests // + lib.optionalAttrs (hasFeature "zve32f") fpTests // + lib.optionalAttrs (hasFeature "zvbb") zvbbTests ) diff --git a/tests/codegen/zvbb.txt b/tests/codegen/zvbb.txt new file mode 100644 index 0000000000..77ed676212 --- /dev/null +++ b/tests/codegen/zvbb.txt @@ -0,0 +1,16 @@ +vandn.vv +vandn.vx +vbrev.v +vbreav8.v +vclz.v +vcpop.v +vctz.v +vrev8.v +vrol.vv +vrol.vx +vror.vi +vror.vv +vror.vx +vwsll.vi +vwsll.vv +vwsll.vx diff --git a/tests/default.nix b/tests/default.nix index f1755c122d..2517f603f7 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,6 +1,6 @@ { lib , configName -, elaborateConfig +, rtlDesignMetadata , newScope , rv32-stdenv , runCommand @@ -11,23 +11,43 @@ }: let - extension = lib.head elaborateConfig.parameter.extensions; - xLen = if lib.hasInfix "ve32" extension then 32 else 64; - isFp = lib.hasInfix "f" extension; - vLen = let vLen = elaborateConfig.parameter.vLen; in - assert builtins.bitAnd vLen (vLen - 1) == 0; # vLen should be power of 2 - assert vLen >= 32; - vLen; + hasExt = cmp: lib.any (ext: cmp == (lib.toLower ext)) rtlDesignMetadata.extensions; + + # Add an extra abstract layer between test case and RTL design, so that we can have clean and organized way + # for developer to specify their required features without the need to parse ISA string themselves. + currentFeatures = [ + "vlen:${rtlDesignMetadata.vlen}" + "dlen:${rtlDesignMetadata.dlen}" + "xlen:${if (lib.hasPrefix "rv32" rtlDesignMetadata.march) then "32" else "64"}" + ] + ++ lib.optionals (hasExt "zve32f") [ "zve32f" ] + ++ lib.optionals (hasExt "zvbb") [ "zvbb" ]; + + # isSubSetOf m n: n is subset of m + isSubsetOf = m: n: lib.all (x: lib.elem x m) n; scope = lib.recurseIntoAttrs (lib.makeScope newScope (casesSelf: { recurseForDerivations = true; - inherit xLen vLen isFp verilator-emu verilator-emu-trace vcs-emu vcs-emu-trace; + inherit verilator-emu verilator-emu-trace vcs-emu vcs-emu-trace rtlDesignMetadata currentFeatures; makeEmuResult = casesSelf.callPackage ./make-emu-result.nix { }; makeBuilder = casesSelf.callPackage ./builder.nix { }; + # Read casePath/features-required.json to get extra feature information. + # Like the requirement of zve32f, or requirement for higher vlen. + # Empty list means no extra requirement for RTL design, then the baseline zve32x will be used. + # + # TODO: check user specified features are correct or not + getTestRequiredFeatures = sourcePath: + let + extraFeatures = lib.path.append sourcePath "features-required.json"; + in + if lib.pathExists extraFeatures then + builtins.fromJSON (lib.fileContents extraFeatures) + else [ ]; + findAndBuild = dir: build: lib.recurseIntoAttrs (lib.pipe (builtins.readDir dir) [ # filter out all non-directory entrires and underscore-prefixed directories @@ -43,7 +63,10 @@ let inherit caseName sourcePath; }) ) - (lib.filterAttrs (caseName: caseDrv: assert caseDrv ? isFp; caseDrv.isFp -> isFp)) + (lib.filterAttrs (caseName: caseDrv: + assert caseDrv ? featuresRequired; + # Test the case required extensions is supported by rtl design + isSubsetOf currentFeatures caseDrv.featuresRequired)) ]); t1main = ./t1_main.S; linkerScript = ./t1.ld; @@ -69,10 +92,12 @@ let # This allows Nix to resolve the path only once, while still pulling all tests into the local Nix store. _allEmuResult = let - testPlan = builtins.fromJSON (lib.readFile ../.github/cases/${configName}/default.json); + testPlan = builtins.fromJSON + (lib.readFile ../.github/cases/${configName}/default.json); # flattern the attr set to a list of test case derivations # AttrSet (AttrSet Derivation) -> List Derivation - allCases = lib.filter (val: lib.isDerivation val && lib.hasAttr val.pname testPlan) + allCases = lib.filter + (val: lib.isDerivation val && lib.hasAttr val.pname testPlan) (lib.concatLists (map lib.attrValues (lib.attrValues scopeStripped))); script = '' mkdir -p $out @@ -86,7 +111,10 @@ let '') allCases); in - runCommand "catch-${configName}-all-emu-result-for-ci" { } script; + runCommand + "catch-${configName}-all-emu-result-for-ci" + { } + script; _allVCSEmuResult = let @@ -109,7 +137,8 @@ let all = let - allCases = lib.filter lib.isDerivation + allCases = lib.filter + lib.isDerivation (lib.concatLists (map lib.attrValues (lib.attrValues scopeStripped))); script = '' mkdir -p $out/configs @@ -121,6 +150,9 @@ let '') allCases); in - runCommand "build-all-testcases" { } script; + runCommand + "build-all-testcases" + { } + script; in lib.recurseIntoAttrs (scopeStripped // { inherit all _allEmuResult _allVCSEmuResult; }) diff --git a/tests/intrinsic/linear_normalization/features-required.json b/tests/intrinsic/linear_normalization/features-required.json new file mode 100644 index 0000000000..892f81d203 --- /dev/null +++ b/tests/intrinsic/linear_normalization/features-required.json @@ -0,0 +1 @@ +["zve32f"] diff --git a/tests/intrinsic/linear_normalization/isFp b/tests/intrinsic/linear_normalization/isFp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/intrinsic/softmax/features-required.json b/tests/intrinsic/softmax/features-required.json new file mode 100644 index 0000000000..892f81d203 --- /dev/null +++ b/tests/intrinsic/softmax/features-required.json @@ -0,0 +1 @@ +["zve32f"] diff --git a/tests/intrinsic/softmax/isFp b/tests/intrinsic/softmax/isFp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/mlir/default.nix b/tests/mlir/default.nix index 6ebb29fe2d..96ba1218f8 100644 --- a/tests/mlir/default.nix +++ b/tests/mlir/default.nix @@ -1,8 +1,8 @@ -{ lib -, linkerScript +{ linkerScript , buddy-mlir , makeBuilder , findAndBuild +, getTestRequiredFeatures , t1main }: @@ -14,7 +14,7 @@ let src = sourcePath; - isFp = lib.pathExists (lib.path.append sourcePath "isFp"); + featuresRequired = getTestRequiredFeatures sourcePath; nativeBuildInputs = [ buddy-mlir ]; @@ -60,4 +60,4 @@ let meta.description = "testcase '${caseName}', written in MLIR"; }; in - findAndBuild ./. build +findAndBuild ./. build diff --git a/tests/rvv_bench/mandelbrot/features-required.json b/tests/rvv_bench/mandelbrot/features-required.json new file mode 100644 index 0000000000..892f81d203 --- /dev/null +++ b/tests/rvv_bench/mandelbrot/features-required.json @@ -0,0 +1 @@ +["zve32f"] diff --git a/tests/rvv_bench/mandelbrot/isFp b/tests/rvv_bench/mandelbrot/isFp deleted file mode 100644 index e69de29bb2..0000000000