29 #include "mlir/IR/Attributes.h"
30 #include "mlir/Pass/Pass.h"
31 #include "mlir/Support/FileUtilities.h"
32 #include "llvm/ADT/SmallPtrSet.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/FormatAdapters.h"
35 #include "llvm/Support/FormatVariadic.h"
36 #include "llvm/Support/MemoryBuffer.h"
37 #include "llvm/Support/Path.h"
39 #define DEBUG_TYPE "firrtl-blackbox-reader"
43 #define GEN_PASS_DEF_BLACKBOXREADER
44 #include "circt/Dialect/FIRRTL/Passes.h.inc"
48 using namespace circt;
49 using namespace firrtl;
58 struct AnnotationInfo {
62 hw::OutputFileAttr outputFileAttr;
64 StringAttr inlineText;
68 void print(raw_ostream &os,
unsigned indent = 0)
const {
69 os << llvm::formatv(
"name: {1}\n"
70 "{0}outputFile: {2}\n"
72 llvm::fmt_pad(
"", indent, 0), name,
73 outputFileAttr.getFilename(),
74 outputFileAttr.getExcludeFromFilelist().getValue());
79 struct BlackBoxReaderPass
80 :
public circt::firrtl::impl::BlackBoxReaderBase<BlackBoxReaderPass> {
81 void runOnOperation()
override;
82 bool runOnAnnotation(Operation *op,
Annotation anno, OpBuilder &builder,
83 bool isCover, AnnotationInfo &annotationInfo);
84 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &builder);
85 hw::OutputFileAttr
getOutputFile(Operation *origOp, StringAttr fileNameAttr,
86 bool isCover =
false);
88 bool isDut(Operation *module);
90 using BlackBoxReaderBase::inputPrefix;
95 SmallVector<emit::FileOp> fileListFiles;
105 StringRef testBenchDir;
115 StringRef resourceFileName;
122 DenseMap<Operation *, bool> dutModuleMap;
132 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
137 void BlackBoxReaderPass::runOnOperation() {
139 CircuitOp circuitOp = getOperation();
142 instanceGraph = &getAnalysis<InstanceGraph>();
143 auto context = &getContext();
146 bool anythingChanged =
false;
153 resourceFileName =
"firrtl_black_box_resource_files.f";
157 SmallVector<Attribute, 4> filteredAnnos;
161 if (
auto resourceFN = annot.getMember<StringAttr>(
"resourceFileName")) {
162 resourceFileName = resourceFN.getValue();
167 <<
" annotation missing \"resourceFileName\" attribute";
171 filteredAnnos.push_back(annot.getDict());
175 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
176 coverDir = dir.getValue();
181 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
182 testBenchDir = dir.getValue();
188 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
189 targetDir = target.getValue();
193 <<
" annotation missing \"targetDir\" attribute";
203 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir <<
"\n"
204 <<
"Black box resource file name: "
205 << resourceFileName <<
"\n");
208 auto builder = circuitOp.getBodyBuilder();
212 for (
auto &op : *circuitOp.getBodyBlock()) {
213 FModuleOp module = dyn_cast<FModuleOp>(op);
217 return signalPassFailure();
220 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
222 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
224 for (
auto extmoduleOp : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
226 llvm::dbgs().indent(2)
227 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
228 llvm::dbgs().indent(4) <<
"annotations:\n";
233 bool foundBBoxAnno =
false;
234 annotations.removeAnnotations([&](
Annotation anno) {
235 AnnotationInfo annotationInfo;
236 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
239 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
246 auto [ptr, inserted] =
247 emittedFileMap.try_emplace(annotationInfo.name, annotationInfo);
249 emittedFileMap[annotationInfo.name] = annotationInfo;
251 auto &fileAttr = ptr->second.outputFileAttr;
252 SmallString<64> directory(fileAttr.getDirectory());
254 annotationInfo.outputFileAttr.getDirectory());
255 fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
256 context, directory, annotationInfo.name.getValue(),
258 fileAttr.getExcludeFromFilelist().getValue());
262 foundBBoxAnno =
true;
267 annotations.addAnnotations({bboxAnno});
268 anythingChanged =
true;
270 annotations.applyToOperation(extmoduleOp);
273 LLVM_DEBUG(llvm::dbgs() <<
"emittedFiles:\n");
274 Location loc = builder.getUnknownLoc();
275 for (
auto &[verilogName, annotationInfo] : emittedFileMap) {
277 llvm::dbgs().indent(2) <<
"verilogName: " << verilogName <<
"\n";
278 llvm::dbgs().indent(2) <<
"annotationInfo:\n";
279 annotationInfo.print(llvm::dbgs().indent(4) <<
"- ", 6);
282 auto fileName = ns.newName(
"blackbox_" + verilogName.getValue());
284 auto fileOp = builder.create<emit::FileOp>(
285 loc, annotationInfo.outputFileAttr.getFilename(), fileName,
286 [&, text = annotationInfo.inlineText] {
287 builder.create<emit::VerbatimOp>(loc, text);
290 if (!annotationInfo.outputFileAttr.getExcludeFromFilelist().getValue())
291 fileListFiles.push_back(fileOp);
297 if (!fileListFiles.empty()) {
299 llvm::sort(fileListFiles.begin(), fileListFiles.end(),
300 [](emit::FileOp fileA, emit::FileOp fileB) {
301 return fileA.getFileName() < fileB.getFileName();
305 SmallVector<Attribute> symbols;
306 for (emit::FileOp file : fileListFiles)
309 builder.create<emit::FileListOp>(
310 loc, builder.getStringAttr(resourceFileName),
311 builder.getArrayAttr(symbols),
312 builder.getStringAttr(ns.newName(
"blackbox_filelist")));
316 if (!anythingChanged)
317 markAllAnalysesPreserved();
318 markAnalysesPreserved<InstanceGraph>();
321 emittedFileMap.clear();
322 fileListFiles.clear();
329 bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
330 OpBuilder &builder,
bool isCover,
331 AnnotationInfo &annotationInfo) {
334 auto name = anno.
getMember<StringAttr>(
"name");
335 auto text = anno.
getMember<StringAttr>(
"text");
336 if (!name || !text) {
338 <<
" annotation missing \"name\" or \"text\" attribute";
343 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
344 annotationInfo.name = name;
345 annotationInfo.inlineText = text;
351 auto path = anno.
getMember<StringAttr>(
"path");
354 <<
" annotation missing \"path\" attribute";
358 SmallString<128> inputPath(inputPrefix);
360 auto text = loadFile(op, inputPath, builder);
362 op->emitError(
"Cannot find file ") << inputPath;
366 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
367 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
368 annotationInfo.name = name;
369 annotationInfo.inlineText = text;
379 StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
380 OpBuilder &builder) {
381 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
382 << llvm::sys::path::filename(inputPath) <<
"` from `"
383 << inputPath <<
"`\n");
386 std::string errorMessage;
387 auto input = mlir::openInputFile(inputPath, &errorMessage);
392 return builder.getStringAttr(input->getBuffer());
397 StringAttr fileNameAttr,
399 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
400 if (outputFile && !outputFile.isDirectory()) {
406 auto *context = &getContext();
407 auto fileName = fileNameAttr.getValue();
408 auto ext = llvm::sys::path::extension(fileName);
409 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
410 auto outDir = targetDir;
414 outDir = outputFile.getFilename();
419 else if (!testBenchDir.empty() && targetDir ==
"." && dut && !isDut(origOp))
420 outDir = testBenchDir;
426 SmallString<128> outputFilePath(outDir);
428 return hw::OutputFileAttr::getFromFilename(context, outputFilePath, exclude);
433 bool BlackBoxReaderPass::isDut(Operation *module) {
435 auto iter = dutModuleMap.find(module);
436 if (iter != dutModuleMap.end())
437 return iter->getSecond();
440 dutModuleMap[module] =
true;
443 auto *node = instanceGraph->lookup(cast<igraph::ModuleOpInterface>(module));
444 bool anyParentIsDut =
false;
446 for (
auto *u : node->uses()) {
447 if (cast<InstanceOp>(u->getInstance().getOperation()).getLowerToBind())
450 auto dut = isDut(u->getInstance()->getParentOfType<FModuleOp>());
452 dutModuleMap[module] = dut;
453 anyParentIsDut |= dut;
455 dutModuleMap[module] = anyParentIsDut;
456 return anyParentIsDut;
463 std::unique_ptr<mlir::Pass>
465 auto pass = std::make_unique<BlackBoxReaderPass>();
467 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
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.