27 #include "mlir/IR/Attributes.h"
28 #include "mlir/Pass/Pass.h"
29 #include "mlir/Support/FileUtilities.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"
40 #define GEN_PASS_DEF_BLACKBOXREADER
41 #include "circt/Dialect/FIRRTL/Passes.h.inc"
45 using namespace circt;
46 using namespace firrtl;
55 struct AnnotationInfo {
59 hw::OutputFileAttr outputFileAttr;
61 StringAttr inlineText;
65 void print(raw_ostream &os,
unsigned indent = 0)
const {
66 os << llvm::formatv(
"name: {1}\n"
67 "{0}outputFile: {2}\n"
69 llvm::fmt_pad(
"", indent, 0), name,
70 outputFileAttr.getFilename(),
71 outputFileAttr.getExcludeFromFilelist().getValue());
76 struct BlackBoxReaderPass
77 :
public circt::firrtl::impl::BlackBoxReaderBase<BlackBoxReaderPass> {
78 void runOnOperation()
override;
79 bool runOnAnnotation(Operation *op,
Annotation anno, OpBuilder &builder,
80 bool isCover, AnnotationInfo &annotationInfo);
81 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &builder);
82 hw::OutputFileAttr
getOutputFile(Operation *origOp, StringAttr fileNameAttr,
83 bool isCover =
false);
85 bool isDut(Operation *module);
87 using BlackBoxReaderBase::inputPrefix;
92 SmallVector<emit::FileOp> fileListFiles;
102 StringRef testBenchDir;
112 StringRef resourceFileName;
119 DenseMap<Operation *, bool> dutModuleMap;
129 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
134 void BlackBoxReaderPass::runOnOperation() {
136 CircuitOp circuitOp = getOperation();
139 instanceGraph = &getAnalysis<InstanceGraph>();
140 auto context = &getContext();
143 bool anythingChanged =
false;
150 resourceFileName =
"firrtl_black_box_resource_files.f";
154 SmallVector<Attribute, 4> filteredAnnos;
158 if (
auto resourceFN = annot.getMember<StringAttr>(
"resourceFileName")) {
159 resourceFileName = resourceFN.getValue();
164 <<
" annotation missing \"resourceFileName\" attribute";
168 filteredAnnos.push_back(annot.getDict());
172 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
173 coverDir = dir.getValue();
178 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
179 testBenchDir = dir.getValue();
185 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
186 targetDir = target.getValue();
190 <<
" annotation missing \"targetDir\" attribute";
200 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir <<
"\n"
201 <<
"Black box resource file name: "
202 << resourceFileName <<
"\n");
205 auto builder = circuitOp.getBodyBuilder();
209 for (
auto &op : *circuitOp.getBodyBlock()) {
210 FModuleLike module = dyn_cast<FModuleLike>(op);
214 return signalPassFailure();
217 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
219 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
221 for (
auto extmoduleOp : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
223 llvm::dbgs().indent(2)
224 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
225 llvm::dbgs().indent(4) <<
"annotations:\n";
230 bool foundBBoxAnno =
false;
231 annotations.removeAnnotations([&](
Annotation anno) {
232 AnnotationInfo annotationInfo;
233 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
236 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
243 auto [ptr, inserted] =
244 emittedFileMap.try_emplace(annotationInfo.name, annotationInfo);
246 emittedFileMap[annotationInfo.name] = annotationInfo;
248 auto &fileAttr = ptr->second.outputFileAttr;
249 SmallString<64> directory(fileAttr.getDirectory());
251 annotationInfo.outputFileAttr.getDirectory());
252 fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
253 context, directory, annotationInfo.name.getValue(),
255 fileAttr.getExcludeFromFilelist().getValue());
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.outputFileAttr.getFilename(), fileName,
283 [&, text = annotationInfo.inlineText] {
284 builder.create<emit::VerbatimOp>(loc, text);
287 if (!annotationInfo.outputFileAttr.getExcludeFromFilelist().getValue())
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),
308 builder.getArrayAttr(symbols),
309 builder.getStringAttr(ns.newName(
"blackbox_filelist")));
313 if (!anythingChanged)
314 markAllAnalysesPreserved();
315 markAnalysesPreserved<InstanceGraph>();
318 emittedFileMap.clear();
319 fileListFiles.clear();
326 bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
327 OpBuilder &builder,
bool isCover,
328 AnnotationInfo &annotationInfo) {
331 auto name = anno.
getMember<StringAttr>(
"name");
332 auto text = anno.
getMember<StringAttr>(
"text");
333 if (!name || !text) {
335 <<
" annotation missing \"name\" or \"text\" attribute";
340 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
341 annotationInfo.name = name;
342 annotationInfo.inlineText = text;
348 auto path = anno.
getMember<StringAttr>(
"path");
351 <<
" annotation missing \"path\" attribute";
355 SmallString<128> inputPath(inputPrefix);
357 auto text = loadFile(op, inputPath, builder);
359 op->emitError(
"Cannot find file ") << inputPath;
363 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
364 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
365 annotationInfo.name = name;
366 annotationInfo.inlineText = text;
376 StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
377 OpBuilder &builder) {
378 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
379 << llvm::sys::path::filename(inputPath) <<
"` from `"
380 << inputPath <<
"`\n");
383 std::string errorMessage;
384 auto input = mlir::openInputFile(inputPath, &errorMessage);
389 return builder.getStringAttr(input->getBuffer());
394 StringAttr fileNameAttr,
396 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
397 if (outputFile && !outputFile.isDirectory()) {
403 auto *context = &getContext();
404 auto fileName = fileNameAttr.getValue();
405 auto ext = llvm::sys::path::extension(fileName);
406 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
407 auto outDir = targetDir;
411 outDir = outputFile.getFilename();
416 else if (!testBenchDir.empty() && targetDir ==
"." && dut && !isDut(origOp))
417 outDir = testBenchDir;
423 SmallString<128> outputFilePath(outDir);
425 return hw::OutputFileAttr::getFromFilename(context, outputFilePath, exclude);
430 bool BlackBoxReaderPass::isDut(Operation *module) {
432 auto iter = dutModuleMap.find(module);
433 if (iter != dutModuleMap.end())
434 return iter->getSecond();
437 dutModuleMap[module] =
true;
440 auto *node = instanceGraph->lookup(cast<igraph::ModuleOpInterface>(module));
441 bool anyParentIsDut =
false;
443 for (
auto *u : node->uses()) {
444 if (cast<InstanceOp>(u->getInstance().getOperation()).getLowerToBind())
447 auto dut = isDut(u->getInstance()->getParentOfType<FModuleOp>());
449 dutModuleMap[module] = dut;
450 anyParentIsDut |= dut;
452 dutModuleMap[module] = anyParentIsDut;
453 return anyParentIsDut;
460 std::unique_ptr<mlir::Pass>
462 auto pass = std::make_unique<BlackBoxReaderPass>();
464 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
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
LogicalResult extractDUT(FModuleLike mod, FModuleLike &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * blackBoxInlineAnnoClass
void makeCommonPrefix(SmallString< 64 > &a, StringRef b)
Truncate a to the common prefix of a and b.
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.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
llvm::raw_ostream & debugFooter(int width=80)
Write a boilerplate footer to the debug stream to indicate that a pass has ended.
The namespace of a CircuitOp, generally inhabited by modules.