27 #include "mlir/IR/Attributes.h"
28 #include "mlir/Support/FileUtilities.h"
29 #include "llvm/ADT/SmallPtrSet.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/FormatAdapters.h"
32 #include "llvm/Support/FormatVariadic.h"
33 #include "llvm/Support/MemoryBuffer.h"
34 #include "llvm/Support/Path.h"
36 #define DEBUG_TYPE "firrtl-blackbox-reader"
38 using namespace circt;
39 using namespace firrtl;
50 enum class Priority { TargetDir = 0, Verification, Explicit, TestBench, Unset };
53 struct AnnotationInfo {
57 StringAttr outputFile;
59 StringAttr inlineText;
64 Priority priority = Priority::Unset;
66 bool excludeFromFileList =
false;
70 void print(raw_ostream &os,
unsigned indent = 0)
const {
71 if (priority == Priority::Unset) {
75 os << llvm::formatv(
"name: {1}\n"
76 "{0}outputFile: {2}\n"
79 llvm::fmt_pad(
"", indent, 0), name, outputFile,
80 (
unsigned)priority, excludeFromFileList);
86 struct OutputFileInfo {
89 bool excludeFromFileList;
92 struct BlackBoxReaderPass :
public BlackBoxReaderBase<BlackBoxReaderPass> {
93 void runOnOperation()
override;
95 bool isCover, AnnotationInfo &annotationInfo);
96 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &
builder);
97 OutputFileInfo getOutputFile(Operation *origOp, StringAttr fileNameAttr,
98 bool isCover =
false);
100 bool isDut(Operation *module);
102 using BlackBoxReaderBase::inputPrefix;
107 SmallVector<emit::FileOp> fileListFiles;
117 StringRef testBenchDir;
127 StringRef resourceFileName;
134 DenseMap<Operation *, bool> dutModuleMap;
144 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
149 void BlackBoxReaderPass::runOnOperation() {
150 CircuitOp circuitOp = getOperation();
153 instanceGraph = &getAnalysis<InstanceGraph>();
154 auto context = &getContext();
157 bool anythingChanged =
false;
164 resourceFileName =
"firrtl_black_box_resource_files.f";
168 SmallVector<Attribute, 4> filteredAnnos;
172 if (
auto resourceFN = annot.getMember<StringAttr>(
"resourceFileName")) {
173 resourceFileName = resourceFN.getValue();
178 <<
" annotation missing \"resourceFileName\" attribute";
182 filteredAnnos.push_back(annot.getDict());
186 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
187 coverDir = dir.getValue();
192 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
193 testBenchDir = dir.getValue();
199 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
200 targetDir = target.getValue();
204 <<
" annotation missing \"targetDir\" attribute";
214 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir <<
"\n"
215 <<
"Black box resource file name: "
216 << resourceFileName <<
"\n");
219 auto builder = circuitOp.getBodyBuilder();
223 for (
auto &op : *circuitOp.getBodyBlock()) {
224 FModuleOp module = dyn_cast<FModuleOp>(op);
228 return signalPassFailure();
231 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
235 for (
auto extmoduleOp : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
237 llvm::dbgs().indent(2)
238 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
239 llvm::dbgs().indent(4) <<
"annotations:\n";
244 bool foundBBoxAnno =
false;
245 annotations.removeAnnotations([&](
Annotation anno) {
246 AnnotationInfo annotationInfo;
247 if (!runOnAnnotation(extmoduleOp, anno,
builder, isCover, annotationInfo))
250 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
252 auto &bestAnnotationInfo = emittedFileMap[annotationInfo.name];
253 if (annotationInfo.priority < bestAnnotationInfo.priority) {
254 bestAnnotationInfo = annotationInfo;
259 foundBBoxAnno =
true;
264 annotations.addAnnotations({bboxAnno});
265 anythingChanged =
true;
267 annotations.applyToOperation(extmoduleOp);
270 LLVM_DEBUG(llvm::dbgs() <<
"emittedFiles:\n");
271 Location loc =
builder.getUnknownLoc();
272 for (
auto &[verilogName, annotationInfo] : emittedFileMap) {
274 llvm::dbgs().indent(2) <<
"verilogName: " << verilogName <<
"\n";
275 llvm::dbgs().indent(2) <<
"annotationInfo:\n";
276 annotationInfo.print(llvm::dbgs().indent(4) <<
"- ", 6);
279 auto fileName = ns.newName(
"blackbox_" + verilogName.getValue());
281 auto fileOp =
builder.create<emit::FileOp>(
282 loc, annotationInfo.outputFile, fileName,
283 [&, text = annotationInfo.inlineText] {
284 builder.create<emit::VerbatimOp>(loc, text);
287 if (!annotationInfo.excludeFromFileList)
288 fileListFiles.push_back(fileOp);
294 if (!fileListFiles.empty()) {
296 llvm::sort(fileListFiles.begin(), fileListFiles.end(),
297 [](emit::FileOp fileA, emit::FileOp fileB) {
298 return fileA.getFileName() < fileB.getFileName();
302 SmallVector<Attribute> symbols;
303 for (emit::FileOp file : fileListFiles)
306 builder.create<emit::FileListOp>(
307 loc,
builder.getStringAttr(resourceFileName),
309 builder.getStringAttr(ns.newName(
"blackbox_filelist")));
313 if (!anythingChanged)
314 markAllAnalysesPreserved();
315 markAnalysesPreserved<InstanceGraph>();
318 emittedFileMap.clear();
319 fileListFiles.clear();
325 bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
326 OpBuilder &
builder,
bool isCover,
327 AnnotationInfo &annotationInfo) {
330 auto name = anno.
getMember<StringAttr>(
"name");
331 auto text = anno.
getMember<StringAttr>(
"text");
332 if (!name || !text) {
334 <<
" annotation missing \"name\" or \"text\" attribute";
339 auto outputFile = getOutputFile(op, name, isCover);
340 annotationInfo.outputFile = outputFile.fileName;
341 annotationInfo.name = name;
342 annotationInfo.inlineText = text;
343 annotationInfo.priority = outputFile.priority;
344 annotationInfo.excludeFromFileList = outputFile.excludeFromFileList;
350 auto path = anno.
getMember<StringAttr>(
"path");
353 <<
" annotation missing \"path\" attribute";
357 SmallString<128> inputPath(inputPrefix);
359 auto text = loadFile(op, inputPath,
builder);
361 op->emitError(
"Cannot find file ") << inputPath;
365 auto name =
builder.getStringAttr(llvm::sys::path::filename(path));
366 auto outputFile = getOutputFile(op, name, isCover);
367 annotationInfo.outputFile = outputFile.fileName;
368 annotationInfo.name = name;
369 annotationInfo.inlineText = text;
370 annotationInfo.priority = outputFile.priority;
371 annotationInfo.excludeFromFileList = outputFile.excludeFromFileList;
381 StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
383 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
384 << llvm::sys::path::filename(inputPath) <<
"` from `"
385 << inputPath <<
"`\n");
388 std::string errorMessage;
389 auto input = mlir::openInputFile(inputPath, &errorMessage);
394 return builder.getStringAttr(input->getBuffer());
398 OutputFileInfo BlackBoxReaderPass::getOutputFile(Operation *origOp,
399 StringAttr fileNameAttr,
401 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
402 if (outputFile && !outputFile.isDirectory()) {
403 return {outputFile.getFilename(), Priority::TargetDir,
404 outputFile.getExcludeFromFilelist().getValue()};
409 auto *context = &getContext();
410 auto fileName = fileNameAttr.getValue();
411 auto ext = llvm::sys::path::extension(fileName);
412 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
413 auto outDir = std::make_pair(targetDir, Priority::TargetDir);
417 outDir = {outputFile.getFilename(), Priority::Explicit};
422 else if (!testBenchDir.empty() && targetDir.equals(
".") && dut &&
424 outDir = {testBenchDir, Priority::TestBench};
426 outDir = {coverDir, Priority::Verification};
430 SmallString<128> outputFilePath(outDir.first);
432 return {
StringAttr::get(context, outputFilePath), outDir.second, exclude};
437 bool BlackBoxReaderPass::isDut(Operation *module) {
439 auto iter = dutModuleMap.find(module);
440 if (iter != dutModuleMap.end())
441 return iter->getSecond();
445 dutModuleMap[module] =
true;
448 auto *node = instanceGraph->lookup(cast<igraph::ModuleOpInterface>(module));
449 bool anyParentIsDut =
false;
451 for (
auto *u : node->uses()) {
452 if (cast<InstanceOp>(u->getInstance().getOperation()).getLowerToBind())
455 auto dut = isDut(u->getInstance()->getParentOfType<FModuleOp>());
457 dutModuleMap[module] = dut;
458 anyParentIsDut |= dut;
460 dutModuleMap[module] = anyParentIsDut;
461 return anyParentIsDut;
468 std::unique_ptr<mlir::Pass>
470 auto pass = std::make_unique<BlackBoxReaderPass>();
472 pass->inputPrefix = inputPrefix->str();
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...
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.