From 7c06f7e49846c9faa589d9a94467a283431e42cb Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Sun, 14 Jan 2024 20:19:52 +0200 Subject: [PATCH] [HACK] Add some initial patterns + ability to load previous set of test vectors --- Sources/Fault/Entries/asm.swift | 15 ++------- Sources/Fault/Entries/main.swift | 58 ++++++++++++++++++++------------ Sources/Fault/RNGFactory.swift | 6 ++-- Sources/Fault/Simulation.swift | 47 +++++++++++++++++++++----- Sources/Fault/TVGenerator.swift | 53 ++++++++++++++++++++++++++--- Sources/Fault/TestVector.swift | 21 ++++++++++-- 6 files changed, 149 insertions(+), 51 deletions(-) diff --git a/Sources/Fault/Entries/asm.swift b/Sources/Fault/Entries/asm.swift index f007300..79fb469 100644 --- a/Sources/Fault/Entries/asm.swift +++ b/Sources/Fault/Entries/asm.swift @@ -129,17 +129,6 @@ func assemble(arguments: [String]) -> Int32 { outputMap[name] = i } - func pad(_ number: BigUInt, digits: Int, radix: Int) -> String { - var padded = String(number, radix: radix) - let length = padded.count - if digits > length { - for _ in 0 ..< (digits - length) { - padded = "0" + padded - } - } - return padded - } - var jsOutputLength = 0 for output in jsOutputOrder { jsOutputLength += output.width @@ -187,14 +176,14 @@ func assemble(arguments: [String]) -> Int32 { return EX_DATAERR } } - binaryString += pad(value, digits: element.width, radix: 2).reversed() + binaryString += value.pad(digits: element.width, radix: 2).reversed() } var outputBinary = "" for element in orderOutput { var value: BigUInt = 0 if let locus = outputMap[element.name] { value = outputDecimal[i][locus] - outputBinary += pad(value, digits: element.width, radix: 2) + outputBinary += value.pad(digits: element.width, radix: 2) } else { if element.kind == .bypassOutput { outputBinary += String(repeating: "x", count: element.width) diff --git a/Sources/Fault/Entries/main.swift b/Sources/Fault/Entries/main.swift index 6e7eea3..8c469e9 100644 --- a/Sources/Fault/Entries/main.swift +++ b/Sources/Fault/Entries/main.swift @@ -50,7 +50,7 @@ let subcommands: OrderedDictionary = [ let yosysTest = "'\(yosysExecutable)' -V".sh(silent: true) if yosysTest != EX_OK { - Stderr.print("Yosys must be installed to PATH on your computer for Fault to work. Fault will now quit.") + Stderr.print("Yosys must be installed to PATH on your computer for Fault to work. Fault will now quit.") exit(EX_UNAVAILABLE) } @@ -118,7 +118,7 @@ func main(arguments: [String]) -> Int32 { cli.addOptions(svfFilePath) let faultPointFilePath = StringOption( - longFlag: "output-fault-points", + longFlag: "output-faultPoints", helpMessage: "Path to the output yml file listing all generated fault points. (Default: nil)" ) cli.addOptions(faultPointFilePath) @@ -208,11 +208,17 @@ func main(arguments: [String]) -> Int32 { ) cli.addOptions(clock) - let tvSet = StringOption( - longFlag: "tvSet", - helpMessage: ".json file describing an external TV set to be simulated. (Default: TVs are internally generated by one of the TVGen options. )" + let externalTVSet = StringOption( + longFlag: "externalTVSet", + helpMessage: ".json file describing an external set of test-vectors to be simulated for coverage, with NO FURTHER GENERATION attempted. (Optional)" ) - cli.addOptions(tvSet) + cli.addOptions(externalTVSet) + + let startingTVSetOpt = StringOption( + longFlag: "iteratingUpon", + helpMessage: ".json file of test vectors to start with; iterating further as necessary. (Default: None/empty)" + ) + cli.addOptions(startingTVSetOpt) let defs = StringOption( longFlag: "define", @@ -342,25 +348,25 @@ func main(arguments: [String]) -> Int32 { // MARK: TV Generation Mode Selection - var tvSetVectors: [TestVector] = [] - var tvSetInputs: [Port] = [] + var etvSetVectors: [TestVector] = [] + var etvSetInputs: [Port] = [] - if let tvSetTest = tvSet.value { + if let tvSetTest = externalTVSet.value { if !fileManager.fileExists(atPath: tvSetTest) { Stderr.print("TVs JSON file '\(tvSetTest)' not found.") return EX_NOINPUT } do { if tvSetTest.hasSuffix(".json") { - (tvSetVectors, tvSetInputs) = try TVSet.readFromJson(file: tvSetTest) + (etvSetVectors, etvSetInputs) = try TVSet.readFromJson(file: tvSetTest) } else { - (tvSetVectors, tvSetInputs) = try TVSet.readFromText(file: tvSetTest) + (etvSetVectors, etvSetInputs) = try TVSet.readFromText(file: tvSetTest) } } catch { cli.printUsage() return EX_USAGE } - print("Read \(tvSetVectors.count) vectors.") + print("Read \(etvSetVectors.count) externally-generated vectors to verify.") } if let tvGenerator = tvGen.value, ETVGFactory.validNames.contains(tvGenerator) { @@ -371,22 +377,22 @@ func main(arguments: [String]) -> Int32 { Stderr.print("Bench file '\(benchUnwrapped)' not found.") return EX_NOINPUT } - (tvSetVectors, tvSetInputs) = etvgen.generate(file: benchUnwrapped, module: "\(definition.name)") + (etvSetVectors, etvSetInputs) = etvgen.generate(file: benchUnwrapped, module: "\(definition.name)") - if tvSetVectors.count == 0 { + if etvSetVectors.count == 0 { Stderr.print("Bench netlist appears invalid (no vectors generated). Are you sure there are no floating nets/outputs?") return EX_DATAERR } else { - print("Generated \(tvSetVectors.count) test vectors.") + print("Generated \(etvSetVectors.count) test vectors using external utilties to verify.") } } let tvMinimumCoverage = Float(tvMinimumCoverageInt) / 100.0 let finalTvCeiling = Int( ceiling.value ?? ( - tvSetVectors.count == 0 ? + etvSetVectors.count == 0 ? defaultCeiling : - String(tvSetVectors.count) + String(etvSetVectors.count) ) )! @@ -407,13 +413,13 @@ func main(arguments: [String]) -> Int32 { var faultPoints: Set = [] var gateCount = 0 var inputsMinusIgnored: [Port] = [] - if tvSetVectors.count == 0 { + if etvSetVectors.count == 0 { inputsMinusIgnored = inputs.filter { !ignoredInputs.contains($0.name) } } else { - tvSetInputs.sort { $0.ordinal < $1.ordinal } - inputsMinusIgnored = tvSetInputs.filter { + etvSetInputs.sort { $0.ordinal < $1.ordinal } + inputsMinusIgnored = etvSetInputs.filter { !ignoredInputs.contains($0.name) } } @@ -457,6 +463,15 @@ func main(arguments: [String]) -> Int32 { print("Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports.") + // MARK: Load Initial Set + + var initialTVInfo: TVInfo? = nil + if let startingTVSet = startingTVSetOpt.value { + let loadedInitialTVInfo = try TVInfo.fromJSON(file: startingTVSet) + print("Loaded \(loadedInitialTVInfo.coverageList.count) initial test vectors.") + initialTVInfo = loadedInitialTVInfo + } + // MARK: Simulation let startTime = CFAbsoluteTimeGetCurrent() @@ -479,7 +494,8 @@ func main(arguments: [String]) -> Int32 { minimumCoverage: tvMinimumCoverage, ceiling: finalTvCeiling, randomGenerator: randomGenerator, - TVSet: tvSetVectors, + initialTVInfo: initialTVInfo, + externalTestVectors: etvSetVectors, sampleRun: sampleRun.value, clock: clock.value, defines: defines, diff --git a/Sources/Fault/RNGFactory.swift b/Sources/Fault/RNGFactory.swift index b980666..09dba9c 100644 --- a/Sources/Fault/RNGFactory.swift +++ b/Sources/Fault/RNGFactory.swift @@ -16,7 +16,7 @@ import BigInt import Foundation protocol URNG { - init() + init(allBits: Int) func generate(bits: Int) -> BigUInt } @@ -28,11 +28,11 @@ enum URNGFactory { return true } - static func get(name: String) -> URNG? { + static func get(name: String) -> URNG.Type? { guard let metaType = registry[name] else { return nil } - return metaType.init() + return metaType } static var validNames: [String] { diff --git a/Sources/Fault/Simulation.swift b/Sources/Fault/Simulation.swift index 6ff6673..59a7c3f 100644 --- a/Sources/Fault/Simulation.swift +++ b/Sources/Fault/Simulation.swift @@ -232,7 +232,8 @@ enum Simulator { minimumCoverage: Float, ceiling: Int, randomGenerator: String, - TVSet: [TestVector], + initialTVInfo: TVInfo? = nil, + externalTestVectors: [TestVector], sampleRun: Bool, clock: String?, defines: Set = [], @@ -240,20 +241,29 @@ enum Simulator { with vvpExecutable: String ) throws -> (coverageList: [TVCPair], coverageMeta: CoverageMeta) { var testVectorHash: Set = [] - var coverageList: [TVCPair] = [] - var coverage: Float = 0.0 - var sa0Covered: Set = [] sa0Covered.reserveCapacity(faultPoints.count) var sa1Covered: Set = [] sa1Covered.reserveCapacity(faultPoints.count) + if let tvInfo = initialTVInfo { + coverageList = tvInfo.coverageList + for tvcPair in coverageList { + testVectorHash.insert(tvcPair.vector) + sa0Covered.formUnion(tvcPair.coverage.sa0) + sa0Covered.formUnion(tvcPair.coverage.sa1) + } + } + + var coverage: Float = 0.0 + var totalTVAttempts = 0 var tvAttempts = min(initialVectorCount, ceiling, sampleRun ? 1 : Int.max) - let simulateOnly = (TVSet.count != 0) - let rng: URNG = URNGFactory.get(name: randomGenerator)! + let simulateOnly = (externalTestVectors.count != 0) + let totalBitWidth = inputs.reduce(0) { $0 + $1.width } + let backupRng: URNG = URNGFactory.get(name: randomGenerator)!.init(allBits: totalBitWidth) while coverage < minimumCoverage, totalTVAttempts < ceiling { if totalTVAttempts > 0 { @@ -266,12 +276,33 @@ enum Simulator { var futureList: [Future] = [] var testVectors: [TestVector] = [] for index in 0 ..< tvAttempts { + let overallIndex = totalTVAttempts + index + + var rng: URNG = backupRng + if overallIndex == 0 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .allZero, complement: false) + } else if overallIndex == 1 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .allZero, complement: true) + } else if overallIndex == 2 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .alternating, complement: false) + } else if overallIndex == 3 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .alternating, complement: true) + } else if overallIndex == 4 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .halfAndHalf, complement: false) + } else if overallIndex == 5 { + rng = PatternGenerator(allBits: totalBitWidth, pattern: .halfAndHalf, complement: true) + } var testVector: TestVector = [] if simulateOnly { - testVector = TVSet[totalTVAttempts + index] + testVector = externalTestVectors[overallIndex] } else { + var assembled: BigUInt = 0 + var bitsSoFar = 0 for input in inputs { - testVector.append(rng.generate(bits: input.width)) + let value = rng.generate(bits: input.width) + assembled |= (value << bitsSoFar) + bitsSoFar += input.width + testVector.append(value) } } if testVectorHash.contains(testVector) { diff --git a/Sources/Fault/TVGenerator.swift b/Sources/Fault/TVGenerator.swift index 543bb31..a31b5fc 100644 --- a/Sources/Fault/TVGenerator.swift +++ b/Sources/Fault/TVGenerator.swift @@ -105,7 +105,7 @@ class PODEM: ExternalTestVectorGenerator { // MARK: Random Generators class SwiftRNG: URNG { - required init() {} + required init(allBits _: Int) {} func generate(bits: Int) -> BigUInt { BigUInt.randomInteger(withMaximumWidth: bits) @@ -155,7 +155,7 @@ class LFSR: URNG { var polynomialHex: UInt let nbits: UInt - required init(nbits: UInt = 64) { + required init(allBits _: Int, nbits: UInt) { let max: UInt = (nbits == 64) ? UInt(pow(Double(2), Double(63)) - 1) : (1 << nbits) - 1 let polynomial = LFSR.taps[nbits]! @@ -168,8 +168,8 @@ class LFSR: URNG { } } - required convenience init() { - self.init(nbits: 64) + required convenience init(allBits: Int) { + self.init(allBits: allBits, nbits: 64) } static func parity(number: UInt) -> UInt { @@ -206,3 +206,48 @@ class LFSR: URNG { static let registered = URNGFactory.register(name: "LFSR", type: LFSR.self) } + +class PatternGenerator: URNG { + enum Pattern { + case allZero + case halfAndHalf + case alternating + } + + let pattern: Pattern + let complement: Bool + var number: BigUInt + + init(allBits: Int, pattern: Pattern, complement: Bool) { + self.pattern = pattern + self.complement = complement + number = BigUInt(0) + switch self.pattern { + case .halfAndHalf: + let halfBits = allBits / 2 + number = (BigUInt(1) << halfBits) - 1 + case .alternating: + for _ in 0 ..< allBits { + number = (number << 1) | ((number & 1) ^ 1) + } + default: + break + } + } + + required convenience init(allBits: Int) { + self.init(allBits: allBits, pattern: .allZero, complement: false) + } + + func generate(bits: Int) -> BigUInt { + let mask = (BigUInt(1) << bits) - 1 + let result = number & mask + number >>= bits + // print("returning \(bits) bits: \(result.pad(digits: bits, radix: 2))") + if complement { + return result ^ mask + } else { + return result + } + } +} diff --git a/Sources/Fault/TestVector.swift b/Sources/Fault/TestVector.swift index 07f25f2..630fbef 100644 --- a/Sources/Fault/TestVector.swift +++ b/Sources/Fault/TestVector.swift @@ -18,6 +18,19 @@ import Foundation typealias TestVector = [BigUInt] +extension BigUInt { + func pad(digits: Int, radix: Int) -> String { + var padded = String(self, radix: radix) + let length = padded.count + if digits > length { + for _ in 0 ..< (digits - length) { + padded = "0" + padded + } + } + return padded + } +} + struct Coverage: Codable { var sa0: [String] var sa1: [String] @@ -51,12 +64,16 @@ struct TVInfo: Codable { self.outputs = outputs self.coverageList = coverageList } + + static func fromJSON(file: String) throws -> TVInfo { + let data = try Data(contentsOf: URL(fileURLWithPath: file), options: .mappedIfSafe) + return try JSONDecoder().decode(TVInfo.self, from: data) + } } 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 { + guard let tvInfo = try? TVInfo.fromJSON(file: file) else { Stderr.print("File '\(file)' is invalid.") exit(EX_DATAERR) }