27#include "mlir/IR/ImplicitLocOpBuilder.h"
28#include "mlir/IR/Location.h"
29#include "mlir/Pass/Pass.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/StringSwitch.h"
32#include "llvm/Support/JSON.h"
33#include "llvm/Support/Path.h"
37#define GEN_PASS_DEF_CREATESIFIVEMETADATA
38#include "circt/Dialect/FIRRTL/Passes.h.inc"
43using namespace firrtl;
51 DenseMap<Operation *, hw::InnerSymbolNamespace> &moduleNamespaces)
52 : context(circtOp->getContext()), circtOp(circtOp),
55 hierPathCache(&circtNamespace,
56 OpBuilder::InsertPoint(circtOp.getBodyBlock(),
57 circtOp.getBodyBlock()->begin())),
58 instanceInfo(instanceInfo), moduleNamespaces(moduleNamespaces) {}
62 mlir::ImplicitLocOpBuilder &builderOM) {
64 auto id = DistinctAttr::create(UnitAttr::get(context));
65 TargetKind kind = TargetKind::Reference;
69 fields.append(
"id",
id);
70 fields.append(
"class", StringAttr::get(context,
"circt.tracker"));
72 fields.append(
"circt.nonlocal", mlir::FlatSymbolRefAttr::get(nla));
74 annos.addAnnotations(DictionaryAttr::get(context, fields));
75 annos.applyToOperation(op);
76 if (isa<InstanceOp, FModuleLike>(op))
77 kind = TargetKind::Instance;
81 return PathOp::create(builderOM, kind,
id);
84 void createMemorySchema() {
86 auto unknownLoc = mlir::UnknownLoc::get(context);
87 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
88 unknownLoc, circtOp.getBodyBlock());
93 mlir::Type extraPortsType[] = {
94 StringType::get(context),
95 StringType::get(context),
96 FIntegerType::get(context)
98 StringRef extraPortFields[3] = {
"name",
"direction",
"width"};
100 extraPortsClass = ClassOp::create(builderOM,
"ExtraPortsMemorySchema",
101 extraPortFields, extraPortsType);
103 mlir::Type classFieldTypes[14] = {
104 StringType::get(context),
105 FIntegerType::get(context),
106 FIntegerType::get(context),
107 FIntegerType::get(context),
108 FIntegerType::get(context),
109 FIntegerType::get(context),
110 FIntegerType::get(context),
111 FIntegerType::get(context),
112 FIntegerType::get(context),
113 StringType::get(context),
114 ListType::get(context, cast<PropertyType>(PathType::get(context))),
115 BoolType::get(context),
117 context, cast<PropertyType>(
118 detail::getInstanceTypeForClassLike(extraPortsClass))),
119 ListType::get(context, cast<PropertyType>(StringType::get(context))),
122 memorySchemaClass = ClassOp::create(builderOM,
"MemorySchema",
123 memoryParamNames, classFieldTypes);
127 SmallVector<PortInfo> mports;
128 memoryMetadataClass = ClassOp::create(
129 builderOM, builderOM.getStringAttr(
"MemoryMetadata"), mports);
132 void createRetimeModulesSchema() {
133 auto unknownLoc = mlir::UnknownLoc::get(context);
134 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
135 unknownLoc, circtOp.getBodyBlock());
136 Type classFieldTypes[] = {StringType::get(context)};
137 retimeModulesSchemaClass =
138 ClassOp::create(builderOM,
"RetimeModulesSchema",
139 retimeModulesParamNames, classFieldTypes);
141 SmallVector<PortInfo> mports;
142 retimeModulesMetadataClass = ClassOp::create(
143 builderOM, builderOM.getStringAttr(
"RetimeModulesMetadata"), mports);
146 void addRetimeModule(FModuleLike module) {
147 if (!retimeModulesSchemaClass)
148 createRetimeModulesSchema();
149 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
150 module->getLoc(), retimeModulesMetadataClass.getBodyBlock());
154 StringConstantOp::create(builderOM, module.getModuleNameAttr());
155 auto object = ObjectOp::create(builderOM, retimeModulesSchemaClass,
156 module.getModuleNameAttr());
158 auto inPort = ObjectSubfieldOp::create(builderOM,
object, 0);
159 PropAssignOp::create(builderOM, inPort, modEntry);
160 auto portIndex = retimeModulesMetadataClass.getNumPorts();
161 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
163 PortInfo(builderOM.getStringAttr(module.getName() +
"_field"),
164 object.getType(), Direction::Out)}};
165 retimeModulesMetadataClass.insertPorts(newPorts);
166 auto blockarg = retimeModulesMetadataClass.getBodyBlock()->addArgument(
167 object.getType(), module->getLoc());
168 PropAssignOp::create(builderOM, blockarg,
object);
171 void addBlackBoxModulesSchema() {
172 auto unknownLoc = mlir::UnknownLoc::get(context);
173 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
174 unknownLoc, circtOp.getBodyBlock());
175 Type classFieldTypes[] = {
176 StringType::get(context), BoolType::get(context),
177 ListType::get(context, cast<PropertyType>(StringType::get(context)))};
178 blackBoxModulesSchemaClass =
179 ClassOp::create(builderOM,
"SitestBlackBoxModulesSchema",
180 blackBoxModulesParamNames, classFieldTypes);
181 SmallVector<PortInfo> mports;
182 blackBoxMetadataClass = ClassOp::create(
183 builderOM, builderOM.getStringAttr(
"SitestBlackBoxMetadata"), mports);
186 void addBlackBoxModule(FExtModuleOp module,
bool inDut,
187 ArrayRef<StringRef> libraries) {
188 if (!blackBoxModulesSchemaClass)
189 addBlackBoxModulesSchema();
190 StringRef defName = *
module.getDefname();
191 if (!blackboxModules.insert(defName).second)
193 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
194 module.getLoc(), blackBoxMetadataClass.getBodyBlock());
196 StringConstantOp::create(builderOM, module.getDefnameAttr());
197 auto inDutAttr = BoolConstantOp::create(builderOM, inDut);
199 SmallVector<Value> libValues;
200 for (StringRef libName : libraries) {
202 if (libName == defName) {
203 libNameAttr = modEntry;
205 libNameAttr = StringConstantOp::create(
206 builderOM, builderOM.getStringAttr(libName));
208 libValues.push_back(libNameAttr);
210 auto blackBoxResourcesList = ListCreateOp::create(
213 builderOM.getContext(),
214 cast<PropertyType>(StringType::get(builderOM.getContext()))),
217 auto object = ObjectOp::create(builderOM, blackBoxModulesSchemaClass,
218 module.getModuleNameAttr());
220 auto inPortModuleName = ObjectSubfieldOp::create(builderOM,
object, 0);
221 PropAssignOp::create(builderOM, inPortModuleName, modEntry);
222 auto inPortInDut = ObjectSubfieldOp::create(builderOM,
object, 2);
223 PropAssignOp::create(builderOM, inPortInDut, inDutAttr);
224 auto inPortBlackBoxResources =
225 ObjectSubfieldOp::create(builderOM,
object, 4);
226 PropAssignOp::create(builderOM, inPortBlackBoxResources,
227 blackBoxResourcesList);
228 auto portIndex = blackBoxMetadataClass.getNumPorts();
229 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
231 PortInfo(builderOM.getStringAttr(module.getName() +
"_field"),
232 object.getType(), Direction::Out)}};
233 blackBoxMetadataClass.insertPorts(newPorts);
234 auto blockarg = blackBoxMetadataClass.getBodyBlock()->addArgument(
235 object.getType(), module->getLoc());
236 PropAssignOp::create(builderOM, blockarg,
object);
239 void addMemory(FMemModuleOp mem) {
240 if (!memorySchemaClass)
241 createMemorySchema();
242 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
243 mem.getLoc(), memoryMetadataClass.getBodyBlock());
244 auto createConstField = [&](Attribute constVal) -> Value {
245 if (
auto boolConstant = dyn_cast_or_null<mlir::BoolAttr>(constVal))
246 return BoolConstantOp::create(builderOM, boolConstant);
247 if (
auto intConstant = dyn_cast_or_null<mlir::IntegerAttr>(constVal)) {
248 auto type = cast<IntegerType>(intConstant.getType());
249 if (type.isSignlessInteger() || type.isUnsigned()) {
250 auto width = type.getWidth();
251 auto apInt = intConstant.getValue();
252 if (apInt.isNegative()) {
254 apInt = apInt.zext(width);
256 type = IntegerType::get(context, width, IntegerType::Signed);
257 intConstant = IntegerAttr::get(type, apInt);
259 return FIntegerConstantOp::create(builderOM, intConstant);
261 if (
auto strConstant = dyn_cast_or_null<mlir::StringAttr>(constVal))
262 return StringConstantOp::create(builderOM, strConstant);
266 auto memPaths = instancePathCache.getAbsolutePaths(mem);
267 SmallVector<Value> memoryHierPaths;
268 SmallVector<Value> finalInstanceNames;
281 for (
auto memPath : memPaths) {
283 igraph::InstanceOpInterface finalInst = memPath.leaf();
284 finalInstanceNames.emplace_back(StringConstantOp::create(
285 builderOM, finalInst.getInstanceNameAttr()));
287 SmallVector<Attribute> namepath;
288 bool foundDut =
false;
292 igraph::InstanceOpInterface preExtractedLeafInstance;
293 for (
auto inst :
llvm::drop_end(memPath)) {
296 inst->getParentOfType<FModuleOp>()))
302 if (inst->getParentOfType<LayerBlockOp>()) {
309 return getModuleNamespace(mod);
311 preExtractedLeafInstance = inst;
314 if (!namepath.empty()) {
316 auto nla = hierPathCache.getOrCreatePath(
317 ArrayAttr::get(context, namepath), mem->getLoc(),
"memHier");
318 pathRef =
createPathRef(preExtractedLeafInstance, nla, builderOM);
327 memoryHierPaths.push_back(pathRef);
330 auto finalInstNamesList = ListCreateOp::create(
332 ListType::get(context, cast<PropertyType>(StringType::get(context))),
334 auto hierpaths = ListCreateOp::create(
336 ListType::get(context, cast<PropertyType>(PathType::get(context))),
338 SmallVector<Value> memFields;
340 auto object = ObjectOp::create(builderOM, memorySchemaClass, mem.getName());
341 SmallVector<Value> extraPortsList;
342 ClassType extraPortsType;
343 for (
auto attr : mem.getExtraPortsAttr()) {
345 auto port = cast<DictionaryAttr>(attr);
346 auto portName = createConstField(port.getAs<StringAttr>(
"name"));
347 auto direction = createConstField(port.getAs<StringAttr>(
"direction"));
348 auto width = createConstField(port.getAs<IntegerAttr>(
"width"));
350 ObjectOp::create(builderOM, extraPortsClass,
"extraPorts");
351 extraPortsType = extraPortsObj.getType();
352 auto inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 0);
353 PropAssignOp::create(builderOM, inPort, portName);
354 inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 2);
355 PropAssignOp::create(builderOM, inPort, direction);
356 inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 4);
357 PropAssignOp::create(builderOM, inPort, width);
358 extraPortsList.push_back(extraPortsObj);
360 auto extraPorts = ListCreateOp::create(
361 builderOM, memorySchemaClass.getPortType(24), extraPortsList);
362 for (
auto field :
llvm::enumerate(memoryParamNames)) {
363 auto propVal = createConstField(
364 llvm::StringSwitch<TypedAttr>(field.value())
365 .Case(
"name", builderOM.getStringAttr(mem.getName()))
366 .Case(
"depth", mem.getDepthAttr())
367 .Case(
"width", mem.getDataWidthAttr())
368 .Case(
"maskBits", mem.getMaskBitsAttr())
369 .Case(
"readPorts", mem.getNumReadPortsAttr())
370 .Case(
"writePorts", mem.getNumWritePortsAttr())
371 .Case(
"readwritePorts", mem.getNumReadWritePortsAttr())
372 .Case(
"readLatency", mem.getReadLatencyAttr())
373 .Case(
"writeLatency", mem.getWriteLatencyAttr())
374 .Case(
"hierarchy", {})
375 .Case(
"inDut", BoolAttr::get(context, inDut))
376 .Case(
"extraPorts", {})
377 .Case(
"preExtInstName", {})
378 .Case(
"ruwBehavior", builderOM.getStringAttr(
379 stringifyRUWBehavior(mem.getRuw()))));
381 if (field.value() ==
"hierarchy")
383 else if (field.value() ==
"preExtInstName")
384 propVal = finalInstNamesList;
386 propVal = extraPorts;
395 ObjectSubfieldOp::create(builderOM,
object, 2 * field.index());
396 PropAssignOp::create(builderOM, inPort, propVal);
398 auto portIndex = memoryMetadataClass.getNumPorts();
399 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
400 {portIndex,
PortInfo(builderOM.getStringAttr(mem.getName() +
"_field"),
401 object.getType(), Direction::Out)}};
402 memoryMetadataClass.insertPorts(newPorts);
403 auto blockarg = memoryMetadataClass.getBodyBlock()->addArgument(
404 object.getType(), mem->getLoc());
405 PropAssignOp::create(builderOM, blockarg,
object);
408 ObjectOp instantiateSifiveMetadata(FModuleOp topMod) {
409 if (!blackBoxMetadataClass && !memoryMetadataClass &&
410 !retimeModulesMetadataClass && !instanceInfo.
hasDut())
412 auto builder = mlir::ImplicitLocOpBuilder::atBlockEnd(
413 mlir::UnknownLoc::get(circtOp->getContext()), circtOp.getBodyBlock());
414 SmallVector<PortInfo> mports;
415 auto sifiveMetadataClass = ClassOp::create(
416 builder, builder.getStringAttr(
"SiFive_Metadata"), mports);
417 builder.setInsertionPointToStart(sifiveMetadataClass.getBodyBlock());
419 auto addPort = [&](Value obj, StringRef fieldName) {
420 auto portIndex = sifiveMetadataClass.getNumPorts();
421 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
422 {portIndex,
PortInfo(builder.getStringAttr(fieldName +
"_field_" +
424 obj.getType(), Direction::Out)}};
425 sifiveMetadataClass.insertPorts(newPorts);
426 auto blockarg = sifiveMetadataClass.getBodyBlock()->addArgument(
427 obj.getType(), topMod->getLoc());
428 PropAssignOp::create(builder, blockarg, obj);
430 if (blackBoxMetadataClass)
431 addPort(ObjectOp::create(builder, blackBoxMetadataClass,
432 builder.getStringAttr(
"blackbox_metadata")),
435 if (memoryMetadataClass)
436 addPort(ObjectOp::create(builder, memoryMetadataClass,
437 builder.getStringAttr(
"memory_metadata")),
440 if (retimeModulesMetadataClass)
442 ObjectOp::create(builder, retimeModulesMetadataClass,
443 builder.getStringAttr(
"retime_modules_metadata")),
446 if (instanceInfo.
hasDut()) {
447 auto dutMod = instanceInfo.
getDut();
451 SmallVector<Value, 2> pathOpsToDut;
453 auto dutPaths = instancePathCache.getAbsolutePaths(dutMod);
455 for (
auto dutPath : dutPaths) {
456 SmallVector<Attribute> namepath;
458 for (
auto inst : dutPath)
461 return getModuleNamespace(mod);
463 if (namepath.empty())
467 auto leafInst = dutPath.leaf();
468 auto nla = hierPathCache.getOrCreatePath(
469 ArrayAttr::get(context, namepath), dutMod->getLoc(),
"dutNLA");
471 pathOpsToDut.emplace_back(
createPathRef(leafInst, nla, builder));
474 auto pathList = ListCreateOp::create(
476 ListType::get(context, cast<PropertyType>(PathType::get(context))),
478 addPort(pathList,
"dutModulePath");
481 builder.setInsertionPointToEnd(topMod.getBodyBlock());
482 return ObjectOp::create(builder, sifiveMetadataClass,
483 builder.getStringAttr(
"sifive_metadata"));
488 return moduleNamespaces.try_emplace(module, module).first->second;
490 MLIRContext *context;
497 DenseMap<Operation *, hw::InnerSymbolNamespace> &moduleNamespaces;
498 ClassOp memorySchemaClass, extraPortsClass;
499 ClassOp memoryMetadataClass;
500 ClassOp retimeModulesMetadataClass, retimeModulesSchemaClass;
501 ClassOp blackBoxModulesSchemaClass, blackBoxMetadataClass;
502 StringRef memoryParamNames[14] = {
503 "name",
"depth",
"width",
"maskBits",
504 "readPorts",
"writePorts",
"readwritePorts",
"writeLatency",
505 "readLatency",
"ruwBehavior",
"hierarchy",
"inDut",
506 "extraPorts",
"preExtInstName"};
507 StringRef retimeModulesParamNames[1] = {
"moduleName"};
508 StringRef blackBoxModulesParamNames[3] = {
"moduleName",
"inDut",
"libraries"};
509 llvm::SmallDenseSet<StringRef> blackboxModules;
512class CreateSiFiveMetadataPass
513 :
public circt::firrtl::impl::CreateSiFiveMetadataBase<
514 CreateSiFiveMetadataPass> {
517 LogicalResult emitRetimeModulesMetadata(ObjectModelIR &omir);
518 LogicalResult emitSitestBlackboxMetadata(ObjectModelIR &omir);
519 LogicalResult emitMemoryMetadata(ObjectModelIR &omir);
520 void runOnOperation()
override;
524 return moduleNamespaces.try_emplace(module, module).first->second;
527 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
533 CreateSiFiveMetadataPass(
bool replSeqMem, StringRef replSeqMemFile) {
534 this->replSeqMem = replSeqMem;
535 this->replSeqMemFile = replSeqMemFile.str();
543CreateSiFiveMetadataPass::emitMemoryMetadata(ObjectModelIR &omir) {
548 auto addSymbolToVerbatimOp =
550 llvm::SmallVectorImpl<Attribute> &symbols) -> SmallString<8> {
552 if (
auto module = dyn_cast<FModuleLike>(op))
553 symbol = FlatSymbolRefAttr::get(module);
554 else if (
auto hierPath = dyn_cast<hw::HierPathOp>(op))
555 symbol = FlatSymbolRefAttr::get(hierPath);
559 return getModuleNamespace(mod);
562 auto [it, inserted] = symbolIndices.try_emplace(symbol, symbols.size());
564 symbols.push_back(symbol);
567 (
"{{" + Twine(it->second) +
"}}").
toVector(str);
573 auto createMemMetadata = [&](FMemModuleOp mem,
574 llvm::json::OStream &jsonStream,
575 std::string &seqMemConfStr,
576 SmallVectorImpl<Attribute> &jsonSymbols,
577 SmallVectorImpl<Attribute> &seqMemSymbols) {
580 auto width = mem.getDataWidth();
583 if (mem.getReadLatency() < 1 || mem.getWriteLatency() < 1 || width <= 0)
585 auto memExtSym = FlatSymbolRefAttr::get(SymbolTable::getSymbolName(mem));
586 auto symId = seqMemSymbols.size();
587 seqMemSymbols.push_back(memExtSym);
589 auto isMasked = mem.isMasked();
590 auto maskGran = width;
592 maskGran /= mem.getMaskBits();
595 for (uint32_t i = 0; i < mem.getNumWritePorts(); ++i) {
596 if (!portStr.empty())
598 portStr += isMasked ?
"mwrite" :
"write";
600 for (uint32_t i = 0; i < mem.getNumReadPorts(); ++i) {
601 if (!portStr.empty())
605 for (uint32_t i = 0; i < mem.getNumReadWritePorts(); ++i) {
606 if (!portStr.empty())
608 portStr += isMasked ?
"mrw" :
"rw";
612 !isMasked ?
"" :
" mask_gran " + std::to_string(maskGran);
614 seqMemConfStr = (StringRef(seqMemConfStr) +
"name {{" + Twine(symId) +
615 "}} depth " + Twine(mem.getDepth()) +
" width " +
616 Twine(width) +
" ports " + portStr + maskGranStr +
617 (mem.getRuw() == RUWBehavior::Undefined
619 :
" ruw " + stringifyRUWBehavior(mem.getRuw())) +
627 jsonStream.object([&] {
628 jsonStream.attribute(
"module_name",
629 addSymbolToVerbatimOp(mem, jsonSymbols));
630 jsonStream.attribute(
"depth", (int64_t)mem.getDepth());
631 jsonStream.attribute(
"width", (int64_t)width);
632 jsonStream.attribute(
"masked", isMasked);
633 jsonStream.attribute(
"read", mem.getNumReadPorts());
634 jsonStream.attribute(
"write", mem.getNumWritePorts());
635 jsonStream.attribute(
"readwrite", mem.getNumReadWritePorts());
636 jsonStream.attribute(
"ruw_behavior", stringifyRUWBehavior(mem.getRuw()));
638 jsonStream.attribute(
"mask_granularity", (int64_t)maskGran);
639 jsonStream.attributeArray(
"extra_ports", [&] {
640 for (
auto attr : mem.getExtraPorts()) {
641 jsonStream.object([&] {
642 auto port = cast<DictionaryAttr>(attr);
643 auto name = port.getAs<StringAttr>(
"name").getValue();
644 jsonStream.attribute(
"name", name);
645 auto direction = port.getAs<StringAttr>(
"direction").getValue();
646 jsonStream.attribute(
"direction", direction);
647 auto width = port.getAs<IntegerAttr>(
"width").getUInt();
648 jsonStream.attribute(
"width", width);
653 jsonStream.attributeArray(
"hierarchy", [&] {
656 auto *dutNode = omir.instancePathCache.instanceGraph.lookup(dutMod);
657 for (
auto p : omir.instancePathCache.getRelativePaths(mem, dutNode)) {
663 if (llvm::any_of(p, [](igraph::InstanceOpInterface inst) {
664 if (
auto instanceOp = dyn_cast<InstanceOp>(inst.getOperation()))
672 SmallVector<Attribute> namepath;
673 for (
auto inst :
llvm::drop_end(p)) {
676 return getModuleNamespace(mod);
678 namepath.push_back(instRef);
687 SmallString<64> hierName;
688 hierName.append(addSymbolToVerbatimOp(dutMod, jsonSymbols));
689 hierName.append(
".");
690 if (!namepath.empty()) {
691 auto nla = omir.hierPathCache.getOrCreatePath(
692 ArrayAttr::get(mem->getContext(), namepath), mem->getLoc(),
694 hierName.append(addSymbolToVerbatimOp(nla, jsonSymbols));
695 hierName.append(
".");
697 hierName.append(p.leaf().getInstanceName());
699 jsonStream.value(hierName);
705 std::string dutJsonBuffer;
706 llvm::raw_string_ostream dutOs(dutJsonBuffer);
707 llvm::json::OStream dutJson(dutOs, 2);
708 SmallVector<Attribute, 8> seqMemSymbols;
709 SmallVector<Attribute, 8> jsonSymbols;
711 std::string seqMemConfStr;
713 for (
auto mem : circuitOp.getOps<FMemModuleOp>())
714 createMemMetadata(mem, dutJson, seqMemConfStr, jsonSymbols,
719 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
720 circuitOp.getBodyBlock());
722 auto dirAnno = annos.getAnnotation(metadataDirAnnoClass);
723 StringRef metadataDir =
"metadata";
725 if (
auto dir = dirAnno.getMember<StringAttr>(
"dirname"))
726 metadataDir = dir.getValue();
730 SmallString<128> seqMemsJsonPath(metadataDir);
731 llvm::sys::path::append(seqMemsJsonPath,
"seq_mems.json");
732 emit::FileOp::create(builder, seqMemsJsonPath, [&] {
733 sv::VerbatimOp::create(builder, dutJsonBuffer, ValueRange{},
734 builder.getArrayAttr(jsonSymbols));
739 if (replSeqMemFile.empty()) {
740 emitError(circuitOp->getLoc())
741 <<
"metadata emission failed, the option "
742 "`-repl-seq-mem-file=<filename>` is mandatory for specifying a "
743 "valid seq mem metadata file";
747 emit::FileOp::create(builder, replSeqMemFile, [&] {
748 sv::VerbatimOp::create(builder, seqMemConfStr, ValueRange{},
749 builder.getArrayAttr(seqMemSymbols));
763 StringRef &filename) {
768 if (error || !anno.
isClass(annoClass))
772 if (!filename.empty()) {
773 op->emitError(
"more than one ") << annoClass <<
" annotation attached";
779 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
781 op->emitError(annoClass) <<
" requires a filename";
787 filename = filenameAttr.getValue();
788 if (filename.empty()) {
789 op->emitError(annoClass) <<
" requires a non-empty filename";
798 return failure(error);
804CreateSiFiveMetadataPass::emitRetimeModulesMetadata(ObjectModelIR &omir) {
814 if (filename.empty())
819 llvm::raw_string_ostream os(buffer);
820 llvm::json::OStream j(os, 2);
824 SmallVector<Attribute> symbols;
825 SmallString<3> placeholder;
827 for (
auto module : circuitOp.
getBodyBlock()->getOps<FModuleLike>()) {
835 j.value((
"{{" + Twine(index++) +
"}}").str());
836 symbols.push_back(SymbolRefAttr::get(module.getModuleNameAttr()));
837 omir.addRetimeModule(module);
842 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
843 circuitOp.getBodyBlock());
844 emit::FileOp::create(builder, filename, [&] {
845 sv::VerbatimOp::create(builder, builder.getStringAttr(buffer), ValueRange{},
846 builder.getArrayAttr(symbols));
854CreateSiFiveMetadataPass::emitSitestBlackboxMetadata(ObjectModelIR &omir) {
858 std::array<StringRef, 3> blackListedAnnos = {
860 blackBoxInlineAnnoClass,
861 blackBoxPathAnnoClass,
867 StringRef dutFilename, testFilename;
871 circuitOp, sitestTestHarnessBlackBoxAnnoClass, testFilename)))
875 if (dutFilename.empty() && testFilename.empty())
881 SmallVector<StringRef> dutLibs;
882 SmallVector<StringRef> testLibs;
884 for (
auto extModule : circuitOp.
getBodyBlock()->getOps<FExtModuleOp>()) {
885 SmallVector<StringRef> libs;
889 if (!extModule.getDefname())
894 bool isBlacklistedBlackbox =
895 llvm::any_of(blackListedAnnos, [&](
auto blackListedAnno) {
896 return annos.hasAnnotation(blackListedAnno);
899 if (!isBlacklistedBlackbox)
900 libs.push_back(*extModule.getDefname());
902 if (
auto libsAnno = annos.getAnnotation(sitestBlackBoxLibrariesAnnoClass)) {
903 if (
auto libsAttr = libsAnno.getMember<ArrayAttr>(
"libraries")) {
904 for (
auto lib : libsAttr) {
905 if (
auto libStr = dyn_cast<StringAttr>(lib)) {
906 libs.push_back(libStr.getValue());
918 for (StringRef lib : libs)
919 dutLibs.push_back(lib);
921 for (StringRef lib : libs)
922 testLibs.push_back(lib);
924 omir.addBlackBoxModule(extModule, inDut, libs);
928 auto createOutput = [&](SmallVectorImpl<StringRef> &names,
929 StringRef filename) {
930 if (filename.empty())
934 std::sort(names.begin(), names.end());
935 names.erase(std::unique(names.begin(), names.end()), names.end());
940 llvm::raw_string_ostream os(buffer);
941 llvm::json::OStream j(os, 2);
943 for (
auto &name : names)
948 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
949 circuitOp.getBodyBlock());
951 emit::FileOp::create(builder, filename, [&] {
952 emit::VerbatimOp::create(builder, StringAttr::get(
context, buffer));
956 createOutput(testLibs, testFilename);
957 createOutput(dutLibs, dutFilename);
962void CreateSiFiveMetadataPass::runOnOperation() {
963 auto circuits = getOperation().getOps<CircuitOp>();
964 if (circuits.empty())
967 circuitOp = *circuits.begin();
969 if (!llvm::hasSingleElement(circuits)) {
970 mlir::emitError(circuitOp.getLoc(),
971 "cannot process multiple circuit operations")
972 .attachNote((*std::next(circuits.begin())).getLoc())
973 <<
"second circuit here";
974 return signalPassFailure();
977 auto &instanceGraph = getAnalysis<InstanceGraph>();
978 instanceInfo = &getAnalysis<InstanceInfo>();
979 ObjectModelIR omir(circuitOp, instanceGraph, *instanceInfo, moduleNamespaces);
981 if (failed(emitRetimeModulesMetadata(omir)) ||
982 failed(emitSitestBlackboxMetadata(omir)) ||
983 failed(emitMemoryMetadata(omir)))
984 return signalPassFailure();
986 if (FModuleOp topMod = dyn_cast<FModuleOp>(*node->getModule()))
987 if (
auto objectOp = omir.instantiateSifiveMetadata(topMod)) {
988 auto portIndex = topMod.getNumPorts();
989 SmallVector<std::pair<unsigned, PortInfo>> ports = {
991 PortInfo(StringAttr::get(objectOp->getContext(),
"metadataObj"),
992 AnyRefType::get(objectOp->getContext()), Direction::Out)}};
993 topMod.insertPorts(ports);
994 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
995 topMod->getLoc(), topMod.getBodyBlock());
996 auto objectCast = ObjectAnyRefCastOp::create(builderOM, objectOp);
997 PropAssignOp::create(builderOM, topMod.getArgument(portIndex),
static std::unique_ptr< Context > context
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static Block * getBodyBlock(FModuleLike mod)
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
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.
igraph::InstanceGraphNode * getTopLevelNode() override
Get the node corresponding to the top-level module of a circuit.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool isEffectiveDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test and the circuit has a design-under-test.
static bool isInstanceUnderLayer(InstanceOp inst)
Return true if an instance op should be considered "under a layer" for the purposes of metadata emiss...
bool hasDut()
Return true if this circuit has a design-under-test.
igraph::ModuleOpInterface getEffectiveDut()
Return the "effective" design-under-test.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
igraph::InstancePathCache InstancePathCache
PathOp createPathRef(Operation *op, hw::HierPathOp nla, mlir::ImplicitLocOpBuilder &builderOM)
Add the tracker annotation to the op and get a PathOp to the op.
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.
This holds the name and type that describes the module's ports.
A data structure that caches and provides paths to module instances in the IR.