Skip to content

Commit

Permalink
Use pyosys-based nl2bench
Browse files Browse the repository at this point in the history
  • Loading branch information
donn committed Sep 30, 2024
1 parent de4a0c2 commit d000d0f
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 117 deletions.
179 changes: 113 additions & 66 deletions Sources/Fault/Entries/atpg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,97 +12,137 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import ArgumentParser
import BigInt
import Collections
import ArgumentParser
import CoreFoundation // Not automatically imported on Linux
import CoreFoundation // Not automatically imported on Linux
import Defile
import Foundation
import PythonKit
import Yams


extension Fault {
struct ATPG: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Generate/import test vectors for a particular circuit and calculate coverage."
abstract:
"Generate/import test vectors for a particular circuit and calculate coverage."
)

@Option(
name: [.short, .long], help: "Path to the output JSON file. (Default: input + .tv.json)"
)

@Option(name: [.short, .long], help: "Path to the output JSON file. (Default: input + .tv.json)")
var output: String?

@Option(help: "Path to the output SVF file. (Default: input + .tv.svf)")
var outputSvf: String?

@Option(name: [.long, .customLong("output-faultPoints")], help: "Path to the output yml file listing all generated fault points. (Default: nil)")

@Option(
name: [.long, .customLong("output-faultPoints")],
help: "Path to the output yml file listing all generated fault points. (Default: nil)")
var outputFaultPoints: String?

@Option(name: [.long, .customLong("output-covered")], help: "Path to the output yml file listing coverage metadata, i.e., ratio and fault points covered. (Default: nil)")

@Option(
name: [.long, .customLong("output-covered")],
help:
"Path to the output yml file listing coverage metadata, i.e., ratio and fault points covered. (Default: nil)"
)
var outputCoverageMetadata: String?

@Option(name: [.short, .long, .customLong("cellModel")], help: "A Verilog model with which standard cells can be simulated.")

@Option(
name: [.short, .long, .customLong("cellModel")],
help: "A Verilog model with which standard cells can be simulated.")
var cellModel: String

@Option(name: [.customShort("v"), .long], help: "Number of test vectors to generate in the first batch.")

@Option(
name: [.customShort("v"), .long],
help: "Number of test vectors to generate in the first batch.")
var tvCount: Int = 100

@Option(name: [.customShort("r"), .long], help: "Increment in test vector count in subsequent batches should sufficient coverage not be reached.")

@Option(
name: [.customShort("r"), .long],
help:
"Increment in test vector count in subsequent batches should sufficient coverage not be reached."
)
var increment: Int = 50

@Option(name: [.short, .long], help: "The minimum coverage to reach before ceasing increments. If set to 0, only the initial batch is run.")

@Option(
name: [.short, .long],
help:
"The minimum coverage to reach before ceasing increments. If set to 0, only the initial batch is run."
)
var minCoverage: Float = 80

@Option(help: "Ceiling for Test Vector increments: if this number is reached, no more increments will occur regardless the coverage.")

@Option(
help:
"Ceiling for Test Vector increments: if this number is reached, no more increments will occur regardless the coverage."
)
var ceiling: Int?

@Option(help: "Type of the pseudo-random internal test-vector-generator.")
var tvGen: String = "swift"

@Option(help: "A \(MemoryLayout<UInt>.size)-byte value to use as an RNG seed for test vector generators, provided as a hexadecimal string (without 0x).")

@Option(
help:
"A \(MemoryLayout<UInt>.size)-byte value to use as an RNG seed for test vector generators, provided as a hexadecimal string (without 0x)."
)
var rngSeed: String = "DEADCAFEDEADF00D"

@Option(name: [.customShort("g"), .long], help: "Use an external TV Generator: Atalanta or PODEM.")

@Option(
name: [.customShort("g"), .long],
help: "Use an external TV Generator: Atalanta or PODEM.")
var etvGen: String?

@Option(name: [.short, .long], help: "Netlist in bench format. (Required iff generator is set to Atalanta or PODEM.)")

@Option(
name: [.short, .long],
help: "Netlist in bench format. (Required iff generator is set to Atalanta or PODEM.)")
var bench: String?

@Flag(help: "Generate only one testbench for inspection, and do not delete it.")
var sampleRun: Bool = false

@OptionGroup
var bypass: BypassOptions

@Option(help: "If provided, this JSON file's test vectors are simulated and no generation is attempted.")

@Option(
help:
"If provided, this JSON file's test vectors are simulated and no generation is attempted."
)
var externalTVSet: String?

@Option(help: "If provided, this JSON file's test vector are used as the initial set of test vectors, with iterations taking place with them in mind.")

@Option(
help:
"If provided, this JSON file's test vector are used as the initial set of test vectors, with iterations taking place with them in mind."
)
var iteratingUpon: String?

@Option(name: [.customShort("D"), .customLong("define")], help: "Define statements to include during simulations.")

@Option(
name: [.customShort("D"), .customLong("define")],
help: "Define statements to include during simulations.")
var defines: [String] = []

@Option(name: [.customShort("I"), .customLong("include")], help: "Extra verilog models to include during simulations.")

@Option(
name: [.customShort("I"), .customLong("include")],
help: "Extra verilog models to include during simulations.")
var includes: [String] = []

@Argument(help: "The cutaway netlist to generate patterns for.")
var file: String

mutating func run() throws {

if !TVGeneratorFactory.validNames.contains(tvGen) {
throw ValidationError("Invalid test-vector generator \(tvGen).")
}

let fileManager = FileManager()
guard fileManager.fileExists(atPath: file) else {
throw ValidationError("File '\(file)' not found.")
}

guard fileManager.fileExists(atPath: cellModel) else {
throw ValidationError("Cell model file '\(cellModel)' not found.")
}

if !cellModel.hasSuffix(".v"), !cellModel.hasSuffix(".sv") {
Stderr.print(
"Warning: Cell model file provided does not end with .v or .sv."
Expand All @@ -111,7 +151,7 @@ extension Fault {

let jsonOutput = output ?? file.replacingExtension(".cut.v", with: ".tv.json")
let svfOutput = outputSvf ?? file.replacingExtension(".cut.v", with: ".tv.svf")

// MARK: Importing Python and Pyverilog

let parse = Python.import("pyverilog.vparser.parser").parse
Expand Down Expand Up @@ -160,29 +200,31 @@ extension Fault {
Stderr.print("Unknown external test vector generator '\(tvGenerator)'.")
Foundation.exit(EX_USAGE)
}
let benchUnwrapped = bench! // Program exits if etvGen.value isn't nil and bench.value is or vice versa

let benchUnwrapped = bench! // Program exits if etvGen.value isn't nil and bench.value is or vice versa

if !fileManager.fileExists(atPath: benchUnwrapped) {
throw ValidationError("Bench file '\(benchUnwrapped)' not found.")
}
(etvSetVectors, etvSetInputs) = etvgen.generate(file: benchUnwrapped, module: "\(definition.name)")
(etvSetVectors, etvSetInputs) = etvgen.generate(
file: benchUnwrapped, module: "\(definition.name)")

if etvSetVectors.count == 0 {
Stderr.print("Bench netlist appears invalid (no vectors generated). Are you sure there are no floating nets/outputs?")
Stderr.print(
"Bench netlist appears invalid (no vectors generated). Are you sure there are no floating nets/outputs?"
)
Foundation.exit(EX_DATAERR)
} else {
print("Generated \(etvSetVectors.count) test vectors using external utilties to verify.")
print(
"Generated \(etvSetVectors.count) test vectors using external utilties to verify."
)
}
}

let tvMinimumCoverage = minCoverage / 100
let finalTvCeiling: Int = ceiling ?? (
etvSetVectors.count == 0 ?
1000 :
etvSetVectors.count
)

let finalTvCeiling: Int =
ceiling ?? (etvSetVectors.count == 0 ? 1000 : etvSetVectors.count)

let finalRNGSeed = UInt(rngSeed, radix: 16)!

do {
Expand Down Expand Up @@ -217,9 +259,7 @@ extension Fault {
evtInputsMinusIgnored.append(input)
}
}
print(inputsMinusIgnored);
print(evtInputsMinusIgnored);
assert(inputsMinusIgnored.count == evtInputsMinusIgnored.count);
assert(inputsMinusIgnored.count == evtInputsMinusIgnored.count)
inputsMinusIgnored = evtInputsMinusIgnored
}

Expand All @@ -232,7 +272,7 @@ extension Fault {
} 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)]")
}
}
Expand All @@ -257,10 +297,14 @@ extension Fault {
}

if warnAboutDFF {
print("Warning: D-flipflops were found in this netlist. Are you sure you ran it through 'fault cut'?")
print(
"Warning: D-flipflops were found in this netlist. Are you sure you ran it through 'fault cut'?"
)
}

print("Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports.")
print(
"Found \(faultPoints.count) fault sites in \(gateCount) gates and \(ports.count) ports."
)

// MARK: Load Initial Set

Expand Down Expand Up @@ -316,16 +360,18 @@ extension Fault {
coverageList: result.coverageList
)
let jsonRawOutput = jsonOutput.replacingExtension(".tv.json", with: ".raw_tv.json")

print("Writing raw generated test vectors in Fault JSON format to \(jsonOutput)")
try encoder.encode(rawTVInfo).write(to: URL(fileURLWithPath: jsonRawOutput))

let tvInfo = TVInfo(
inputs: inputsMinusIgnored,
outputs: outputs,
coverageList: Compactor.compact(coverageList: result.coverageList)
)
print("Writing compacted generated test vectors in Fault JSON format to \(jsonOutput)")
print(
"Writing compacted generated test vectors in Fault JSON format to \(jsonOutput)"
)
try encoder.encode(tvInfo).write(to: URL(fileURLWithPath: jsonOutput))

// try File.open(svfOutput, mode: .write) {
Expand All @@ -334,7 +380,8 @@ extension Fault {
// }

if let coverageMetaFilePath = outputCoverageMetadata {
print("Writing YAML file of final coverage metadata to \(coverageMetaFilePath)")
print(
"Writing YAML file of final coverage metadata to \(coverageMetaFilePath)")
try File.open(coverageMetaFilePath, mode: .write) {
try $0.write(string: YAMLEncoder().encode(result.coverageMeta))
}
Expand Down
72 changes: 36 additions & 36 deletions Sources/Fault/Synthesis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,56 +25,56 @@ enum Synthesis {
) -> String {
let opt = optimize ? "opt" : ""
return """
# read liberty
read_liberty -lib -ignore_miss_dir -setattr blackbox \(libertyFile)
# read liberty
read_liberty -lib -ignore_miss_dir -setattr blackbox \(libertyFile)
# read black boxes
read_verilog -sv -lib \(blackboxedModules.map { "'\($0)'" }.joined(separator: " "))
# read black boxes
read_verilog -sv -lib \(blackboxedModules.map { "'\($0)'" }.joined(separator: " "))
# read design
read_verilog -sv \(files.map { "'\($0)'" }.joined(separator: " "))
# read design
read_verilog -sv \(files.map { "'\($0)'" }.joined(separator: " "))
# check design hierarchy
hierarchy \(checkHierarchy ? "-check" : "") -top \(module)
flatten;
# check design hierarchy
hierarchy \(checkHierarchy ? "-check" : "") -top \(module)
flatten;
# translate processes (always blocks)
proc; \(opt)
# translate processes (always blocks)
proc; \(opt)
# detect and optimize FSM encodings
fsm; \(opt)
# detect and optimize FSM encodings
fsm; \(opt)
# implement memories (arrays)
memory; \(opt)
# implement memories (arrays)
memory; \(opt)
# convert to gate logic
techmap; \(opt)
# convert to gate logic
techmap; \(opt)
# flatten
flatten; \(opt)
# flatten
flatten; \(opt)
# mapping flip-flops to mycells.lib
dfflibmap -liberty \(libertyFile)
# mapping flip-flops to mycells.lib
dfflibmap -liberty \(libertyFile)
# expose dff
\(cutting ? "expose -cut -evert-dff; \(opt)" : "")
# expose dff
\(cutting ? "expose -cut -evert-dff; \(opt)" : "")
# mapping logic to mycells.lib
abc -liberty \(libertyFile)
splitnets -ports
# mapping logic to mycells.lib
abc -liberty \(libertyFile)
splitnets
# print gate count
stat
# print gate count
stat
# cleanup
opt_clean -purge
# cleanup
opt_clean -purge
# names
# autoname
# names
# autoname
write_verilog -noexpr -nohex -nodec -defparam \(output)+attrs
write_verilog -noexpr -noattr -noexpr -nohex -nodec -defparam \(output)
# write_blif -gates -unbuf DFFSR D Q \(output).blif
"""
write_verilog -noexpr -nohex -nodec -defparam \(output)+attrs
write_verilog -noexpr -noattr -noexpr -nohex -nodec -defparam \(output)
# write_blif -gates -unbuf DFFSR D Q \(output).blif
"""
}
}
Loading

0 comments on commit d000d0f

Please sign in to comment.