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> {
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);
91 SmallVector<emit::FileOp> fileListFiles;
101 StringRef testBenchDir;
109 DenseMap<Operation *, bool> dutModuleMap;
119 llvm::MapVector<StringAttr, AnnotationInfo> emittedFileMap;
124void BlackBoxReaderPass::runOnOperation() {
126 CircuitOp circuitOp = getOperation();
129 instanceGraph = &getAnalysis<InstanceGraph>();
130 instanceInfo = &getAnalysis<InstanceInfo>();
131 auto context = &getContext();
134 bool anythingChanged =
false;
144 SmallVector<Attribute, 4> filteredAnnos;
146 filteredAnnos.push_back(annot.getDict());
150 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
151 coverDir = dir.getValue();
156 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
157 testBenchDir = dir.getValue();
163 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
164 targetDir = target.getValue();
168 <<
" annotation missing \"targetDir\" attribute";
178 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir
182 auto builder = circuitOp.getBodyBuilder();
184 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
186 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
188 for (
auto extmoduleOp : circuitOp.
getBodyBlock()->getOps<FExtModuleOp>()) {
190 llvm::dbgs().indent(2)
191 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
192 llvm::dbgs().indent(4) <<
"annotations:\n";
197 bool foundBBoxAnno =
false;
198 annotations.removeAnnotations([&](
Annotation anno) {
199 AnnotationInfo annotationInfo;
200 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
203 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
210 auto [ptr, inserted] =
211 emittedFileMap.try_emplace(annotationInfo.name, annotationInfo);
213 emittedFileMap[annotationInfo.name] = annotationInfo;
215 auto &fileAttr = ptr->second.outputFileAttr;
216 SmallString<64> directory(fileAttr.getDirectory());
218 annotationInfo.outputFileAttr.getDirectory());
219 fileAttr = hw::OutputFileAttr::getFromDirectoryAndFilename(
220 context, directory, annotationInfo.name.getValue(),
222 fileAttr.getExcludeFromFilelist().getValue());
226 foundBBoxAnno =
true;
231 annotations.addAnnotations({bboxAnno});
232 anythingChanged =
true;
234 annotations.applyToOperation(extmoduleOp);
237 LLVM_DEBUG(llvm::dbgs() <<
"emittedFiles:\n");
238 Location loc = builder.getUnknownLoc();
239 for (
auto &[verilogName, annotationInfo] : emittedFileMap) {
241 llvm::dbgs().indent(2) <<
"verilogName: " << verilogName <<
"\n";
242 llvm::dbgs().indent(2) <<
"annotationInfo:\n";
243 annotationInfo.print(llvm::dbgs().indent(4) <<
"- ", 6);
246 auto fileName = ns.newName(
"blackbox_" + verilogName.getValue());
248 auto fileOp = builder.create<emit::FileOp>(
249 loc, annotationInfo.outputFileAttr.getFilename(), fileName,
250 [&, text = annotationInfo.inlineText] {
251 builder.create<emit::VerbatimOp>(loc, text);
254 if (!annotationInfo.outputFileAttr.getExcludeFromFilelist().getValue())
255 fileListFiles.push_back(fileOp);
261 if (!fileListFiles.empty()) {
263 llvm::sort(fileListFiles.begin(), fileListFiles.end(),
264 [](emit::FileOp fileA, emit::FileOp fileB) {
265 return fileA.getFileName() < fileB.getFileName();
269 SmallVector<Attribute> symbols;
270 for (emit::FileOp file : fileListFiles)
271 symbols.push_back(FlatSymbolRefAttr::
get(file.getSymNameAttr()));
275 if (!anythingChanged)
276 markAllAnalysesPreserved();
277 markAnalysesPreserved<InstanceGraph, InstanceInfo>();
280 emittedFileMap.clear();
281 fileListFiles.clear();
288bool BlackBoxReaderPass::runOnAnnotation(Operation *op,
Annotation anno,
289 OpBuilder &builder,
bool isCover,
290 AnnotationInfo &annotationInfo) {
293 auto name = anno.
getMember<StringAttr>(
"name");
294 auto text = anno.
getMember<StringAttr>(
"text");
295 if (!name || !text) {
297 <<
" annotation missing \"name\" or \"text\" attribute";
302 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
303 annotationInfo.name = name;
304 annotationInfo.inlineText = text;
310 auto path = anno.
getMember<StringAttr>(
"path");
313 <<
" annotation missing \"path\" attribute";
317 SmallString<128> inputPath(inputPrefix);
319 auto text = loadFile(op, inputPath, builder);
321 op->emitError(
"Cannot find file ") << inputPath;
325 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
326 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
327 annotationInfo.name = name;
328 annotationInfo.inlineText = text;
338StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
339 OpBuilder &builder) {
340 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
341 << llvm::sys::path::filename(inputPath) <<
"` from `"
342 << inputPath <<
"`\n");
345 std::string errorMessage;
346 auto input = mlir::openInputFile(inputPath, &errorMessage);
351 return builder.getStringAttr(input->getBuffer());
355hw::OutputFileAttr BlackBoxReaderPass::getOutputFile(Operation *origOp,
356 StringAttr fileNameAttr,
360 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
361 if (outputFile && !outputFile.isDirectory()) {
367 auto *context = &getContext();
368 auto fileName = fileNameAttr.getValue();
369 auto ext = llvm::sys::path::extension(fileName);
370 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
371 auto outDir = targetDir;
375 outDir = outputFile.getFilename();
380 else if (!testBenchDir.empty() && targetDir ==
"." &&
381 !instanceInfo->anyInstanceInEffectiveDesign(
382 cast<igraph::ModuleOpInterface>(origOp)))
383 outDir = testBenchDir;
389 SmallString<128> outputFilePath(outDir);
390 llvm::sys::path::append(outputFilePath, fileName);
391 return hw::OutputFileAttr::getFromFilename(context, outputFilePath, exclude);
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
static void print(TypedAttr val, llvm::raw_ostream &os)
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
void makeCommonPrefix(SmallString< 64 > &a, StringRef b)
Truncate a to the common prefix of a and b.
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.