diff --git a/.gitignore b/.gitignore index 37d40d7..8758252 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ parsetab.py .swiftpm/ abc.history +temp.txt diff --git a/.swift-version b/.swift-version index 37c2d99..3659ea2 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.4 +5.8 diff --git a/Benchmarks/Benchmarks/aes128.v b/Benchmarks/Benchmarks/aes128.v index 3516dc4..8b0c54c 100644 --- a/Benchmarks/Benchmarks/aes128.v +++ b/Benchmarks/Benchmarks/aes128.v @@ -14,8 +14,9 @@ * limitations under the License. */ -module aes128(clk, state, key, out); +module aes128(clk, state, key, out, reset); input clk; + input reset; input [127:0] state, key; output [127:0] out; reg [127:0] s0, k0; diff --git a/Benchmarks/test.sh b/Benchmarks/test.sh index d405ea0..ad27416 100755 --- a/Benchmarks/test.sh +++ b/Benchmarks/test.sh @@ -10,7 +10,7 @@ function usage() { echo " --all Runs Fault's complete flow" echo " --synth Runs synthesis script" echo " --cut Runs cut option on the synthesized netlist" - echo " -g, --tvgen Runs fault simulation using the specified TV generator (swift, atalanta, LFSR, PODEM)" + echo " -g, --tvgen Runs fault simulation using the specified TV generator (swift, atalanta, LFSR, PODEM, PodemQuest)" echo " --delay Runs the transition fault simulator" echo " --chain Runs chain option on the synthesized netlist" echo " --tap Runs tap option on the chained netlist" @@ -194,7 +194,7 @@ do ignoring="-i $ignored_input,$clock_signal,$reset_signal" fi # Check tvgen type - if [ $tvgen = "atalanta" ] || [ $tvgen = "podem" ]; + if [ $tvgen = "atalanta" ] || [ $tvgen = "podem" ] || [ $tvgen = "PodemQuest" ]; then bench=$cut_netlist.bench echo "Generating bench circuit for $cut_netlist" diff --git a/Sources/Fault/Entries/atpg.swift b/Sources/Fault/Entries/atpg.swift index 31f7782..f53cc35 100644 --- a/Sources/Fault/Entries/atpg.swift +++ b/Sources/Fault/Entries/atpg.swift @@ -12,97 +12,131 @@ // See the License for the specific language governing permissions and // limitations under the License. +import ArgumentParser import BigInt import Collections -import ArgumentParser -import CoreFoundation // Not automatically imported on Linux +import CoreFoundation // Not automatically imported on Linux import Defile import Foundation import PythonKit import Yams - extension Fault { struct ATPG: ParsableCommand { static let configuration = CommandConfiguration( - abstract: "Generate/import test vectors for a particular circuit and calculate coverage." + abstract: + "Generate/import test vectors for a particular circuit and calculate coverage." + ) + + @Option( + name: [.short, .long], help: "Path to the output JSON file. (Default: input + .tv.json)" ) - - @Option(name: [.short, .long], help: "Path to the output JSON file. (Default: input + .tv.json)") var output: String? - + @Option(help: "Path to the output SVF file. (Default: input + .tv.svf)") var outputSvf: String? - - @Option(name: [.long, .customLong("output-faultPoints")], help: "Path to the output yml file listing all generated fault points. (Default: nil)") + + @Option( + name: [.long, .customLong("output-faultPoints")], + help: "Path to the output yml file listing all generated fault points. (Default: nil)") var outputFaultPoints: String? - - @Option(name: [.long, .customLong("output-covered")], help: "Path to the output yml file listing coverage metadata, i.e., ratio and fault points covered. (Default: nil)") + + @Option( + name: [.long, .customLong("output-covered")], + help: + "Path to the output yml file listing coverage metadata, i.e., ratio and fault points covered. (Default: nil)" + ) var outputCoverageMetadata: String? - - @Option(name: [.short, .long, .customLong("cellModel")], help: "A Verilog model with which standard cells can be simulated.") + + @Option( + name: [.short, .long, .customLong("cellModel")], + help: "A Verilog model with which standard cells can be simulated.") var cellModel: String - - @Option(name: [.customShort("v"), .long], help: "Number of test vectors to generate in the first batch.") + + @Option( + name: [.customShort("v"), .long], + help: "Number of test vectors to generate in the first batch.") var tvCount: Int = 100 - - @Option(name: [.customShort("r"), .long], help: "Increment in test vector count in subsequent batches should sufficient coverage not be reached.") + + @Option( + name: [.customShort("r"), .long], + help: + "Increment in test vector count in subsequent batches should sufficient coverage not be reached." + ) var increment: Int = 50 - - @Option(name: [.short, .long], help: "The minimum coverage to reach before ceasing increments. If set to 0, only the initial batch is run.") + + @Option( + name: [.short, .long], + help: + "The minimum coverage to reach before ceasing increments. If set to 0, only the initial batch is run." + ) var minCoverage: Float = 80 - - @Option(help: "Ceiling for Test Vector increments: if this number is reached, no more increments will occur regardless the coverage.") + + @Option( + help: + "Ceiling for Test Vector increments: if this number is reached, no more increments will occur regardless the coverage." + ) var ceiling: Int? - + @Option(help: "Type of the pseudo-random internal test-vector-generator.") var tvGen: String = "swift" - - @Option(help: "A \(MemoryLayout.size)-byte value to use as an RNG seed for test vector generators, provided as a hexadecimal string (without 0x).") + + @Option( + help: + "A \(MemoryLayout.size)-byte value to use as an RNG seed for test vector generators, provided as a hexadecimal string (without 0x)." + ) var rngSeed: String = "DEADCAFEDEADF00D" - - @Option(name: [.customShort("g"), .long], help: "Use an external TV Generator: Atalanta or PODEM.") + + @Option( + name: [.customShort("g"), .long], + help: "Use an external TV Generator: Atalanta or PODEM.") var etvGen: String? - - @Option(name: [.short, .long], help: "Netlist in bench format. (Required iff generator is set to Atalanta or PODEM.)") + + @Option( + name: [.short, .long], + help: "Netlist in bench format. (Required iff generator is set to Atalanta or PODEM.)") var bench: String? - + @Flag(help: "Generate only one testbench for inspection, and do not delete it.") var sampleRun: Bool = false - + @OptionGroup var bypass: BypassOptions - - @Option(help: "If provided, this JSON file's test vectors are simulated and no generation is attempted.") - var externalTVSet: String? - - @Option(help: "If provided, this JSON file's test vector are used as the initial set of test vectors, with iterations taking place with them in mind.") + + @Option( + help: + "If provided, this JSON file's test vector are used as the initial set of test vectors, with iterations taking place with them in mind." + ) var iteratingUpon: String? - - @Option(name: [.customShort("D"), .customLong("define")], help: "Define statements to include during simulations.") + + @Option( + name: [.customShort("D"), .customLong("define")], + help: "Define statements to include during simulations.") var defines: [String] = [] - - @Option(name: [.customShort("I"), .customLong("include")], help: "Extra verilog models to include during simulations.") + + @Option( + name: [.customShort("I"), .customLong("include")], + help: "Extra verilog models to include during simulations.") var includes: [String] = [] - + @Argument(help: "The cutaway netlist to generate patterns for.") var file: String - + mutating func run() throws { - + if !TVGeneratorFactory.validNames.contains(tvGen) { throw ValidationError("Invalid test-vector generator \(tvGen).") } - + let fileManager = FileManager() guard fileManager.fileExists(atPath: file) else { throw ValidationError("File '\(file)' not found.") } - + guard fileManager.fileExists(atPath: cellModel) else { throw ValidationError("Cell model file '\(cellModel)' not found.") } - + if !cellModel.hasSuffix(".v"), !cellModel.hasSuffix(".sv") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv." @@ -111,7 +145,7 @@ extension Fault { let jsonOutput = output ?? file.replacingExtension(".cut.v", with: ".tv.json") let svfOutput = outputSvf ?? file.replacingExtension(".cut.v", with: ".tv.svf") - + // MARK: Importing Python and Pyverilog let parse = Python.import("pyverilog.vparser.parser").parse @@ -143,46 +177,36 @@ extension Fault { var etvSetVectors: [TestVector] = [] var etvSetInputs: [Port] = [] - if let tvSetTest = externalTVSet { - if !fileManager.fileExists(atPath: tvSetTest) { - throw ValidationError("TVs JSON file '\(tvSetTest)' not found.") - } - if tvSetTest.hasSuffix(".json") { - (etvSetVectors, etvSetInputs) = try TVSet.readFromJson(file: tvSetTest) - } else { - (etvSetVectors, etvSetInputs) = try TVSet.readFromText(file: tvSetTest) - } - print("Read \(etvSetVectors.count) externally-generated vectors to verify.") - } - if let tvGenerator = etvGen { guard let etvgen = ETVGFactory.get(name: tvGenerator) else { Stderr.print("Unknown external test vector generator '\(tvGenerator)'.") Foundation.exit(EX_USAGE) } - - let benchUnwrapped = bench! // Program exits if etvGen.value isn't nil and bench.value is or vice versa + + let benchUnwrapped = bench! // Program exits if etvGen.value isn't nil and bench.value is or vice versa if !fileManager.fileExists(atPath: benchUnwrapped) { throw ValidationError("Bench file '\(benchUnwrapped)' not found.") } - (etvSetVectors, etvSetInputs) = etvgen.generate(file: benchUnwrapped, module: "\(definition.name)") + (etvSetVectors, etvSetInputs) = etvgen.generate( + file: benchUnwrapped, module: "\(definition.name)") if etvSetVectors.count == 0 { - Stderr.print("Bench netlist appears invalid (no vectors generated). Are you sure there are no floating nets/outputs?") + Stderr.print( + "Bench netlist appears invalid (no vectors generated). Are you sure there are no floating nets/outputs?" + ) Foundation.exit(EX_DATAERR) } else { - print("Generated \(etvSetVectors.count) test vectors using external utilties to verify.") + print( + "Generated \(etvSetVectors.count) test vectors using external utilties to verify." + ) } } let tvMinimumCoverage = minCoverage / 100 - let finalTvCeiling: Int = ceiling ?? ( - etvSetVectors.count == 0 ? - 1000 : - etvSetVectors.count - ) - + let finalTvCeiling: Int = + ceiling ?? (etvSetVectors.count == 0 ? 1000 : etvSetVectors.count) + let finalRNGSeed = UInt(rngSeed, radix: 16)! do { @@ -217,7 +241,7 @@ extension Fault { evtInputsMinusIgnored.append(input) } } - assert(inputsMinusIgnored.count == evtInputsMinusIgnored.count); + assert(inputsMinusIgnored.count == evtInputsMinusIgnored.count) inputsMinusIgnored = evtInputsMinusIgnored } @@ -230,7 +254,7 @@ extension Fault { } else { let minimum = min(port.from, port.to) let maximum = max(port.from, port.to) - for i in minimum ... maximum { + for i in minimum...maximum { faultPoints.insert("\(port.name) [\(i)]") } } @@ -255,10 +279,14 @@ extension Fault { } if warnAboutDFF { - print("Warning: D-flipflops were found in this netlist. Are you sure you ran it through 'fault cut'?") + print( + "Warning: D-flipflops were found in this netlist. Are you sure you ran it through 'fault cut'?" + ) } - print("Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports.") + print( + "Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports." + ) // MARK: Load Initial Set @@ -314,16 +342,18 @@ extension Fault { coverageList: result.coverageList ) let jsonRawOutput = jsonOutput.replacingExtension(".tv.json", with: ".raw_tv.json") - + print("Writing raw generated test vectors in Fault JSON format to \(jsonOutput)…") try encoder.encode(rawTVInfo).write(to: URL(fileURLWithPath: jsonRawOutput)) - + let tvInfo = TVInfo( inputs: inputsMinusIgnored, outputs: outputs, coverageList: Compactor.compact(coverageList: result.coverageList) ) - print("Writing compacted generated test vectors in Fault JSON format to \(jsonOutput)…") + print( + "Writing compacted generated test vectors in Fault JSON format to \(jsonOutput)…" + ) try encoder.encode(tvInfo).write(to: URL(fileURLWithPath: jsonOutput)) // try File.open(svfOutput, mode: .write) { @@ -332,7 +362,8 @@ extension Fault { // } if let coverageMetaFilePath = outputCoverageMetadata { - print("Writing YAML file of final coverage metadata to \(coverageMetaFilePath)…") + print( + "Writing YAML file of final coverage metadata to \(coverageMetaFilePath)…") try File.open(coverageMetaFilePath, mode: .write) { try $0.write(string: YAMLEncoder().encode(result.coverageMeta)) } diff --git a/Sources/Fault/Entries/main.swift b/Sources/Fault/Entries/main.swift index 09ba8ab..c44fad7 100644 --- a/Sources/Fault/Entries/main.swift +++ b/Sources/Fault/Entries/main.swift @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +import ArgumentParser import BigInt import Collections -import ArgumentParser -import CoreFoundation // Not automatically imported on Linux +import CoreFoundation // Not automatically imported on Linux import Defile import Foundation import PythonKit import Yams -let VERSION = "0.7.1" +let VERSION = "0.8.0" var env = ProcessInfo.processInfo.environment let iverilogBase = env["FAULT_IVL_BASE"] ?? "/usr/local/lib/ivl" @@ -29,20 +29,22 @@ let iverilogExecutable = env["FAULT_IVERILOG"] ?? env["PYVERILOG_IVERILOG"] ?? " let vvpExecutable = env["FAULT_VVP"] ?? "vvp" let yosysExecutable = env["FAULT_YOSYS"] ?? "yosys" -_ = [ // Register all RNGs +_ = [ // Register all RNGs SwiftRNG.registered, LFSR.registered, PatternGenerator.registered, ] -_ = [ // Register all TVGens +_ = [ // Register all TVGens Atalanta.registered, Quaigh.registered, PODEM.registered, + PodemQuest.registered, ] let yosysTest = "'\(yosysExecutable)' -V".sh(silent: true) if yosysTest != EX_OK { - Stderr.print("Yosys must be installed to PATH on your computer for Fault to work. Fault will now quit.") + Stderr.print( + "Yosys must be installed to PATH on your computer for Fault to work. Fault will now quit.") exit(EX_UNAVAILABLE) } @@ -55,7 +57,8 @@ let pythonVersions = { if let pythonPath = env["PYTHONPATH"] { sys.path.append(pythonPath) } else { - let pythonPathProcess = "python3 -c \"import sys; print(':'.join(sys.path), end='')\"".shOutput() + let pythonPathProcess = "python3 -c \"import sys; print(':'.join(sys.path), end='')\"" + .shOutput() let pythonPath = pythonPathProcess.output let pythonPathComponents = pythonPath.components(separatedBy: ":") for component in pythonPathComponents { @@ -69,7 +72,7 @@ let pythonVersions = { Stderr.print("\(error)") exit(EX_UNAVAILABLE) } -}() // Just to check +}() // Just to check struct Fault: ParsableCommand { static let configuration = CommandConfiguration( diff --git a/Sources/Fault/Synthesis.swift b/Sources/Fault/Synthesis.swift index 01d47b2..10a6da2 100644 --- a/Sources/Fault/Synthesis.swift +++ b/Sources/Fault/Synthesis.swift @@ -25,54 +25,56 @@ enum Synthesis { ) -> String { let opt = optimize ? "opt" : "" return """ - # read liberty - read_liberty -lib -ignore_miss_dir -setattr blackbox \(libertyFile) + # read liberty + read_liberty -lib -ignore_miss_dir -setattr blackbox \(libertyFile) - # read black boxes - read_verilog -sv -lib \(blackboxedModules.map { "'\($0)'" }.joined(separator: " ")) + # read black boxes + read_verilog -sv -lib \(blackboxedModules.map { "'\($0)'" }.joined(separator: " ")) - # read design - read_verilog -sv \(files.map { "'\($0)'" }.joined(separator: " ")) + # read design + read_verilog -sv \(files.map { "'\($0)'" }.joined(separator: " ")) - # check design hierarchy - hierarchy \(checkHierarchy ? "-check" : "") -top \(module) + # check design hierarchy + hierarchy \(checkHierarchy ? "-check" : "") -top \(module) + flatten; - # translate processes (always blocks) - proc; \(opt) + # translate processes (always blocks) + proc; \(opt) - # detect and optimize FSM encodings - fsm; \(opt) + # detect and optimize FSM encodings + fsm; \(opt) - # implement memories (arrays) - memory; \(opt) + # implement memories (arrays) + memory; \(opt) - # convert to gate logic - techmap; \(opt) + # convert to gate logic + techmap; \(opt) - # flatten - flatten; \(opt) + # flatten + flatten; \(opt) - # mapping flip-flops to mycells.lib - dfflibmap -liberty \(libertyFile) + # mapping flip-flops to mycells.lib + dfflibmap -liberty \(libertyFile) - # expose dff - \(cutting ? "expose -cut -evert-dff; \(opt)" : "") + # expose dff + \(cutting ? "expose -cut -evert-dff; \(opt)" : "") - # mapping logic to mycells.lib - abc -liberty \(libertyFile) + # mapping logic to mycells.lib + abc -liberty \(libertyFile) + splitnets - # print gate count - stat + # print gate count + stat - # cleanup - opt_clean -purge + # cleanup + opt_clean -purge - # names - # autoname + # names + # autoname - write_verilog -noexpr -nohex -nodec -defparam \(output)+attrs - write_verilog -noexpr -noattr -noexpr -nohex -nodec -defparam \(output) - # write_blif -gates -unbuf DFFSR D Q \(output).blif - """ + write_verilog -noexpr -nohex -nodec -defparam \(output)+attrs + write_verilog -noexpr -noattr -noexpr -nohex -nodec -defparam \(output) + # write_blif -gates -unbuf DFFSR D Q \(output).blif + """ } } diff --git a/Sources/Fault/TVGenerator.swift b/Sources/Fault/TVGenerator.swift index b3def59..17e7432 100644 --- a/Sources/Fault/TVGenerator.swift +++ b/Sources/Fault/TVGenerator.swift @@ -316,3 +316,28 @@ class PODEM: ExternalTestVectorGenerator { static let registered = ETVGFactory.register(name: "PODEM", type: PODEM.self) } + + +class PodemQuest: ExternalTestVectorGenerator { + + required init() {} + + func generate(file: String, module: String) -> ([TestVector], [Port]) { + let output = file.replacingExtension(".bench", with: ".test") + let podemQuest = "podemquest -i\(file) -o \(output)".sh() + + if podemQuest != EX_OK { + exit(podemQuest) + } + + do { + let (testvectors, inputs) = try TVSet.readFromTest(output, withInputsFrom: file) + return (vectors: testvectors, inputs: inputs) + } catch { + Stderr.print("Internal software error: \(error)") + exit(EX_SOFTWARE) + } + } + + static let registered = ETVGFactory.register(name: "PodemQuest", type: PodemQuest.self) +} diff --git a/default.nix b/default.nix index c3d21f2..b32977d 100644 --- a/default.nix +++ b/default.nix @@ -9,105 +9,100 @@ yosys, verilog, quaigh, - nl2bench, ncurses, makeBinaryWrapper, -}: -let +}: let generated = swiftpm2nix.helpers ./nix; - pyenv = (python3.withPackages(ps: with ps; [pyverilog])); + pyenv = python3.withPackages (ps: with ps; [nl2bench pyverilog]); stdenv = swiftPackages.stdenv; in -stdenv.mkDerivation (finalAttrs: { - name = "fault"; - - src = nix-gitignore.gitignoreSourcePure ./.gitignore ./.; - - swiftpmFlags = [ - "--verbose" - ]; - # ++ lib.lists.optional swiftPackages.stdenv.isDarwin [ - # "-Xcc" - # "-mmacosx-version-min=11" - # "-Xcc" - # "-target" - # "-Xcc" - # "x86_64-apple-macosx11" - # "-Xswiftc" - # "-target" - # "-Xswiftc" - # "x86_64-apple-macosx11" - # ]; - nativeBuildInputs = [ swift swiftpm makeBinaryWrapper ]; - - buildInputs = with swiftPackages; [ - Foundation - XCTest - ] ++ lib.lists.optional (!stdenv.isDarwin) [Dispatch]; - - propagatedBuildInputs = [ - pyenv - yosys - verilog - quaigh - nl2bench - ]; - - nativeCheckInputs = with python3.pkgs; [ - pytest - ]; - - configurePhase = generated.configure; - - # This doesn't work on Linux otherwise and I don't know why. - preBuild = if stdenv.isDarwin then "" else '' - export LD_LIBRARY_PATH=${swiftPackages.Dispatch}/lib:$LD_LIBRARY_PATH - ''; - - doCheck = true; - - faultEnv = '' - export PYTHONPATH=${pyenv}/${pyenv.sitePackages} - export PATH=${verilog}/bin:$PATH - export PATH=${yosys}/bin:$PATH - export PYTHON_LIBRARY=${pyenv}/lib/lib${pyenv.libPrefix}${swiftPackages.stdenv.hostPlatform.extensions.sharedLibrary} - export FAULT_IVL_BASE=${verilog}/lib/ivl - ''; - - checkPhase = '' - runHook preCheck - ${finalAttrs.faultEnv} - PYTEST_FAULT_BIN="$(swiftpmBinPath)/fault" pytest - runHook postCheck - ''; + stdenv.mkDerivation (finalAttrs: { + name = "fault"; - installPhase = '' - runHook preInstall - binPath="$(swiftpmBinPath)" - mkdir -p $out/bin - cp $binPath/fault $out/bin/fault - ln -s ${nl2bench}/bin/nl2bench $out/bin/nl2bench - runHook postInstall - ''; - - fixupPhase = '' - runHook preFixup - wrapProgram $out/bin/fault\ - --prefix PYTHONPATH : ${pyenv}/${pyenv.sitePackages}\ - --prefix PATH : ${verilog}/bin\ - --prefix PATH : ${quaigh}/bin\ - --prefix PATH : ${yosys}/bin\ - --set PYTHON_LIBRARY ${pyenv}/lib/lib${pyenv.libPrefix}${swiftPackages.stdenv.hostPlatform.extensions.sharedLibrary}\ - --set FAULT_IVL_BASE ${verilog}/lib/ivl - runHook postFixup - ''; - - meta = with lib; { - description = "Open-source EDA's missing DFT toolchain"; - homepage = "https://github.com/AUCOHL/Fault"; - license = licenses.asl20; - platforms = platforms.linux ++ platforms.darwin; - }; - - shellHook = finalAttrs.faultEnv + finalAttrs.preBuild; -}) + src = nix-gitignore.gitignoreSourcePure ./.gitignore ./.; + + swiftpmFlags = [ + "--verbose" + ]; + # ++ lib.lists.optional swiftPackages.stdenv.isDarwin [ + # "-Xcc" + # "-mmacosx-version-min=11" + # "-Xcc" + # "-target" + # "-Xcc" + # "x86_64-apple-macosx11" + # "-Xswiftc" + # "-target" + # "-Xswiftc" + # "x86_64-apple-macosx11" + # ]; + nativeBuildInputs = [swift swiftpm makeBinaryWrapper]; + + buildInputs = with swiftPackages; + [ + Foundation + XCTest + ] + ++ lib.lists.optional (!stdenv.isDarwin) [Dispatch]; + + nativeCheckInputs = with python3.pkgs; [ + pytest + ]; + + configurePhase = generated.configure; + + # This doesn't work on Linux otherwise and I don't know why. + preBuild = + if stdenv.isDarwin + then "" + else '' + export LD_LIBRARY_PATH=${swiftPackages.Dispatch}/lib:$LD_LIBRARY_PATH + ''; + + doCheck = true; + + faultEnv = '' + export PATH=${pyenv}/bin:${quaigh}/bin:${yosys}/bin:${verilog}/bin:$PATH + export PYTHONPATH=${pyenv}/${pyenv.sitePackages} + export PYTHON_LIBRARY=${pyenv}/lib/lib${pyenv.libPrefix}${swiftPackages.stdenv.hostPlatform.extensions.sharedLibrary} + export FAULT_IVL_BASE=${verilog}/lib/ivl + ''; + + checkPhase = '' + runHook preCheck + ${finalAttrs.faultEnv} + PYTEST_FAULT_BIN="$(swiftpmBinPath)/fault" pytest + runHook postCheck + ''; + + installPhase = '' + runHook preInstall + binPath="$(swiftpmBinPath)" + mkdir -p $out/bin + cp $binPath/fault $out/bin/fault + ln -s ${python3.pkgs.nl2bench}/bin/nl2bench $out/bin/nl2bench + runHook postInstall + ''; + + fixupPhase = '' + runHook preFixup + wrapProgram $out/bin/fault\ + --prefix PATH : ${pyenv}/bin\ + --prefix PATH : ${verilog}/bin\ + --prefix PATH : ${quaigh}/bin\ + --prefix PATH : ${yosys}/bin\ + --prefix PYTHONPATH : ${pyenv}/${pyenv.sitePackages}\ + --set PYTHON_LIBRARY ${pyenv}/lib/lib${pyenv.libPrefix}${swiftPackages.stdenv.hostPlatform.extensions.sharedLibrary}\ + --set FAULT_IVL_BASE ${verilog}/lib/ivl + runHook postFixup + ''; + + meta = with lib; { + description = "Open-source EDA's missing DFT toolchain"; + homepage = "https://github.com/AUCOHL/Fault"; + license = licenses.asl20; + platforms = platforms.linux ++ platforms.darwin; + }; + + shellHook = finalAttrs.faultEnv + finalAttrs.preBuild; + }) diff --git a/docs/Source/installation.md b/docs/Source/installation.md index 1c3fba1..492fe28 100644 --- a/docs/Source/installation.md +++ b/docs/Source/installation.md @@ -56,3 +56,4 @@ are some pointers nevertheless. - [Quaigh](https://github.com/coloquinte/quaigh) (Optional but really recommended) - [Atalanta](https://github.com/hsluoyz/atalanta) (Optional) - [NTU EE PODEM](https://github.com/donn/VLSI-Testing) (Optional) +//todo: add PodemQuest diff --git a/flake.lock b/flake.lock index e44be17..9d57cda 100644 --- a/flake.lock +++ b/flake.lock @@ -86,11 +86,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1718284527, - "narHash": "sha256-J3Enh0VOQO9kW9xjSJmCly/6K4Jma8UL/LrCJd1/tP8=", + "lastModified": 1728905391, + "narHash": "sha256-iox9yGNG4MwSKhQuwegLcDW6wVGzfdBPrh8SrhSLA8c=", "owner": "efabless", "repo": "nix-eda", - "rev": "90f2333a2dd634fa3462b6b23a8c99d4132d340e", + "rev": "0814aa6c1c7d556aa08212cc875063cff62cb9b0", "type": "github" }, "original": { @@ -120,14 +120,17 @@ "libparse": "libparse", "nix-eda": [ "nix-eda" + ], + "quaigh": [ + "quaigh" ] }, "locked": { - "lastModified": 1721923472, - "narHash": "sha256-Rvjd/0WYNTVg1hF1ODK7KagXrs4eLP/mTZROA+psWag=", + "lastModified": 1729429745, + "narHash": "sha256-dQgCxAcoEv1NTqSRpNGEE4NhNufc76Cyc3HDInqL2+0=", "owner": "donn", "repo": "nl2bench", - "rev": "4fc66fb534bff125d437f7807d1dd660a08ed03c", + "rev": "23d4e0954d56ec15a12bb27064501e93566ee8e7", "type": "github" }, "original": { @@ -145,16 +148,15 @@ ] }, "locked": { - "lastModified": 1722015420, + "lastModified": 1722162293, "narHash": "sha256-WILtL6WKXs5pB5Jujx9HIT2w1jiVTZymXC7DTuqLPEM=", - "owner": "donn", + "owner": "coloquinte", "repo": "quaigh", - "rev": "39b14e2d3e45b5e04338cf22adeede35dddc3dd6", + "rev": "2fec998178d4e48c5379dd0a2025f8688797f99a", "type": "github" }, "original": { - "owner": "donn", - "ref": "fix_nix_linux", + "owner": "coloquinte", "repo": "quaigh", "type": "github" } diff --git a/flake.nix b/flake.nix index 099b2a6..f71a029 100644 --- a/flake.nix +++ b/flake.nix @@ -1,43 +1,77 @@ { inputs = { nix-eda.url = github:efabless/nix-eda; + quaigh = { + url = github:coloquinte/quaigh; + inputs.nixpkgs.follows = "nix-eda/nixpkgs"; + }; nl2bench = { url = github:donn/nl2bench; inputs.nix-eda.follows = "nix-eda"; - }; - quaigh = { - url = github:donn/quaigh/fix_nix_linux; - inputs.nixpkgs.follows = "nix-eda/nixpkgs"; + inputs.quaigh.follows = "quaigh"; }; }; - outputs = {self, nix-eda, quaigh, nl2bench, ...}: { - packages = nix-eda.forAllSystems { current = self; withInputs = [nix-eda quaigh nl2bench]; } (util: with util; rec{ - atalanta = callPackage ./nix/atalanta.nix {}; - podem = callPackage ./nix/podem.nix {}; - fault = callPackage ./default.nix {}; - default = fault; + outputs = { + self, + nix-eda, + quaigh, + nl2bench, + ... + }: let + nixpkgs = nix-eda.inputs.nixpkgs; + lib = nixpkgs.lib; + in { + overlays = { + default = lib.composeManyExtensions [ + (nix-eda.flakesToOverlay [quaigh]) + nl2bench.overlays.default + (pkgs': pkgs: let + callPackage = lib.callPackageWith pkgs'; + in { + atalanta = callPackage ./nix/atalanta.nix {}; + podem = callPackage ./nix/podem.nix {}; + fault = callPackage ./default.nix {}; + }) + ]; + }; + + legacyPackages = nix-eda.forAllSystems ( + system: + import nixpkgs { + inherit system; + overlays = [nix-eda.overlays.default self.overlays.default]; + } + ); + + packages = nix-eda.forAllSystems (system: { + inherit (self.legacyPackages.${system}) atalanta podem fault; + default = self.packages.${system}.fault; }); - - devShells = nix-eda.forAllSystems { withInputs = [nix-eda quaigh nl2bench self]; } (util: with util; rec { - mac-testing = pkgs.stdenvNoCC.mkDerivation (with pkgs; let - pyenv = (python3.withPackages(ps: with ps; [pyverilog pyyaml pytest])); + + devShells = nix-eda.forAllSystems ( + system: let + pkgs = self.legacyPackages."${system}"; + callPackage = lib.callPackageWith pkgs; in { - # Use the host's Clang and Swift - name = "shell"; - buildInputs = [ - yosys - verilog - pkgs.quaigh - pkgs.nl2bench - pyenv - gtkwave - ]; - - PYTHON_LIBRARY="${pyenv}/lib/lib${python3.libPrefix}${stdenvNoCC.hostPlatform.extensions.sharedLibrary}"; - PYTHONPATH="${pyenv}/${pyenv.sitePackages}"; - FAULT_IVL_BASE="${verilog}/lib/ivl"; - }); - }); + mac-testing = pkgs.stdenvNoCC.mkDerivation (with pkgs; let + pyenv = python3.withPackages (ps: with ps; [pyverilog pyyaml pytest nl2bench]); + in { + # Use the host's Clang and Swift + name = "shell"; + buildInputs = [ + yosys + verilog + pkgs.quaigh + pyenv + gtkwave + ]; + + PYTHON_LIBRARY = "${pyenv}/lib/lib${python3.libPrefix}${stdenvNoCC.hostPlatform.extensions.sharedLibrary}"; + PYTHONPATH = "${pyenv}/${pyenv.sitePackages}"; + FAULT_IVL_BASE = "${verilog}/lib/ivl"; + }); + } + ); }; } diff --git a/nix/atalanta.nix b/nix/atalanta.nix index 612af38..c4bd39c 100644 --- a/nix/atalanta.nix +++ b/nix/atalanta.nix @@ -6,19 +6,19 @@ gccStdenv.mkDerivation { pname = "atalanta"; version = "2.0+"; - + src = fetchFromGitHub { owner = "hsluoyz"; repo = "atalanta"; rev = "a8e07fe4af80c55b0d4ca77e382731b03ad731dc"; sha256 = "sha256-e/E9qSPc0Pb+kLE8k169dXtAatCy8qUKHi5nNef5VUE="; }; - + installPhase = '' mkdir -p $out/bin cp atalanta $out/bin ''; - + meta = with lib; { description = "A modified ATPG (Automatic Test Pattern Generation) tool and fault simulator, orginally from VirginiaTech University."; homepage = "https://github.com/hsluoyz/Atalanta"; diff --git a/nix/podem.nix b/nix/podem.nix index 745eab9..791be30 100644 --- a/nix/podem.nix +++ b/nix/podem.nix @@ -6,27 +6,27 @@ gccStdenv.mkDerivation { name = "nctu-ee-podem"; version = "0.1.0"; - + src = fetchFromGitHub { owner = "donn"; repo = "VLSI-Testing"; rev = "ff82db776521b294d79d54acc00b7b6eaaa5846d"; sha256 = "sha256-Nj8hQb9XlRjIIrfht8VNEfORmwtb+WWrP6UVlWgo81A="; }; - + postPatch = '' sed -i 's/^LIBS.*/LIBS = /' podem/Makefile ''; - + buildPhase = '' make -C podem ''; - + installPhase = '' mkdir -p $out/bin cp podem/atpg $out/bin/atpg-podem ''; - + meta = with lib; { description = "A C++ implementation of PODEM used in the testing course of the NCTU EE program"; homepage = "https://github.com/cylinbao/VLSI-Testing"; diff --git a/requirements.txt b/requirements.txt index c994428..e73d785 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ pyverilog -nl2bench +nl2bench>=0.5.0