From fd28467e116a1a7300f142568b9bd52d0d45e5b3 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 1 Jan 2024 11:34:46 +0200 Subject: [PATCH 1/5] Add PDK Config YAML + Up requirement to Swift 5.4/macOS 12 --- .gitignore | 5 +- Package.resolved | 9 ++++ Package.swift | 11 +++-- Readme.md | 2 +- Sources/Fault/Entries/chain.swift | 76 +++++++++++++++++++++++-------- Sources/PDKConfiguration.swift | 44 ++++++++++++++++++ Tech/osu035/pdk.yml | 12 +++++ shell.nix | 15 ++++++ 8 files changed, 148 insertions(+), 26 deletions(-) create mode 100644 Sources/PDKConfiguration.swift create mode 100644 Tech/osu035/pdk.yml create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index 9d53407..7aac6a1 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ Netlists *.test *.log *.vvp -parsetab.py \ No newline at end of file +parsetab.py + +.swiftpm/ +abc.history diff --git a/Package.resolved b/Package.resolved index 957fd8a..d507c3f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -45,6 +45,15 @@ "revision": "c77231820148515a77e9bba5016a57e5a88ef007", "version": null } + }, + { + "package": "Yams", + "repositoryURL": "https://github.com/jpsim/Yams.git", + "state": { + "branch": null, + "revision": "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", + "version": "5.0.6" + } } ] }, diff --git a/Package.swift b/Package.swift index 0812f75..37dc7b5 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.4 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "Fault", platforms: [ - .macOS(.v10_13) // executableURL and a bunch of other things are not available before High Sierra + .macOS(.v11) // executableURL and a bunch of other things are not available before High Sierra ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -15,14 +15,15 @@ let package = Package( .package(url: "https://github.com/pvieito/PythonKit", .branch("master")), .package(url: "https://github.com/pvieito/CommandLineKit", .branch("master")), .package(url: "https://github.com/donn/Defile.git", from: "5.2.1"), - .package(url: "https://github.com/attaswift/BigInt.git", from: "5.2.1") + .package(url: "https://github.com/attaswift/BigInt.git", from: "5.2.1"), + .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. - .target( + .executableTarget( name: "Fault", - dependencies: ["PythonKit", "CommandLineKit", "Defile", "OrderedDictionary", "BigInt"], + dependencies: ["PythonKit", "CommandLineKit", "Defile", "OrderedDictionary", "BigInt", "Yams"], path: "Sources" ), .testTarget( diff --git a/Readme.md b/Readme.md index 767b08e..f74ec70 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,5 @@ # 🧪 Fault -![Swift 5.2+](https://img.shields.io/badge/Swift-5.2-orange?logo=swift) ![Docker Image Available for x86-64](https://img.shields.io/static/v1?logo=docker&label=docker&message=x86_64) ![AppImage Available for Linux x86-64](https://img.shields.io/static/v1?label=appimage&message=x86_64&color=blue) +![Swift 5.4+](https://img.shields.io/badge/Swift-5.4-orange?logo=swift) ![Docker Image Available for x86-64](https://img.shields.io/static/v1?logo=docker&label=docker&message=x86_64) ![AppImage Available for Linux x86-64](https://img.shields.io/static/v1?label=appimage&message=x86_64&color=blue) Fault is a complete open source design for testing (DFT) Solution that includes automatic test pattern generation for netlists, scan chain stitching, synthesis scripts and a number of other convenience features. diff --git a/Sources/Fault/Entries/chain.swift b/Sources/Fault/Entries/chain.swift index 329677b..8778bcf 100644 --- a/Sources/Fault/Entries/chain.swift +++ b/Sources/Fault/Entries/chain.swift @@ -2,6 +2,18 @@ import Foundation import CommandLineKit import PythonKit import Defile +import Yams + +func getMatchingDFFInfo(from list: [DFFMatch], for cell: String, fnmatch: PythonObject) -> DFFMatch? { + for dffinfo in list { + for name in dffinfo.name.components(separatedBy: ",") { + if Bool(fnmatch.fnmatch(cell, name))! { + return dffinfo + } + } + } + return nil +} func scanChainCreate(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -42,7 +54,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let clockInv = StringOption( longFlag: "invClock", - helpMessage: "Inverted clk tree source cell name (Default: none)" + helpMessage: "Inverter clk tree source cell name (Default: none)" ) cli.addOptions(clockInv) @@ -65,13 +77,21 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) cli.addOptions(liberty) + + let pdkConfigOpt = StringOption( + shortFlag: "p", + longFlag: "pdkConfig", + helpMessage: "Name for the YAML PDK config file. Recommended." + ) + cli.addOptions(pdkConfigOpt) + let dffOpt = StringOption( shortFlag: "d", longFlag: "dff", - helpMessage: "Flip-flop cell names ,comma,seperated (Default: DFFSR,DFFNEGX1,DFFPOSX1)" + helpMessage: "Optional override for the DFF names from the PDK config." ) cli.addOptions(dffOpt) - + let isolated = StringOption( longFlag: "isolating", helpMessage: "Isolated module definitions (.v) (Hard un-scannable blocks). (Default: none)" @@ -160,7 +180,22 @@ func scanChainCreate(arguments: [String]) -> Int32 { Stderr.print("Invoke fault chain --help for more info.") return EX_USAGE } - + + var pdkConfig: PDKConfiguration = PDKConfiguration(dffMatches: [DFFMatch(name: "DFFSR,DFFNEGX1,DFFPOSX1", clk: "CLK", d: "D", q: "Q")]) + if let pdkConfigPath = pdkConfigOpt.value { + let pdkConfigYML = File.read(pdkConfigPath)! + let decoder = YAMLDecoder() + do { + pdkConfig = try decoder.decode(PDKConfiguration.self, from: pdkConfigYML) + } catch { + Stderr.print("Invalid YAML file \(pdkConfigPath): \(error).") + return EX_DATAERR + } + } + if let dffOverride = dffOpt.value { + pdkConfig.dffMatches.last!.name = dffOverride + } + if !fileManager.fileExists(atPath: libertyFile) { Stderr.print("Liberty file '\(libertyFile)' not found.") return EX_NOINPUT @@ -311,7 +346,9 @@ func scanChainCreate(arguments: [String]) -> Int32 { definition.portlist.ports = Python.tuple(ports) var wireDeclarations: [PythonObject] = [] - var wrapperCells: [PythonObject] = [] + var cellDeclarations: [PythonObject] = [] + + let fnmatch = Python.import("fnmatch") var warn = false var blackbox = false @@ -322,9 +359,10 @@ func scanChainCreate(arguments: [String]) -> Int32 { if type == "InstanceList" { let instance = itemDeclaration.instances[0] let instanceName = String(describing: instance.module) - if dffNames.contains(instanceName) { + if let dffinfo = getMatchingDFFInfo(from: pdkConfig.dffMatches, for: instanceName, fnmatch: fnmatch) { for hook in instance.portlist { - if hook.portname == "CLK" { + let portnameStr = String(describing: hook.portname) + if portnameStr == dffinfo.clk { if String(describing: hook.argname) == clockName { hook.argname = clkSourceId } @@ -332,7 +370,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { warn = true } } - if hook.portname == "D" { + if portnameStr == dffinfo.d { let ternary = Node.Cond( shiftIdentifier, previousOutput, @@ -341,7 +379,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { hook.argname = ternary } - if hook.portname == "Q" { + if portnameStr == dffinfo.q { previousOutput = hook.argname } } @@ -389,10 +427,10 @@ func scanChainCreate(arguments: [String]) -> Int32 { let doutName = elementName + "_\(i)" + "__dout" let doutStatement = Node.Wire(doutName) - wrapperCells.append(doutStatement) + cellDeclarations.append(doutStatement) list.append(Node.Identifier(doutName)) - wrapperCells.append( + cellDeclarations.append( scCreator.create( ordinal: 0, max: 0, @@ -409,10 +447,10 @@ func scanChainCreate(arguments: [String]) -> Int32 { let dinName = elementName + "_\(i)" + "__din" let dinStatement = Node.Wire(dinName) - wrapperCells.append(dinStatement) + cellDeclarations.append(dinStatement) list.append(Node.Identifier(dinName)) - wrapperCells.append( + cellDeclarations.append( scCreator.create( ordinal: 0, max: 0, @@ -451,7 +489,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { statements.append(doutStatement) hook.argname = Node.Identifier(doutName) - wrapperCells.append( + cellDeclarations.append( scCreator.create( ordinal: 0, max: 0, @@ -467,10 +505,10 @@ func scanChainCreate(arguments: [String]) -> Int32 { let dinName = argName + "__din" let dinStatement = Node.Wire(dinName) - wrapperCells.append(dinStatement) + cellDeclarations.append(dinStatement) hook.argname = Node.Identifier(dinName) - wrapperCells.append( + cellDeclarations.append( scCreator.create( ordinal: 0, max: 0, @@ -557,10 +595,10 @@ func scanChainCreate(arguments: [String]) -> Int32 { } if let item = blackboxItem { - wrapperCells.append(item) + cellDeclarations.append(item) } - definition.items = Python.tuple(statements + wireDeclarations + wrapperCells + assignStatements) + definition.items = Python.tuple(statements + wireDeclarations + cellDeclarations + assignStatements) definition.name = Python.str(alteredName) print("Internal scan chain successfuly constructed. Length: " , internalOrder.count) @@ -902,4 +940,4 @@ func scanChainCreate(arguments: [String]) -> Int32 { return EX_SOFTWARE } return EX_OK -} \ No newline at end of file +} diff --git a/Sources/PDKConfiguration.swift b/Sources/PDKConfiguration.swift new file mode 100644 index 0000000..98208b0 --- /dev/null +++ b/Sources/PDKConfiguration.swift @@ -0,0 +1,44 @@ + +class DFFMatch: Codable, CustomStringConvertible { + var name: String + var clk: String + var d: String + var q: String + + init(name: String, clk: String, d: String, q: String) { + self.name = name + self.clk = clk + self.d = d + self.q = q + } + + var description: String { + return " \(self.q)>" + } +} + +class MuxInfo: Codable { + var name: String + var a: String + var b: String + var y: String + var s: String + + init(name: String, a: String, b: String, y: String, s: String) { + self.name = name + self.a = a + self.b = b + self.y = y + self.s = s + } +} + +class PDKConfiguration: Codable { + var dffMatches: [DFFMatch] + var muxInfo: MuxInfo? + + init(dffMatches: [DFFMatch], muxInfo: MuxInfo? = nil) { + self.dffMatches = dffMatches + self.muxInfo = muxInfo + } +} diff --git a/Tech/osu035/pdk.yml b/Tech/osu035/pdk.yml new file mode 100644 index 0000000..c0b7100 --- /dev/null +++ b/Tech/osu035/pdk.yml @@ -0,0 +1,12 @@ +dffMatches: + # Default match should be the last element + - name: DFFSR,DFFNEGX1,DFFPOSX1 + d: D + q: Q + clk: CLK +muxInfo: + name: MUX2X1 + a: A + b: B + s: S + "y": "Y" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..58c8136 --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +{ + pkgs? import {} +}: +with pkgs; stdenvNoCC.mkDerivation { + # Use the host's Clang and Swift, they're hopelessly broken in Nix + name = "shell"; + buildInputs = [ + yosys + verilog + (python3.withPackages(ps: with ps; [pyverilog])) + ]; + + PYTHON_LIBRARY="${python3}/lib/lib${python3.libPrefix}${stdenvNoCC.hostPlatform.extensions.sharedLibrary}"; + FAULT_IVL_BASE="${verilog}/lib/ivl"; +} From f61eb99e78f8993058cd5eaa9087a4fa38493c6b Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 1 Jan 2024 13:40:36 +0200 Subject: [PATCH 2/5] Add Mux + SwiftFormat --- .swiftversion | 1 + Contributing.md | 10 +- Package.swift | 6 +- Sources/Fault/Bench.swift | 67 ++++--- Sources/Fault/BoundaryScanRegister.swift | 44 ++-- Sources/Fault/Compaction.swift | 97 +++++---- Sources/Fault/Entries/asm.swift | 61 +++--- Sources/Fault/Entries/bench.swift | 147 +++++++------- Sources/Fault/Entries/chain.swift | 244 +++++++++++++---------- Sources/Fault/Entries/compact.swift | 30 ++- Sources/Fault/Entries/cut.swift | 63 +++--- Sources/Fault/Entries/jtag.swift | 149 ++++++++------ Sources/Fault/Entries/main.swift | 115 ++++++----- Sources/Fault/Entries/synth.swift | 24 ++- Sources/Fault/Future.swift | 16 +- Sources/Fault/JTAG.swift | 46 +++-- Sources/Fault/JTAGStrings.swift | 26 ++- Sources/Fault/Metadata.swift | 33 ++- Sources/Fault/Mux.swift | 66 ++++++ Sources/Fault/Port.swift | 58 ++++-- Sources/Fault/RNGFactory.swift | 22 +- Sources/Fault/SCLConfiguration.swift | 59 ++++++ Sources/Fault/SerialVectorFormat.swift | 30 ++- Sources/Fault/Simulation.swift | 233 +++++++++++----------- Sources/Fault/String.swift | 26 ++- Sources/Fault/Synthesis.swift | 20 +- Sources/Fault/TVGenerator.swift | 65 +++--- Sources/Fault/TestVector.swift | 84 ++++---- Sources/PDKConfiguration.swift | 44 ---- Tech/osu035/{pdk.yml => config.yml} | 1 + Tests/FaultTests/FaultTests.swift | 47 ++--- Tests/FaultTests/XCTestManifests.swift | 10 +- atalanta_podem_build.swift | 12 +- install.swift | 12 +- 34 files changed, 1177 insertions(+), 791 deletions(-) create mode 100644 .swiftversion create mode 100644 Sources/Fault/Mux.swift create mode 100644 Sources/Fault/SCLConfiguration.swift delete mode 100644 Sources/PDKConfiguration.swift rename Tech/osu035/{pdk.yml => config.yml} (88%) diff --git a/.swiftversion b/.swiftversion new file mode 100644 index 0000000..37c2d99 --- /dev/null +++ b/.swiftversion @@ -0,0 +1 @@ +5.4 diff --git a/Contributing.md b/Contributing.md index 497aad4..2809020 100644 --- a/Contributing.md +++ b/Contributing.md @@ -15,13 +15,7 @@ Make your changes and then submit them as a pull requests to the `main` branch. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. -## The Approval Process -For a PR to be merged, there are two requirements: - -- It must pass all automated checks. -- An OpenLane team member must inspect and approve the PR. - # Licensing and Copyright -Please add you (or your employer's) copyright headers to any files to which you have made major edits. +Please add your (or your employer's) copyright headers to any files to which you have made major edits. -Please note all code contributions must have the same license as Fault, i.e., the Apache License, version 2.0. \ No newline at end of file +Please note all code contributions must have the same license as Fault, i.e., the Apache License, version 2.0. diff --git a/Package.swift b/Package.swift index 37dc7b5..a82b775 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "Fault", platforms: [ - .macOS(.v11) // executableURL and a bunch of other things are not available before High Sierra + .macOS(.v11), // executableURL and a bunch of other things are not available before High Sierra ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -16,7 +16,7 @@ let package = Package( .package(url: "https://github.com/pvieito/CommandLineKit", .branch("master")), .package(url: "https://github.com/donn/Defile.git", from: "5.2.1"), .package(url: "https://github.com/attaswift/BigInt.git", from: "5.2.1"), - .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6") + .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -29,6 +29,6 @@ let package = Package( .testTarget( name: "FaultTests", dependencies: ["Fault"] - ) + ), ] ) diff --git a/Sources/Fault/Bench.swift b/Sources/Fault/Bench.swift index e3b8ead..88dfb14 100644 --- a/Sources/Fault/Bench.swift +++ b/Sources/Fault/Bench.swift @@ -1,28 +1,39 @@ +// 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]){ + init(cells: [BenchCell]) { self.cells = cells } 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: String = "" + + var cellOutput = "" for output in outputs { cellOutput = String(describing: output.name) } @@ -33,23 +44,21 @@ struct BenchCircuit: Codable { } for item in definition.items { - let type = Python.type(item).__name__ if type == "InstanceList" { let instance = item.instances[0] - let outArgname = String(describing: instance.portlist[0].argname) - let output = (outArgname == cellOutput) ? outArgname : "__\(outArgname)___" - + let outArgname = String(describing: instance.portlist[0].argname) + let output = (outArgname == cellOutput) ? outArgname : "__\(outArgname)___" + var benchStatement = "(" - for hook in instance.portlist[1...]{ + for hook in instance.portlist[1...] { let argname = String(describing: hook.argname) - + if cellInputs.contains(argname) { benchStatement += "\(hook.argname), " - } - else { + } else { benchStatement += "__\(hook.argname)___, " } } @@ -60,25 +69,20 @@ struct BenchCircuit: Codable { switch instance.module { case "and": cellStatements.append("\(output) = AND" + benchStatement) - break case "or": cellStatements.append("\(output) = OR" + benchStatement) - break 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___)" + "\(output) = AND(__or_out___, __nand_out___)", ]) - break case "buf": cellStatements.append("\(output) = BUFF" + benchStatement) - break case "not": cellStatements.append("\(output) = NOT" + benchStatement) - break default: print("[Warning]: can't expand \(instance.module) in \(cellName) to primitive cells") } @@ -97,7 +101,6 @@ struct BenchCircuit: Codable { return cells } - } struct BenchCell: Codable { @@ -111,46 +114,48 @@ struct BenchCell: Codable { 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 { + 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 = self.statements + 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) = ") + withTemplate: "\(outputName) = " + ) - range = NSRange(benchStatements[index].startIndex..., in: benchStatements[index]) + range = NSRange(benchStatements[index].startIndex..., in: benchStatements[index]) benchStatements[index] = regexWires.stringByReplacingMatches( in: benchStatements[index], options: [], range: range, - withTemplate: "__\(name)") + 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]) + 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 ) + withTemplate: name + ) } } @@ -166,4 +171,4 @@ struct BenchCell: Codable { return "" } } -} \ No newline at end of file +} diff --git a/Sources/Fault/BoundaryScanRegister.swift b/Sources/Fault/BoundaryScanRegister.swift index 544ec95..fa1dbf2 100644 --- a/Sources/Fault/BoundaryScanRegister.swift +++ b/Sources/Fault/BoundaryScanRegister.swift @@ -1,3 +1,17 @@ +// 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 @@ -6,7 +20,7 @@ class BoundaryScanRegisterCreator { private var inputName: String private var outputName: String var counter: Int = 0 - + var clock: String var reset: String var resetActive: Simulator.Active @@ -30,22 +44,22 @@ class BoundaryScanRegisterCreator { using Node: PythonObject ) { self.name = name - self.inputName = "\(name)_input" - self.outputName = "\(name)_output" + inputName = "\(name)_input" + outputName = "\(name)_output" self.clock = clock - self.clockIdentifier = Node.Identifier(clock) + clockIdentifier = Node.Identifier(clock) self.reset = reset - self.resetIdentifier = Node.Identifier(reset) + resetIdentifier = Node.Identifier(reset) self.resetActive = resetActive self.testing = testing - self.testingIdentifier = Node.Identifier(testing) + testingIdentifier = Node.Identifier(testing) self.shift = shift - self.shiftIdentifier = Node.Identifier(shift) + shiftIdentifier = Node.Identifier(shift) self.Node = Node } @@ -66,8 +80,8 @@ class BoundaryScanRegisterCreator { let ordinalConstant = Node.Constant(ordinal) let name = input ? inputName : outputName - let dinArg = (max == 0) ? dinIdentifier: Node.Pointer(dinIdentifier, ordinalConstant) - let doutArg = (max == 0) ? doutIdentifier: Node.Pointer(doutIdentifier, ordinalConstant) + let dinArg = (max == 0) ? dinIdentifier : Node.Pointer(dinIdentifier, ordinalConstant) + let doutArg = (max == 0) ? doutIdentifier : Node.Pointer(doutIdentifier, ordinalConstant) let portArguments = [ Node.PortArg("din", dinArg), @@ -77,7 +91,7 @@ class BoundaryScanRegisterCreator { Node.PortArg("clock", clockIdentifier), Node.PortArg("reset", resetIdentifier), Node.PortArg("testing", testingIdentifier), - Node.PortArg("shift", shiftIdentifier) + Node.PortArg("shift", shiftIdentifier), ] let submoduleInstance = Node.Instance( @@ -97,7 +111,7 @@ class BoundaryScanRegisterCreator { } var inputDefinition: String { - return """ + """ module \(inputName) ( din, dout, @@ -123,12 +137,12 @@ class BoundaryScanRegisterCreator { assign sout = store; assign dout = testing ? store : din; endmodule - + """ } var outputDefinition: String { - return """ + """ module \(outputName) ( din, dout, @@ -153,7 +167,7 @@ class BoundaryScanRegisterCreator { assign sout = store; assign dout = din; endmodule - + """ } -} \ No newline at end of file +} diff --git a/Sources/Fault/Compaction.swift b/Sources/Fault/Compaction.swift index 8740bd2..12f72de 100644 --- a/Sources/Fault/Compaction.swift +++ b/Sources/Fault/Compaction.swift @@ -1,29 +1,40 @@ - -class Compactor { - +// 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. + +enum Compactor { static func compact( coverageList: [TVCPair] ) -> [TVCPair] { - var sa0 = Set() var sa1 = Set() var sa0Covered = Set() var sa1Covered = Set() - + let tvCount = coverageList.count // Construct Set of all Faults - for tvPair in coverageList{ + for tvPair in coverageList { sa0.formUnion(tvPair.coverage.sa0) sa1.formUnion(tvPair.coverage.sa1) } // Find Essential TVs print("Finding essential test vectors…") - let result = + let result = Compactor.findEssentials(coverageList: coverageList, sa0: sa0, sa1: sa1) - + print("Found \(result.vectors.count) essential test vectors.") // Essential TV columns @@ -34,16 +45,16 @@ class Compactor { for fault in result.faultSA1 { sa1Covered.insert(fault) } - - var rowCount: [TestVector:UInt] = [TestVector:UInt](); + + var rowCount = [TestVector: UInt]() for tvPair in coverageList { - rowCount[tvPair.vector] = + rowCount[tvPair.vector] = UInt(tvPair.coverage.sa0.count + tvPair.coverage.sa1.count) } var vectors = result.vectors - + func exec( tvPair: TVCPair, sa0Covered: Set, @@ -52,12 +63,12 @@ class Compactor { var sa0: [String] = [] var sa1: [String] = [] - if(tvPair.coverage.sa0.count != 0){ + if tvPair.coverage.sa0.count != 0 { sa0 = tvPair.coverage.sa0.filter { !sa0Covered.contains($0) } } - if(tvPair.coverage.sa1.count != 0){ + if tvPair.coverage.sa1.count != 0 { sa1 = tvPair.coverage.sa1.filter { !sa1Covered.contains($0) } @@ -69,8 +80,8 @@ class Compactor { repeat { let sortedCount = rowCount.sorted { $0.1 > $1.1 } - let tvPairDominant = coverageList.filter({$0.vector == sortedCount[0].key})[0] - + let tvPairDominant = coverageList.filter { $0.vector == sortedCount[0].key }[0] + for fault in tvPairDominant.coverage.sa0 { sa0Covered.insert(fault) } @@ -82,7 +93,7 @@ class Compactor { // Update Row Count for tvPair in coverageList { let future = Future { - return exec( + exec( tvPair: tvPair, sa0Covered: sa0Covered, sa1Covered: sa1Covered @@ -95,12 +106,12 @@ class Compactor { let (count, vector) = future.value as! (Int, TestVector) rowCount[vector] = UInt(count) } - + vectors.insert(sortedCount[0].key) - } while ((sa0Covered.count != sa0.count) || (sa1Covered.count != sa1.count)) + } while (sa0Covered.count != sa0.count) || (sa1Covered.count != sa1.count) let filtered = coverageList.filter { vectors.contains($0.vector) } - + // Verify that Compaction didn't reduce the coverage var sa0Final = Set() var sa1Final = Set() @@ -109,26 +120,24 @@ class Compactor { sa0Final.formUnion(tvPair.coverage.sa0) sa1Final.formUnion(tvPair.coverage.sa1) } - if sa0 == sa0Final && sa1 == sa1Final { - let ratio = (1 - (Float(filtered.count) / Float(tvCount))) * 100 + if sa0 == sa0Final, sa1 == sa1Final { + let ratio = (1 - (Float(filtered.count) / Float(tvCount))) * 100 print("Initial TV Count: \(tvCount). Compacted TV Count: \(filtered.count). ") print("Compaction is successfuly concluded with a reduction percentage of : \(String(format: "%.2f", ratio))% .\n") - } - else { + } else { print("Error: All faults aren't covered after compaction .\n") } return filtered - } - + } + private static func findEssentials( - coverageList: [TVCPair] , + coverageList: [TVCPair], sa0: Set, sa1: Set - ) -> ( vectors: Set , faultSA0: [String], faultSA1: [String]) { - + ) -> (vectors: Set, faultSA0: [String], faultSA1: [String]) { var vectors = Set() - var faultSA0 : [String] = [] - var faultSA1 : [String] = [] + var faultSA0: [String] = [] + var faultSA1: [String] = [] func sa0Exec( fault: String, @@ -136,23 +145,23 @@ class Compactor { ) -> (fault: String, count: Int, tvRow: TestVector) { var count = 0 var tvRow: TestVector = [] - for tvPair in coverageList{ - if (tvPair.coverage.sa0.contains(fault)){ + for tvPair in coverageList { + if tvPair.coverage.sa0.contains(fault) { count = count + 1 tvRow = tvPair.vector } } return (fault: fault, count: count, tvRow: tvRow) } - + func sa1Exec( fault: String, coverageList: [TVCPair] ) -> (fault: String, count: Int, tvRow: TestVector) { var count = 0 var tvRow: TestVector = [] - for tvPair in coverageList{ - if (tvPair.coverage.sa1.contains(fault)){ + for tvPair in coverageList { + if tvPair.coverage.sa1.contains(fault) { count = count + 1 tvRow = tvPair.vector } @@ -163,30 +172,30 @@ class Compactor { var sa0Futures: [Future] = [] for fault in sa0 { let future = Future { - return sa0Exec(fault: fault, coverageList: coverageList) + sa0Exec(fault: fault, coverageList: coverageList) } - sa0Futures.append(future) + sa0Futures.append(future) } var sa1Futures: [Future] = [] for fault in sa1 { let future = Future { - return sa1Exec(fault: fault, coverageList: coverageList) + sa1Exec(fault: fault, coverageList: coverageList) } - sa1Futures.append(future) + sa1Futures.append(future) } for future in sa0Futures { let (fault, count, tvRow) = future.value as! (String, Int, TestVector) - if (count == 1){ + if count == 1 { faultSA0.append(fault) vectors.insert(tvRow) } } - + for future in sa1Futures { let (fault, count, tvRow) = future.value as! (String, Int, TestVector) - if (count == 1){ + if count == 1 { faultSA1.append(fault) vectors.insert(tvRow) } @@ -198,4 +207,4 @@ class Compactor { faultSA1: faultSA1 ) } -} \ No newline at end of file +} diff --git a/Sources/Fault/Entries/asm.swift b/Sources/Fault/Entries/asm.swift index 9c089a4..1d6cc5f 100644 --- a/Sources/Fault/Entries/asm.swift +++ b/Sources/Fault/Entries/asm.swift @@ -1,8 +1,22 @@ -import Foundation +// 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 BigInt import CommandLineKit -import PythonKit import Defile -import BigInt +import Foundation +import PythonKit func assemble(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -26,7 +40,6 @@ func assemble(arguments: [String]) -> Int32 { ) cli.addOptions(filePath) - let goldenFilePath = StringOption( shortFlag: "O", longFlag: "goldenOutput", @@ -53,7 +66,7 @@ func assemble(arguments: [String]) -> Int32 { if jsonArgs.count != 1 || vArgs.count != 1 { Stderr.print("fault asm requires exactly one .json argument and one .v argument.") Stderr.print("Invoke fault asm --help for more info.") - return EX_USAGE + return EX_USAGE } let json = jsonArgs[0] @@ -75,10 +88,10 @@ func assemble(arguments: [String]) -> Int32 { let (chain, _, _) = ChainMetadata.extract(file: netlist) - let order = chain.filter{ $0.kind != .output } - let orderSorted = order.sorted(by: { $0.ordinal < $1.ordinal}) + let order = chain.filter { $0.kind != .output } + let orderSorted = order.sorted(by: { $0.ordinal < $1.ordinal }) - let orderOutput = chain.filter{ $0.kind != .input } + let orderOutput = chain.filter { $0.kind != .input } let outputSorted = orderOutput.sorted(by: { $0.ordinal < $1.ordinal }) let jsInputOrder = tvinfo.inputs @@ -87,8 +100,8 @@ func assemble(arguments: [String]) -> Int32 { var inputMap: [String: Int] = [:] var outputMap: [String: Int] = [:] - // Check input order - let chainOrder = orderSorted.filter{ $0.kind != .bypassInput } + // Check input order + let chainOrder = orderSorted.filter { $0.kind != .bypassInput } if chainOrder.count != jsInputOrder.count { print("[Error]: number of inputs in the json \(jsInputOrder.count) doesn't equal to the scan-chain registers \(chainOrder.count).") @@ -105,8 +118,8 @@ func assemble(arguments: [String]) -> Int32 { } for (i, output) in jsOutputOrder.enumerated() { - var name = (output.name.hasPrefix("\\")) ? String(output.name.dropFirst(1)): output.name - name = name.hasSuffix(".q") ? String(name.dropLast(2)): name + var name = (output.name.hasPrefix("\\")) ? String(output.name.dropFirst(1)) : output.name + name = name.hasSuffix(".q") ? String(name.dropLast(2)) : name outputMap[name] = i } @@ -114,7 +127,7 @@ func assemble(arguments: [String]) -> Int32 { var padded = String(number, radix: radix) let length = padded.count if digits > length { - for _ in 0..<(digits - length) { + for _ in 0 ..< (digits - length) { padded = "0" + padded } } @@ -134,20 +147,20 @@ func assemble(arguments: [String]) -> Int32 { } var pointer = 0 var list: [BigUInt] = [] - let binFromhex = String(hex, radix: 2) + let binFromhex = String(hex, radix: 2) let padLength = jsOutputLength - binFromhex.count let outputBinary = (String(repeating: "0", count: padLength) + binFromhex).reversed() - for output in jsOutputOrder{ + for output in jsOutputOrder { let start = outputBinary.index(outputBinary.startIndex, offsetBy: pointer) let end = outputBinary.index(start, offsetBy: output.width) - let value = String(outputBinary[start.. Int32 { value = tvcPair.vector[locus] } else { if element.kind == .bypassInput { - value = 0 + value = 0 } else { print("Chain register \(element.name) not found in the TVs.") return EX_DATAERR } } binaryString += pad(value, digits: element.width, radix: 2).reversed() - } + } var outputBinary = "" for element in orderOutput { var value: BigUInt = 0 - if let locus = outputMap[element.name] { + if let locus = outputMap[element.name] { value = outputDecimal[i][locus] outputBinary += pad(value, digits: element.width, radix: 2) } else { @@ -191,7 +204,7 @@ func assemble(arguments: [String]) -> Int32 { } let vectorCount = tvinfo.coverageList.count - let vectorLength = order.map{ $0.width }.reduce(0, +) + let vectorLength = order.map(\.width).reduce(0, +) let vecMetadata = binMetadata(count: vectorCount, length: vectorLength) let outMetadata = binMetadata(count: vectorCount, length: outputLength) @@ -209,16 +222,16 @@ func assemble(arguments: [String]) -> Int32 { try $0.print(String.boilerplate) try $0.print("/* FAULT METADATA: '\(vecMetadataString)' END FAULT METADATA */") try $0.print(binFileVec, terminator: "") - } + } try File.open(goldenOutput, mode: .write) { try $0.print(String.boilerplate) try $0.print("/* FAULT METADATA: '\(outMetadataString)' END FAULT METADATA */") try $0.print(binFileOut, terminator: "") - } + } } catch { Stderr.print("Could not access file \(vectorOutput) or \(goldenOutput)") return EX_CANTCREAT } return EX_OK -} \ No newline at end of file +} diff --git a/Sources/Fault/Entries/bench.swift b/Sources/Fault/Entries/bench.swift index 12b8e6b..f1bb7be 100644 --- a/Sources/Fault/Entries/bench.swift +++ b/Sources/Fault/Entries/bench.swift @@ -1,11 +1,26 @@ -import Foundation -import CoreFoundation +// 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 CommandLineKit -import PythonKit +import CoreFoundation import Defile +import Foundation +import PythonKit func bench(arguments: [String]) -> Int32 { // MARK: CommandLine Processing + let cli = CommandLineKit.CommandLine(arguments: arguments) let help = BoolOption( @@ -55,7 +70,7 @@ func bench(arguments: [String]) -> Int32 { Stderr.print("File '\(file)' not found.") return EX_NOINPUT } - + let output = filePath.value ?? "\(file).bench" guard let cellModel = cellsOption.value else { @@ -76,7 +91,7 @@ func bench(arguments: [String]) -> Int32 { cellModelsFile = "\(cellModel).json" let cellModels = - "grep -E -- \"\\bmodule\\b|\\bendmodule\\b|and|xor|or|not(\\s+|\\()|buf|input.*;|output.*;\" \(cellModel)".shOutput(); + "grep -E -- \"\\bmodule\\b|\\bendmodule\\b|and|xor|or|not(\\s+|\\()|buf|input.*;|output.*;\" \(cellModel)".shOutput() let pattern = "(?s)(?:module).*?(?:endmodule)" var cellDefinitions = "" @@ -84,54 +99,54 @@ func bench(arguments: [String]) -> Int32 { 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)!]) } - 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())" - let _ = "mkdir -p \(folderName)".sh() - - defer { - let _ = "rm -rf \(folderName)".sh() - } - let CellFile = "\(folderName)/cells.v" - - try File.open(CellFile, mode: .write) { - try $0.print(cellDefinitions) - } + cellDefinitions = matches.joined(separator: "\n") - // MARK: Importing Python and Pyverilog - let parse = Python.import("pyverilog.vparser.parser").parse + let folderName = "\(NSTemporaryDirectory())/thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" + let _ = "mkdir -p \(folderName)".sh() - // MARK: Parse - let ast = parse([CellFile])[0] - let description = ast[dynamicMember: "description"] + defer { + let _ = "rm -rf \(folderName)".sh() + } + let CellFile = "\(folderName)/cells.v" - 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) + try File.open(CellFile, mode: .write) { + try $0.print(cellDefinitions) + } - guard let string = String(data: data, encoding: .utf8) else { - throw "Could not create utf8 string." - } + // MARK: Importing Python and Pyverilog - try File.open(cellModelsFile, mode: .write) { - try $0.print(string) - } + let parse = Python.import("pyverilog.vparser.parser").parse + + // MARK: 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 "Could not create utf8 string." + } + + try File.open(cellModelsFile, mode: .write) { + try $0.print(string) + } } catch { Stderr.print("Internal error: \(error)") return EX_SOFTWARE } - } - else if !cellModel.hasSuffix(".json") { + } else if !cellModel.hasSuffix(".json") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv or .json." ) @@ -141,6 +156,7 @@ func bench(arguments: [String]) -> Int32 { } do { // MARK: Processing Library Cells + let data = try Data(contentsOf: URL(fileURLWithPath: cellModelsFile), options: .mappedIfSafe) guard let benchCells = try? JSONDecoder().decode(BenchCircuit.self, from: data) else { Stderr.print("File '\(cellsOption.value!)' is invalid.") @@ -152,9 +168,11 @@ func bench(arguments: [String]) -> Int32 { } // MARK: Importing Python and Pyverilog + let parse = Python.import("pyverilog.vparser.parser").parse // MARK: Parse + let ast = parse([file])[0] let description = ast[dynamicMember: "description"] var definitionOptional: PythonObject? @@ -176,17 +194,16 @@ func bench(arguments: [String]) -> Int32 { var inputNames: [String] = [] var usedInputs: [String] = [] var floatingOutputs: [String] = [] - var benchStatements: String = "" + var benchStatements = "" for input in inputs { if input.width > 1 { - let range = (input.from > input.to) ? input.to...input.from : input.from...input.to + let range = (input.from > input.to) ? input.to ... input.from : input.from ... input.to for index in range { let name = "\(input.name)[\(index)]" inputNames.append(name) benchStatements += "INPUT(\(name)) \n" } - } - else { + } else { let name = input.name inputNames.append(name) benchStatements += "INPUT(\(name)) \n" @@ -194,29 +211,27 @@ func bench(arguments: [String]) -> Int32 { } for item in definition.items { - let type = Python.type(item).__name__ // Process gates if type == "InstanceList" { let instance = item.instances[0] - let cellName = String(describing: instance.module) + let cellName = String(describing: instance.module) let instanceName = String(describing: instance.name) let cell = cellsDict[cellName]! - var inputs: [String:String] = [:] + 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 { + 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 { + } else { if type == "Pointer" { inputs[portname] = "\(hook.argname.var)[\(hook.argname.ptr)]" } else { @@ -224,21 +239,19 @@ func bench(arguments: [String]) -> Int32 { } } } - + let statements = try cell.extract(name: instanceName, inputs: inputs, output: outputs) - benchStatements += "\(statements) \n" - - usedInputs.append(contentsOf: Array(inputs.values)) - } - else if type == "Assign"{ + benchStatements += "\(statements) \n" + usedInputs.append(contentsOf: Array(inputs.values)) + } else if type == "Assign" { var right = "" if Python.type(item.right.var).__name__ == "Pointer" { right = "\(item.right.var.var)[\(item.right.var.ptr)]" } else { right = "\(item.right.var)" } - + var left = "" if Python.type(item.left.var).__name__ == "Pointer" { left = "\(item.left.var.var)[\(item.left.var.ptr)]" @@ -255,7 +268,6 @@ func bench(arguments: [String]) -> Int32 { usedInputs.append(right) } - } } let ignoredInputs = inputNames.filter { !usedInputs.contains($0) } @@ -264,19 +276,18 @@ func bench(arguments: [String]) -> Int32 { let filteredOutputs = outputs.filter { !floatingOutputs.contains($0.name) } for output in filteredOutputs { if output.width > 1 { - let range = (output.from > output.to) ? output.to...output.from : output.from...output.to + let range = (output.from > output.to) ? output.to ... output.from : output.from ... output.to for index in range { benchStatements += "OUTPUT(\(output.name)[\(index)]) \n" } - } - else { + } else { benchStatements += "OUTPUT(\(output.name)) \n" } } var floatingStatements = "" for input in ignoredInputs { - floatingStatements += "OUTPUT(\(input)) \n" + floatingStatements += "OUTPUT(\(input)) \n" } let boilerplate = """ @@ -294,6 +305,6 @@ func bench(arguments: [String]) -> Int32 { Stderr.print("Internal error: \(error)") return EX_SOFTWARE } - + return EX_OK -} \ No newline at end of file +} diff --git a/Sources/Fault/Entries/chain.swift b/Sources/Fault/Entries/chain.swift index 8778bcf..c2d0482 100644 --- a/Sources/Fault/Entries/chain.swift +++ b/Sources/Fault/Entries/chain.swift @@ -1,7 +1,21 @@ -import Foundation +// 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 CommandLineKit -import PythonKit import Defile +import Foundation +import PythonKit import Yams func getMatchingDFFInfo(from list: [DFFMatch], for cell: String, fnmatch: PythonObject) -> DFFMatch? { @@ -9,8 +23,8 @@ func getMatchingDFFInfo(from list: [DFFMatch], for cell: String, fnmatch: Python for name in dffinfo.name.components(separatedBy: ",") { if Bool(fnmatch.fnmatch(cell, name))! { return dffinfo - } - } + } + } } return nil } @@ -45,7 +59,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { helpMessage: "Verify scan chain using given cell model." ) cli.addOptions(verifyOpt) - + let clockOpt = StringOption( longFlag: "clock", helpMessage: "Clock signal to add to --ignoring and use in simulation. (Required.)." @@ -77,13 +91,12 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) cli.addOptions(liberty) - - let pdkConfigOpt = StringOption( - shortFlag: "p", - longFlag: "pdkConfig", - helpMessage: "Name for the YAML PDK config file. Recommended." + let sclConfigOpt = StringOption( + shortFlag: "s", + longFlag: "sclConfig", + helpMessage: "Name for the YAML SCL config file. Recommended." ) - cli.addOptions(pdkConfigOpt) + cli.addOptions(sclConfigOpt) let dffOpt = StringOption( shortFlag: "d", @@ -91,7 +104,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { helpMessage: "Optional override for the DFF names from the PDK config." ) cli.addOptions(dffOpt) - + let isolated = StringOption( longFlag: "isolating", helpMessage: "Isolated module definitions (.v) (Hard un-scannable blocks). (Default: none)" @@ -126,7 +139,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { ("clockDR", "Input/Output scan-cell clock DR"), ("update", "Input/Output scan-cell update"), ("test", "test mode enable"), - ("tck", "test clock") + ("tck", "test clock"), ] { let option = StringOption( longFlag: name, @@ -162,13 +175,13 @@ func scanChainCreate(arguments: [String]) -> Int32 { Stderr.print("File '\(file)' not found.") return EX_NOINPUT } - + guard let clockName = clockOpt.value else { Stderr.print("Option --clock is required.") Stderr.print("Invoke fault chain --help for more info.") return EX_USAGE } - + guard let resetName = resetOpt.value else { Stderr.print("Option --reset is required.") Stderr.print("Invoke fault chain --help for more info.") @@ -180,22 +193,25 @@ func scanChainCreate(arguments: [String]) -> Int32 { Stderr.print("Invoke fault chain --help for more info.") return EX_USAGE } - - var pdkConfig: PDKConfiguration = PDKConfiguration(dffMatches: [DFFMatch(name: "DFFSR,DFFNEGX1,DFFPOSX1", clk: "CLK", d: "D", q: "Q")]) - if let pdkConfigPath = pdkConfigOpt.value { - let pdkConfigYML = File.read(pdkConfigPath)! + + var sclConfig = SCLConfiguration(dffMatches: [DFFMatch(name: "DFFSR,DFFNEGX1,DFFPOSX1", clk: "CLK", d: "D", q: "Q")]) + if let sclConfigPath = sclConfigOpt.value { + guard let sclConfigYML = File.read(sclConfigPath) else { + Stderr.print("File not found: \(sclConfigPath)") + return EX_NOINPUT + } let decoder = YAMLDecoder() do { - pdkConfig = try decoder.decode(PDKConfiguration.self, from: pdkConfigYML) + sclConfig = try decoder.decode(SCLConfiguration.self, from: sclConfigYML) } catch { - Stderr.print("Invalid YAML file \(pdkConfigPath): \(error).") + Stderr.print("Invalid YAML file \(sclConfigPath): \(error).") return EX_DATAERR } } if let dffOverride = dffOpt.value { - pdkConfig.dffMatches.last!.name = dffOverride + sclConfig.dffMatches.last!.name = dffOverride } - + if !fileManager.fileExists(atPath: libertyFile) { Stderr.print("Liberty file '\(libertyFile)' not found.") return EX_NOINPUT @@ -212,7 +228,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { Stderr.print("Cell model file '\(modelTest)' not found.") return EX_NOINPUT } - if !modelTest.hasSuffix(".v") && !modelTest.hasSuffix(".sv") { + if !modelTest.hasSuffix(".v"), !modelTest.hasSuffix(".sv") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv.\n" ) @@ -222,21 +238,19 @@ func scanChainCreate(arguments: [String]) -> Int32 { let output = filePath.value ?? "\(file).chained.v" let intermediate = output + ".intermediate.v" let bsrLocation = output + ".bsr.v" - - let dffNames: Set - = Set(dffOpt.value?.components(separatedBy: ",").filter {$0 != ""} ?? ["DFFSR", "DFFNEGX1", "DFFPOSX1"]) - var ignoredInputs: Set - = Set(ignored.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - let defines: Set - = Set(defs.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) + var ignoredInputs + = Set(ignored.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) + + let defines + = Set(defs.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) ignoredInputs.insert(clockName) ignoredInputs.insert(resetName) - let includeFiles: Set - = Set(include.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - + let includeFiles + = Set(include.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) + var includeString = "" for file in includeFiles { if !fileManager.fileExists(atPath: file) { @@ -249,14 +263,16 @@ func scanChainCreate(arguments: [String]) -> Int32 { } // MARK: Importing Python and Pyverilog + let parse = Python.import("pyverilog.vparser.parser").parse let Node = Python.import("pyverilog.vparser.ast") let Generator = Python.import("pyverilog.ast_code_generator.codegen").ASTCodeGenerator() - + // MARK: Parse + let ast = parse([args[0]])[0] let description = ast[dynamicMember: "description"] var definitionOptional: PythonObject? @@ -297,6 +313,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { } // MARK: Internal signals + print("Chaining internal flip-flops…") let definitionName = String(describing: definition.name) let alteredName = "__UNIT__UNDER__FINANGLING__" @@ -318,10 +335,11 @@ func scanChainCreate(arguments: [String]) -> Int32 { let clkSourceName = "__clk_source__" let clkSourceId = Node.Identifier(clkSourceName) - let invClkSourceName: String = "__clk_source_n__" - var invClkSourceId: PythonObject? + let invClkSourceName = "__clk_source_n__" + var invClkSourceId: PythonObject? // MARK: Register chaining original module + var previousOutput = inputIdentifier let statements = Python.list() @@ -345,11 +363,15 @@ func scanChainCreate(arguments: [String]) -> Int32 { definition.portlist.ports = Python.tuple(ports) - var wireDeclarations: [PythonObject] = [] + var wireDeclarations: [PythonObject] = [] var cellDeclarations: [PythonObject] = [] - + let fnmatch = Python.import("fnmatch") + var muxCreator: MuxCreator? = nil + if let muxInfo = sclConfig.muxInfo { + muxCreator = MuxCreator(using: Node, muxInfo: muxInfo) + } var warn = false var blackbox = false var blackboxItem: PythonObject? @@ -358,25 +380,33 @@ func scanChainCreate(arguments: [String]) -> Int32 { // Process gates if type == "InstanceList" { let instance = itemDeclaration.instances[0] - let instanceName = String(describing: instance.module) - if let dffinfo = getMatchingDFFInfo(from: pdkConfig.dffMatches, for: instanceName, fnmatch: fnmatch) { + let moduleName = String(describing: instance.module) + let instanceName = String(describing: instance.name) + if let dffinfo = getMatchingDFFInfo(from: sclConfig.dffMatches, for: moduleName, fnmatch: fnmatch) { for hook in instance.portlist { let portnameStr = String(describing: hook.portname) if portnameStr == dffinfo.clk { if String(describing: hook.argname) == clockName { hook.argname = clkSourceId - } - else { + } else { warn = true } } if portnameStr == dffinfo.d { - let ternary = Node.Cond( - shiftIdentifier, - previousOutput, - hook.argname - ) - hook.argname = ternary + if let mc = muxCreator { + let (muxCellDecls, muxWireDecls, muxOut) = mc.create(for: instanceName, selection: shiftIdentifier, a: previousOutput, b: hook.argname) + hook.argname = muxOut + cellDeclarations += muxCellDecls + wireDeclarations += muxWireDecls + + } else { + let ternary = Node.Cond( + shiftIdentifier, + previousOutput, + hook.argname + ) + hook.argname = ternary + } } if portnameStr == dffinfo.q { @@ -391,19 +421,20 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) ) - } else if let name = isolatedName, name == instanceName { + } else if let name = isolatedName, name == moduleName { // MARK: Isolating hard blocks + print("Chaining blackbox module…") let (_, inputs, _) = try Port.extract(from: isolatedOptional!) - let isolatedInputs = inputs.map { $0.name } - + let isolatedInputs = inputs.map(\.name) + var counter = 0 - + let scCreator = BoundaryScanRegisterCreator( name: "BoundaryScanRegister", clock: tckName, reset: resetName, - resetActive: resetActiveLow.value ? .low : .high, + resetActive: resetActiveLow.value ? .low : .high, testing: testName, shift: shiftName, using: Node @@ -416,7 +447,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { if hookType == "Concat" { var list: [PythonObject] = [] - for (i, element) in hook.argname.list.enumerated() { + for (i, element) in hook.argname.list.enumerated() { let elementName = String(describing: element.name) if ignoredInputs.contains(elementName) { continue @@ -428,8 +459,8 @@ func scanChainCreate(arguments: [String]) -> Int32 { let doutStatement = Node.Wire(doutName) cellDeclarations.append(doutStatement) - list.append(Node.Identifier(doutName)) - + list.append(Node.Identifier(doutName)) + cellDeclarations.append( scCreator.create( ordinal: 0, @@ -441,15 +472,15 @@ func scanChainCreate(arguments: [String]) -> Int32 { input: !input ) ) - kind = .bypassInput + kind = .bypassInput } else { let dinName = elementName + "_\(i)" + "__din" let dinStatement = Node.Wire(dinName) cellDeclarations.append(dinStatement) - list.append(Node.Identifier(dinName)) - + list.append(Node.Identifier(dinName)) + cellDeclarations.append( scCreator.create( ordinal: 0, @@ -465,11 +496,11 @@ func scanChainCreate(arguments: [String]) -> Int32 { } internalOrder.append( ChainRegister( - name: instanceName + "_\(portName)_\(i)", + name: moduleName + "_\(portName)_\(i)", kind: kind ) - ) - previousOutput = + ) + previousOutput = Node.Identifier(inputName.uniqueName(counter + 1)) counter += 1 } @@ -480,7 +511,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { if ignoredInputs.contains(argName) { continue } - + var kind: ChainRegister.Kind if input { let doutName = argName + "__dout" @@ -488,7 +519,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { statements.append(doutStatement) hook.argname = Node.Identifier(doutName) - + cellDeclarations.append( scCreator.create( ordinal: 0, @@ -500,14 +531,14 @@ func scanChainCreate(arguments: [String]) -> Int32 { input: !input ) ) - kind = .bypassInput - } else { + kind = .bypassInput + } else { let dinName = argName + "__din" let dinStatement = Node.Wire(dinName) cellDeclarations.append(dinStatement) hook.argname = Node.Identifier(dinName) - + cellDeclarations.append( scCreator.create( ordinal: 0, @@ -521,33 +552,33 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) kind = .bypassOutput } - + internalOrder.append( ChainRegister( - name: instanceName + "_\(hook.portname)", + name: moduleName + "_\(hook.portname)", kind: kind ) - ) + ) previousOutput = Node.Identifier(inputName.uniqueName(counter + 1)) counter += 1 } } - for i in 0...counter { + for i in 0 ... counter { wireDeclarations.append(Node.Wire(inputName.uniqueName(i))) } blackbox = true } - if let invClockName = clockInv.value, instanceName == invClockName { + if let invClockName = clockInv.value, moduleName == invClockName { for hook in instance.portlist { if String(describing: hook.argname) == clockName { invClkSourceId = Node.Identifier(invClkSourceName) hook.argname = invClkSourceId! } } - } + } } if !blackbox { @@ -555,14 +586,13 @@ func scanChainCreate(arguments: [String]) -> Int32 { } else { blackboxItem = itemDeclaration } - } if warn { print("[Warning]: Detected flip-flops with clock different from \(clockName).") } - var assignStatements: [PythonObject] = [] + var assignStatements: [PythonObject] = [] let finalAssignment = Node.Assign( Node.Lvalue(outputIdentifier), Node.Rvalue(previousOutput) @@ -579,31 +609,32 @@ func scanChainCreate(arguments: [String]) -> Int32 { Node.Rvalue(clockCond) ) assignStatements.append(clkSourceAssignment) - + if let invClkId = invClkSourceId { let invClockCond = Node.Cond( Node.Identifier(testName), Node.Unot(Node.Identifier(tckName)), Node.Identifier(clockName) ) - + let invClockAssignment = Node.Assign( Node.Lvalue(invClkId), Node.Rvalue(invClockCond) ) assignStatements.append(invClockAssignment) } - + if let item = blackboxItem { cellDeclarations.append(item) } - + definition.items = Python.tuple(statements + wireDeclarations + cellDeclarations + assignStatements) definition.name = Python.str(alteredName) - - print("Internal scan chain successfuly constructed. Length: " , internalOrder.count) + + print("Internal scan chain successfuly constructed. Length: ", internalOrder.count) // MARK: Chaining boundary registers + print("Creating and chaining boundary flip-flops…") var order: [ChainRegister] = [] let boundaryCount: Int = try { @@ -618,7 +649,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { statements.append(Node.Input(testName)) if let clock = clockOpt.value { - statements.append(Node.Input(clock)) + statements.append(Node.Input(clock)) } let portArguments = Python.list() @@ -633,7 +664,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) var counter = 0 - + let initialAssignment = Node.Assign( Node.Lvalue(Node.Identifier(inputName.uniqueName(0))), Node.Rvalue(inputIdentifier) @@ -643,7 +674,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { for input in inputs { let inputStatement = Node.Input(input.name) - if (input.name != clockName && input.name != resetName){ + if input.name != clockName, input.name != resetName { statements.append(inputStatement) } if ignoredInputs.contains(input.name) { @@ -653,7 +684,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { )) continue } - + let doutName = String(describing: input.name) + "__dout" let doutStatement = Node.Wire(doutName) if input.width > 1 { @@ -674,7 +705,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let minimum = min(input.from, input.to) let maximum = max(input.from, input.to) - for i in (minimum)...(maximum) { + for i in minimum ... maximum { statements.append( bsrCreator.create( ordinal: i, @@ -715,7 +746,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { inputName, Node.Identifier(inputName.uniqueName(counter)) )) - + counter += 1 // as a skip order += internalOrder @@ -743,18 +774,18 @@ func scanChainCreate(arguments: [String]) -> Int32 { output.name, Node.Identifier(dinName) )) - + let minimum = min(output.from, output.to) let maximum = max(output.from, output.to) - for i in (minimum)...(maximum) { + for i in minimum ... maximum { statements.append( bsrCreator.create( ordinal: i, max: maximum, din: dinName, dout: output.name, - sin: inputName.uniqueName(counter), + sin: inputName.uniqueName(counter), sout: inputName.uniqueName(counter + 1), input: false ) @@ -791,7 +822,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { statements.append(boundaryAssignment) var wireDeclarations: [PythonObject] = [] - for i in 0...counter { + for i in 0 ... counter { wireDeclarations.append(Node.Wire(inputName.uniqueName(i))) } @@ -809,7 +840,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let boundaryScanRegisters = parse([bsrLocation])[0][dynamicMember: "description"].definitions - + let definitions = Python.list(description.definitions) definitions.extend(boundaryScanRegisters) definitions.append(supermodel) @@ -817,18 +848,19 @@ func scanChainCreate(arguments: [String]) -> Int32 { return counter - 1 // Accounting for skip }() - - - print("Boundary scan cells successfuly chained. Length: " , boundaryCount) + print("Boundary scan cells successfuly chained. Length: ", boundaryCount) + if internalOrder.count == 0 { + print("Warning: No internal scan elements found. Are your DFFs configured properly?") + } let chainLength = boundaryCount + internalOrder.count - print("Total scan-chain length: " , chainLength) + print("Total scan-chain length: ", chainLength) let metadata = ChainMetadata( boundaryCount: boundaryCount, internalCount: internalOrder.count, order: order, - shift: shiftName, + shift: shiftName, sin: inputName, sout: outputName ) @@ -836,13 +868,12 @@ func scanChainCreate(arguments: [String]) -> Int32 { Stderr.print("Could not generate metadata string.") return EX_SOFTWARE } - + try File.open(intermediate, mode: .write) { try $0.print(Generator.visit(ast)) } let netlist: String = { - if !skipSynth.value { let script = Synthesis.script( for: definitionName, @@ -853,6 +884,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { ) // MARK: Yosys + print("Resynthesizing with yosys…") let result = "echo '\(script)' | '\(yosysExecutable)' > /dev/null".sh() @@ -865,7 +897,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { return intermediate } }() - + guard let content = File.read(netlist) else { throw "Could not re-read created file." } @@ -877,6 +909,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { } // MARK: Verification + if let model = verifyOpt.value { print("Verifying scan chain integrity…") let ast = parse([netlist])[0] @@ -897,7 +930,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let verified = try Simulator.simulate( verifying: definitionName, - in: netlist, + in: netlist, isolating: isolated.value, with: model, ports: ports, @@ -918,8 +951,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { using: iverilogExecutable, with: vvpExecutable ) - print("done") - if (verified) { + if verified { print("Scan chain verified successfully.") } else { print("Scan chain verification failed.") @@ -928,13 +960,13 @@ func scanChainCreate(arguments: [String]) -> Int32 { print("・Ensure that the reset is active high- pass --activeLow for activeLow.") } if internalOrder.count == 0 { - print("・Ensure that D flip-flop cell name starts with \(dffNames).") + print("・Ensure that D flip-flop cell names match those either in the defaults, the PDK config, or the overrides.") } print("・Ensure that there are no other asynchronous resets anywhere in the circuit.") } } print("Done.") - + } catch { Stderr.print("Internal software error: \(error)") return EX_SOFTWARE diff --git a/Sources/Fault/Entries/compact.swift b/Sources/Fault/Entries/compact.swift index 7b5d179..fbc4cb2 100644 --- a/Sources/Fault/Entries/compact.swift +++ b/Sources/Fault/Entries/compact.swift @@ -1,6 +1,20 @@ -import Foundation +// 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 CommandLineKit import Defile +import Foundation import PythonKit func compactTestVectors(arguments: [String]) -> Int32 { @@ -58,8 +72,8 @@ func compactTestVectors(arguments: [String]) -> Int32 { let file = args[0] if !fileManager.fileExists(atPath: file) { - Stderr.print("File '\(file)' not found.") - return EX_NOINPUT + Stderr.print("File '\(file)' not found.") + return EX_NOINPUT } if let modelTest = verifyOpt.value { @@ -67,7 +81,7 @@ func compactTestVectors(arguments: [String]) -> Int32 { Stderr.print("Cell model file '\(modelTest)' not found.") return EX_NOINPUT } - if !modelTest.hasSuffix(".v") && !modelTest.hasSuffix(".sv") { + if !modelTest.hasSuffix(".v"), !modelTest.hasSuffix(".sv") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv.\n" ) @@ -118,7 +132,7 @@ func compactTestVectors(arguments: [String]) -> Int32 { try File.open(output, mode: .write) { try $0.print(string) } - + if let modelTest = verifyOpt.value { print("Running simulations using the compacted set…") let verifiedOutput = "\(output).verified.json" @@ -130,14 +144,14 @@ func compactTestVectors(arguments: [String]) -> Int32 { "-m", "100", "--tvSet", output, "-o", verifiedOutput, - netlistOpt.value! + netlistOpt.value!, ] exit(main(arguments: mainArguments)) - } + } } catch { Stderr.print(error.localizedDescription) return EX_NOINPUT } return EX_OK -} \ No newline at end of file +} diff --git a/Sources/Fault/Entries/cut.swift b/Sources/Fault/Entries/cut.swift index 3d94f5c..fcaf483 100644 --- a/Sources/Fault/Entries/cut.swift +++ b/Sources/Fault/Entries/cut.swift @@ -1,7 +1,21 @@ -import Foundation +// 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 CommandLineKit -import PythonKit import Defile +import Foundation +import PythonKit func cut(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -67,13 +81,13 @@ func cut(arguments: [String]) -> Int32 { return EX_NOINPUT } - let dffNames: Set - = Set(dffOpt.value?.components(separatedBy: ",").filter {$0 != ""} ?? ["DFFSR", "DFFNEGX1", "DFFPOSX1"]) + let dffNames + = Set(dffOpt.value?.components(separatedBy: ",").filter { $0 != "" } ?? ["DFFSR", "DFFNEGX1", "DFFPOSX1"]) let output = filePath.value ?? "\(file).cut.v" // MARK: Importing Python and Pyverilog - + let parse = Python.import("pyverilog.vparser.parser").parse let Node = Python.import("pyverilog.vparser.ast") @@ -107,14 +121,14 @@ func cut(arguments: [String]) -> Int32 { break } } - + guard let definition = definitionOptional else { Stderr.print("No module found.") exit(EX_DATAERR) } - let hardIgnoredInputs: Set - = Set(ignored.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) + let hardIgnoredInputs + = Set(ignored.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) do { let ports = Python.list(definition.portlist.ports) @@ -173,23 +187,23 @@ func cut(arguments: [String]) -> Int32 { items.append(inputAssignment) items.append(outputAssignment) - + } else if let blakcboxName = isolatedName, blakcboxName == instanceName { include = false - - guard let isolatedDefinition = isolatedOptional else { + + guard let isolatedDefinition = isolatedOptional else { Stderr.print("No module definition for blackbox \(blakcboxName)") exit(EX_DATAERR) } let (_, inputs, _) = try Port.extract(from: isolatedDefinition) - let bbInputNames = inputs.map { $0.name } + let bbInputNames = inputs.map(\.name) for hook in instance.portlist { let portName = String(describing: hook.portname) let hookType = Python.type(hook.argname).__name__ let input = bbInputNames.contains(portName) - + if hookType == "Concat" { let list = hook.argname.list for (i, element) in list.enumerated() { @@ -203,16 +217,13 @@ func cut(arguments: [String]) -> Int32 { Node.Lvalue(Node.Identifier(name)), Node.Rvalue(element) ) - - } - else { - name = instanceName + "_\(portName)_\(i)" + } else { + name = instanceName + "_\(portName)_\(i)" statement = Node.Input(name) assignStatement = Node.Assign( Node.Lvalue(element), Node.Rvalue(Node.Identifier(name)) ) - } items.append(assignStatement) declarations.append(statement) @@ -228,8 +239,8 @@ func cut(arguments: [String]) -> Int32 { var statement: PythonObject var assignStatement: PythonObject if input { - name = "\\" + instanceName + "_\(portName).q" - statement = Node.Output(name) + name = "\\" + instanceName + "_\(portName).q" + statement = Node.Output(name) assignStatement = Node.Assign( Node.Lvalue(Node.Identifier(name)), Node.Rvalue(hook.argname) @@ -245,11 +256,11 @@ func cut(arguments: [String]) -> Int32 { items.append(assignStatement) declarations.append(statement) ports.append(Node.Port(name, Python.None, Python.None, Python.None)) - } + } } } } - + if include { items.append(item) } @@ -258,7 +269,7 @@ func cut(arguments: [String]) -> Int32 { if declarations.count == 0 { print("[Warning]: Failed to detect flip-flop cells named: \(dffNames).") } - + definition.portlist.ports = ports definition.items = Python.tuple(declarations + items) @@ -269,7 +280,7 @@ func cut(arguments: [String]) -> Int32 { } catch { Stderr.print("An internal software error has occurred.") return EX_SOFTWARE - } - + } + return EX_OK -} \ No newline at end of file +} diff --git a/Sources/Fault/Entries/jtag.swift b/Sources/Fault/Entries/jtag.swift index c0248ea..7cb2d16 100644 --- a/Sources/Fault/Entries/jtag.swift +++ b/Sources/Fault/Entries/jtag.swift @@ -1,7 +1,21 @@ -import Foundation +// 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 CommandLineKit -import PythonKit import Defile +import Foundation +import PythonKit func jtagCreate(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -55,15 +69,15 @@ func jtagCreate(arguments: [String]) -> Int32 { let testvectors = StringOption( shortFlag: "t", longFlag: "testVectors", - helpMessage: ".bin file for test vectors." + helpMessage: ".bin file for test vectors." ) cli.addOptions(testvectors) let goldenOutput = StringOption( shortFlag: "g", longFlag: "goldenOutput", - helpMessage: - " .bin file for golden output." + helpMessage: + " .bin file for golden output." ) cli.addOptions(goldenOutput) @@ -110,7 +124,7 @@ func jtagCreate(arguments: [String]) -> Int32 { ("tdi", "JTAG test data input"), ("tdo", "JTAG test data output"), ("tdo_paden_o", "TDO Enable pad (active low) "), - ("trst", "JTAG test reset (active low)") + ("trst", "JTAG test reset (active low)"), ] { let option = StringOption( longFlag: name, @@ -119,7 +133,7 @@ func jtagCreate(arguments: [String]) -> Int32 { cli.addOptions(option) names[name] = (default: name, option: option) } - + do { try cli.parse() } catch { @@ -146,27 +160,26 @@ func jtagCreate(arguments: [String]) -> Int32 { Stderr.print("File '\(file)' not found.") return EX_NOINPUT } - - let (_, boundaryCount, internalCount) = ChainMetadata.extract(file: file) - + + let (_, boundaryCount, internalCount) = ChainMetadata.extract(file: file) + guard let clockName = clockOpt.value else { Stderr.print("Option --clock is required.") Stderr.print("Invoke fault jtag --help for more info.") return EX_USAGE } - + guard let resetName = resetOpt.value else { Stderr.print("Option --reset is required.") Stderr.print("Invoke fault jtag --help for more info.") return EX_USAGE } + var ignoredInputs + = Set(ignored.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) - var ignoredInputs: Set - = Set(ignored.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - - let defines: Set - = Set(defs.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) + let defines + = Set(defs.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) ignoredInputs.insert(clockName) ignoredInputs.insert(resetName) @@ -181,7 +194,7 @@ func jtagCreate(arguments: [String]) -> Int32 { Stderr.print("Liberty file '\(libertyFile)' not found.") return EX_NOINPUT } - + if !libertyFile.hasSuffix(".lib") { Stderr.print( "Warning: Liberty file provided does not end with .lib." @@ -193,7 +206,7 @@ func jtagCreate(arguments: [String]) -> Int32 { Stderr.print("Cell model file '\(modelTest)' not found.") return EX_NOINPUT } - if !modelTest.hasSuffix(".v") && !modelTest.hasSuffix(".sv") { + if !modelTest.hasSuffix(".v"), !modelTest.hasSuffix(".sv") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv." ) @@ -216,9 +229,9 @@ func jtagCreate(arguments: [String]) -> Int32 { } } - let includeFiles: Set - = Set(include.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - + let includeFiles + = Set(include.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) + var includeString = "" for file in includeFiles { if !fileManager.fileExists(atPath: file) { @@ -229,11 +242,12 @@ func jtagCreate(arguments: [String]) -> Int32 { `include "\(file)" """ } - + let output = filePath.value ?? "\(file).jtag.v" let intermediate = output + ".intermediate.v" // MARK: Importing Python and Pyverilog + let parse = Python.import("pyverilog.vparser.parser").parse let Node = Python.import("pyverilog.vparser.ast") @@ -242,6 +256,7 @@ func jtagCreate(arguments: [String]) -> Int32 { Python.import("pyverilog.ast_code_generator.codegen").ASTCodeGenerator() // MARK: Parse + let ast = parse([args[0]])[0] let description = ast[dynamicMember: "description"] var definitionOptional: PythonObject? @@ -258,7 +273,7 @@ func jtagCreate(arguments: [String]) -> Int32 { return EX_DATAERR } - let sinName = names["sin"]!.option.value + let sinName = names["sin"]!.option.value ?? names["sin"]!.default let soutName = names["sout"]!.option.value ?? names["sout"]!.default @@ -266,27 +281,28 @@ func jtagCreate(arguments: [String]) -> Int32 { ?? names["shift"]!.default let testName = names["test"]!.option.value ?? names["test"]!.default - let tmsName = names["tms"]!.option.value + let tmsName = names["tms"]!.option.value ?? names["tms"]!.default - let tdiName = names["tdi"]!.option.value + let tdiName = names["tdi"]!.option.value ?? names["tdi"]!.default - let tdoName = names["tdo"]!.option.value + let tdoName = names["tdo"]!.option.value ?? names["tdo"]!.default - let tdoenableName = names["tdo_paden_o"]!.option.value + let tdoenableName = names["tdo_paden_o"]!.option.value ?? names["tdo_paden_o"]!.default - let tckName = names["tck"]!.option.value + let tckName = names["tck"]!.option.value ?? names["tck"]!.default - let trstName = names["trst"]!.option.value + let trstName = names["trst"]!.option.value ?? names["trst"]!.default // MARK: Internal signals + print("Creating top module…") let definitionName = String(describing: definition.name) let alteredName = "__DESIGN__UNDER__TEST__" do { let (_, inputs, outputs) = try Port.extract(from: definition) - definition.name = Python.str(alteredName); + definition.name = Python.str(alteredName) let ports = Python.list(definition.portlist.ports) let chainPorts: [String] = [ @@ -294,24 +310,30 @@ func jtagCreate(arguments: [String]) -> Int32 { soutName, shiftName, tckName, - testName - ] + testName, + ] let topModulePorts = Python.list(ports.filter { !chainPorts.contains(String($0.name)!) }) topModulePorts.append(Node.Port( - tmsName, Python.None, Python.None, Python.None)) + tmsName, Python.None, Python.None, Python.None + )) topModulePorts.append(Node.Port( - tckName, Python.None, Python.None, Python.None)) + tckName, Python.None, Python.None, Python.None + )) topModulePorts.append(Node.Port( - tdiName, Python.None, Python.None, Python.None)) + tdiName, Python.None, Python.None, Python.None + )) topModulePorts.append(Node.Port( - tdoName, Python.None, Python.None, Python.None)) + tdoName, Python.None, Python.None, Python.None + )) topModulePorts.append(Node.Port( - trstName, Python.None, Python.None, Python.None)) + trstName, Python.None, Python.None, Python.None + )) topModulePorts.append(Node.Port( - tdoenableName, Python.None, Python.None, Python.None)) + tdoenableName, Python.None, Python.None, Python.None + )) let statements = Python.list() statements.append(Node.Input(tmsName)) @@ -323,7 +345,7 @@ func jtagCreate(arguments: [String]) -> Int32 { let portArguments = Python.list() for input in inputs { - if(!chainPorts.contains(input.name)){ + if !chainPorts.contains(input.name) { let inputStatement = Node.Input(input.name) portArguments.append(Node.PortArg( input.name, @@ -337,8 +359,7 @@ func jtagCreate(arguments: [String]) -> Int32 { inputStatement.width = width } statements.append(inputStatement) - } - else { + } else { let portIdentifier = input.name portArguments.append(Node.PortArg( input.name, @@ -348,7 +369,7 @@ func jtagCreate(arguments: [String]) -> Int32 { } for output in outputs { - if(!chainPorts.contains(output.name)){ + if !chainPorts.contains(output.name) { let outputStatement = Node.Output(output.name) if output.width > 1 { let width = Node.Width( @@ -364,17 +385,18 @@ func jtagCreate(arguments: [String]) -> Int32 { Node.Identifier(output.name) )) } - - // MARK: tap module - print("Stitching tap port…") + + // MARK: tap module + + print("Stitching tap port…") let tapInfo = TapInfo.default let tapCreator = TapCreator( name: "tap_wrapper", using: Node ) - let tapModule = tapCreator.create( - tapInfo: tapInfo, + let tapModule = tapCreator.create( + tapInfo: tapInfo, tms: tmsName, tck: tckName, tdi: tdiName, @@ -412,28 +434,25 @@ func jtagCreate(arguments: [String]) -> Int32 { let tempDir = "\(NSTemporaryDirectory())" - let tapLocation = "\(tempDir)/top.v" let wrapperLocation = "\(tempDir)/wrapper.v" do { - try File.open(tapLocation, mode: .write) { + try File.open(tapLocation, mode: .write) { try $0.print(TapCreator.top) } try File.open(wrapperLocation, mode: .write) { try $0.print(TapCreator.wrapper) } - } catch { - - } + } catch {} let tapDefinition = parse([tapLocation])[0][dynamicMember: "description"].definitions - + let wrapperDefinition = parse([wrapperLocation])[0][dynamicMember: "description"].definitions - + try? File.delete(tapLocation) try? File.delete(wrapperLocation) @@ -446,7 +465,7 @@ func jtagCreate(arguments: [String]) -> Int32 { try File.open(intermediate, mode: .write) { try $0.print(Generator.visit(ast)) } - + let _ = Synthesis.script( for: definitionName, in: [intermediate], @@ -466,6 +485,7 @@ func jtagCreate(arguments: [String]) -> Int32 { ) // MARK: Yosys + print("Resynthesizing with yosys…") let result = "echo '\(script)' | '\(yosysExecutable)' > /dev/null".sh() @@ -493,6 +513,7 @@ func jtagCreate(arguments: [String]) -> Int32 { } // MARK: Verification + if let model = verifyOpt.value { print("Verifying tap port integrity…") let ast = parse([netlist])[0] @@ -533,7 +554,7 @@ func jtagCreate(arguments: [String]) -> Int32 { with: vvpExecutable ) print("Done.") - if (verified) { + if verified { print("Tap port verified successfully.") } else { print("Tap port verification failed.") @@ -545,10 +566,11 @@ func jtagCreate(arguments: [String]) -> Int32 { } // MARK: Test bench + if let tvFile = testvectors.value { print("Generating testbench for test vectors…") let behavior - = Array( + = [Simulator.Behavior]( repeating: .holdHigh, count: ignoredInputs.count ) @@ -557,7 +579,7 @@ func jtagCreate(arguments: [String]) -> Int32 { let testbecnh = netlist + ".tv" + ".tb.sv" let verified = try Simulator.simulate( verifying: definitionName, - in: netlist, + in: netlist, isolating: blackbox.value, with: model, ports: ports, @@ -574,7 +596,7 @@ func jtagCreate(arguments: [String]) -> Int32 { tdo: tdoName, trst: trstName, output: testbecnh, - chainLength: internalCount + boundaryCount, + chainLength: internalCount + boundaryCount, vecbinFile: testvectors.value!, outbinFile: goldenOutput.value!, vectorCount: vectorCount, @@ -585,21 +607,20 @@ func jtagCreate(arguments: [String]) -> Int32 { using: iverilogExecutable, with: vvpExecutable ) - if (verified) { + if verified { print("Test vectors verified successfully.") } else { print("Test vector simulation failed.") if !resetActiveLow.value { // default is ignored inputs are held high print("・Ensure that ignored inputs in the simulation are held low. Pass --holdLow if reset is active high.") } - } + } } } } catch { Stderr.print("Internal software error: \(error)") return EX_SOFTWARE } - - return EX_OK -} \ No newline at end of file + return EX_OK +} diff --git a/Sources/Fault/Entries/main.swift b/Sources/Fault/Entries/main.swift index 3a5af5f..6f34f89 100644 --- a/Sources/Fault/Entries/main.swift +++ b/Sources/Fault/Entries/main.swift @@ -1,12 +1,26 @@ -import Foundation -import CoreFoundation // Not automatically imported on Linux +// 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 BigInt import CommandLineKit -import PythonKit +import CoreFoundation // Not automatically imported on Linux import Defile +import Foundation import OrderedDictionary -import BigInt +import PythonKit -let VERSION = "0.5.2" +let VERSION = "0.6.0" var env = ProcessInfo.processInfo.environment let iverilogBase = env["FAULT_IVL_BASE"] ?? "/usr/local/lib/ivl" @@ -14,13 +28,13 @@ let iverilogExecutable = env["FAULT_IVERILOG"] ?? env["PYVERILOG_IVERILOG"] ?? " let vvpExecutable = env["FAULT_VVP"] ?? "vvp" let yosysExecutable = env["FAULT_YOSYS"] ?? "yosys" -let _ = [ // Register all RNGs +_ = [ // Register all RNGs SwiftRNG.registered, - LFSR.registered + LFSR.registered, ] -let _ = [ // Register all TVGens +_ = [ // Register all TVGens Atalanta.registered, - PODEM.registered + PODEM.registered, ] let subcommands: OrderedDictionary = [ @@ -30,7 +44,7 @@ let subcommands: OrderedDictionary = [ "asm": (func: assemble, desc: "test vector assembly"), "compact": (func: compactTestVectors, desc: "test vector static compaction"), "tap": (func: jtagCreate, desc: "JTAG port insertion"), - "bench": (func: bench, desc: "verilog netlist to bench format conversion") + "bench": (func: bench, desc: "verilog netlist to bench format conversion"), ] let yosysTest = "'\(yosysExecutable)' -V".sh(silent: true) @@ -39,7 +53,7 @@ if yosysTest != EX_OK { exit(EX_UNAVAILABLE) } -let pythonVersions = ({ +let pythonVersions = { // Test Yosys, Python () -> (python: String, pyverilog: String) in do { @@ -55,18 +69,18 @@ let pythonVersions = ({ sys.path.append(component) } } - + let pyverilogVersion = try Python.attemptImport("pyverilog").__version__ return (python: "\(pythonVersion)", pyverilog: "\(pyverilogVersion)") } catch { Stderr.print("\(error)") exit(EX_UNAVAILABLE) } -}) () // Just to check - +}() // Just to check func main(arguments: [String]) -> Int32 { // MARK: CommandLine Processing + let cli = CommandLineKit.CommandLine(arguments: arguments) let defaultTVCount = "100" @@ -135,7 +149,7 @@ func main(arguments: [String]) -> Int32 { helpMessage: "Ceiling for Test Vector increments: if this number is reached, no more increments will occur regardless the coverage. (Default: \(defaultCeiling).)" ) cli.addOptions(ceiling) - + let rng = StringOption( longFlag: "rng", helpMessage: "Type of the RNG used in Internal TV Generation: LFSR or swift. (Default: swift.)" @@ -157,7 +171,7 @@ func main(arguments: [String]) -> Int32 { cli.addOptions(bench) let sampleRun = BoolOption( - longFlag: "sampleRun", + longFlag: "sampleRun", helpMessage: "Generate only one testbench for inspection, do not delete it." ) cli.addOptions(sampleRun) @@ -168,7 +182,7 @@ func main(arguments: [String]) -> Int32 { helpMessage: "Inputs,to,ignore,separated,by,commas. (Default: none)" ) cli.addOptions(ignored) - + let holdLow = BoolOption( longFlag: "holdLow", helpMessage: "Hold ignored inputs to low in the simulation instead of high. (Default: holdHigh)" @@ -192,7 +206,7 @@ func main(arguments: [String]) -> Int32 { helpMessage: "define statements to include during simulations. (Default: none)" ) cli.addOptions(defs) - + let include = StringOption( longFlag: "inc", helpMessage: "Verilog files to include during simulations. (Default: none)" @@ -227,14 +241,14 @@ func main(arguments: [String]) -> Int32 { Stderr.print("Invoke fault --help for more info.") return EX_USAGE } - + let randomGenerator = rng.value ?? defaultRNG guard let tvAttempts = Int(testVectorCount.value ?? defaultTVCount), let tvIncrement = Int(testVectorIncrement.value ?? defaultTVIncrement), let tvMinimumCoverageInt = Int(minimumCoverage.value ?? defaultMinimumCoverage), - Int(ceiling.value ?? defaultCeiling) != nil && URNGFactory.validNames.contains(randomGenerator) && ((tvGen.value == nil) == (bench.value == nil)) + Int(ceiling.value ?? defaultCeiling) != nil, URNGFactory.validNames.contains(randomGenerator), (tvGen.value == nil) == (bench.value == nil) else { cli.printUsage() return EX_USAGE @@ -251,19 +265,19 @@ func main(arguments: [String]) -> Int32 { Stderr.print("Option --clock is required.") Stderr.print("Invoke fault --help for more info.") return EX_USAGE - } + } guard let cells = cellsOption.value else { Stderr.print("Option --cellModel is required.") Stderr.print("Invoke fault --help for more info.") return EX_USAGE - } + } if !fileManager.fileExists(atPath: cells) { Stderr.print("Cell model file '\(cells)' not found.") return EX_NOINPUT } - if !cells.hasSuffix(".v") && !cells.hasSuffix(".sv") { + if !cells.hasSuffix(".v"), !cells.hasSuffix(".sv") { Stderr.print( "Warning: Cell model file provided does not end with .v or .sv." ) @@ -271,22 +285,22 @@ func main(arguments: [String]) -> Int32 { let jsonOutput = filePath.value ?? "\(file).tv.json" let svfOutput = svfFilePath.value ?? "\(file).tv.svf" - var ignoredInputs: Set - = Set(ignored.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - + var ignoredInputs + = Set(ignored.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) + ignoredInputs.insert(clockName) let behavior - = Array( + = [Simulator.Behavior]( repeating: holdLow.value ? .holdLow : .holdHigh, count: ignoredInputs.count ) - let defines: Set - = Set(defs.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) + let defines + = Set(defs.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) + + let includeFiles + = Set(include.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) - let includeFiles: Set - = Set(include.value?.components(separatedBy: ",").filter {$0 != ""} ?? []) - var includeString = "" for file in includeFiles { if !fileManager.fileExists(atPath: file) { @@ -297,11 +311,13 @@ func main(arguments: [String]) -> Int32 { `include "\(file)" """ } - + // MARK: Importing Python and Pyverilog + let parse = Python.import("pyverilog.vparser.parser").parse // MARK: Parsing and Processing + let parseResult = parse([file]) let ast = parseResult[0] let description = ast[dynamicMember: "description"] @@ -323,7 +339,8 @@ func main(arguments: [String]) -> Int32 { print("Processing module \(definition.name)…") // MARK: TV Generation Mode Selection - var tvSetVectors:[TestVector] = [] + + var tvSetVectors: [TestVector] = [] var tvSetInputs: [Port] = [] if let tvSetTest = tvSet.value { @@ -332,18 +349,18 @@ func main(arguments: [String]) -> Int32 { return EX_NOINPUT } do { - if tvSetTest.hasSuffix(".json"){ + if tvSetTest.hasSuffix(".json") { (tvSetVectors, tvSetInputs) = try TVSet.readFromJson(file: tvSetTest) } else { (tvSetVectors, tvSetInputs) = try TVSet.readFromText(file: tvSetTest) } - } catch{ + } catch { cli.printUsage() return EX_USAGE } print("Read \(tvSetVectors.count) vectors.") } - + if let tvGenerator = tvGen.value, ETVGFactory.validNames.contains(tvGenerator) { let etvgen = ETVGFactory.get(name: tvGenerator)! let benchUnwrapped = bench.value! // Program exits if tvGen.value isn't nil and bench.value is or vice versa @@ -366,14 +383,14 @@ func main(arguments: [String]) -> Int32 { let finalTvCeiling = Int( ceiling.value ?? ( tvSetVectors.count == 0 ? - defaultCeiling: - String(tvSetVectors.count) + defaultCeiling : + String(tvSetVectors.count) ) )! do { let (ports, inputs, outputs) = try Port.extract(from: definition) - + if inputs.count == 0 { print("Module has no inputs.") return EX_OK @@ -382,8 +399,9 @@ func main(arguments: [String]) -> Int32 { print("Module has no outputs.") return EX_OK } - + // MARK: Discover fault points + var faultPoints: Set = [] var gateCount = 0 var inputsMinusIgnored: [Port] = [] @@ -397,7 +415,7 @@ func main(arguments: [String]) -> Int32 { !ignoredInputs.contains($0.name) } } - + for (_, port) in ports { if ignoredInputs.contains(port.name) { continue @@ -407,14 +425,14 @@ func main(arguments: [String]) -> Int32 { } 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)]") } } } var warnAboutDFF = false - + for itemDeclaration in definition.items { let type = Python.type(itemDeclaration).__name__ @@ -438,6 +456,7 @@ func main(arguments: [String]) -> Int32 { print("Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports.") // MARK: Simulation + let startTime = CFAbsoluteTimeGetCurrent() print("Performing simulations…") @@ -464,15 +483,15 @@ func main(arguments: [String]) -> Int32 { using: iverilogExecutable, with: vvpExecutable ) - + let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime print("Time elapsed: \(String(format: "%.2f", timeElapsed))s.") print("Simulations concluded: Coverage \(result.coverage * 100)%") - + let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted - + let tvInfo = TVInfo( inputs: inputsMinusIgnored, outputs: outputs, @@ -490,7 +509,7 @@ func main(arguments: [String]) -> Int32 { try File.open(jsonOutput, mode: .write) { try $0.print(string) } - + try File.open(svfOutput, mode: .write) { try $0.print(svfString) } diff --git a/Sources/Fault/Entries/synth.swift b/Sources/Fault/Entries/synth.swift index 4552daa..1bcbf93 100644 --- a/Sources/Fault/Entries/synth.swift +++ b/Sources/Fault/Entries/synth.swift @@ -1,6 +1,20 @@ -import Foundation +// 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 CommandLineKit import Defile +import Foundation func synth(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -16,7 +30,7 @@ func synth(arguments: [String]) -> Int32 { let topModule = StringOption(shortFlag: "t", longFlag: "top", helpMessage: "Top module. (Required.)") cli.addOptions(topModule) - + do { try cli.parse() } catch { @@ -35,7 +49,7 @@ func synth(arguments: [String]) -> Int32 { Stderr.print("At least one Verilog file is required.") Stderr.print("Invoke fault synth --help for more info.") return EX_USAGE - } + } let fileManager = FileManager() let files = args @@ -63,7 +77,7 @@ func synth(arguments: [String]) -> Int32 { Stderr.print("Liberty file '\(libertyFile)' not found.") return EX_NOINPUT } - + if !libertyFile.hasSuffix(".lib") { Stderr.print( "Warning: Liberty file provided does not end with .lib." @@ -78,7 +92,7 @@ func synth(arguments: [String]) -> Int32 { let result = "echo '\(script)' | '\(yosysExecutable)'".sh() if result != EX_OK { - Stderr.print("A yosys error has occurred."); + Stderr.print("A yosys error has occurred.") return Int32(result) } diff --git a/Sources/Fault/Future.swift b/Sources/Fault/Future.swift index 5c0a040..0a31a78 100644 --- a/Sources/Fault/Future.swift +++ b/Sources/Fault/Future.swift @@ -1,3 +1,17 @@ +// 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 public class Future { @@ -18,4 +32,4 @@ public class Future { let value = store! return value } -} \ No newline at end of file +} diff --git a/Sources/Fault/JTAG.swift b/Sources/Fault/JTAG.swift index e960678..7dc4c79 100644 --- a/Sources/Fault/JTAG.swift +++ b/Sources/Fault/JTAG.swift @@ -1,6 +1,20 @@ +// 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 BigInt import Foundation import PythonKit -import BigInt class TapCreator { var name: String @@ -12,8 +26,8 @@ class TapCreator { self.name = name self.Node = Node } - - func create( + + func create( tapInfo: TapInfo, tms: String, tck: String, @@ -25,11 +39,11 @@ class TapCreator { sout: String, shift: String, test: String - )-> ( tapModule: PythonObject, wires: [PythonObject]) { + ) -> (tapModule: PythonObject, wires: [PythonObject]) { let pads = tapInfo.tap let chain = tapInfo.chain - let wireDeclarations: [PythonObject] = [ + let wireDeclarations: [PythonObject] = [ Node.Wire(pads.tdo), Node.Wire(pads.tdoEnable_n), Node.Wire(pads.tms), @@ -38,7 +52,7 @@ class TapCreator { Node.Wire(chain.sin), Node.Wire(chain.sout), Node.Wire(chain.shift), - Node.Wire(chain.test) + Node.Wire(chain.test), ] let portArguments = [ @@ -83,18 +97,18 @@ class TapCreator { Node.PortArg( chain.shift, Node.Identifier(shift) - ) + ), ] let submoduleInstance = Node.Instance( - self.name, - "__" + self.name + "__", + name, + "__" + name + "__", Python.tuple(portArguments), Python.tuple() ) let tapModule = Node.InstanceList( - self.name, + name, Python.tuple(), Python.tuple([submoduleInstance]) ) @@ -135,10 +149,10 @@ struct Chain: Codable { var test: String init( - sin: String, - sout: String, - shift: String, - test: String + sin: String, + sout: String, + shift: String, + test: String ) { self.sin = sin self.sout = sout @@ -151,7 +165,7 @@ struct Chain: Codable { struct TapInfo: Codable { var tap: Tap var chain: Chain - + init( tap: Tap, chain: Chain @@ -161,4 +175,4 @@ struct TapInfo: Codable { } static let `default`: TapInfo = try! JSONDecoder().decode(TapInfo.self, from: Data(TapCreator.info.utf8)) -} \ No newline at end of file +} diff --git a/Sources/Fault/JTAGStrings.swift b/Sources/Fault/JTAGStrings.swift index d05759c..569cf37 100644 --- a/Sources/Fault/JTAGStrings.swift +++ b/Sources/Fault/JTAGStrings.swift @@ -1,3 +1,17 @@ +// 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. + extension TapCreator { static let info = """ { @@ -49,7 +63,7 @@ extension TapCreator { output shift; output test; output sin; - + wire chain_tdi_i; wire __trst_high__; wire tdo_padoe_o; @@ -58,7 +72,7 @@ extension TapCreator { wire run_test_idle_o; wire test_logic_reset_o; wire exit1_dr_o; - + assign chain_tdi_i = sout; // negate trst (active low according to the standard) @@ -90,7 +104,7 @@ extension TapCreator { endmodule """ - + static let top: String = """ ////////////////////////////////////////////////////////////////////// //// //// @@ -232,13 +246,13 @@ extension TapCreator { sample_preload_select_o, mbist_select_o, debug_select_o, - + // Select signals for internal scan-chain preload_chain_o, // TDO signal that is connected to TDI of sub-modules. tdo_o, - + // TDI signals from sub-modules debug_tdi_i, // from debug module bs_chain_tdi_i, // from Boundary Scan Chain @@ -757,4 +771,4 @@ extension TapCreator { endmodule """ -} \ No newline at end of file +} diff --git a/Sources/Fault/Metadata.swift b/Sources/Fault/Metadata.swift index 244ec6d..97ea554 100644 --- a/Sources/Fault/Metadata.swift +++ b/Sources/Fault/Metadata.swift @@ -1,14 +1,29 @@ -import Foundation +// 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 Defile +import Foundation struct ChainRegister: Codable { enum Kind: String, Codable { case input case output case dff - case bypassInput // bypass when loading the TV (loaded with zeros) + case bypassInput // bypass when loading the TV (loaded with zeros) case bypassOutput // bypass when shifting out the response () } + var name: String var kind: Kind var width: Int @@ -43,7 +58,7 @@ struct ChainMetadata: Codable { self.sin = sin self.sout = sout } - + static func extract(file: String) -> ([ChainRegister], Int, Int) { guard let string = File.read(file) else { Stderr.print("Could not read file '\(file)'") @@ -54,7 +69,7 @@ struct ChainMetadata: Codable { Stderr.print("Fault metadata not provided.") exit(EX_NOINPUT) } - let slice = components[1]; + let slice = components[1] if !slice.contains("' END FAULT METADATA */") { Stderr.print("Fault metadata not terminated.") exit(EX_NOINPUT) @@ -76,15 +91,15 @@ struct ChainMetadata: Codable { struct binMetadata: Codable { var count: Int var length: Int - init ( + init( count: Int, length: Int ) { self.count = count self.length = length } - static func extract(file: String) -> (Int, Int) { + static func extract(file: String) -> (Int, Int) { guard let binString = File.read(file) else { Stderr.print("Could not read file '\(file)'") exit(EX_NOINPUT) @@ -95,13 +110,13 @@ struct binMetadata: Codable { Stderr.print("Fault metadata not terminated.") exit(EX_NOINPUT) } - + let decoder = JSONDecoder() let metadataString = slice.components(separatedBy: "' END FAULT METADATA */")[0] guard let metadata = try? decoder.decode(binMetadata.self, from: metadataString.data(using: .utf8)!) else { Stderr.print("Metadata json is invalid.") exit(EX_DATAERR) } - return(count: metadata.count, length: metadata.length) + return (count: metadata.count, length: metadata.length) } -} \ No newline at end of file +} diff --git a/Sources/Fault/Mux.swift b/Sources/Fault/Mux.swift new file mode 100644 index 0000000..4b8928a --- /dev/null +++ b/Sources/Fault/Mux.swift @@ -0,0 +1,66 @@ +// 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 + +class MuxCreator { + var Node: PythonObject + var muxInfo: MuxInfo + init(using Node: PythonObject, muxInfo: MuxInfo) { + self.Node = Node + self.muxInfo = muxInfo + } + + func create( + for instance: String, + selection: PythonObject, + a: PythonObject, + b: PythonObject + ) -> (cellDeclarations: [PythonObject], wireDeclarations: [PythonObject], replacementHook: PythonObject) { + let muxName = instance + "__scanchain_mux" + let outputWireName = "\(muxName)_\(muxInfo.y)" + let outputWireDecl = Node.Wire(outputWireName) + let outputWire = Node.Identifier(outputWireName) + + // Cell + let portArguments = [ + Node.PortArg(muxInfo.a, a), + Node.PortArg(muxInfo.b, b), + Node.PortArg(muxInfo.s, selection), + Node.PortArg(muxInfo.y, outputWire), + ] + let instance = Node.Instance( + muxInfo.name, + muxName, + Python.tuple(portArguments), + Python.tuple() + ) + let instanceDecl = Node.InstanceList( + muxInfo.name, + Python.tuple(), + Python.tuple([instance]) + ) + // Hook + var hook = outputWire + if muxInfo.invertedOutput { + hook = Node.Unot(hook) + } + return ([ + instanceDecl, + ], [ + outputWireDecl, + ], hook) + } +} diff --git a/Sources/Fault/Port.swift b/Sources/Fault/Port.swift index 5082db1..3ded858 100644 --- a/Sources/Fault/Port.swift +++ b/Sources/Fault/Port.swift @@ -1,5 +1,19 @@ -import PythonKit +// 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 class Port: Codable { enum Polarity: String, Codable { @@ -7,6 +21,7 @@ class Port: Codable { case output case unknown } + var name: String var polarity: Polarity? var from: Int @@ -14,13 +29,13 @@ class Port: Codable { var ordinal: Int var width: Int { - return from < to ? to - from + 1 : from - to + 1 + from < to ? to - from + 1 : from - to + 1 } init(name: String, at ordinal: Int) { self.name = name - self.from = 0 - self.to = 0 + from = 0 + to = 0 self.ordinal = ordinal } @@ -33,7 +48,7 @@ class Port: Codable { let port = Port(name: "\(portDeclaration.name)", at: i) ports["\(portDeclaration.name)"] = port } - + for itemDeclaration in definition.items { let type = Python.type(itemDeclaration).__name__ // Process port declarations further @@ -41,15 +56,15 @@ class Port: Codable { let declaration = itemDeclaration.list[0] let declType = Python.type(declaration).__name__ if declType == "Parameter" { - paramaters["\(declaration.name)"] = + paramaters["\(declaration.name)"] = Port.evaluate(expr: declaration.value.var, params: paramaters) } else if declType == "Input" || declType == "Output" { guard let port = ports["\(declaration.name)"] else { throw "Unknown port \(declaration.name)" } if declaration.width != Python.None { - let msb = Port.evaluate(expr: declaration.width.msb, params: paramaters) - let lsb = Port.evaluate(expr: declaration.width.lsb, params: paramaters) + let msb = Port.evaluate(expr: declaration.width.msb, params: paramaters) + let lsb = Port.evaluate(expr: declaration.width.lsb, params: paramaters) port.from = msb port.to = lsb } @@ -80,39 +95,38 @@ class Port: Codable { let left = Port.evaluate(expr: expr.left, params: params) let right = Port.evaluate(expr: expr.right, params: params) value = Port.op[type]!(left, right) - break case "IntConst": value = Int("\(expr.value)")! - break case "Identifier": value = params["\(expr.name)"]! - break default: - print("Got unknow expression type \(type)"); + print("Got unknow expression type \(type)") exit(EX_DATAERR) - } + } return value } static let op = [ "Minus": sub, "Plus": add, - "Sll": sll + "Sll": sll, ] } extension Port: CustomStringConvertible { var description: String { - return "Port(\(name): \(polarity ?? .unknown)[\(from)..\(to)])" + "Port(\(name): \(polarity ?? .unknown)[\(from)..\(to)])" } } -func add (left: Int, right: Int) -> Int { - return left + right +func add(left: Int, right: Int) -> Int { + left + right +} + +func sub(left: Int, right: Int) -> Int { + left - right } -func sub (left: Int, right: Int) -> Int { - return left - right + +func sll(left: Int, right: Int) -> Int { + left << right } -func sll(left: Int ,right: Int) -> Int { - return left << right -} \ No newline at end of file diff --git a/Sources/Fault/RNGFactory.swift b/Sources/Fault/RNGFactory.swift index fc2dba2..b980666 100644 --- a/Sources/Fault/RNGFactory.swift +++ b/Sources/Fault/RNGFactory.swift @@ -1,12 +1,26 @@ -import Foundation +// 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 BigInt +import Foundation protocol URNG { init() func generate(bits: Int) -> BigUInt } -class URNGFactory { +enum URNGFactory { private static var registry: [String: URNG.Type] = [:] static func register(name: String, type: T.Type) -> Bool { @@ -22,6 +36,6 @@ class URNGFactory { } static var validNames: [String] { - return [String](registry.keys) + [String](registry.keys) } -} \ No newline at end of file +} diff --git a/Sources/Fault/SCLConfiguration.swift b/Sources/Fault/SCLConfiguration.swift new file mode 100644 index 0000000..0412f3d --- /dev/null +++ b/Sources/Fault/SCLConfiguration.swift @@ -0,0 +1,59 @@ +// 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. + +class DFFMatch: Codable, CustomStringConvertible { + var name: String + var clk: String + var d: String + var q: String + + init(name: String, clk: String, d: String, q: String) { + self.name = name + self.clk = clk + self.d = d + self.q = q + } + + var description: String { + " \(q)>" + } +} + +class MuxInfo: Codable { + var name: String + var a: String + var b: String + var y: String + var s: String + var invertedOutput: Bool = false + + init(name: String, a: String, b: String, y: String, s: String, invertedOutput: Bool = false) { + self.name = name + self.a = a + self.b = b + self.y = y + self.s = s + self.invertedOutput = invertedOutput + } +} + +class SCLConfiguration: Codable { + var dffMatches: [DFFMatch] + var muxInfo: MuxInfo? + + init(dffMatches: [DFFMatch], muxInfo: MuxInfo? = nil) { + self.dffMatches = dffMatches + self.muxInfo = muxInfo + } +} diff --git a/Sources/Fault/SerialVectorFormat.swift b/Sources/Fault/SerialVectorFormat.swift index c13f0b6..63fd702 100644 --- a/Sources/Fault/SerialVectorFormat.swift +++ b/Sources/Fault/SerialVectorFormat.swift @@ -1,11 +1,23 @@ -import BigInt +// 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. -class SerialVectorCreator { +import BigInt +enum SerialVectorCreator { static func create( tvInfo: TVInfo ) throws -> String { - var scanStatements = "" let chainLength: Int = { @@ -15,7 +27,7 @@ class SerialVectorCreator { } return count }() - + for tvcPair in tvInfo.coverageList { var tdi = "" let testVector = tvcPair.vector @@ -27,10 +39,10 @@ class SerialVectorCreator { if let tdiInt = BigUInt(tdi, radix: 2) { let tdiHex = String(tdiInt, radix: 16) - + let mask = String(repeating: "f", count: tdiHex.count) if let output = BigUInt(tvcPair.goldenOutput, radix: 16) { - let hexOutput = String(output, radix: 16) + let hexOutput = String(output, radix: 16) scanStatements += "SDR \(chainLength) TDI (\(tdiHex)) MASK (\(mask)) TDO (\(hexOutput)); \n" } else { print("TV golden output \(tvcPair.goldenOutput) is invalid.") @@ -41,7 +53,7 @@ class SerialVectorCreator { } var svf: String { - return """ + """ ! Begin Test Program ! Disable Test Reset line TRST OFF; @@ -60,6 +72,6 @@ class SerialVectorCreator { \(scanStatements) """ } - return svf; + return svf } -} \ No newline at end of file +} diff --git a/Sources/Fault/Simulation.swift b/Sources/Fault/Simulation.swift index 17c8efb..e644821 100644 --- a/Sources/Fault/Simulation.swift +++ b/Sources/Fault/Simulation.swift @@ -1,9 +1,23 @@ -import Foundation +// 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 BigInt import Defile +import Foundation import PythonKit -import BigInt -class Simulator { +enum Simulator { enum Behavior: Int { case holdHigh = 1 case holdLow = 0 @@ -14,7 +28,7 @@ class Simulator { for faultPoints: Set, in file: String, module: String, - with cells: String, + with cells: String, ports: [String: Port], inputs: [Port], ignoring ignoredInputs: Set, @@ -27,8 +41,8 @@ class Simulator { filePrefix: String = ".", defines: Set = [], includes: String, - using iverilogExecutable: String, - with vvpExecutable: String + using _: String, + with _: String ) throws -> (faults: [String], goldenOutput: String) { var portWires = "" var portHooks = "" @@ -42,7 +56,7 @@ class Simulator { } let folderName = "\(filePrefix)/thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" - let _ = "mkdir -p \(folderName)".sh() + _ = "mkdir -p \(folderName)".sh() var inputAssignment = "" var fmtString = "" @@ -73,29 +87,28 @@ class Simulator { defineStatements += "-D\(def) " } - var outputCount = 0 + var outputCount = 0 var outputComparison = "" var outputAssignment = "" for output in outputs { let name = (output.name.hasPrefix("\\")) ? output.name : "\\\(output.name)" outputComparison += " ( \(name) != \(name).gm ) || " if output.width > 1 { - for i in 0.. (coverageList: [TVCPair], coverage: Float) { - var testVectorHash: Set = [] var coverageList: [TVCPair] = [] @@ -224,20 +236,20 @@ class Simulator { var totalTVAttempts = 0 var tvAttempts = (initialVectorCount < ceiling) ? initialVectorCount : ceiling - + let simulateOnly = (TVSet.count != 0) let rng: URNG = URNGFactory.get(name: randomGenerator)! - while coverage < minimumCoverage && totalTVAttempts < ceiling { + while coverage < minimumCoverage, totalTVAttempts < ceiling { if totalTVAttempts > 0 { print("Minimum coverage not met (\(coverage * 100)%/\(minimumCoverage * 100)%,) incrementing to \(totalTVAttempts + tvAttempts)…") } var futureList: [Future] = [] var testVectors: [TestVector] = [] - for index in 0.. = [], includes: String, - using iverilogExecutable: String, - with vvpExecutable: String + using _: String, + with _: String ) throws -> Bool { - var portWires = "" var portHooks = "" for (rawName, port) in ports { @@ -394,15 +405,15 @@ class Simulator { for input in inputs { let name = (input.name.hasPrefix("\\")) ? input.name : "\\\(input.name)" if input.name == reset { - inputAssignment += " \(name) = \( resetActive == .low ? 0 : 1 ) ;\n" + inputAssignment += " \(name) = \(resetActive == .low ? 0 : 1) ;\n" } else { inputAssignment += " \(name) = 0 ;\n" } } var serial = "0" - for _ in 0.. = [], includes: String, - using iverilogExecutable: String, - with vvpExecutable: String + using _: String, + with _: String ) throws -> Bool { var portWires = "" var portHooks = "" @@ -509,12 +520,11 @@ class Simulator { for input in inputs { let name = (input.name.hasPrefix("\\")) ? input.name : "\\\(input.name)" if input.name == reset { - inputInit += " \(name) = \( resetActive == .low ? 0 : 1 ) ;\n" - } - else { + inputInit += " \(name) = \(resetActive == .low ? 0 : 1) ;\n" + } else { inputInit += " \(name) = 0 ;\n" } - } + } var clockCreator = "" if !clock.isEmpty { @@ -526,11 +536,11 @@ class Simulator { resetToggler = "\(reset) = ~\(reset);" } - var serial: String = "" - for _ in 0.., behavior: [Behavior], - outputs: [Port], + outputs _: [Port], clock: String, reset: String, resetActive: Active = .low, @@ -639,18 +649,17 @@ class Simulator { tdo: String, trst: String, output: String, - chainLength: Int , + chainLength _: Int, vecbinFile: String, outbinFile: String, - vectorCount: Int, + vectorCount: Int, vectorLength: Int, outputLength: Int, defines: Set = [], includes: String, - using iverilogExecutable: String, - with vvpExecutable: String + using _: String, + with _: String ) throws -> Bool { - var portWires = "" var portHooks = "" for (rawName, port) in ports { @@ -669,17 +678,15 @@ class Simulator { for input in inputs { let name = (input.name.hasPrefix("\\")) ? input.name : "\\\(input.name)" if input.name == reset { - inputAssignment += " \(name) = \( resetActive == .low ? 0 : 1 ) ;\n" + inputAssignment += " \(name) = \(resetActive == .low ? 0 : 1) ;\n" } else if input.name == tms { inputAssignment += " \(name) = 1 ;\n" - } - else { + } else { inputAssignment += " \(name) = 0 ;\n" - if (input.name != clock && !tapPorts.contains(input.name)){ - } + if input.name != clock, !tapPorts.contains(input.name) {} } - } - + } + var clockCreator = "" if !clock.isEmpty { clockCreator = "always #(`CLOCK_PERIOD / 2) \(clock) = ~\(clock);" @@ -689,14 +696,14 @@ class Simulator { resetToggler = "\(reset) = ~\(reset);" } var testStatements = "" - for i in 0.. Bool { - try File.open(output, mode: .write) { try $0.print(bench) } @@ -834,8 +840,7 @@ class Simulator { } let iverilogResult = "'\(iverilogExecutable)' -B '\(iverilogBase)' \(define) -Ttyp -o \(aoutName) \(output) 2>&1 > /dev/null".shOutput() - - + if iverilogResult.terminationStatus != EX_OK { Stderr.print("An iverilog error has occurred: ") Stderr.print(iverilogResult.output) @@ -850,50 +855,50 @@ class Simulator { return vvpTask.output.contains("SUCCESS_STRING") } - private static func createTasks (tms: String, tdi: String) -> String { - return """ - task shiftIR; - input[3:0] __instruction__; - integer i; - begin - for (i = 0; i< 5; i = i + 1) begin - \(tms) = __tmsPattern__[i]; - #(`CLOCK_PERIOD) ; - end + private static func createTasks(tms: String, tdi: String) -> String { + """ + task shiftIR; + input[3:0] __instruction__; + integer i; + begin + for (i = 0; i< 5; i = i + 1) begin + \(tms) = __tmsPattern__[i]; + #(`CLOCK_PERIOD) ; + end - // At shift-IR: shift new instruction on tdi line - for (i = 0; i < 4; i = i + 1) begin - \(tdi) = __instruction__[i]; - if(i == 3) begin - \(tms) = __tmsPattern__[5]; // exit-ir + // At shift-IR: shift new instruction on tdi line + for (i = 0; i < 4; i = i + 1) begin + \(tdi) = __instruction__[i]; + if(i == 3) begin + \(tms) = __tmsPattern__[5]; // exit-ir + end + #(`CLOCK_PERIOD) ; end + + \(tms) = __tmsPattern__[6]; // update-ir #(`CLOCK_PERIOD) ; + \(tms) = __tmsPattern__[7]; // run test-idle + #(`CLOCK_PERIOD * 3) ; end + endtask - \(tms) = __tmsPattern__[6]; // update-ir - #(`CLOCK_PERIOD) ; - \(tms) = __tmsPattern__[7]; // run test-idle - #(`CLOCK_PERIOD * 3) ; - end - endtask - - task enterShiftDR; - begin - \(tms) = 1; // select DR - #(`CLOCK_PERIOD) ; - \(tms) = 0; // capture DR -- shift DR - #(`CLOCK_PERIOD * 2) ; - end - endtask + task enterShiftDR; + begin + \(tms) = 1; // select DR + #(`CLOCK_PERIOD) ; + \(tms) = 0; // capture DR -- shift DR + #(`CLOCK_PERIOD * 2) ; + end + endtask - task exitDR; - begin - \(tms) = 1; // Exit DR -- update DR - #(`CLOCK_PERIOD * 2) ; - \(tms) = 0; // Run test-idle - #(`CLOCK_PERIOD) ; - end - endtask - """ + task exitDR; + begin + \(tms) = 1; // Exit DR -- update DR + #(`CLOCK_PERIOD * 2) ; + \(tms) = 0; // Run test-idle + #(`CLOCK_PERIOD) ; + end + endtask + """ } } diff --git a/Sources/Fault/String.swift b/Sources/Fault/String.swift index 9e3c8a8..d5aa3d1 100644 --- a/Sources/Fault/String.swift +++ b/Sources/Fault/String.swift @@ -1,5 +1,19 @@ -import Foundation +// 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 Defile +import Foundation extension String { func shOutput() -> (terminationStatus: Int32, output: String) { @@ -31,7 +45,7 @@ extension String { task.executableURL = URL(fileURLWithPath: "/usr/bin/env") task.arguments = ["sh", "-c", self] - if (silent) { + if silent { task.standardOutput = FileHandle.nullDevice task.standardError = FileHandle.nullDevice } @@ -49,7 +63,7 @@ extension String { } func uniqueName(_ number: Int) -> String { - return "__" + self + "_" + String(describing: number) + "__" + "__" + self + "_" + String(describing: number) + "__" } } @@ -74,12 +88,12 @@ extension String { let date = Date() let dateString = dateFormatter.string(from: date) - return """ + return """ /* Automatically generated by Fault Do not modify. Generated on: \(dateString) */ - """; + """ } -} \ No newline at end of file +} diff --git a/Sources/Fault/Synthesis.swift b/Sources/Fault/Synthesis.swift index acb8a40..950d35a 100644 --- a/Sources/Fault/Synthesis.swift +++ b/Sources/Fault/Synthesis.swift @@ -1,4 +1,18 @@ -struct Synthesis { +// 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. + +enum Synthesis { enum Gate: String { case and = "AND" case nand = "NAND" @@ -25,7 +39,7 @@ struct Synthesis { ) -> String { let opt = optimize ? "opt" : "" return """ - read_verilog \(files.map({(file) in "'\(file)'" }).joined(separator: " ")) + read_verilog \(files.map { file in "'\(file)'" }.joined(separator: " ")) # check design hierarchy hierarchy \(checkHierarchy ? "-check" : "") -top \(module) @@ -63,4 +77,4 @@ struct Synthesis { write_verilog -noattr -noexpr \(output) """ } -} \ No newline at end of file +} diff --git a/Sources/Fault/TVGenerator.swift b/Sources/Fault/TVGenerator.swift index 805b256..543bb31 100644 --- a/Sources/Fault/TVGenerator.swift +++ b/Sources/Fault/TVGenerator.swift @@ -1,13 +1,27 @@ -import Foundation +// 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 BigInt import Defile +import Foundation protocol ExternalTestVectorGenerator { init() func generate(file: String, module: String) -> ([TestVector], [Port]) } -class ETVGFactory { +enum ETVGFactory { private static var registry: [String: ExternalTestVectorGenerator.Type] = [:] static func register(name: String, type: T.Type) -> Bool { @@ -23,7 +37,7 @@ class ETVGFactory { } static var validNames: [String] { - return [String](registry.keys) + [String](registry.keys) } } @@ -31,13 +45,12 @@ class Atalanta: ExternalTestVectorGenerator { required init() {} func generate(file: String, module: String) -> ([TestVector], [Port]) { - let tempDir = "\(NSTemporaryDirectory())" let folderName = "\(tempDir)thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" let _ = "mkdir -p '\(folderName)'".sh() defer { - let _ = "rm -rf '\(folderName)'".sh() + let _ = "rm -rf '\(folderName)'".sh() } let output = "\(folderName)/\(module).test" @@ -46,7 +59,7 @@ class Atalanta: ExternalTestVectorGenerator { if atalanta != EX_OK { exit(atalanta) } - + do { let (testvectors, inputs) = try TVSet.readFromTest(file: output) return (vectors: testvectors, inputs: inputs) @@ -68,7 +81,7 @@ class PODEM: ExternalTestVectorGenerator { let folderName = "\(tempDir)thr\(Unmanaged.passUnretained(Thread.current).toOpaque())" let _ = "mkdir -p '\(folderName)'".sh() defer { - let _ = "rm -rf '\(folderName)'".sh() + let _ = "rm -rf '\(folderName)'".sh() } let output = "\(folderName)/\(module).out" @@ -77,7 +90,7 @@ class PODEM: ExternalTestVectorGenerator { if podem != EX_OK { exit(podem) } - do { + do { let (testvectors, inputs) = try TVSet.readFromText(file: output) return (vectors: testvectors, inputs: inputs) } catch { @@ -90,19 +103,19 @@ class PODEM: ExternalTestVectorGenerator { } // MARK: Random Generators + class SwiftRNG: URNG { required init() {} func generate(bits: Int) -> BigUInt { - return BigUInt.randomInteger(withMaximumWidth: bits) + BigUInt.randomInteger(withMaximumWidth: bits) } static let registered = URNGFactory.register(name: "swift", type: SwiftRNG.self) } - class LFSR: URNG { - static let taps: [UInt: Array] = [ + static let taps: [UInt: [UInt]] = [ // nbits : Feedback Polynomial 2: [2, 1], 3: [3, 2], @@ -135,23 +148,23 @@ class LFSR: URNG { 30: [30, 6, 4, 1], 31: [31, 28], 32: [32, 30, 26, 25], - 64: [64, 63, 61, 60] - ]; + 64: [64, 63, 61, 60], + ] var seed: UInt var polynomialHex: UInt - let nbits: UInt - - required init(nbits: UInt = 64) { - let max: UInt = (nbits == 64) ? UInt(pow(Double(2),Double(63))-1) : (1 << nbits) - 1 - let polynomial = LFSR.taps[nbits]! + let nbits: UInt + + required init(nbits: UInt = 64) { + let max: UInt = (nbits == 64) ? UInt(pow(Double(2), Double(63)) - 1) : (1 << nbits) - 1 + let polynomial = LFSR.taps[nbits]! - self.seed = UInt.random(in: 1...max) + seed = UInt.random(in: 1 ... max) self.nbits = nbits - self.polynomialHex = 0 + polynomialHex = 0 for tap in polynomial { - self.polynomialHex = self.polynomialHex | (1 << (nbits-tap)); + polynomialHex = polynomialHex | (1 << (nbits - tap)) } } @@ -163,16 +176,16 @@ class LFSR: URNG { var parityVal: UInt = 0 var numberTemp = number while numberTemp != 0 { - parityVal ^= 1 + parityVal ^= 1 numberTemp = numberTemp & (numberTemp - 1) } return parityVal } func rand() -> UInt { - let feedbackBit: UInt = LFSR.parity(number: self.seed & self.polynomialHex) - self.seed = (self.seed >> 1) | (feedbackBit << (self.nbits - 1)) - return self.seed + let feedbackBit: UInt = LFSR.parity(number: seed & polynomialHex) + seed = (seed >> 1) | (feedbackBit << (nbits - 1)) + return seed } func generate(bits: Int) -> BigUInt { @@ -192,4 +205,4 @@ class LFSR: URNG { } static let registered = URNGFactory.register(name: "LFSR", type: LFSR.self) -} \ No newline at end of file +} diff --git a/Sources/Fault/TestVector.swift b/Sources/Fault/TestVector.swift index 8168e64..07f25f2 100644 --- a/Sources/Fault/TestVector.swift +++ b/Sources/Fault/TestVector.swift @@ -1,6 +1,20 @@ -import Foundation +// 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 BigInt import Defile +import Foundation typealias TestVector = [BigUInt] @@ -39,21 +53,21 @@ struct TVInfo: Codable { } } -class TVSet { +enum TVSet { static func readFromJson(file: String) throws -> ([TestVector], [Port]) { let data = try Data(contentsOf: URL(fileURLWithPath: file), options: .mappedIfSafe) guard let tvInfo = try? JSONDecoder().decode(TVInfo.self, from: data) else { Stderr.print("File '\(file)' is invalid.") exit(EX_DATAERR) } - let vectors = tvInfo.coverageList.map{ $0.vector } - return (vectors: vectors , inputs: tvInfo.inputs) + let vectors = tvInfo.coverageList.map(\.vector) + return (vectors: vectors, inputs: tvInfo.inputs) } - static func readFromText(file path: String) throws -> ([TestVector], [Port]){ + static func readFromText(file path: String) throws -> ([TestVector], [Port]) { var inputs: [Port] = [] var vectors: [TestVector] = [] - + guard let file = File.open(path) else { Stderr.print("Test vector set input file '\(path)' not found.") exit(EX_DATAERR) @@ -63,7 +77,7 @@ class TVSet { let ports = lines[0].components(separatedBy: " ") for (index, port) in ports.enumerated() { - if port != "PI"{ + if port != "PI" { inputs.append(Port(name: port, at: index)) } } @@ -71,17 +85,16 @@ class TVSet { var readPorts = true for line in lines[1...] { - let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines) - if (trimmedLine[trimmedLine.startIndex].isNumber){ - let testvector = Array(trimmedLine).map {BigUInt(String($0), radix: 2)!} + if trimmedLine[trimmedLine.startIndex].isNumber { + let testvector = Array(trimmedLine).map { BigUInt(String($0), radix: 2)! } vectors.append(testvector) readPorts = false } else if readPorts { let ports = trimmedLine.components(separatedBy: " ") - for (index, port) in ports.enumerated(){ - if port != "PI"{ + for (index, port) in ports.enumerated() { + if port != "PI" { inputs.append(Port(name: port, at: index)) } } @@ -92,18 +105,18 @@ class TVSet { } return (vectors: vectors, inputs: inputs) } + static func readFromTest(file: String) throws -> ([TestVector], [Port]) { var vectors: [TestVector] = [] var inputs: [Port] = [] do { - let string = try String(contentsOf: URL(fileURLWithPath: file), encoding: .utf8) let inputPattern = "(?s)(?<=\\* Primary inputs :).*?(?=\\* Primary outputs:)" let tvPattern = "(?s)(?<=: ).*?(?= [0-1]*)" let multibitPattern = "(?.*).{1}(?<=\\[)(?[0-9]+)(?=\\])" - var inputResult = "" + var inputResult = "" if let range = string.range(of: inputPattern, options: .regularExpression) { inputResult = String(string[range]) inputResult = inputResult.trimmingCharacters(in: .whitespacesAndNewlines) @@ -112,54 +125,53 @@ class TVSet { let ports = inputResult.components(separatedBy: " ") let multiBitRegex = try NSRegularExpression(pattern: multibitPattern) - var multiBitPorts : [String: Port] = [:] - var portName:String = "", bitNumber:Int = 0 + var multiBitPorts: [String: Port] = [:] + var portName = "", bitNumber = 0 - var count = 0; - for port in ports { - if !port.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty{ + var count = 0 + for port in ports { + if !port.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { if let match = multiBitRegex.firstMatch(in: port, options: [], range: NSRange(location: 0, length: port.utf16.count)) { - if let nameRange = Range(match.range(at: 1), in: port) { + if let nameRange = Range(match.range(at: 1), in: port) { portName = String(port[nameRange]) let exists = multiBitPorts[portName] != nil if !exists { multiBitPorts[portName] = Port(name: portName, at: count) multiBitPorts[portName]!.from = 0 - } + } } if let bitRange = Range(match.range(at: 2), in: port) { bitNumber = Int(port[bitRange])! multiBitPorts[portName]!.to = bitNumber } - } - else { + } else { inputs.append(Port(name: port, at: count)) - } - count += 1 + } + count += 1 } } - var vectorSlices: [Range] = [] + var vectorSlices: [Range] = [] for port in multiBitPorts.values { inputs.append(port) - vectorSlices.append(port.ordinal..<(port.to+port.ordinal)+1) + vectorSlices.append(port.ordinal ..< (port.to + port.ordinal) + 1) } vectorSlices.sort { $0.lowerBound < $1.lowerBound } let vectorRegex = try NSRegularExpression(pattern: tvPattern) let range = NSRange(string.startIndex..., in: string) - let results = vectorRegex.matches(in: string, range: range) - let matches = results.map { String(string[Range($0.range, in: string)!])} + let results = vectorRegex.matches(in: string, range: range) + let matches = results.map { String(string[Range($0.range, in: string)!]) } + + inputs.sort { $0.ordinal < $1.ordinal } - inputs.sort {$0.ordinal < $1.ordinal } - for match in matches { let vector = Array(match) if vector.count != 0 { var testVector: TestVector = [] var start = 0 for slice in vectorSlices { - let lowerVec = vector[start.. \(self.q)>" - } -} - -class MuxInfo: Codable { - var name: String - var a: String - var b: String - var y: String - var s: String - - init(name: String, a: String, b: String, y: String, s: String) { - self.name = name - self.a = a - self.b = b - self.y = y - self.s = s - } -} - -class PDKConfiguration: Codable { - var dffMatches: [DFFMatch] - var muxInfo: MuxInfo? - - init(dffMatches: [DFFMatch], muxInfo: MuxInfo? = nil) { - self.dffMatches = dffMatches - self.muxInfo = muxInfo - } -} diff --git a/Tech/osu035/pdk.yml b/Tech/osu035/config.yml similarity index 88% rename from Tech/osu035/pdk.yml rename to Tech/osu035/config.yml index c0b7100..d10f050 100644 --- a/Tech/osu035/pdk.yml +++ b/Tech/osu035/config.yml @@ -10,3 +10,4 @@ muxInfo: b: B s: S "y": "Y" + invertedOutput: true diff --git a/Tests/FaultTests/FaultTests.swift b/Tests/FaultTests/FaultTests.swift index ff6b0c0..0ba3d2b 100644 --- a/Tests/FaultTests/FaultTests.swift +++ b/Tests/FaultTests/FaultTests.swift @@ -1,13 +1,13 @@ -import XCTest import class Foundation.Bundle +import XCTest var env = ProcessInfo.processInfo.environment extension Process { - func startAndBlock() throws { - try self.launch() - self.waitUntilExit() - } + func startAndBlock() throws { + try launch() + waitUntilExit() + } } extension String { @@ -58,12 +58,12 @@ final class FaultTests: XCTestCase { // Fault Tests let binary = productsDirectory.appendingPathComponent("Fault") - let newProcess = { ()-> Process in - let new = Process() - new.executableURL = binary - new.environment = ProcessInfo.processInfo.environment - new.environment!["PYTHONPATH"] = venvLibVersion - return new + let newProcess = { () -> Process in + let new = Process() + new.executableURL = binary + new.environment = ProcessInfo.processInfo.environment + new.environment!["PYTHONPATH"] = venvLibVersion + return new } let liberty = "Tech/osu035/osu035_stdcells.lib" @@ -73,7 +73,7 @@ final class FaultTests: XCTestCase { let topModule = "SPM" let clock = "clk" let reset = "rst" - let ignoredInputs = "\(reset)" + let ignoredInputs = "\(reset)" let fileSynth = "Netlists/" + fileName + ".netlist.v" let fileCut = fileSynth + ".cut.v" @@ -87,7 +87,6 @@ final class FaultTests: XCTestCase { process.arguments = ["synth", "-l", liberty, "-t", topModule, "-o", fileSynth, fileName] try process.startAndBlock() - XCTAssertEqual(process.terminationStatus, 0) print("1/6") // 1. Cut @@ -95,7 +94,6 @@ final class FaultTests: XCTestCase { process.arguments = ["cut", "-o", fileCut, fileSynth] try process.startAndBlock() - XCTAssertEqual(process.terminationStatus, 0) // 2. Simulate @@ -104,7 +102,6 @@ final class FaultTests: XCTestCase { try process.startAndBlock() print("2/6") - XCTAssertEqual(process.terminationStatus, 0) // 3. Chain @@ -114,7 +111,6 @@ final class FaultTests: XCTestCase { try process.startAndBlock() print("3/6") - XCTAssertEqual(process.terminationStatus, 0) // 4. Assemble @@ -123,7 +119,6 @@ final class FaultTests: XCTestCase { try process.startAndBlock() print("4/6") - XCTAssertEqual(process.terminationStatus, 0) // 5. Compact @@ -143,17 +138,17 @@ final class FaultTests: XCTestCase { /// Returns path to the built products directory. var productsDirectory: URL { - #if os(macOS) - for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { - return bundle.bundleURL.deletingLastPathComponent() - } - fatalError("couldn't find the products directory") - #else - return Bundle.main.bundleURL - #endif + #if os(macOS) + for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { + return bundle.bundleURL.deletingLastPathComponent() + } + fatalError("couldn't find the products directory") + #else + return Bundle.main.bundleURL + #endif } static var allTests = [ - ("testFull", testFull) + ("testFull", testFull), ] } diff --git a/Tests/FaultTests/XCTestManifests.swift b/Tests/FaultTests/XCTestManifests.swift index c6c2912..a2a0a47 100644 --- a/Tests/FaultTests/XCTestManifests.swift +++ b/Tests/FaultTests/XCTestManifests.swift @@ -1,9 +1,9 @@ import XCTest #if !canImport(ObjectiveC) -public func allTests() -> [XCTestCaseEntry] { - return [ - testCase(FaultTests.allTests), - ] -} + public func allTests() -> [XCTestCaseEntry] { + [ + testCase(FaultTests.allTests), + ] + } #endif diff --git a/atalanta_podem_build.swift b/atalanta_podem_build.swift index c6f814a..b855e50 100755 --- a/atalanta_podem_build.swift +++ b/atalanta_podem_build.swift @@ -13,7 +13,6 @@ extension String { } func main() -> Int32 { - let apt = "which apt-get".sh() if apt == 0 { let _ = "sudo apt-get install -y make flex bison libreadline-dev libncurses5-dev libncursesw5-dev ".sh() @@ -22,7 +21,7 @@ func main() -> Int32 { let env = ProcessInfo.processInfo.environment let execPrefix = env["EXEC_PREFIX"] ?? "/usr/local/bin" - + let previousCWD = env["PWD"]! let atalanta = installAtalanta(execPrefix: execPrefix, previousCWD: previousCWD) @@ -30,7 +29,7 @@ func main() -> Int32 { let _ = "echo Failed to install Atalanta".sh() return atalanta } - + let podem = installPODEM(execPrefix: execPrefix, previousCWD: previousCWD) if podem != 0 { @@ -42,7 +41,6 @@ func main() -> Int32 { } func installAtalanta(execPrefix: String, previousCWD: String) -> Int32 { - defer { chdir(previousCWD) } @@ -67,7 +65,6 @@ func installAtalanta(execPrefix: String, previousCWD: String) -> Int32 { } func installPODEM(execPrefix: String, previousCWD: String) -> Int32 { - defer { chdir(previousCWD) } @@ -80,7 +77,7 @@ func installPODEM(execPrefix: String, previousCWD: String) -> Int32 { chdir("podem") let make = "make".sh() - if make != 0 { + if make != 0 { return make } @@ -90,4 +87,5 @@ func installPODEM(execPrefix: String, previousCWD: String) -> Int32 { } return EX_OK } -exit(main()) \ No newline at end of file + +exit(main()) diff --git a/install.swift b/install.swift index 1bbc729..c78e865 100755 --- a/install.swift +++ b/install.swift @@ -38,7 +38,7 @@ extension String { task.executableURL = URL(fileURLWithPath: "/usr/bin/env") task.arguments = ["sh", "-c", self] - if (silent) { + if silent { task.standardOutput = FileHandle.nullDevice task.standardError = FileHandle.nullDevice } @@ -85,7 +85,6 @@ if action == .install { } } - let ivlPath = "[ -d '\(iverilogBase)' ]".shOutput() if ivlPath.terminationStatus != EX_OK { print("Warning: The directory \(iverilogBase) was not found. You may need to export the environment variable 'FAULT_IVL_BASE' when using Fault.") @@ -120,7 +119,7 @@ if action == .install { if podem.terminationStatus != EX_OK { print("Optional component podem does not seem to be installed.") } - + print("Installing Fault…") let fileManager = FileManager() @@ -147,13 +146,13 @@ if action == .install { } let venvPath = "\(path)/FaultInstall/venv" - + let venvCreate = "python3 -m venv '\(venvPath)'".sh() if venvCreate != EX_OK { print("Could not create Python virtual environment: process failed with exit code \(venvCreate).") exit(EX_CANTCREAT) } - + let pipInstall = "'\(venvPath)/bin/python3' -m pip install -r ./requirements.txt".sh() if pipInstall != EX_OK { print("Could not install Python dependencies: process failed with exit code \(pipInstall).") @@ -172,7 +171,6 @@ if action == .install { let libPython = libPythonProcess.output - let launchScript = """ #!/bin/bash set -e @@ -229,4 +227,4 @@ if action == .install { } print("Installed.") -} \ No newline at end of file +} From 1654fa7e6cb0c3e6e2e0d8e4f8d603a266c7eb81 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 1 Jan 2024 15:22:17 +0200 Subject: [PATCH 3/5] Add pragma keep --- Sources/Fault/Mux.swift | 7 +++++++ Sources/Fault/Synthesis.swift | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/Fault/Mux.swift b/Sources/Fault/Mux.swift index 4b8928a..5c33679 100644 --- a/Sources/Fault/Mux.swift +++ b/Sources/Fault/Mux.swift @@ -52,12 +52,19 @@ class MuxCreator { Python.tuple(), Python.tuple([instance]) ) + let pragma = Node.Pragma( + Node.PragmaEntry( + "keep" + ) + ) + // Hook var hook = outputWire if muxInfo.invertedOutput { hook = Node.Unot(hook) } return ([ + pragma, instanceDecl, ], [ outputWireDecl, diff --git a/Sources/Fault/Synthesis.swift b/Sources/Fault/Synthesis.swift index 950d35a..77f814b 100644 --- a/Sources/Fault/Synthesis.swift +++ b/Sources/Fault/Synthesis.swift @@ -74,7 +74,8 @@ enum Synthesis { # cleanup opt_clean -purge - write_verilog -noattr -noexpr \(output) + write_verilog -noexpr \(output).attrs.v + write_verilog -noexpr -noattr \(output) """ } } From fa156cc951f74ba13e0a14b89fb56c11ef3dee37 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 1 Jan 2024 17:33:13 +0200 Subject: [PATCH 4/5] Made `cut` also accept yaml config file --- Dockerfile | 4 +- Sources/Fault/Entries/chain.swift | 23 +++------- Sources/Fault/Entries/cut.swift | 69 +++++++++++++++++++--------- Sources/Fault/SCLConfiguration.swift | 12 +++++ 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/Dockerfile b/Dockerfile index c581608..8308fd9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,6 +70,8 @@ RUN cp /lib64/libtcl8.5.so /build/lib # Fault Setup WORKDIR /fault COPY . . +ENV CC=clang +ENV CXX=clang++ RUN swift build --static-swift-stdlib -c release RUN cp /fault/.build/x86_64-unknown-linux-gnu/release/Fault /build/bin/fault WORKDIR / @@ -93,4 +95,4 @@ ENV PATH=/build/bin:$PATH\ FAULT_IVERILOG=/build/bin/iverilog\ FAULT_VVP=/build/bin/vvp -CMD [ "/bin/bash" ] \ No newline at end of file +CMD [ "/bin/bash" ] diff --git a/Sources/Fault/Entries/chain.swift b/Sources/Fault/Entries/chain.swift index c2d0482..b323d3f 100644 --- a/Sources/Fault/Entries/chain.swift +++ b/Sources/Fault/Entries/chain.swift @@ -18,17 +18,6 @@ import Foundation import PythonKit import Yams -func getMatchingDFFInfo(from list: [DFFMatch], for cell: String, fnmatch: PythonObject) -> DFFMatch? { - for dffinfo in list { - for name in dffinfo.name.components(separatedBy: ",") { - if Bool(fnmatch.fnmatch(cell, name))! { - return dffinfo - } - } - } - return nil -} - func scanChainCreate(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -49,7 +38,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let ignored = StringOption( shortFlag: "i", longFlag: "ignoring", - helpMessage: "Inputs,to,ignore,separated,by,commas." + helpMessage: "Inputs to ignore. Comma-delimited." ) cli.addOptions(ignored) @@ -68,13 +57,13 @@ func scanChainCreate(arguments: [String]) -> Int32 { let clockInv = StringOption( longFlag: "invClock", - helpMessage: "Inverter clk tree source cell name (Default: none)" + helpMessage: "Inverter clk tree source cell name. (Default: none)" ) cli.addOptions(clockInv) let resetOpt = StringOption( longFlag: "reset", - helpMessage: "Reset signal to add to --ignoring and use in simulation. (Required.)" + helpMessage: "Reset signal to add to --ignoring and use in simulation. (Required.)" ) cli.addOptions(resetOpt) @@ -94,14 +83,14 @@ func scanChainCreate(arguments: [String]) -> Int32 { let sclConfigOpt = StringOption( shortFlag: "s", longFlag: "sclConfig", - helpMessage: "Name for the YAML SCL config file. Recommended." + helpMessage: "Path for the YAML SCL config file. Recommended." ) cli.addOptions(sclConfigOpt) let dffOpt = StringOption( shortFlag: "d", longFlag: "dff", - helpMessage: "Optional override for the DFF names from the PDK config." + helpMessage: "Optional override for the DFF names from the PDK config. Comma-delimited. " ) cli.addOptions(dffOpt) @@ -113,7 +102,7 @@ func scanChainCreate(arguments: [String]) -> Int32 { let defs = StringOption( longFlag: "define", - helpMessage: "define statements to include during simulations. (Default: none)" + helpMessage: "define statements to include during simulations. Comma-delimited. (Default: none)" ) cli.addOptions(defs) diff --git a/Sources/Fault/Entries/cut.swift b/Sources/Fault/Entries/cut.swift index fcaf483..ba1d76c 100644 --- a/Sources/Fault/Entries/cut.swift +++ b/Sources/Fault/Entries/cut.swift @@ -16,6 +16,7 @@ import CommandLineKit import Defile import Foundation import PythonKit +import Yams func cut(arguments: [String]) -> Int32 { let cli = CommandLineKit.CommandLine(arguments: arguments) @@ -30,20 +31,27 @@ func cut(arguments: [String]) -> Int32 { let dffOpt = StringOption( shortFlag: "d", longFlag: "dff", - helpMessage: "Flip-flop cell names,comma,separated (Default: DFF)." + helpMessage: "Override for flip-flop cell names. Comma-delimited. (Default: DFF)." ) cli.addOptions(dffOpt) + let sclConfigOpt = StringOption( + shortFlag: "s", + longFlag: "sclConfig", + helpMessage: "Path for the YAML SCL config file. Recommended." + ) + cli.addOptions(sclConfigOpt) + let blackbox = StringOption( longFlag: "blackbox", - helpMessage: "Blackbox module definitions (.v) seperated by commas. (Default: none)" + helpMessage: "Blackbox module definitions (.v). Comma-delimited. (Default: none)" ) cli.addOptions(blackbox) let ignored = StringOption( shortFlag: "i", longFlag: "ignoring", - helpMessage: "Hard module inputs to ignore when cutting seperated by commas. (Default: none)" + helpMessage: "Module inputs to ignore when cutting. Comma-delimited. (Default: none)" ) cli.addOptions(ignored) @@ -81,9 +89,6 @@ func cut(arguments: [String]) -> Int32 { return EX_NOINPUT } - let dffNames - = Set(dffOpt.value?.components(separatedBy: ",").filter { $0 != "" } ?? ["DFFSR", "DFFNEGX1", "DFFPOSX1"]) - let output = filePath.value ?? "\(file).cut.v" // MARK: Importing Python and Pyverilog @@ -126,6 +131,24 @@ func cut(arguments: [String]) -> Int32 { Stderr.print("No module found.") exit(EX_DATAERR) } + + var sclConfig = SCLConfiguration(dffMatches: [DFFMatch(name: "DFFSR,DFFNEGX1,DFFPOSX1", clk: "CLK", d: "D", q: "Q")]) + if let sclConfigPath = sclConfigOpt.value { + guard let sclConfigYML = File.read(sclConfigPath) else { + Stderr.print("File not found: \(sclConfigPath)") + return EX_NOINPUT + } + let decoder = YAMLDecoder() + do { + sclConfig = try decoder.decode(SCLConfiguration.self, from: sclConfigYML) + } catch { + Stderr.print("Invalid YAML file \(sclConfigPath): \(error).") + return EX_DATAERR + } + } + if let dffOverride = dffOpt.value { + sclConfig.dffMatches.last!.name = dffOverride + } let hardIgnoredInputs = Set(ignored.value?.components(separatedBy: ",").filter { $0 != "" } ?? []) @@ -135,6 +158,8 @@ func cut(arguments: [String]) -> Int32 { var declarations: [PythonObject] = [] var items: [PythonObject] = [] + let fnmatch = Python.import("fnmatch") + for item in definition.items { var include = true @@ -142,12 +167,12 @@ func cut(arguments: [String]) -> Int32 { // Process gates if type == "InstanceList" { let instance = item.instances[0] - let instanceName = String(describing: instance.module) - if dffNames.contains(instanceName) { - let instanceName = String(describing: instance.name) - let outputName = "\\" + instanceName + ".q" + let moduleName = String(describing: instance.module) + if let dffinfo = getMatchingDFFInfo(from: sclConfig.dffMatches, for: moduleName, fnmatch: fnmatch) { + let moduleName = String(describing: instance.name) + let outputName = "\\" + moduleName + ".q" - let inputIdentifier = Node.Identifier(instanceName) + let inputIdentifier = Node.Identifier(moduleName) let outputIdentifier = Node.Identifier(outputName) include = false @@ -155,25 +180,25 @@ func cut(arguments: [String]) -> Int32 { var qArg: PythonObject? for hook in instance.portlist { - if hook.portname == "D" { + if String(describing: hook.portname) == dffinfo.d { dArg = hook.argname } - if hook.portname == "Q" { + if String(describing: hook.portname) == dffinfo.q { qArg = hook.argname } } guard let d = dArg, let q = qArg else { Stderr.print( - "Cell \(instanceName) missing either a 'D' or 'Q' port." + "Cell \(moduleName) missing either a 'D' or 'Q' port." ) return EX_DATAERR } - ports.append(Node.Port(instanceName, Python.None, Python.None, Python.None)) + ports.append(Node.Port(moduleName, Python.None, Python.None, Python.None)) ports.append(Node.Port(outputName, Python.None, Python.None, Python.None)) - declarations.append(Node.Input(instanceName)) + declarations.append(Node.Input(moduleName)) declarations.append(Node.Output(outputName)) let inputAssignment = Node.Assign( @@ -188,7 +213,7 @@ func cut(arguments: [String]) -> Int32 { items.append(inputAssignment) items.append(outputAssignment) - } else if let blakcboxName = isolatedName, blakcboxName == instanceName { + } else if let blakcboxName = isolatedName, blakcboxName == moduleName { include = false guard let isolatedDefinition = isolatedOptional else { @@ -211,14 +236,14 @@ func cut(arguments: [String]) -> Int32 { var statement: PythonObject var assignStatement: PythonObject if input { - name = "\\" + instanceName + "_\(portName)_\(i).q" + name = "\\" + moduleName + "_\(portName)_\(i).q" statement = Node.Output(name) assignStatement = Node.Assign( Node.Lvalue(Node.Identifier(name)), Node.Rvalue(element) ) } else { - name = instanceName + "_\(portName)_\(i)" + name = moduleName + "_\(portName)_\(i)" statement = Node.Input(name) assignStatement = Node.Assign( Node.Lvalue(element), @@ -239,14 +264,14 @@ func cut(arguments: [String]) -> Int32 { var statement: PythonObject var assignStatement: PythonObject if input { - name = "\\" + instanceName + "_\(portName).q" + name = "\\" + moduleName + "_\(portName).q" statement = Node.Output(name) assignStatement = Node.Assign( Node.Lvalue(Node.Identifier(name)), Node.Rvalue(hook.argname) ) } else { - name = instanceName + ".\(portName)" + name = moduleName + ".\(portName)" statement = Node.Input(name) assignStatement = Node.Assign( Node.Lvalue(hook.argname), @@ -267,7 +292,7 @@ func cut(arguments: [String]) -> Int32 { } if declarations.count == 0 { - print("[Warning]: Failed to detect flip-flop cells named: \(dffNames).") + print("[Warning]: Failed to detect any flip-flop cells.") } definition.portlist.ports = ports diff --git a/Sources/Fault/SCLConfiguration.swift b/Sources/Fault/SCLConfiguration.swift index 0412f3d..8c471dc 100644 --- a/Sources/Fault/SCLConfiguration.swift +++ b/Sources/Fault/SCLConfiguration.swift @@ -11,6 +11,7 @@ // 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 PythonKit class DFFMatch: Codable, CustomStringConvertible { var name: String @@ -30,6 +31,17 @@ class DFFMatch: Codable, CustomStringConvertible { } } +func getMatchingDFFInfo(from list: [DFFMatch], for cell: String, fnmatch: PythonObject) -> DFFMatch? { + for dffinfo in list { + for name in dffinfo.name.components(separatedBy: ",") { + if Bool(fnmatch.fnmatch(cell, name))! { + return dffinfo + } + } + } + return nil +} + class MuxInfo: Codable { var name: String var a: String From 906555e2839c22cb47a872bca0f0f8a52292e14a Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 1 Jan 2024 17:39:06 +0200 Subject: [PATCH 5/5] Fix lint --- .swiftversion => .swift-version | 0 Sources/Fault/Entries/cut.swift | 2 +- Sources/Fault/Mux.swift | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename .swiftversion => .swift-version (100%) diff --git a/.swiftversion b/.swift-version similarity index 100% rename from .swiftversion rename to .swift-version diff --git a/Sources/Fault/Entries/cut.swift b/Sources/Fault/Entries/cut.swift index ba1d76c..0135348 100644 --- a/Sources/Fault/Entries/cut.swift +++ b/Sources/Fault/Entries/cut.swift @@ -131,7 +131,7 @@ func cut(arguments: [String]) -> Int32 { Stderr.print("No module found.") exit(EX_DATAERR) } - + var sclConfig = SCLConfiguration(dffMatches: [DFFMatch(name: "DFFSR,DFFNEGX1,DFFPOSX1", clk: "CLK", d: "D", q: "Q")]) if let sclConfigPath = sclConfigOpt.value { guard let sclConfigYML = File.read(sclConfigPath) else { diff --git a/Sources/Fault/Mux.swift b/Sources/Fault/Mux.swift index 5c33679..dff291f 100644 --- a/Sources/Fault/Mux.swift +++ b/Sources/Fault/Mux.swift @@ -57,7 +57,7 @@ class MuxCreator { "keep" ) ) - + // Hook var hook = outputWire if muxInfo.invertedOutput {