CIRCT  19.0.0git
CreateSiFiveMetadata.cpp
Go to the documentation of this file.
1 //===- CreateSiFiveMetadata.cpp - Create various metadata -------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the CreateSiFiveMetadata pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
26 #include "circt/Dialect/OM/OMOps.h"
27 #include "circt/Dialect/SV/SVOps.h"
28 #include "mlir/IR/ImplicitLocOpBuilder.h"
29 #include "mlir/IR/Location.h"
30 #include "llvm/ADT/DepthFirstIterator.h"
31 #include "llvm/ADT/STLExtras.h"
32 #include "llvm/ADT/StringSwitch.h"
33 #include "llvm/Support/JSON.h"
34 #include "llvm/Support/Path.h"
35 
36 using namespace circt;
37 using namespace firrtl;
39 
40 namespace {
41 
42 struct ObjectModelIR {
43  ObjectModelIR(mlir::ModuleOp moduleOp) : moduleOp(moduleOp) {}
44  void createMemorySchema() {
45  auto *context = moduleOp.getContext();
46  auto unknownLoc = mlir::UnknownLoc::get(context);
47  auto builderOM =
48  mlir::ImplicitLocOpBuilder::atBlockEnd(unknownLoc, moduleOp.getBody());
49 
50  // Add all the properties of a memory as fields of the class.
51  // The types must match exactly with the FMemModuleOp attribute type.
52 
53  mlir::Type classFieldTypes[] = {
54  om::SymbolRefType::get(context),
55  mlir::IntegerType::get(context, 64, IntegerType::Unsigned),
56  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
57  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
58  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
59  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
60  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
61  mlir::IntegerType::get(context, 32, IntegerType::Unsigned),
62  mlir::IntegerType::get(context, 32, IntegerType::Unsigned)};
63 
64  memorySchemaClass = om::ClassOp::buildSimpleClassOp(
65  builderOM, unknownLoc, "MemorySchema", memoryParamNames,
66  memoryParamNames, classFieldTypes);
67 
68  // Now create the class that will instantiate metadata class with all the
69  // memories of the circt.
70  memoryMetadataClass =
71  builderOM.create<circt::om::ClassOp>("MemoryMetadata");
72  memoryMetadataClass.getRegion().emplaceBlock();
73  }
74 
75  void createRetimeModulesSchema() {
76  auto *context = moduleOp.getContext();
77  auto unknownLoc = mlir::UnknownLoc::get(context);
78  auto builderOM =
79  mlir::ImplicitLocOpBuilder::atBlockEnd(unknownLoc, moduleOp.getBody());
80  Type classFieldTypes[] = {om::SymbolRefType::get(context)};
81  retimeModulesSchemaClass = om::ClassOp::buildSimpleClassOp(
82  builderOM, unknownLoc, "RetimeModulesSchema", retimeModulesParamNames,
83  retimeModulesParamNames, classFieldTypes);
84 
85  retimeModulesMetadataClass =
86  builderOM.create<circt::om::ClassOp>("RetimeModulesMetadata");
87  retimeModulesMetadataClass.getRegion().emplaceBlock();
88  }
89 
90  void addRetimeModule(FModuleLike module) {
91  if (!retimeModulesSchemaClass)
92  createRetimeModulesSchema();
93  auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
94  module->getLoc(), retimeModulesMetadataClass.getBodyBlock());
95  auto modEntry =
96  builderOM.create<om::ConstantOp>(om::SymbolRefAttr::get(module));
97  auto object = builderOM.create<om::ObjectOp>(retimeModulesSchemaClass,
98  ValueRange({modEntry}));
99  builderOM.create<om::ClassFieldOp>(
100  builderOM.getStringAttr("mod_" + module.getModuleName()), object);
101  }
102 
103  void addBlackBoxModulesSchema() {
104  auto *context = moduleOp.getContext();
105  auto unknownLoc = mlir::UnknownLoc::get(context);
106  auto builderOM =
107  mlir::ImplicitLocOpBuilder::atBlockEnd(unknownLoc, moduleOp.getBody());
108  Type classFieldTypes[] = {om::SymbolRefType::get(context)};
109  blackBoxModulesSchemaClass = om::ClassOp::buildSimpleClassOp(
110  builderOM, unknownLoc, "SitestBlackBoxModulesSchema",
111  blackBoxModulesParamNames, blackBoxModulesParamNames, classFieldTypes);
112  blackBoxMetadataClass =
113  builderOM.create<circt::om::ClassOp>("SitestBlackBoxMetadata");
114  blackBoxMetadataClass.getRegion().emplaceBlock();
115  }
116 
117  void addBlackBoxModule(FExtModuleOp module) {
118  if (!blackBoxModulesSchemaClass)
119  addBlackBoxModulesSchema();
120  auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
121  module.getLoc(), blackBoxMetadataClass.getBodyBlock());
122  auto modEntry =
123  builderOM.create<om::ConstantOp>(om::SymbolRefAttr::get(module));
124  auto object = builderOM.create<om::ObjectOp>(blackBoxModulesSchemaClass,
125  ValueRange({modEntry}));
126  builderOM.create<om::ClassFieldOp>(
127  builderOM.getStringAttr("exterMod_" + module.getName()), object);
128  }
129 
130  void addMemory(FMemModuleOp mem) {
131  if (!memorySchemaClass)
132  createMemorySchema();
133  auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
134  mem.getLoc(), memoryMetadataClass.getBodyBlock());
135  auto createConstField = [&](Attribute constVal) {
136  return builderOM.create<om::ConstantOp>(constVal.cast<mlir::TypedAttr>());
137  };
138 
139  SmallVector<Value> memFields;
140  for (auto field : memoryParamNames)
141  memFields.push_back(createConstField(
142  llvm::StringSwitch<TypedAttr>(field)
143  .Case("name", om::SymbolRefAttr::get(mem))
144  .Case("depth", mem.getDepthAttr())
145  .Case("width", mem.getDataWidthAttr())
146  .Case("maskBits", mem.getMaskBitsAttr())
147  .Case("readPorts", mem.getNumReadPortsAttr())
148  .Case("writePorts", mem.getNumWritePortsAttr())
149  .Case("readwritePorts", mem.getNumReadWritePortsAttr())
150  .Case("readLatency", mem.getReadLatencyAttr())
151  .Case("writeLatency", mem.getWriteLatencyAttr())));
152 
153  auto object = builderOM.create<om::ObjectOp>(memorySchemaClass, memFields);
154  builderOM.create<om::ClassFieldOp>(
155  builderOM.getStringAttr("mem_" + mem.getName()), object);
156  }
157  mlir::ModuleOp moduleOp;
158  om::ClassOp memorySchemaClass;
159  om::ClassOp memoryMetadataClass;
160  om::ClassOp retimeModulesMetadataClass, retimeModulesSchemaClass;
161  om::ClassOp blackBoxModulesSchemaClass, blackBoxMetadataClass;
162  StringRef memoryParamNames[9] = {
163  "name", "depth", "width", "maskBits", "readPorts",
164  "writePorts", "readwritePorts", "writeLatency", "readLatency"};
165  StringRef retimeModulesParamNames[1] = {"moduleName"};
166  StringRef blackBoxModulesParamNames[1] = {"moduleName"};
167 };
168 
169 class CreateSiFiveMetadataPass
170  : public CreateSiFiveMetadataBase<CreateSiFiveMetadataPass> {
171  LogicalResult emitRetimeModulesMetadata(ObjectModelIR &omir);
172  LogicalResult emitSitestBlackboxMetadata(ObjectModelIR &omir);
173  LogicalResult emitMemoryMetadata(ObjectModelIR &omir);
174  void runOnOperation() override;
175 
176  /// Get the cached namespace for a module.
177  hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
178  return moduleNamespaces.try_emplace(module, module).first->second;
179  }
180  // The set of all modules underneath the design under test module.
181  DenseSet<Operation *> dutModuleSet;
182  /// Cached module namespaces.
183  DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
184  // The design under test module.
185  FModuleOp dutMod;
186  CircuitOp circuitOp;
187 
188 public:
189  CreateSiFiveMetadataPass(bool replSeqMem, StringRef replSeqMemFile) {
190  this->replSeqMem = replSeqMem;
191  this->replSeqMemFile = replSeqMemFile.str();
192  }
193 };
194 } // end anonymous namespace
195 
196 /// This function collects all the firrtl.mem ops and creates a verbatim op with
197 /// the relevant memory attributes.
198 LogicalResult
199 CreateSiFiveMetadataPass::emitMemoryMetadata(ObjectModelIR &omir) {
200  if (!replSeqMem)
201  return success();
202 
203  // The instance graph analysis will be required to print the hierarchy names
204  // of the memory.
205  auto instancePathCache = InstancePathCache(getAnalysis<InstanceGraph>());
206 
207  // Everything goes in the DUT if (1) there is no DUT specified or (2) if the
208  // DUT is the top module.
209  bool everythingInDUT =
210  !dutMod ||
211  instancePathCache.instanceGraph.getTopLevelNode()->getModule() == dutMod;
213  auto addSymbolToVerbatimOp =
214  [&](Operation *op,
215  llvm::SmallVectorImpl<Attribute> &symbols) -> SmallString<8> {
216  Attribute symbol;
217  if (auto module = dyn_cast<FModuleLike>(op))
218  symbol = FlatSymbolRefAttr::get(module);
219  else
220  symbol = firrtl::getInnerRefTo(
221  op, [&](auto mod) -> hw::InnerSymbolNamespace & {
222  return getModuleNamespace(mod);
223  });
224 
225  auto [it, inserted] = symbolIndices.try_emplace(symbol, symbols.size());
226  if (inserted)
227  symbols.push_back(symbol);
228 
229  SmallString<8> str;
230  ("{{" + Twine(it->second) + "}}").toVector(str);
231  return str;
232  };
233  // This lambda, writes to the given Json stream all the relevant memory
234  // attributes. Also adds the memory attrbutes to the string for creating the
235  // memmory conf file.
236  auto createMemMetadata = [&](FMemModuleOp mem,
237  llvm::json::OStream &jsonStream,
238  std::string &seqMemConfStr,
239  SmallVectorImpl<Attribute> &jsonSymbols,
240  SmallVectorImpl<Attribute> &seqMemSymbols) {
241  omir.addMemory(mem);
242  // Get the memory data width.
243  auto width = mem.getDataWidth();
244  // Metadata needs to be printed for memories which are candidates for
245  // macro replacement. The requirements for macro replacement::
246  // 1. read latency and write latency of one.
247  // 2. undefined read-under-write behavior.
248  if (!((mem.getReadLatency() == 1 && mem.getWriteLatency() == 1) &&
249  width > 0))
250  return;
251  auto memExtSym = FlatSymbolRefAttr::get(SymbolTable::getSymbolName(mem));
252  auto symId = seqMemSymbols.size();
253  seqMemSymbols.push_back(memExtSym);
254  // Compute the mask granularity.
255  auto isMasked = mem.isMasked();
256  auto maskGran = width;
257  if (isMasked)
258  maskGran /= mem.getMaskBits();
259  // Now create the config string for the memory.
260  std::string portStr;
261  for (uint32_t i = 0; i < mem.getNumWritePorts(); ++i) {
262  if (!portStr.empty())
263  portStr += ",";
264  portStr += isMasked ? "mwrite" : "write";
265  }
266  for (uint32_t i = 0; i < mem.getNumReadPorts(); ++i) {
267  if (!portStr.empty())
268  portStr += ",";
269  portStr += "read";
270  }
271  for (uint32_t i = 0; i < mem.getNumReadWritePorts(); ++i) {
272  if (!portStr.empty())
273  portStr += ",";
274  portStr += isMasked ? "mrw" : "rw";
275  }
276 
277  auto maskGranStr =
278  !isMasked ? "" : " mask_gran " + std::to_string(maskGran);
279  seqMemConfStr = (StringRef(seqMemConfStr) + "name {{" + Twine(symId) +
280  "}} depth " + Twine(mem.getDepth()) + " width " +
281  Twine(width) + " ports " + portStr + maskGranStr + "\n")
282  .str();
283 
284  // Do not emit any JSON for memories which are not in the DUT.
285  if (!everythingInDUT && !dutModuleSet.contains(mem))
286  return;
287  // This adds a Json array element entry corresponding to this memory.
288  jsonStream.object([&] {
289  jsonStream.attribute("module_name",
290  addSymbolToVerbatimOp(mem, jsonSymbols));
291  jsonStream.attribute("depth", (int64_t)mem.getDepth());
292  jsonStream.attribute("width", (int64_t)width);
293  jsonStream.attribute("masked", isMasked);
294  jsonStream.attribute("read", mem.getNumReadPorts());
295  jsonStream.attribute("write", mem.getNumWritePorts());
296  jsonStream.attribute("readwrite", mem.getNumReadWritePorts());
297  if (isMasked)
298  jsonStream.attribute("mask_granularity", (int64_t)maskGran);
299  jsonStream.attributeArray("extra_ports", [&] {
300  for (auto attr : mem.getExtraPorts()) {
301  jsonStream.object([&] {
302  auto port = cast<DictionaryAttr>(attr);
303  auto name = port.getAs<StringAttr>("name").getValue();
304  jsonStream.attribute("name", name);
305  auto direction = port.getAs<StringAttr>("direction").getValue();
306  jsonStream.attribute("direction", direction);
307  auto width = port.getAs<IntegerAttr>("width").getUInt();
308  jsonStream.attribute("width", width);
309  });
310  }
311  });
312  // Record all the hierarchy names.
313  SmallVector<std::string> hierNames;
314  jsonStream.attributeArray("hierarchy", [&] {
315  // Get the absolute path for the parent memory, to create the
316  // hierarchy names.
317  auto paths = instancePathCache.getAbsolutePaths(mem);
318  for (auto p : paths) {
319  if (p.empty())
320  continue;
321  auto top = p.top();
322  std::string hierName =
323  addSymbolToVerbatimOp(top->getParentOfType<FModuleOp>(),
324  jsonSymbols)
325  .c_str();
326  auto finalInst = p.leaf();
327  for (auto inst : llvm::drop_end(p)) {
328  auto parentModule = inst->getParentOfType<FModuleOp>();
329  if (dutMod == parentModule)
330  hierName =
331  addSymbolToVerbatimOp(parentModule, jsonSymbols).c_str();
332 
333  hierName = hierName + "." +
334  addSymbolToVerbatimOp(inst, jsonSymbols).c_str();
335  }
336  hierName += ("." + finalInst.getInstanceName()).str();
337 
338  hierNames.push_back(hierName);
339  // Only include the memory path if it is under the DUT or we are in a
340  // situation where everything is deemed to be "in the DUT", i.e., when
341  // the DUT is the top module or when no DUT is specified.
342  if (everythingInDUT ||
343  llvm::any_of(p, [&](circt::igraph::InstanceOpInterface inst) {
344  return llvm::all_of(inst.getReferencedModuleNamesAttr(),
345  [&](Attribute attr) {
346  return attr == dutMod.getNameAttr();
347  });
348  }))
349  jsonStream.value(hierName);
350  }
351  });
352  });
353  };
354 
355  std::string dutJsonBuffer;
356  llvm::raw_string_ostream dutOs(dutJsonBuffer);
357  llvm::json::OStream dutJson(dutOs, 2);
358  SmallVector<Attribute, 8> seqMemSymbols;
359  SmallVector<Attribute, 8> jsonSymbols;
360 
361  std::string seqMemConfStr;
362  dutJson.array([&] {
363  for (auto mem : circuitOp.getOps<FMemModuleOp>())
364  createMemMetadata(mem, dutJson, seqMemConfStr, jsonSymbols,
365  seqMemSymbols);
366  });
367 
368  auto *context = &getContext();
369  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
370  circuitOp.getBodyBlock());
371  AnnotationSet annos(circuitOp);
372  auto dirAnno = annos.getAnnotation(metadataDirectoryAttrName);
373  StringRef metadataDir = "metadata";
374  if (dirAnno)
375  if (auto dir = dirAnno.getMember<StringAttr>("dirname"))
376  metadataDir = dir.getValue();
377 
378  // Use unknown loc to avoid printing the location in the metadata files.
379  {
380  SmallString<128> seqMemsJsonPath(metadataDir);
381  llvm::sys::path::append(seqMemsJsonPath, "seq_mems.json");
382  builder.create<emit::FileOp>(seqMemsJsonPath, [&] {
383  builder.create<sv::VerbatimOp>(dutJsonBuffer, ValueRange{},
384  builder.getArrayAttr(jsonSymbols));
385  });
386  }
387 
388  {
389  if (replSeqMemFile.empty()) {
390  emitError(circuitOp->getLoc())
391  << "metadata emission failed, the option "
392  "`-repl-seq-mem-file=<filename>` is mandatory for specifying a "
393  "valid seq mem metadata file";
394  return failure();
395  }
396 
397  builder.create<emit::FileOp>(replSeqMemFile, [&] {
398  builder.create<sv::VerbatimOp>(seqMemConfStr, ValueRange{},
399  builder.getArrayAttr(seqMemSymbols));
400  });
401  }
402 
403  return success();
404 }
405 
406 /// This will search for a target annotation and remove it from the operation.
407 /// If the annotation has a filename, it will be returned in the output
408 /// argument. If the annotation is missing the filename member, or if more than
409 /// one matching annotation is attached, it will print an error and return
410 /// failure.
411 static LogicalResult removeAnnotationWithFilename(Operation *op,
412  StringRef annoClass,
413  StringRef &filename) {
414  filename = "";
415  bool error = false;
416  AnnotationSet::removeAnnotations(op, [&](Annotation anno) {
417  // If there was a previous error or its not a match, continue.
418  if (error || !anno.isClass(annoClass))
419  return false;
420 
421  // If we have already found a matching annotation, error.
422  if (!filename.empty()) {
423  op->emitError("more than one ") << annoClass << " annotation attached";
424  error = true;
425  return false;
426  }
427 
428  // Get the filename from the annotation.
429  auto filenameAttr = anno.getMember<StringAttr>("filename");
430  if (!filenameAttr) {
431  op->emitError(annoClass) << " requires a filename";
432  error = true;
433  return false;
434  }
435 
436  // Require a non-empty filename.
437  filename = filenameAttr.getValue();
438  if (filename.empty()) {
439  op->emitError(annoClass) << " requires a non-empty filename";
440  error = true;
441  return false;
442  }
443 
444  return true;
445  });
446 
447  // If there was a problem above, return failure.
448  return failure(error);
449 }
450 
451 /// This function collects the name of each module annotated and prints them
452 /// all as a JSON array.
453 LogicalResult
454 CreateSiFiveMetadataPass::emitRetimeModulesMetadata(ObjectModelIR &omir) {
455 
456  auto *context = &getContext();
457 
458  // Get the filename, removing the annotation from the circuit.
459  StringRef filename;
461  filename)))
462  return failure();
463 
464  if (filename.empty())
465  return success();
466 
467  // Create a string buffer for the json data.
468  std::string buffer;
469  llvm::raw_string_ostream os(buffer);
470  llvm::json::OStream j(os, 2);
471 
472  // The output is a json array with each element a module name.
473  unsigned index = 0;
474  SmallVector<Attribute> symbols;
475  SmallString<3> placeholder;
476  j.array([&] {
477  for (auto module : circuitOp.getBodyBlock()->getOps<FModuleLike>()) {
478  // The annotation has no supplemental information, just remove it.
479  if (!AnnotationSet::removeAnnotations(module, retimeModuleAnnoClass))
480  continue;
481 
482  // We use symbol substitution to make sure we output the correct thing
483  // when the module goes through renaming.
484  j.value(("{{" + Twine(index++) + "}}").str());
485  symbols.push_back(SymbolRefAttr::get(module.getModuleNameAttr()));
486  omir.addRetimeModule(module);
487  }
488  });
489 
490  // Put the retime information in a verbatim operation.
491  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
492  circuitOp.getBodyBlock());
493  builder.create<emit::FileOp>(filename, [&] {
494  builder.create<sv::VerbatimOp>(builder.getStringAttr(buffer), ValueRange{},
495  builder.getArrayAttr(symbols));
496  });
497  return success();
498 }
499 
500 /// This function finds all external modules which will need to be generated for
501 /// the test harness to run.
502 LogicalResult
503 CreateSiFiveMetadataPass::emitSitestBlackboxMetadata(ObjectModelIR &omir) {
504 
505  // Any extmodule with these annotations or one of these ScalaClass classes
506  // should be excluded from the blackbox list.
507  std::array<StringRef, 3> classBlackList = {
508  "freechips.rocketchip.util.BlackBoxedROM",
509  "sifive.enterprise.grandcentral.MemTap"};
510  std::array<StringRef, 6> blackListedAnnos = {
513 
514  auto *context = &getContext();
515 
516  // Get the filenames from the annotations.
517  StringRef dutFilename, testFilename;
519  dutFilename)) ||
521  circuitOp, sitestTestHarnessBlackBoxAnnoClass, testFilename)))
522  return failure();
523 
524  // If we don't have either annotation, no need to run this pass.
525  if (dutFilename.empty() && testFilename.empty())
526  return success();
527 
528  // Find all extmodules in the circuit. Check if they are black-listed from
529  // being included in the list. If they are not, separate them into two
530  // groups depending on if theyre in the DUT or the test harness.
531  SmallVector<StringRef> dutModules;
532  SmallVector<StringRef> testModules;
533  for (auto extModule : circuitOp.getBodyBlock()->getOps<FExtModuleOp>()) {
534  // If the module doesn't have a defname, then we can't record it properly.
535  // Just skip it.
536  if (!extModule.getDefname())
537  continue;
538 
539  // If its a generated blackbox, skip it.
540  AnnotationSet annos(extModule);
541  if (llvm::any_of(blackListedAnnos, [&](auto blackListedAnno) {
542  return annos.hasAnnotation(blackListedAnno);
543  }))
544  continue;
545 
546  // If its a blacklisted scala class, skip it.
547  if (auto scalaAnno = annos.getAnnotation(scalaClassAnnoClass)) {
548  auto scalaClass = scalaAnno.getMember<StringAttr>("className");
549  if (scalaClass &&
550  llvm::is_contained(classBlackList, scalaClass.getValue()))
551  continue;
552  }
553 
554  // Record the defname of the module.
555  if (!dutMod || dutModuleSet.contains(extModule)) {
556  dutModules.push_back(*extModule.getDefname());
557  } else {
558  testModules.push_back(*extModule.getDefname());
559  }
560  omir.addBlackBoxModule(extModule);
561  }
562 
563  // This is a helper to create the verbatim output operation.
564  auto createOutput = [&](SmallVectorImpl<StringRef> &names,
565  StringRef filename) {
566  if (filename.empty())
567  return;
568 
569  // Sort and remove duplicates.
570  std::sort(names.begin(), names.end());
571  names.erase(std::unique(names.begin(), names.end()), names.end());
572 
573  // The output is a json array with each element a module name. The
574  // defname of a module can't change so we can output them verbatim.
575  std::string buffer;
576  llvm::raw_string_ostream os(buffer);
577  llvm::json::OStream j(os, 2);
578  j.array([&] {
579  for (auto &name : names)
580  j.value(name);
581  });
582 
583  // Put the information in a verbatim operation.
584  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
585  circuitOp.getBodyBlock());
586 
587  builder.create<emit::FileOp>(filename, [&] {
588  builder.create<emit::VerbatimOp>(StringAttr::get(context, buffer));
589  });
590  };
591 
592  createOutput(testModules, testFilename);
593  createOutput(dutModules, dutFilename);
594 
595  // Clean up all ScalaClassAnnotations, which are no longer needed.
596  for (auto op : circuitOp.getOps<FModuleLike>())
597  AnnotationSet::removeAnnotations(op, scalaClassAnnoClass);
598 
599  return success();
600 }
601 
602 void CreateSiFiveMetadataPass::runOnOperation() {
603 
604  auto moduleOp = getOperation();
605  auto circuits = moduleOp.getOps<CircuitOp>();
606  if (circuits.empty())
607  return;
608  auto cIter = circuits.begin();
609  circuitOp = *cIter++;
610 
611  assert(cIter == circuits.end() &&
612  "cannot handle more than one CircuitOp in a mlir::ModuleOp");
613 
614  auto *body = circuitOp.getBodyBlock();
615 
616  // Find the device under test and create a set of all modules underneath it.
617  auto it = llvm::find_if(*body, [&](Operation &op) -> bool {
619  });
620  if (it != body->end()) {
621  dutMod = dyn_cast<FModuleOp>(*it);
622  auto &instanceGraph = getAnalysis<InstanceGraph>();
623  auto *node = instanceGraph.lookup(cast<igraph::ModuleOpInterface>(*it));
624  llvm::for_each(llvm::depth_first(node),
625  [&](igraph::InstanceGraphNode *node) {
626  dutModuleSet.insert(node->getModule());
627  });
628  }
629  ObjectModelIR omir(moduleOp);
630 
631  if (failed(emitRetimeModulesMetadata(omir)) ||
632  failed(emitSitestBlackboxMetadata(omir)) ||
633  failed(emitMemoryMetadata(omir)))
634  return signalPassFailure();
635 
636  // This pass does not modify the hierarchy.
637  markAnalysesPreserved<InstanceGraph>();
638 
639  // Clear pass-global state as required by MLIR pass infrastructure.
640  dutMod = {};
641  circuitOp = {};
642  dutModuleSet.empty();
643 }
644 
645 std::unique_ptr<mlir::Pass>
647  StringRef replSeqMemFile) {
648  return std::make_unique<CreateSiFiveMetadataPass>(replSeqMem, replSeqMemFile);
649 }
assert(baseType &&"element must be base type")
static LogicalResult removeAnnotationWithFilename(Operation *op, StringRef annoClass, StringRef &filename)
This will search for a target annotation and remove it from the operation.
int32_t width
Definition: FIRRTL.cpp:36
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
Builder builder
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 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 is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
An instance path composed of a series of instances.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
igraph::InstancePathCache InstancePathCache
constexpr const char * blackBoxAnnoClass
constexpr const char * sitestBlackBoxAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * sitestTestHarnessBlackBoxAnnoClass
constexpr const char * dutAnnoClass
std::unique_ptr< mlir::Pass > createCreateSiFiveMetadataPass(bool replSeqMem=false, mlir::StringRef replSeqMemFile="")
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
constexpr const char * dataTapsBlackboxClass
constexpr const char * memTapBlackboxClass
constexpr const char * blackBoxPathAnnoClass
constexpr const char * retimeModulesFileAnnoClass
constexpr const char * blackBoxInlineAnnoClass
constexpr const char * scalaClassAnnoClass
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21