27 #include "mlir/IR/Attributes.h"
28 #include "mlir/Pass/Pass.h"
29 #include "mlir/Support/FileUtilities.h"
30 #include "llvm/ADT/SmallPtrSet.h"
31 #include "llvm/Support/Debug.h"
32 #include "llvm/Support/FormatAdapters.h"
33 #include "llvm/Support/FormatVariadic.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/Path.h"
37 #define DEBUG_TYPE "firrtl-blackbox-reader"
41 #define GEN_PASS_DEF_BLACKBOXREADER
42 #include "circt/Dialect/FIRRTL/Passes.h.inc"
46 using namespace circt;
47 using namespace firrtl;
58 enum class Priority { TargetDir = 0, Verification, Explicit, TestBench, Unset };
61 struct AnnotationInfo {
65 StringAttr outputFile;
67 StringAttr inlineText;
72 Priority priority = Priority::Unset;
74 bool excludeFromFileList =
false;
78 void print(raw_ostream &os,
unsigned indent = 0)
const {
79 if (priority == Priority::Unset) {
83 os << llvm::formatv(
"name: {1}\n"
84 "{0}outputFile: {2}\n"
87 llvm::fmt_pad(
"", indent, 0), name, outputFile,
88 (
unsigned)priority, excludeFromFileList);
94 struct OutputFileInfo {
97 bool excludeFromFileList;
100 struct BlackBoxReaderPass
101 :
public circt::firrtl::impl::BlackBoxReaderBase<BlackBoxReaderPass> {
102 void runOnOperation()
override;
103 bool runOnAnnotation(Operation *op,
Annotation anno, OpBuilder &builder,
104 bool isCover, AnnotationInfo &annotationInfo);
105 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &builder);
106 OutputFileInfo
getOutputFile(Operation *origOp, StringAttr fileNameAttr,
107 bool isCover =
false);
109 bool isDut(Operation *module);
111 using BlackBoxReaderBase::inputPrefix;
116 SmallVector<emit::FileOp> fileListFiles;
126 StringRef testBenchDir;
136 StringRef resourceFileName;
143 DenseMap<Operation *, bool> dutModuleMap;
153 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
158 void BlackBoxReaderPass::runOnOperation() {
159 CircuitOp circuitOp = getOperation();
162 instanceGraph = &getAnalysis<InstanceGraph>();
163 auto context = &getContext();
166 bool anythingChanged =
false;
173 resourceFileName =
"firrtl_black_box_resource_files.f";
177 SmallVector<Attribute, 4> filteredAnnos;
181 if (
auto resourceFN = annot.getMember<StringAttr>(
"resourceFileName")) {
182 resourceFileName = resourceFN.getValue();
187 <<
" annotation missing \"resourceFileName\" attribute";
191 filteredAnnos.push_back(annot.getDict());
195 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
196 coverDir = dir.getValue();
201 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
202 testBenchDir = dir.getValue();
208 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
209 targetDir = target.getValue();
213 <<
" annotation missing \"targetDir\" attribute";
223 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir <<
"\n"
224 <<
"Black box resource file name: "
225 << resourceFileName <<
"\n");
228 auto builder = circuitOp.getBodyBuilder();
232 for (
auto &op : *circuitOp.getBodyBlock()) {
233 FModuleOp module = dyn_cast<FModuleOp>(op);
237 return signalPassFailure();
240 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
242 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
244 for (
auto extmoduleOp : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
246 llvm::dbgs().indent(2)
247 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
248 llvm::dbgs().indent(4) <<
"annotations:\n";
253 bool foundBBoxAnno =
false;
254 annotations.removeAnnotations([&](
Annotation anno) {
255 AnnotationInfo annotationInfo;
256 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
259 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
261 auto &bestAnnotationInfo = emittedFileMap[annotationInfo.name];
262 if (annotationInfo.priority < bestAnnotationInfo.priority) {
263 bestAnnotationInfo = annotationInfo;
268 foundBBoxAnno =
true;
273 annotations.addAnnotations({bboxAnno});
274 anythingChanged =
true;
276 annotations.applyToOperation(extmoduleOp);
279 LLVM_DEBUG(llvm::dbgs() <<
"emittedFiles:\n");
280 Location loc = builder.getUnknownLoc();
281 for (
auto &[verilogName, annotationInfo] : emittedFileMap) {
283 llvm::dbgs().indent(2) <<
"verilogName: " << verilogName <<
"\n";
284 llvm::dbgs().indent(2) <<
"annotationInfo:\n";
285 annotationInfo.print(llvm::dbgs().indent(4) <<
"- ", 6);
288 auto fileName = ns.newName(
"blackbox_" + verilogName.getValue());
290 auto fileOp = builder.create<emit::FileOp>(
291 loc, annotationInfo.outputFile, fileName,
292 [&, text = annotationInfo.inlineText] {
293 builder.create<emit::VerbatimOp>(loc, text);
296 if (!annotationInfo.excludeFromFileList)
297 fileListFiles.push_back(fileOp);
303 if (!fileListFiles.empty()) {
305 llvm::sort(fileListFiles.begin(), fileListFiles.end(),
306 [](emit::FileOp fileA, emit::FileOp fileB) {
307 return fileA.getFileName() < fileB.getFileName();
311 SmallVector<Attribute> symbols;
312 for (emit::FileOp file : fileListFiles)
315 builder.create<emit::FileListOp>(
316 loc, builder.getStringAttr(resourceFileName),
317 builder.getArrayAttr(symbols),
318 builder.getStringAttr(ns.newName(
"blackbox_filelist")));
322 if (!anythingChanged)
323 markAllAnalysesPreserved();
324 markAnalysesPreserved<InstanceGraph>();
327 emittedFileMap.clear();
328 fileListFiles.clear();
334 bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
335 OpBuilder &builder,
bool isCover,
336 AnnotationInfo &annotationInfo) {
339 auto name = anno.
getMember<StringAttr>(
"name");
340 auto text = anno.
getMember<StringAttr>(
"text");
341 if (!name || !text) {
343 <<
" annotation missing \"name\" or \"text\" attribute";
349 annotationInfo.outputFile = outputFile.fileName;
350 annotationInfo.name = name;
351 annotationInfo.inlineText = text;
352 annotationInfo.priority = outputFile.priority;
353 annotationInfo.excludeFromFileList = outputFile.excludeFromFileList;
359 auto path = anno.
getMember<StringAttr>(
"path");
362 <<
" annotation missing \"path\" attribute";
366 SmallString<128> inputPath(inputPrefix);
368 auto text = loadFile(op, inputPath, builder);
370 op->emitError(
"Cannot find file ") << inputPath;
374 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
376 annotationInfo.outputFile = outputFile.fileName;
377 annotationInfo.name = name;
378 annotationInfo.inlineText = text;
379 annotationInfo.priority = outputFile.priority;
380 annotationInfo.excludeFromFileList = outputFile.excludeFromFileList;
390 StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
391 OpBuilder &builder) {
392 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
393 << llvm::sys::path::filename(inputPath) <<
"` from `"
394 << inputPath <<
"`\n");
397 std::string errorMessage;
398 auto input = mlir::openInputFile(inputPath, &errorMessage);
403 return builder.getStringAttr(input->getBuffer());
408 StringAttr fileNameAttr,
410 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
411 if (outputFile && !outputFile.isDirectory()) {
412 return {outputFile.getFilename(), Priority::TargetDir,
413 outputFile.getExcludeFromFilelist().getValue()};
418 auto *context = &getContext();
419 auto fileName = fileNameAttr.getValue();
420 auto ext = llvm::sys::path::extension(fileName);
421 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
422 auto outDir = std::make_pair(targetDir, Priority::TargetDir);
426 outDir = {outputFile.getFilename(), Priority::Explicit};
431 else if (!testBenchDir.empty() && targetDir ==
"." && dut && !isDut(origOp))
432 outDir = {testBenchDir, Priority::TestBench};
434 outDir = {coverDir, Priority::Verification};
438 SmallString<128> outputFilePath(outDir.first);
440 return {
StringAttr::get(context, outputFilePath), outDir.second, exclude};
445 bool BlackBoxReaderPass::isDut(Operation *module) {
447 auto iter = dutModuleMap.find(module);
448 if (iter != dutModuleMap.end())
449 return iter->getSecond();
452 dutModuleMap[module] =
true;
455 auto *node = instanceGraph->lookup(cast<igraph::ModuleOpInterface>(module));
456 bool anyParentIsDut =
false;
458 for (
auto *u : node->uses()) {
459 if (cast<InstanceOp>(u->getInstance().getOperation()).getLowerToBind())
462 auto dut = isDut(u->getInstance()->getParentOfType<FModuleOp>());
464 dutModuleMap[module] = dut;
465 anyParentIsDut |= dut;
467 dutModuleMap[module] = anyParentIsDut;
468 return anyParentIsDut;
475 std::unique_ptr<mlir::Pass>
477 auto pass = std::make_unique<BlackBoxReaderPass>();
479 pass->inputPrefix = inputPrefix->str();
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * extractCoverageAnnoClass
constexpr const char * blackBoxAnnoClass
LogicalResult extractDUT(FModuleOp mod, FModuleOp &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
constexpr const char * testBenchDirAnnoClass
constexpr const char * dutAnnoClass
std::unique_ptr< mlir::Pass > createBlackBoxReaderPass(std::optional< mlir::StringRef > inputPrefix={})
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxPathAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * blackBoxInlineAnnoClass
constexpr const char * blackBoxResourceFileNameAnnoClass
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
The namespace of a CircuitOp, generally inhabited by modules.