28#include "mlir/IR/Attributes.h"
29#include "mlir/Pass/Pass.h"
30#include "mlir/Support/FileUtilities.h"
31#include "llvm/ADT/MapVector.h"
32#include "llvm/Support/Debug.h"
33#include "llvm/Support/FormatAdapters.h"
34#include "llvm/Support/FormatVariadic.h"
35#include "llvm/Support/MemoryBuffer.h"
36#include "llvm/Support/Path.h"
38#define DEBUG_TYPE "firrtl-blackbox-reader"
42#define GEN_PASS_DEF_BLACKBOXREADER
43#include "circt/Dialect/FIRRTL/Passes.h.inc"
57struct AnnotationInfo {
61 hw::OutputFileAttr outputFileAttr;
63 StringAttr inlineText;
67 void print(raw_ostream &os,
unsigned indent = 0)
const {
68 os << llvm::formatv(
"name: {1}\n"
69 "{0}outputFile: {2}\n"
71 llvm::fmt_pad(
"", indent, 0), name,
72 outputFileAttr.getFilename(),
73 outputFileAttr.getExcludeFromFilelist().getValue());
78struct BlackBoxReaderPass
79 :
public circt::firrtl::impl::BlackBoxReaderBase<BlackBoxReaderPass> {
82 void runOnOperation()
override;
83 bool runOnAnnotation(Operation *op, Annotation anno, OpBuilder &builder,
84 bool isCover, AnnotationInfo &annotationInfo);
85 StringAttr loadFile(Operation *op, StringRef inputPath, OpBuilder &builder);
86 hw::OutputFileAttr
getOutputFile(Operation *origOp, StringAttr fileNameAttr,
87 bool isCover =
false);
98 StringRef testBenchDir;
102 InstanceInfo *instanceInfo;
112 llvm::MapVector<StringAttr, AnnotationInfo> filenameToAnnotationInfo;
117void BlackBoxReaderPass::runOnOperation() {
119 CircuitOp circuitOp = getOperation();
120 CircuitNamespace ns(circuitOp);
122 instanceGraph = &getAnalysis<InstanceGraph>();
123 instanceInfo = &getAnalysis<InstanceInfo>();
127 bool anythingChanged =
false;
137 SmallVector<Attribute, 4> filteredAnnos;
138 for (
auto annot : AnnotationSet(circuitOp)) {
139 filteredAnnos.push_back(annot.getDict());
142 if (annot.isClass(extractCoverageAnnoClass))
143 if (
auto dir = annot.getMember<StringAttr>(
"directory")) {
144 coverDir = dir.getValue();
148 if (annot.isClass(testBenchDirAnnoClass))
149 if (
auto dir = annot.getMember<StringAttr>(
"dirname")) {
150 testBenchDir = dir.getValue();
155 if (annot.isClass(blackBoxTargetDirAnnoClass)) {
156 if (
auto target = annot.getMember<StringAttr>(
"targetDir")) {
157 targetDir = target.getValue();
160 circuitOp->emitError(blackBoxTargetDirAnnoClass)
161 <<
" annotation missing \"targetDir\" attribute";
169 AnnotationSet(filteredAnnos,
context).applyToOperation(circuitOp);
171 LLVM_DEBUG(llvm::dbgs() <<
"Black box target directory: " << targetDir
175 auto builder = circuitOp.getBodyBuilder();
177 LLVM_DEBUG(llvm::dbgs() <<
"Visiting extmodules:\n");
179 builder.getDictionaryAttr({{builder.getStringAttr(
"class"),
180 builder.getStringAttr(blackBoxAnnoClass)}});
182 llvm::MapVector<Operation *, SmallVector<StringAttr>>
183 verbatimExtmoduleToFiles;
185 for (
auto extmoduleOp : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
187 llvm::dbgs().indent(2)
188 <<
"- name: " << extmoduleOp.getModuleNameAttr() <<
"\n";
189 llvm::dbgs().indent(4) <<
"annotations:\n";
191 AnnotationSet annotations(extmoduleOp);
192 bool isCover = !coverDir.empty() &&
193 annotations.hasAnnotation(internalVerifBlackBoxAnnoClass);
194 bool foundBBoxAnno =
false;
195 annotations.removeAnnotations([&](Annotation anno) {
196 AnnotationInfo annotationInfo;
197 if (!runOnAnnotation(extmoduleOp, anno, builder, isCover, annotationInfo))
200 LLVM_DEBUG(annotationInfo.print(llvm::dbgs().indent(6) <<
"- ", 8));
203 verbatimExtmoduleToFiles[extmoduleOp].push_back(annotationInfo.name);
210 auto [ptr, inserted] = filenameToAnnotationInfo.try_emplace(
211 annotationInfo.name, annotationInfo);
213 filenameToAnnotationInfo[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);
238 for (
auto &[moduleOp, fileNames] : verbatimExtmoduleToFiles) {
239 AnnotationSet annotations(moduleOp);
240 SmallVector<Attribute> verbatimFiles;
241 llvm::SmallPtrSet<StringAttr, 4> seenFiles;
243 for (StringAttr fileName : fileNames) {
244 if (!seenFiles.insert(fileName).second)
248 auto &annotationInfo = filenameToAnnotationInfo[fileName];
249 auto fileDict = builder.getDictionaryAttr(
250 {{builder.getStringAttr(
"content"), annotationInfo.inlineText},
251 {builder.getStringAttr(
"output_file"),
252 builder.getStringAttr(
253 annotationInfo.outputFileAttr.getFilename().getValue())}});
254 verbatimFiles.push_back(fileDict);
257 if (!verbatimFiles.empty()) {
258 annotations.addAnnotations({builder.getDictionaryAttr(
259 {{builder.getStringAttr(
"class"),
260 builder.getStringAttr(verbatimBlackBoxAnnoClass)},
261 {builder.getStringAttr(
"files"),
262 builder.getArrayAttr(verbatimFiles)}})});
263 annotations.applyToOperation(moduleOp);
264 anythingChanged =
true;
269 if (!anythingChanged)
270 markAllAnalysesPreserved();
271 markAnalysesPreserved<InstanceGraph, InstanceInfo>();
274 filenameToAnnotationInfo.clear();
275 verbatimExtmoduleToFiles.clear();
281bool BlackBoxReaderPass::runOnAnnotation(Operation *op, Annotation anno,
282 OpBuilder &builder,
bool isCover,
283 AnnotationInfo &annotationInfo) {
285 if (anno.
isClass(blackBoxInlineAnnoClass)) {
286 auto name = anno.
getMember<StringAttr>(
"name");
287 auto text = anno.
getMember<StringAttr>(
"text");
288 if (!name || !text) {
289 op->emitError(blackBoxInlineAnnoClass)
290 <<
" annotation missing \"name\" or \"text\" attribute";
295 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
296 annotationInfo.name = name;
297 annotationInfo.inlineText = text;
302 if (anno.
isClass(blackBoxPathAnnoClass)) {
303 auto path = anno.
getMember<StringAttr>(
"path");
305 op->emitError(blackBoxPathAnnoClass)
306 <<
" annotation missing \"path\" attribute";
310 SmallString<128> inputPath(inputPrefix);
312 auto text = loadFile(op, inputPath, builder);
314 op->emitError(
"Cannot find file ") << inputPath;
318 auto name = builder.getStringAttr(llvm::sys::path::filename(path));
319 annotationInfo.outputFileAttr =
getOutputFile(op, name, isCover);
320 annotationInfo.name = name;
321 annotationInfo.inlineText = text;
331StringAttr BlackBoxReaderPass::loadFile(Operation *op, StringRef inputPath,
332 OpBuilder &builder) {
333 LLVM_DEBUG(llvm::dbgs() <<
"Add black box source `"
334 << llvm::sys::path::filename(inputPath) <<
"` from `"
335 << inputPath <<
"`\n");
338 std::string errorMessage;
339 auto input = mlir::openInputFile(inputPath, &errorMessage);
344 return builder.getStringAttr(input->getBuffer());
348hw::OutputFileAttr BlackBoxReaderPass::getOutputFile(Operation *origOp,
349 StringAttr fileNameAttr,
353 auto outputFile = origOp->getAttrOfType<hw::OutputFileAttr>(
"output_file");
354 if (outputFile && !outputFile.isDirectory()) {
361 auto fileName = fileNameAttr.getValue();
362 auto ext = llvm::sys::path::extension(fileName);
363 bool exclude = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
364 auto outDir = targetDir;
368 outDir = outputFile.getFilename();
373 else if (!testBenchDir.empty() && targetDir ==
"." &&
375 cast<igraph::ModuleOpInterface>(origOp)))
376 outDir = testBenchDir;
382 SmallString<128> outputFilePath(outDir);
383 llvm::sys::path::append(outputFilePath, fileName);
384 return hw::OutputFileAttr::getFromFilename(
context, outputFilePath, exclude);
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
static std::unique_ptr< Context > context
static void print(TypedAttr val, llvm::raw_ostream &os)
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
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.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void makeCommonDirectoryPrefix(llvm::SmallVectorImpl< char > &a, StringRef b)
Truncate a in place to the longest common directory prefix of a and b, ensuring that the result ends ...
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.