diff --git a/Dockerfile b/Dockerfile index 0e6332c..c28b01a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM efabless/openlane-tools:yosys-cfe940a98b08f1a5d08fb44427db155ba1f18b62-centos-7 AS yosys # --- -FROM swift:5.6-centos7 AS builder +FROM swift:5.8-centos7 AS builder # Setup Build Environment RUN yum install -y --setopt=skip_missing_names_on_install=False https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm centos-release-scl diff --git a/Package.swift b/Package.swift index ef7fa49..8717c78 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/Fault/BenchCircuit.swift b/Sources/Fault/BenchCircuit.swift index 88dfb14..e83f58a 100644 --- a/Sources/Fault/BenchCircuit.swift +++ b/Sources/Fault/BenchCircuit.swift @@ -21,6 +21,12 @@ struct BenchCircuit: Codable { init(cells: [BenchCell]) { self.cells = cells } + + static func represent(_ item: PythonObject) -> String { + return Python.type(item).__name__ == "Pointer" ? + "\(item.var)[\(item.ptr)]" : + "\(item)" + } static func extract(definitions: PythonObject) throws -> [BenchCell] { var cells: [BenchCell] = [] @@ -49,13 +55,12 @@ struct BenchCircuit: Codable { if type == "InstanceList" { let instance = item.instances[0] - let outArgname = String(describing: instance.portlist[0].argname) + let outArgname = represent(instance.portlist[0].argname) let output = (outArgname == cellOutput) ? outArgname : "__\(outArgname)___" var benchStatement = "(" for hook in instance.portlist[1...] { - let argname = String(describing: hook.argname) - + let argname = represent(hook.argname) if cellInputs.contains(argname) { benchStatement += "\(hook.argname), " } else { @@ -65,7 +70,6 @@ struct BenchCircuit: Codable { benchStatement = String(benchStatement.dropLast(2)) benchStatement += ")" - switch instance.module { case "and": cellStatements.append("\(output) = AND" + benchStatement) diff --git a/Sources/Fault/Entries/atpg.swift b/Sources/Fault/Entries/atpg.swift index 54a18ea..7ec28f1 100644 --- a/Sources/Fault/Entries/atpg.swift +++ b/Sources/Fault/Entries/atpg.swift @@ -61,7 +61,7 @@ extension Fault { @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(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.)") diff --git a/Sources/Fault/Entries/bench.swift b/Sources/Fault/Entries/bench.swift index 8a06276..a591c7d 100644 --- a/Sources/Fault/Entries/bench.swift +++ b/Sources/Fault/Entries/bench.swift @@ -72,11 +72,11 @@ extension Fault { cellDefinitions = matches.joined(separator: "\n") let folderName = "\(NSTemporaryDirectory())/thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - let _ = "mkdir -p \(folderName)".sh() - + try? FileManager.default.createDirectory(atPath: folderName, withIntermediateDirectories: true, attributes: nil) defer { - let _ = "rm -rf \(folderName)".sh() + try? FileManager.default.removeItem(atPath: folderName) } + let cellFile = "\(folderName)/cells.v" try File.open(cellFile, mode: .write) { @@ -170,7 +170,7 @@ extension Fault { for hook in instance.portlist { let portname = String(describing: hook.portname) - let argname = String(describing: hook.argname) + let argname = BenchCircuit.represent(hook.argname) if portname == cell.output { outputs.append(argname) @@ -185,13 +185,8 @@ extension Fault { usedInputs.append(contentsOf: Array(inputs.values)) } else if type == "Assign" { - let right = Python.type(item.right.var).__name__ == "Pointer" ? - "\(item.right.var.var)[\(item.right.var.ptr)]" : - "\(item.right.var)" - - let left = Python.type(item.left.var).__name__ == "Pointer" ? - "\(item.left.var.var)[\(item.left.var.ptr)]" : - "\(item.left.var)" + let right = BenchCircuit.represent(item.right.var) + let left = BenchCircuit.represent(item.left.var) if right == "1'b0" || right == "1'h0" { print("[Warning]: Constants are not recognized by atalanta. Removing \(left) associated gates and nets..") diff --git a/Sources/Fault/Simulation.swift b/Sources/Fault/Simulation.swift index fbdad0f..442ef83 100644 --- a/Sources/Fault/Simulation.swift +++ b/Sources/Fault/Simulation.swift @@ -67,7 +67,7 @@ enum Simulator { } let folderName = "\(filePrefix)/thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - _ = "mkdir -p \(folderName)".sh() + try FileManager.default.createDirectory(atPath: folderName, withIntermediateDirectories: true, attributes: nil) var inputAssignment = "" var fmtString = "" @@ -196,7 +196,7 @@ enum Simulator { let output = File.read(intermediate)! defer { if cleanUp { - _ = "rm -rf \(folderName)".sh() + try? FileManager.default.removeItem(atPath: folderName) } else { print("Find testbenches at : \(folderName)") } diff --git a/Sources/Fault/String.swift b/Sources/Fault/String.swift index 40f6d50..108d777 100644 --- a/Sources/Fault/String.swift +++ b/Sources/Fault/String.swift @@ -44,7 +44,7 @@ extension String { let task = Process() task.executableURL = URL(fileURLWithPath: "/bin/sh") task.arguments = ["-c", self] - + //print("$ \(self)") if silent { task.standardOutput = FileHandle.nullDevice task.standardError = FileHandle.nullDevice diff --git a/Sources/Fault/TVGenerator.swift b/Sources/Fault/TVGenerator.swift index 0e94130..48d41b4 100644 --- a/Sources/Fault/TVGenerator.swift +++ b/Sources/Fault/TVGenerator.swift @@ -248,13 +248,13 @@ class Atalanta: ExternalTestVectorGenerator { let tempDir = "\(NSTemporaryDirectory())" let folderName = "\(tempDir)thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - let _ = "mkdir -p '\(folderName)'".sh() + try? FileManager.default.createDirectory(atPath: folderName, withIntermediateDirectories: true, attributes: nil) defer { - let _ = "rm -rf '\(folderName)'".sh() + try? FileManager.default.removeItem(atPath: folderName) } let output = "\(folderName)/\(module).test" - let atalanta = "atalanta -t \(output) \(file) > /dev/null 2>&1".sh() + let atalanta = "atalanta -t \(output) \(file)".sh() if atalanta != EX_OK { exit(atalanta) @@ -279,9 +279,9 @@ class PODEM: ExternalTestVectorGenerator { let tempDir = "\(NSTemporaryDirectory())" let folderName = "\(tempDir)thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - let _ = "mkdir -p '\(folderName)'".sh() + try? FileManager.default.createDirectory(atPath: folderName, withIntermediateDirectories: true, attributes: nil) defer { - let _ = "rm -rf '\(folderName)'".sh() + try? FileManager.default.removeItem(atPath: folderName) } let output = "\(folderName)/\(module).out" diff --git a/default.nix b/default.nix index f3450b8..fd3c7e8 100644 --- a/default.nix +++ b/default.nix @@ -8,6 +8,7 @@ python3, yosys, verilog, + quaigh, ncurses, makeBinaryWrapper, }: @@ -42,6 +43,7 @@ stdenv.mkDerivation (finalAttrs: { pyenv yosys verilog + quaigh ]; buildInputs = with swiftPackages; [ @@ -87,5 +89,12 @@ stdenv.mkDerivation (finalAttrs: { --set FAULT_IVL_BASE ${verilog}/lib/ivl ''; + 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.preCheck + finalAttrs.preBuild; }) diff --git a/docs/Source/usage.md b/docs/Source/usage.md index 742be1b..05fe58d 100644 --- a/docs/Source/usage.md +++ b/docs/Source/usage.md @@ -77,7 +77,7 @@ is increased if sufficient coverage isn't met. This is done by the following options: ```bash -fault -v -r -m --ceiling -c +fault -v -r -m --ceiling -c --clock --reset [--ignoring [--ignoring ] ] [ --activeLow] ``` - `-v`: Number of the initially generated test vectors. @@ -96,6 +96,14 @@ fault -v -r -m --ceiling --clock --reset -l -c -o + fault chain -i --clock --reset -l -c -o ``` - `-i`: Specifies the inputs to ignore (if any) @@ -209,8 +217,9 @@ set the following options: - `-o`: Path to the output file. (Default: input + .jtag.v) - `--clock`: Clock signal of core logic to use in simulation - `--reset`: Reset signal of core logic to use in simulation. -- `--activeLow`: Reset signal of core logic is active low instead of active - high. +- `--ignoring`: Other signals to ignore +- `--activeLow`: Ignored signals (including reset) signal of core logic are held + low instead of high. - `-c`: Cell models file to verify JTAG port using given cell model. - `-l`: Path to the liberty file for resynthesis. diff --git a/flake.lock b/flake.lock index b892281..ef20829 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,64 @@ { "nodes": { + "cargo2nix": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": [ + "quaigh", + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1705129117, + "narHash": "sha256-LgdDHibvimzYhxBK3kxCk2gAL7k4Hyigl5KI0X9cijA=", + "owner": "cargo2nix", + "repo": "cargo2nix", + "rev": "ae19a9e1f8f0880c088ea155ab66cee1fa001f59", + "type": "github" + }, + "original": { + "owner": "cargo2nix", + "ref": "release-0.11.0", + "repo": "cargo2nix", + "type": "github" + } + }, + "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" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nix-eda": { "inputs": { "nixpkgs": "nixpkgs" @@ -34,9 +93,74 @@ "type": "github" } }, + "quaigh": { + "inputs": { + "cargo2nix": "cargo2nix", + "nixpkgs": [ + "nix-eda", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1719607439, + "narHash": "sha256-ZRWbrI+HCpTZAbgTjqf4IWMuUspk++DI4DGeVTBNumQ=", + "owner": "donn", + "repo": "quaigh", + "rev": "2253ddcfb8ba62683f1da54a54d06734126fb612", + "type": "github" + }, + "original": { + "owner": "donn", + "repo": "quaigh", + "type": "github" + } + }, "root": { "inputs": { - "nix-eda": "nix-eda" + "nix-eda": "nix-eda", + "quaigh": "quaigh" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "quaigh", + "cargo2nix", + "flake-utils" + ], + "nixpkgs": [ + "quaigh", + "cargo2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1705112162, + "narHash": "sha256-IAM0+Uijh/fwlfoeDrOwau9MxcZW3zeDoUHc6Z3xfqM=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "9e0af26ffe52bf955ad5575888f093e41fba0104", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 17bd330..ac52c92 100644 --- a/flake.nix +++ b/flake.nix @@ -1,10 +1,15 @@ { inputs = { nix-eda.url = github:efabless/nix-eda; + quaigh = { + url = github:donn/quaigh; + inputs.nixpkgs.follows = "nix-eda/nixpkgs"; + }; }; - outputs = {self, nix-eda, ...}: { - packages = nix-eda.forAllSystems { current = self; withInputs = [nix-eda]; } (util: with util; rec{ + outputs = {self, nix-eda, quaigh, ...}: { + packages = nix-eda.forAllSystems { current = self; withInputs = [nix-eda quaigh]; } (util: with util; rec{ + atalanta = callPackage ./nix/atalanta.nix {}; fault = callPackage ./default.nix {}; default = fault; }); diff --git a/mac-testing.nix b/mac-testing.nix index 4aab803..a1396eb 100644 --- a/mac-testing.nix +++ b/mac-testing.nix @@ -2,12 +2,12 @@ pkgs? import {} }: with pkgs; stdenvNoCC.mkDerivation { - # Use the host's Clang and Swift, they're hopelessly broken in Nix + # Use the host's Clang and Swift name = "shell"; buildInputs = [ yosys verilog - (python3.withPackages(ps: with ps; [pyverilog])) + (python3.withPackages(ps: with ps; [pyverilog pyyaml])) gtkwave ]; diff --git a/nix/atalanta.nix b/nix/atalanta.nix new file mode 100644 index 0000000..612af38 --- /dev/null +++ b/nix/atalanta.nix @@ -0,0 +1,28 @@ +{ + lib, + fetchFromGitHub, + gccStdenv, +}: +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"; + license = licenses.unfree; + platforms = platforms.linux ++ platforms.darwin; + }; +}