Skip to content

Commit

Permalink
[HACK] Add some initial patterns + ability to load previous set of te…
Browse files Browse the repository at this point in the history
…st vectors
  • Loading branch information
donn committed Jan 14, 2024
1 parent 8516cbf commit 7c06f7e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 51 deletions.
15 changes: 2 additions & 13 deletions Sources/Fault/Entries/asm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
58 changes: 37 additions & 21 deletions Sources/Fault/Entries/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
)
)!

Expand All @@ -407,13 +413,13 @@ func main(arguments: [String]) -> Int32 {
var faultPoints: Set<String> = []
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)
}
}
Expand Down Expand Up @@ -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()
Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions Sources/Fault/RNGFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import BigInt
import Foundation

protocol URNG {
init()
init(allBits: Int)
func generate(bits: Int) -> BigUInt
}

Expand All @@ -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] {
Expand Down
47 changes: 39 additions & 8 deletions Sources/Fault/Simulation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,28 +232,38 @@ enum Simulator {
minimumCoverage: Float,
ceiling: Int,
randomGenerator: String,
TVSet: [TestVector],
initialTVInfo: TVInfo? = nil,
externalTestVectors: [TestVector],
sampleRun: Bool,
clock: String?,
defines: Set<String> = [],
using iverilogExecutable: String,
with vvpExecutable: String
) throws -> (coverageList: [TVCPair], coverageMeta: CoverageMeta) {
var testVectorHash: Set<TestVector> = []

var coverageList: [TVCPair] = []
var coverage: Float = 0.0

var sa0Covered: Set<String> = []
sa0Covered.reserveCapacity(faultPoints.count)
var sa1Covered: Set<String> = []
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 {
Expand All @@ -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) {
Expand Down
53 changes: 49 additions & 4 deletions Sources/Fault/TVGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]!

Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
21 changes: 19 additions & 2 deletions Sources/Fault/TestVector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 7c06f7e

Please sign in to comment.