28#include "mlir/IR/Attributes.h"
29#include "mlir/Pass/Pass.h"
30#include "mlir/Support/FileUtilities.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"
47using namespace firrtl;
56struct AnnotationInfo {
60 hw::OutputFileAttr outputFileAttr;
62 StringAttr inlineText;
66 void print(raw_ostream &os,
unsigned indent = 0)
const {
67 os << llvm::formatv(
"name: {1}\n"
68 "{0}outputFile: {2}\n"
70 llvm::fmt_pad(
"", indent, 0), name,
71 outputFileAttr.getFilename(),
72 outputFileAttr.getExcludeFromFilelist().getValue());
77struct BlackBoxReaderPass
78 :
public circt::firrtl::impl::BlackBoxReaderBase<BlackBoxReaderPass> {
79 void runOnOperation()
override;
80 bool runOnAnnotation(Operation *op,
Annotation anno, OpBuilder &builder,
81 bool isCover, AnnotationInfo &annotationInfo);
82 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &builder);
83 hw::OutputFileAttr
getOutputFile(Operation *origOp, StringAttr fileNameAttr,
84 bool isCover =
false);
86 using BlackBoxReaderBase::inputPrefix;
91 SmallVector<emit::FileOp> fileListFiles;
101 StringRef testBenchDir;
106 StringRef resourceFileName;
114 DenseMap<Operation *, bool> dutModuleMap;
124 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
129void BlackBoxReaderPass::runOnOperation() {
131 CircuitOp circuitOp = getOperation();
134 instanceGraph = &getAnalysis<InstanceGraph>();
135 instanceInfo = &getAnalysis<InstanceInfo>();
136 auto context = &getContext();
139 bool anythingChanged =
false;
146 resourceFileName =
"firrtl_black_box_resource_files.f";
150 SmallVector<Attribute, 4> filteredAnnos;
154 if (
auto resourceFN = annot.getMember<StringAttr>(
"resourceFileName")) {
155 resourceFileName = resourceFN.getValue();
160 <<
" annotation missing \"resourceFileName\" attribute";
164 filteredAnnos.push_back(annot.getDict());
168 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
169 coverDir = dir.getValue();
174 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
175 testBenchDir = dir.getValue();
181 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
182 targetDir = target.getValue();
186 <<
" annotation missing \"targetDir\" attribute";
196 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir <<
"\n"
197 <<
"Black box resource file name: "
198 << resourceFileName <<
"\n");
201 auto builder = circuitOp.getBodyBuilder();
203 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
205 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
207 for (
auto extmoduleOp : circuitOp.
getBodyBlock()->getOps<FExtModuleOp>()) {
209 llvm::dbgs().indent(2)
210 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
211 llvm::dbgs().indent(4) <<
"annotations:\n";
216 bool foundBBoxAnno =
false;
217 annotations.removeAnnotations([&](
Annotation anno) {
218 AnnotationInfo annotationInfo;
219 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
222 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
229 auto [ptr, inserted] =
230 emittedFileMap.try_emplace(annotationInfo.name, annotationInfo);
232 emittedFileMap[annotationInfo.name] = annotationInfo;
234 auto &fileAttr = ptr->second.outputFileAttr;
235 SmallString<64> directory(fileAttr.getDirectory());
237 annotationInfo.outputFileAttr.getDirectory());
238 fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
239 context, directory, annotationInfo.name.getValue(),
241 fileAttr.getExcludeFromFilelist().getValue());
245 foundBBoxAnno =
true;
250 annotations.addAnnotations({bboxAnno});
251 anythingChanged =
true;
253 annotations.applyToOperation(extmoduleOp);
256 LLVM_DEBUG(llvm::dbgs() <<
"emittedFiles:\n");
257 Location loc = builder.getUnknownLoc();
258 for (
auto &[verilogName, annotationInfo] : emittedFileMap) {
260 llvm::dbgs().indent(2) <<
"verilogName: " << verilogName <<
"\n";
261 llvm::dbgs().indent(2) <<
"annotationInfo:\n";
262 annotationInfo.print(llvm::dbgs().indent(4) <<
"- ", 6);
265 auto fileName = ns.newName(
"blackbox_" + verilogName.getValue());
267 auto fileOp = builder.create<emit::FileOp>(
268 loc, annotationInfo.outputFileAttr.getFilename(), fileName,
269 [&, text = annotationInfo.inlineText] {
270 builder.create<emit::VerbatimOp>(loc, text);
273 if (!annotationInfo.outputFileAttr.getExcludeFromFilelist().getValue())
274 fileListFiles.push_back(fileOp);
280 if (!fileListFiles.empty()) {
282 llvm::sort(fileListFiles.begin(), fileListFiles.end(),
283 [](emit::FileOp fileA, emit::FileOp fileB) {
284 return fileA.getFileName() < fileB.getFileName();
288 SmallVector<Attribute> symbols;
289 for (emit::FileOp file : fileListFiles)
290 symbols.push_back(FlatSymbolRefAttr::
get(file.getSymNameAttr()));
292 builder.create<emit::FileListOp>(
293 loc, builder.getStringAttr(resourceFileName),
294 builder.getArrayAttr(symbols),
295 builder.getStringAttr(ns.newName(
"blackbox_filelist")));
299 if (!anythingChanged)
300 markAllAnalysesPreserved();
301 markAnalysesPreserved<InstanceGraph, InstanceInfo>();
304 emittedFileMap.clear();
305 fileListFiles.clear();
312bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
313 OpBuilder &builder,
bool isCover,
314 AnnotationInfo &annotationInfo) {
317 auto name = anno.
getMember<StringAttr>(
"name");
318 auto text = anno.
getMember<StringAttr>(
"text");
319 if (!name || !text) {
321 <<
" annotation missing \"name\" or \"text\" attribute";
326 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
327 annotationInfo.name = name;
328 annotationInfo.inlineText = text;
334 auto path = anno.
getMember<StringAttr>(
"path");
337 <<
" annotation missing \"path\" attribute";
341 SmallString<128> inputPath(inputPrefix);
343 auto text = loadFile(op, inputPath, builder);
345 op->emitError(
"Cannot find file ") << inputPath;
349 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
350 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
351 annotationInfo.name = name;
352 annotationInfo.inlineText = text;
362StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
363 OpBuilder &builder) {
364 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
365 << llvm::sys::path::filename(inputPath) <<
"` from `"
366 << inputPath <<
"`\n");
369 std::string errorMessage;
370 auto input = mlir::openInputFile(inputPath, &errorMessage);
375 return builder.getStringAttr(input->getBuffer());
379hw::OutputFileAttr BlackBoxReaderPass::getOutputFile(Operation *origOp,
380 StringAttr fileNameAttr,
382 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
383 if (outputFile && !outputFile.isDirectory()) {
389 auto *context = &getContext();
390 auto fileName = fileNameAttr.getValue();
391 auto ext = llvm::sys::path::extension(fileName);
392 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
393 auto outDir = targetDir;
398 outDir = outputFile.getFilename();
403 else if (!testBenchDir.empty() && targetDir ==
"." &&
404 !instanceInfo->allInstancesInEffectiveDesign(
405 cast<igraph::ModuleOpInterface>(origOp)))
406 outDir = testBenchDir;
412 SmallString<128> outputFilePath(outDir);
413 llvm::sys::path::append(outputFilePath, fileName);
414 return hw::OutputFileAttr::getFromFilename(context, outputFilePath, exclude);
421std::unique_ptr<mlir::Pass>
423 auto pass = std::make_unique<BlackBoxReaderPass>();
425 pass->inputPrefix = inputPrefix->str();
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
static Block * getBodyBlock(FModuleLike mod)
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
constexpr const char * testBenchDirAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxPathAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::unique_ptr< mlir::Pass > createBlackBoxReaderPass(std::optional< mlir::StringRef > inputPrefix={})
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.