From 688062a5ab17e0050c9d854040a1af1ba194bef6 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 25 Jul 2024 19:27:55 +0300 Subject: [PATCH] Quaigh works! --- Sources/Fault/BenchCircuit.swift | 178 --------------------- Sources/Fault/Entries/asm.swift | 7 +- Sources/Fault/Entries/atpg.swift | 26 +-- Sources/Fault/Entries/bench.swift | 245 ----------------------------- Sources/Fault/Entries/common.swift | 6 +- Sources/Fault/Entries/main.swift | 2 +- Sources/Fault/Module.swift | 2 +- Sources/Fault/Synthesis.swift | 4 +- Sources/Fault/TestVector.swift | 13 +- Tests/RTL/spm/spm.v | 3 - Tests/test_fault.py | 107 ++++++++++--- default.nix | 2 + flake.lock | 84 +++++++++- flake.nix | 19 ++- requirements.txt | 4 +- 15 files changed, 216 insertions(+), 486 deletions(-) delete mode 100644 Sources/Fault/BenchCircuit.swift delete mode 100644 Sources/Fault/Entries/bench.swift diff --git a/Sources/Fault/BenchCircuit.swift b/Sources/Fault/BenchCircuit.swift deleted file mode 100644 index e83f58a..0000000 --- a/Sources/Fault/BenchCircuit.swift +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (C) 2019 The American University in Cairo -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Foundation -import PythonKit - -struct BenchCircuit: Codable { - var cells: [BenchCell] - - 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] = [] - for definition in definitions { - let type = Python.type(definition).__name__ - - if type == "ModuleDef" { - let (_, inputs, outputs) = try Port.extract(from: definition) - let cellName = definition.name - - var cellStatements: [String] = [] - - var cellOutput = "" - for output in outputs { - cellOutput = String(describing: output.name) - } - - var cellInputs: [String] = [] - for input in inputs { - cellInputs.append(String(describing: input.name)) - } - - for item in definition.items { - let type = Python.type(item).__name__ - - if type == "InstanceList" { - let instance = item.instances[0] - - let outArgname = represent(instance.portlist[0].argname) - let output = (outArgname == cellOutput) ? outArgname : "__\(outArgname)___" - - var benchStatement = "(" - for hook in instance.portlist[1...] { - let argname = represent(hook.argname) - if cellInputs.contains(argname) { - benchStatement += "\(hook.argname), " - } else { - benchStatement += "__\(hook.argname)___, " - } - } - - benchStatement = String(benchStatement.dropLast(2)) - benchStatement += ")" - switch instance.module { - case "and": - cellStatements.append("\(output) = AND" + benchStatement) - case "or": - cellStatements.append("\(output) = OR" + benchStatement) - case "xor": - let inputA = instance.portlist[1].argname - let inputB = instance.portlist[2].argname - cellStatements.append(contentsOf: [ - "__or_out___ = OR(\(inputA), \(inputB))", - "__nand_out___ = NAND(\(inputA), \(inputB))", - "\(output) = AND(__or_out___, __nand_out___)", - ]) - case "buf": - cellStatements.append("\(output) = BUFF" + benchStatement) - case "not": - cellStatements.append("\(output) = NOT" + benchStatement) - default: - print("[Warning]: can't expand \(instance.module) in \(cellName) to primitive cells") - } - } - } - - let cell = BenchCell( - name: String(cellName)!, - inputs: cellInputs, - output: cellOutput, - statements: cellStatements - ) - cells.append(cell) - } - } - - return cells - } -} - -struct BenchCell: Codable { - var name: String - var inputs: [String] - var output: String - var statements: [String] - - init( - name: String, - inputs: [String], - output: String, - statements: [String] - ) { - self.name = name - self.inputs = inputs - self.output = output - self.statements = statements - } - - func extract(name: String, inputs: [String: String], output: [String]) throws -> String { - do { - let regexOutput = try NSRegularExpression(pattern: "\(self.output) = ") - let regexWires = try NSRegularExpression(pattern: "___") - let outputName = (output[0].hasPrefix("\\")) ? "\\\(output[0])" : "\(output[0])" - - var benchStatements = statements - for (index, _) in statements.enumerated() { - var range = NSRange(benchStatements[index].startIndex..., in: benchStatements[index]) - benchStatements[index] = regexOutput.stringByReplacingMatches( - in: benchStatements[index], - options: [], - range: range, - withTemplate: "\(outputName) = " - ) - - range = NSRange(benchStatements[index].startIndex..., in: benchStatements[index]) - benchStatements[index] = regexWires.stringByReplacingMatches( - in: benchStatements[index], - options: [], - range: range, - withTemplate: "__\(name)" - ) - - for input in self.inputs { - let regexInput = try NSRegularExpression(pattern: "(?<=\\(|,)\\s*\(input)(?=\\s*,|\\s*\\))") - let name = (inputs[input]!.hasPrefix("\\")) ? "\\\(inputs[input]!)" : "\(inputs[input]!)" - - range = NSRange(benchStatements[index].startIndex..., in: benchStatements[index]) - benchStatements[index] = regexInput.stringByReplacingMatches( - in: benchStatements[index], - options: [], - range: NSRange(benchStatements[index].startIndex..., in: benchStatements[index]), - withTemplate: name - ) - } - } - - var cellDefinition = "" - for statement in benchStatements { - cellDefinition += "\(statement) \n" - } - cellDefinition = String(cellDefinition.dropLast(1)) - - return cellDefinition - } catch { - print("Invalid regex: \(error.localizedDescription)") - return "" - } - } -} diff --git a/Sources/Fault/Entries/asm.swift b/Sources/Fault/Entries/asm.swift index bf60b0b..070bd40 100644 --- a/Sources/Fault/Entries/asm.swift +++ b/Sources/Fault/Entries/asm.swift @@ -51,7 +51,6 @@ extension Fault { let goldenOutput = goldenOutput ?? json.replacingExtension(".tv.json", with: ".au.bin") print("Loading JSON data…") - let start = DispatchTime.now() guard let data = try? Data(contentsOf: URL(fileURLWithPath: json)) else { throw ValidationError("Failed to open test vector JSON file.") } @@ -60,10 +59,6 @@ extension Fault { guard let tvinfo = try? decoder.decode(TVInfo.self, from: data) else { throw ValidationError("Test vector JSON file is invalid.") } - let end = DispatchTime.now() - let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds - let timeInterval = Double(nanoTime) / 1_000_000_000 - print("Loaded JSON data in \(timeInterval)s.") // Extract chain metadata let (chain, _, _) = ChainMetadata.extract(file: verilog) @@ -80,7 +75,7 @@ extension Fault { // Check input order let chainOrder = order.filter { $0.kind != .bypassInput } guard chainOrder.count == jsInputOrder.count else { - throw ValidationError("Number of inputs in the JSON (\(jsInputOrder.count)) does not match scan-chain registers (\(chainOrder.count)).") + throw ValidationError("Number of inputs in the test-vector JSON file (\(jsInputOrder.count)) does not match scan-chain registers (\(chainOrder.count)): Found \(Set(chainOrder.map { $0.name }).symmetricDifference(jsInputOrder.map { $0.name })).") } for (i, input) in jsInputOrder.enumerated() { diff --git a/Sources/Fault/Entries/atpg.swift b/Sources/Fault/Entries/atpg.swift index fc21b7f..51bc6e2 100644 --- a/Sources/Fault/Entries/atpg.swift +++ b/Sources/Fault/Entries/atpg.swift @@ -197,16 +197,24 @@ extension Fault { var faultPoints: Set = [] var gateCount = 0 - var inputsMinusIgnored: [Port] = [] - if etvSetVectors.count == 0 { - inputsMinusIgnored = inputs.filter { - !bypass.bypassedInputs.contains($0.name) - } - } else { - etvSetInputs.sort { $0.ordinal < $1.ordinal } - inputsMinusIgnored = etvSetInputs.filter { - !bypass.bypassedInputs.contains($0.name) + var inputsMinusIgnored: [Port] = inputs.filter { + !bypass.bypassedInputs.contains($0.name) + } + if etvSetVectors.count > 0 { + var evtInputsMinusIgnored: [Port] = [] + var offset = 0 + for (i, input) in etvSetInputs.enumerated() { + if bypass.bypassedInputs.contains(input.name) { + for (j, _) in etvSetVectors.enumerated() { + etvSetVectors[j].remove(at: i - offset) + } + offset += 1 + } else { + evtInputsMinusIgnored.append(input) + } } + assert(inputsMinusIgnored.count == evtInputsMinusIgnored.count); + inputsMinusIgnored = evtInputsMinusIgnored } for (_, port) in ports { diff --git a/Sources/Fault/Entries/bench.swift b/Sources/Fault/Entries/bench.swift deleted file mode 100644 index 9085bc3..0000000 --- a/Sources/Fault/Entries/bench.swift +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (C) 2019-2024 The American University in Cairo -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ArgumentParser -import CoreFoundation -import Defile -import Foundation -import PythonKit -import BigInt - -extension Fault { - struct Bench: ParsableCommand { - static let configuration = CommandConfiguration( - abstract: "Generate a .bench file from Verilog models." - ) - - @Option(name: [.short, .long], help: "Path to the output file. (Default: input + .bench)") - var output: String? - - @Option(name: [.short, .long, .customLong("cellModel")], help: "Path to cell models file. (.v) files are converted to (.json). If .json is available, it could be supplied directly.") - var cellModel: String - - @Argument(help: "Verilog or JSON file.") - var file: String - - mutating func run() throws { - let fileManager = FileManager.default - - // Validate input files - 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.") - } - - let output = self.output ?? file.replacingExtension(".cut.v", with: ".bench") - - var cellModelsFile = cellModel - - // Convert .v to .json if needed - if cellModel.hasSuffix(".v") || cellModel.hasSuffix(".sv") { - print("Creating JSON for the cell models…") - cellModelsFile = "\(cellModel).json" - - // Extract cell definitions from Verilog - let cellModels = "grep -E -- \"\\bmodule\\b|\\bendmodule\\b|and|xor|or|not(\\s+|\\()|buf|input.*;|output.*;\" \(cellModel)".shOutput() - let pattern = "(?s)(?:module).*?(?:endmodule)" - - var cellDefinitions = "" - if let range = cellModels.output.range(of: pattern, options: .regularExpression) { - cellDefinitions = String(cellModels.output[range]) - } - - do { - let regex = try NSRegularExpression(pattern: pattern) - let range = NSRange(cellModels.output.startIndex..., in: cellModels.output) - let results = regex.matches(in: cellModels.output, range: range) - let matches = results.map { String(cellModels.output[Range($0.range, in: cellModels.output)!]) } - - cellDefinitions = matches.joined(separator: "\n") - - let folderName = "\(NSTemporaryDirectory())/thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - try? FileManager.default.createDirectory(atPath: folderName, withIntermediateDirectories: true, attributes: nil) - defer { - try? FileManager.default.removeItem(atPath: folderName) - } - - let cellFile = "\(folderName)/cells.v" - - try File.open(cellFile, mode: .write) { - try $0.print(cellDefinitions) - } - - // Parse using Pyverilog - let parse = Python.import("pyverilog.vparser.parser").parse - let ast = parse([cellFile])[0] - let description = ast[dynamicMember: "description"] - - let cells = try BenchCircuit.extract(definitions: description.definitions) - let circuit = BenchCircuit(cells: cells) - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - let data = try encoder.encode(circuit) - - guard let string = String(data: data, encoding: .utf8) else { - throw ValidationError("Could not create UTF-8 string.") - } - - try File.open(cellModelsFile, mode: .write) { - try $0.print(string) - } - - } catch { - throw ValidationError("Internal error: \(error)") - } - } else if !cellModel.hasSuffix(".json") { - print("Warning: Cell model file provided does not end with .v or .sv or .json. It will be treated as a JSON file.") - } - - // Process library cells - let data = try Data(contentsOf: URL(fileURLWithPath: cellModelsFile), options: .mappedIfSafe) - guard let benchCells = try? JSONDecoder().decode(BenchCircuit.self, from: data) else { - throw ValidationError("File '\(cellModel)' is invalid.") - } - - let cellsDict = benchCells.cells.reduce(into: [String: BenchCell]()) { $0[$1.name] = $1 } - - // Parse using Pyverilog - let parse = Python.import("pyverilog.vparser.parser").parse - let ast = parse([file])[0] - let description = ast[dynamicMember: "description"] - var moduleDef: PythonObject? - - for definition in description.definitions { - if Python.type(definition).__name__ == "ModuleDef" { - moduleDef = definition - break - } - } - - guard let definition = moduleDef else { - throw ValidationError("No module found.") - } - - let (_, inputs, outputs) = try Port.extract(from: definition) - - var allWires: Set = [] - var usedWires: Set = [] - var benchStatements = "" - - for input in inputs { - if input.width > 1 { - for index in input.bits { - let name = "\(input.name)[\(index)]" - allWires.insert(name) - benchStatements += "INPUT(\(name)) \n" - } - } else { - let name = input.name - allWires.insert(name) - benchStatements += "INPUT(\(name)) \n" - } - } - - for item in definition.items { - let type = Python.type(item).__name__ - - if type == "InstanceList" { - let instance = item.instances[0] - let cellName = String(describing: instance.module) - let instanceName = String(describing: instance.name) - let cell = cellsDict[cellName]! - - var inputs: [String: String] = [:] - var outputs: [String] = [] - - for hook in instance.portlist { - let portname = String(describing: hook.portname) - let type = Python.type(hook.argname).__name__ - - if portname == cell.output { - if type == "Pointer" { - outputs.append("\(hook.argname.var)[\(hook.argname.ptr)]") - } else { - outputs.append(String(describing: hook.argname)) - } - } else { - if type == "Pointer" { - inputs[portname] = "\(hook.argname.var)[\(hook.argname.ptr)]" - } else { - inputs[portname] = String(describing: hook.argname) - } - } - } - - let statements = try cell.extract(name: instanceName, inputs: inputs, output: outputs) - benchStatements += "\(statements) \n" - - for used in inputs.values { - usedWires.insert(used) - } - for new: String in outputs { - allWires.insert(new) - } - - } else if type == "Assign" { - 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 supported in the bench format.") - } else { - let statement = "\(left) = BUFF(\(right)) \n" - benchStatements += statement - - usedWires.insert(right) - } - } - } - - for output in outputs { - if output.width > 1 { - for index in output.bits { - let name = "\(output.name)[\(index)]" - usedWires.insert(name) - benchStatements += "OUTPUT(\(name)) \n" - } - } else { - let name = output.name - usedWires.insert(name) - benchStatements += "OUTPUT(\(name)) \n" - } - } - - // for unused in allWires.subtracting(usedWires) { - // benchStatements += "OUTPUT(\(unused))\n" - // } - - let boilerplate = """ - # Bench for \(definition.name) - # Automatically generated by Fault. - # Don't modify. \n - """ - - try File.open(output, mode: .write) { - try $0.print(boilerplate) - try $0.print(benchStatements) - } - - print("Benchmark file generated successfully at \(output).") - } - } -} diff --git a/Sources/Fault/Entries/common.swift b/Sources/Fault/Entries/common.swift index cb6b3b0..5133516 100644 --- a/Sources/Fault/Entries/common.swift +++ b/Sources/Fault/Entries/common.swift @@ -27,7 +27,7 @@ struct BypassOptions: ParsableArguments { var clock: String @Option(name: [.customLong("reset")], help: "Reset name. In addition to being bypassed for certain manipulation operations, during simulations it will always be held low.") - var resetName: String + var resetName: String = "rst" @Flag(name: [.long, .customLong("activeLow")], help: "The reset signal is considered active-low insted, and will be held high during simulations.") var resetActiveLow: Bool = false @@ -43,9 +43,9 @@ struct BypassOptions: ParsableArguments { for bypassed in bypassing { let split = bypassed.components(separatedBy: "=") if split.count == 1 { - result[bypassed] = .holdHigh + result[split[0]] = .holdHigh } else if split.count == 2 { - result[bypassed] = split[1] == "0" ? .holdLow : .holdHigh + result[split[0]] = split[1] == "0" ? .holdLow : .holdHigh } } return result diff --git a/Sources/Fault/Entries/main.swift b/Sources/Fault/Entries/main.swift index 349b3b8..bd6f6e7 100644 --- a/Sources/Fault/Entries/main.swift +++ b/Sources/Fault/Entries/main.swift @@ -74,7 +74,7 @@ let pythonVersions = { struct Fault: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Open-source EDA's missing DFT Toolchain", - subcommands: [ATPG.self, Cut.self, Synth.self, Assemble.self, Tap.self, Bench.self, Chain.self], + subcommands: [ATPG.self, Cut.self, Synth.self, Assemble.self, Tap.self, Chain.self], defaultSubcommand: ATPG.self ) } diff --git a/Sources/Fault/Module.swift b/Sources/Fault/Module.swift index 5e0fe94..846bb2c 100644 --- a/Sources/Fault/Module.swift +++ b/Sources/Fault/Module.swift @@ -147,7 +147,7 @@ struct Port: Codable { extension Port: CustomStringConvertible { var description: String { - "Port(\(name): \(polarity ?? .unknown)[\(from)..\(to)])" + "Port@\(ordinal)(\(name): \(polarity ?? .unknown)[\(from)..\(to)])" } } diff --git a/Sources/Fault/Synthesis.swift b/Sources/Fault/Synthesis.swift index dfb9d28..01d47b2 100644 --- a/Sources/Fault/Synthesis.swift +++ b/Sources/Fault/Synthesis.swift @@ -70,8 +70,8 @@ enum Synthesis { # names # autoname - write_verilog -noexpr \(output)+attrs - write_verilog -noexpr -noattr \(output) + 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/TestVector.swift b/Sources/Fault/TestVector.swift index d9035f0..46fae12 100644 --- a/Sources/Fault/TestVector.swift +++ b/Sources/Fault/TestVector.swift @@ -15,6 +15,7 @@ import BigInt import Defile import Foundation +import Collections typealias TestVector = [BigUInt] @@ -124,17 +125,19 @@ enum TVSet { } static func readFromTest(_ file: String, withInputsFrom bench: String) throws -> ([TestVector], [Port]) { - var inputs: [Port] = [] + var inputDict: OrderedDictionary = [:] let benchStr = File.read(bench)! - let inputRx = #/INPUT\((\w+)\)/# - var ordinal = 0 + let inputRx = #/INPUT\(([^\(\)]+?)(\[\d+\])?\)/# + var ordinal = -1 for line in benchStr.components(separatedBy: "\n") { if let match = try? inputRx.firstMatch(in: line) { - inputs.append(Port(name: String(match.1), at: ordinal)) - ordinal += 1 + let name = String(match.1) + inputDict[name] = inputDict[name] ?? Port(name: name, polarity: .input, from: 0, to: -1, at: { ordinal += 1; return ordinal }()) + inputDict[name]!.to += 1 } } + let inputs: [Port] = inputDict.values.sorted { $0.ordinal < $1.ordinal } var vectors: [TestVector] = [] let testStr = File.read(file)! let tvRx = #/(\d+):\s*([01]+)/# diff --git a/Tests/RTL/spm/spm.v b/Tests/RTL/spm/spm.v index be02ce3..acd221a 100644 --- a/Tests/RTL/spm/spm.v +++ b/Tests/RTL/spm/spm.v @@ -22,7 +22,6 @@ module spm #(parameter bits=32) ( input x, input[bits-1: 0] a, output y, - output always_high, ); wire[bits: 0] y_chain; assign y_chain[0] = 0; @@ -43,8 +42,6 @@ module spm #(parameter bits=32) ( .y_in(y_chain[bits-1:0]), .y_out(y_chain[bits:1]) ); - - assign always_high = 1'b1; endmodule diff --git a/Tests/test_fault.py b/Tests/test_fault.py index 1427b1f..8eb8944 100644 --- a/Tests/test_fault.py +++ b/Tests/test_fault.py @@ -1,31 +1,29 @@ import os +import shlex import pytest import subprocess def run(label, steps): for i, step in enumerate(steps): - print(f"\n=== {label}: Step {i + 1}/6 ===\n") - subprocess.check_call(["swift", "run", "fault"] + step) + print(f"\n=== {label}: Step {i + 1}/{len(steps)} ===\n") + if step[0] == "nl2bench": + cmd = step + else: + cmd = ["swift", "run", "fault"] + step + print(f"$ {shlex.join(cmd)}") + subprocess.check_call(cmd) @pytest.mark.parametrize( - "liberty,models,config,atpg", + "liberty,models,config", [ pytest.param( "Tech/osu035/osu035_stdcells.lib", "Tech/osu035/osu035_stdcells.v", "Tech/osu035/config.yml", - None, id="osu035", ), - pytest.param( - "Tech/osu035/osu035_stdcells.lib", - "Tech/osu035/osu035_stdcells.v", - "Tech/osu035/config.yml", - "Quaigh", - id="osu035/quaigh", - ), # ( # "Tech/sky130_fd_sc_hd/sky130_fd_sc_hd__trimmed.lib", # "Tech/sky130_fd_sc_hd/sky130_fd_sc_hd.v", @@ -34,21 +32,68 @@ def run(label, steps): # ), ], ) -def test_spm(request, liberty, models, config, atpg): - fileName = "Tests/RTL/spm/spm.v" - topModule = "spm" - clock = "clk" - reset = "rst" - +@pytest.mark.parametrize("atpg", ["PRNG", "Quaigh"]) +@pytest.mark.parametrize( + "fileName,topModule,clock,reset,other_bypassed,activeLow", + [ + pytest.param( + "Tests/RTL/spm/spm.v", + "spm", + "clk", + "rst", + [], + True, + id="spm", + ), + pytest.param( + "Benchmarks/ISCAS_89/s27.v", + "s27", + "CK", + "reset", + ["VDD=1", "GND=0"], + False, + id="s27", + ), + # pytest.param( + # "Benchmarks/ISCAS_89/s344.v", + # "s344", + # "CK", + # None, + # ["VDD=1", "GND=0"], + # False, + # id="s344", + # ), + ], +) +def test_flat( + request, + liberty, + models, + config, + fileName, + topModule, + clock, + reset, + other_bypassed, + activeLow, + atpg, +): base = os.path.splitext("Netlists/" + fileName)[0] fileSynth = base + ".nl.v" fileCut = base + ".cut.v" fileJson = base + ".tv.json" + fileBench = base + ".bench" fileChained = base + ".chained.v" fileAsmVec = base + ".tv.bin" fileAsmOut = base + ".au.bin" - bypassOptions = ["--clock", clock, "--reset", reset, "--activeLow"] + bypassOptions = ["--clock", clock] + (["--activeLow"] if activeLow else []) + if reset is not None: + bypassOptions.append("--reset") + bypassOptions.append(reset) + for bypassed in other_bypassed: + bypassOptions.append("--bypassing") + bypassOptions.append(bypassed) for file in [fileSynth, fileCut, fileJson, fileChained, fileAsmVec, fileAsmOut]: try: @@ -56,13 +101,35 @@ def test_spm(request, liberty, models, config, atpg): except OSError: pass + atpg_options = [] + if atpg != "PRNG": + atpg_options = ["-g", atpg, "-b", fileBench] + run( request.node.name, [ ["synth", "-l", liberty, "-t", topModule, "-o", fileSynth, fileName], ["cut", "-o", fileCut, "--sclConfig", config, fileSynth] + bypassOptions, - ["bench", fileCut, "--sclConfig", config, fileSynth] + bypassOptions, - ["-c", models, "-o", fileJson, fileCut] + bypassOptions, + [ + "nl2bench", + "-o", + fileBench, + "-l", + liberty, + fileCut, + ], + [ + "atpg", + "-c", + models, + "-o", + fileJson, + fileCut, + "--output-coverage-metadata", + base + f".{atpg}.coverage.yml", + ] + + bypassOptions + + atpg_options, [ "chain", "-c", diff --git a/default.nix b/default.nix index 9b2223f..7426384 100644 --- a/default.nix +++ b/default.nix @@ -9,6 +9,7 @@ yosys, verilog, quaigh, + nl2bench, ncurses, makeBinaryWrapper, }: @@ -44,6 +45,7 @@ stdenv.mkDerivation (finalAttrs: { yosys verilog quaigh + nl2bench ]; buildInputs = with swiftPackages; [ diff --git a/flake.lock b/flake.lock index ef20829..39efd17 100644 --- a/flake.lock +++ b/flake.lock @@ -59,6 +59,28 @@ "type": "github" } }, + "libparse": { + "inputs": { + "nixpkgs": [ + "nl2bench", + "nix-eda", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1713178934, + "narHash": "sha256-1w6HBBE2bWAD0GM98O8WZRmZDW9+EzD0KFvnnH2ho/k=", + "owner": "efabless", + "repo": "libparse-python", + "rev": "cec8b6dfd3d1c97bc5ccd1d0a41c44e51bc9c1eb", + "type": "github" + }, + "original": { + "owner": "efabless", + "repo": "libparse-python", + "type": "github" + } + }, "nix-eda": { "inputs": { "nixpkgs": "nixpkgs" @@ -77,6 +99,24 @@ "type": "github" } }, + "nix-eda_2": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1719830786, + "narHash": "sha256-meVPi2d6TI44FjuqQc57Ve3UV1GP12uDVrRfHcACBdg=", + "owner": "efabless", + "repo": "nix-eda", + "rev": "8fd46e08259a761d5078c6c3b8b19dd58bb69e75", + "type": "github" + }, + "original": { + "owner": "efabless", + "repo": "nix-eda", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1717144377, @@ -93,6 +133,41 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717144377, + "narHash": "sha256-F/TKWETwB5RaR8owkPPi+SPJh83AQsm6KrQAlJ8v/uA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "805a384895c696f802a9bf5bf4720f37385df547", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nl2bench": { + "inputs": { + "libparse": "libparse", + "nix-eda": "nix-eda_2" + }, + "locked": { + "lastModified": 1721923472, + "narHash": "sha256-Rvjd/0WYNTVg1hF1ODK7KagXrs4eLP/mTZROA+psWag=", + "owner": "donn", + "repo": "nl2bench", + "rev": "4fc66fb534bff125d437f7807d1dd660a08ed03c", + "type": "github" + }, + "original": { + "owner": "donn", + "repo": "nl2bench", + "type": "github" + } + }, "quaigh": { "inputs": { "cargo2nix": "cargo2nix", @@ -102,15 +177,15 @@ ] }, "locked": { - "lastModified": 1719607439, + "lastModified": 1721137344, "narHash": "sha256-ZRWbrI+HCpTZAbgTjqf4IWMuUspk++DI4DGeVTBNumQ=", - "owner": "donn", + "owner": "coloquinte", "repo": "quaigh", - "rev": "2253ddcfb8ba62683f1da54a54d06734126fb612", + "rev": "1b690ebece60a0181df7af22546f13427914cf82", "type": "github" }, "original": { - "owner": "donn", + "owner": "coloquinte", "repo": "quaigh", "type": "github" } @@ -118,6 +193,7 @@ "root": { "inputs": { "nix-eda": "nix-eda", + "nl2bench": "nl2bench", "quaigh": "quaigh" } }, diff --git a/flake.nix b/flake.nix index fe88f9f..5dc6505 100644 --- a/flake.nix +++ b/flake.nix @@ -1,33 +1,38 @@ { inputs = { nix-eda.url = github:efabless/nix-eda; + nl2bench.url = github:donn/nl2bench; quaigh = { - url = github:donn/quaigh; + url = github:coloquinte/quaigh; inputs.nixpkgs.follows = "nix-eda/nixpkgs"; }; }; - outputs = {self, nix-eda, quaigh, ...}: { - packages = nix-eda.forAllSystems { current = self; withInputs = [nix-eda quaigh]; } (util: with util; rec{ + 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; }); - devShells = nix-eda.forAllSystems { withInputs = [nix-eda quaigh self]; } (util: with util; rec { - mac-testing = pkgs.stdenvNoCC.mkDerivation (with pkgs; { + 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])); + in { # Use the host's Clang and Swift name = "shell"; buildInputs = [ yosys verilog pkgs.quaigh - (python3.withPackages(ps: with ps; [pyverilog pyyaml pytest])) + pkgs.nl2bench + pyenv gtkwave ]; - PYTHON_LIBRARY="${python3}/lib/lib${python3.libPrefix}${stdenvNoCC.hostPlatform.extensions.sharedLibrary}"; + PYTHON_LIBRARY="${pyenv}/lib/lib${python3.libPrefix}${stdenvNoCC.hostPlatform.extensions.sharedLibrary}"; + PYTHONPATH="${pyenv}/${pyenv.sitePackages}"; FAULT_IVL_BASE="${verilog}/lib/ivl"; }); }); diff --git a/requirements.txt b/requirements.txt index bf4fe73..c994428 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -https://github.com/PyHDI/Pyverilog/archive/refs/tags/1.3.0.zip -find_libpython \ No newline at end of file +pyverilog +nl2bench