25#include "mlir/IR/ImplicitLocOpBuilder.h"
26#include "mlir/IR/Location.h"
27#include "mlir/Pass/Pass.h"
28#include "llvm/ADT/STLExtras.h"
29#include "llvm/ADT/StringSwitch.h"
30#include "llvm/Support/JSON.h"
31#include "llvm/Support/Path.h"
35#define GEN_PASS_DEF_CREATESIFIVEMETADATA
36#include "circt/Dialect/FIRRTL/Passes.h.inc"
41using namespace firrtl;
49 DenseMap<Operation *, hw::InnerSymbolNamespace> &moduleNamespaces)
50 : context(circtOp->getContext()), circtOp(circtOp),
53 instanceInfo(instanceInfo), moduleNamespaces(moduleNamespaces) {}
57 mlir::ImplicitLocOpBuilder &builderOM) {
59 auto id = DistinctAttr::create(UnitAttr::get(context));
60 TargetKind kind = TargetKind::Reference;
64 fields.append(
"id",
id);
65 fields.append(
"class", StringAttr::get(context,
"circt.tracker"));
67 fields.append(
"circt.nonlocal", mlir::FlatSymbolRefAttr::get(nla));
69 annos.addAnnotations(DictionaryAttr::get(context, fields));
70 annos.applyToOperation(op);
71 if (isa<InstanceOp, FModuleLike>(op))
72 kind = TargetKind::Instance;
76 return PathOp::create(builderOM, kind,
id);
79 void createMemorySchema() {
81 auto unknownLoc = mlir::UnknownLoc::get(context);
82 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
83 unknownLoc, circtOp.getBodyBlock());
88 mlir::Type extraPortsType[] = {
89 StringType::get(context),
90 StringType::get(context),
91 FIntegerType::get(context)
93 StringRef extraPortFields[3] = {
"name",
"direction",
"width"};
95 extraPortsClass = ClassOp::create(builderOM,
"ExtraPortsMemorySchema",
96 extraPortFields, extraPortsType);
98 mlir::Type classFieldTypes[14] = {
99 StringType::get(context),
100 FIntegerType::get(context),
101 FIntegerType::get(context),
102 FIntegerType::get(context),
103 FIntegerType::get(context),
104 FIntegerType::get(context),
105 FIntegerType::get(context),
106 FIntegerType::get(context),
107 FIntegerType::get(context),
108 StringType::get(context),
109 ListType::get(context, cast<PropertyType>(PathType::get(context))),
110 BoolType::get(context),
112 context, cast<PropertyType>(
113 detail::getInstanceTypeForClassLike(extraPortsClass))),
114 ListType::get(context, cast<PropertyType>(StringType::get(context))),
117 memorySchemaClass = ClassOp::create(builderOM,
"MemorySchema",
118 memoryParamNames, classFieldTypes);
122 SmallVector<PortInfo> mports;
123 memoryMetadataClass = ClassOp::create(
124 builderOM, builderOM.getStringAttr(
"MemoryMetadata"), mports);
127 void createRetimeModulesSchema() {
128 auto unknownLoc = mlir::UnknownLoc::get(context);
129 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
130 unknownLoc, circtOp.getBodyBlock());
131 Type classFieldTypes[] = {StringType::get(context)};
132 retimeModulesSchemaClass =
133 ClassOp::create(builderOM,
"RetimeModulesSchema",
134 retimeModulesParamNames, classFieldTypes);
136 SmallVector<PortInfo> mports;
137 retimeModulesMetadataClass = ClassOp::create(
138 builderOM, builderOM.getStringAttr(
"RetimeModulesMetadata"), mports);
141 void addRetimeModule(FModuleLike module) {
142 if (!retimeModulesSchemaClass)
143 createRetimeModulesSchema();
144 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
145 module->getLoc(), retimeModulesMetadataClass.getBodyBlock());
149 StringConstantOp::create(builderOM, module.getModuleNameAttr());
150 auto object = ObjectOp::create(builderOM, retimeModulesSchemaClass,
151 module.getModuleNameAttr());
153 auto inPort = ObjectSubfieldOp::create(builderOM,
object, 0);
154 PropAssignOp::create(builderOM, inPort, modEntry);
155 auto portIndex = retimeModulesMetadataClass.getNumPorts();
156 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
158 PortInfo(builderOM.getStringAttr(module.getName() +
"_field"),
159 object.getType(), Direction::Out)}};
160 retimeModulesMetadataClass.insertPorts(newPorts);
161 auto blockarg = retimeModulesMetadataClass.getBodyBlock()->addArgument(
162 object.getType(), module->getLoc());
163 PropAssignOp::create(builderOM, blockarg,
object);
166 void addBlackBoxModulesSchema() {
167 auto unknownLoc = mlir::UnknownLoc::get(context);
168 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
169 unknownLoc, circtOp.getBodyBlock());
170 Type classFieldTypes[] = {
171 StringType::get(context), BoolType::get(context),
172 ListType::get(context, cast<PropertyType>(StringType::get(context)))};
173 blackBoxModulesSchemaClass =
174 ClassOp::create(builderOM,
"SitestBlackBoxModulesSchema",
175 blackBoxModulesParamNames, classFieldTypes);
176 SmallVector<PortInfo> mports;
177 blackBoxMetadataClass = ClassOp::create(
178 builderOM, builderOM.getStringAttr(
"SitestBlackBoxMetadata"), mports);
181 void addBlackBoxModule(FExtModuleOp module,
bool inDut,
182 ArrayRef<StringRef> libraries) {
183 if (!blackBoxModulesSchemaClass)
184 addBlackBoxModulesSchema();
185 StringRef defName = *
module.getDefname();
186 if (!blackboxModules.insert(defName).second)
188 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
189 module.getLoc(), blackBoxMetadataClass.getBodyBlock());
191 StringConstantOp::create(builderOM, module.getDefnameAttr());
192 auto inDutAttr = BoolConstantOp::create(builderOM, inDut);
194 SmallVector<Value> libValues;
195 for (StringRef libName : libraries) {
197 if (libName == defName) {
198 libNameAttr = modEntry;
200 libNameAttr = StringConstantOp::create(
201 builderOM, builderOM.getStringAttr(libName));
203 libValues.push_back(libNameAttr);
205 auto blackBoxResourcesList = ListCreateOp::create(
208 builderOM.getContext(),
209 cast<PropertyType>(StringType::get(builderOM.getContext()))),
212 auto object = ObjectOp::create(builderOM, blackBoxModulesSchemaClass,
213 module.getModuleNameAttr());
215 auto inPortModuleName = ObjectSubfieldOp::create(builderOM,
object, 0);
216 PropAssignOp::create(builderOM, inPortModuleName, modEntry);
217 auto inPortInDut = ObjectSubfieldOp::create(builderOM,
object, 2);
218 PropAssignOp::create(builderOM, inPortInDut, inDutAttr);
219 auto inPortBlackBoxResources =
220 ObjectSubfieldOp::create(builderOM,
object, 4);
221 PropAssignOp::create(builderOM, inPortBlackBoxResources,
222 blackBoxResourcesList);
223 auto portIndex = blackBoxMetadataClass.getNumPorts();
224 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
226 PortInfo(builderOM.getStringAttr(module.getName() +
"_field"),
227 object.getType(), Direction::Out)}};
228 blackBoxMetadataClass.insertPorts(newPorts);
229 auto blockarg = blackBoxMetadataClass.getBodyBlock()->addArgument(
230 object.getType(), module->getLoc());
231 PropAssignOp::create(builderOM, blockarg,
object);
234 void addMemory(FMemModuleOp mem) {
235 if (!memorySchemaClass)
236 createMemorySchema();
237 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
238 mem.getLoc(), memoryMetadataClass.getBodyBlock());
239 auto createConstField = [&](Attribute constVal) -> Value {
240 if (
auto boolConstant = dyn_cast_or_null<mlir::BoolAttr>(constVal))
241 return BoolConstantOp::create(builderOM, boolConstant);
242 if (
auto intConstant = dyn_cast_or_null<mlir::IntegerAttr>(constVal)) {
243 auto type = cast<IntegerType>(intConstant.getType());
244 if (type.isSignlessInteger() || type.isUnsigned()) {
245 auto width = type.getWidth();
246 auto apInt = intConstant.getValue();
247 if (apInt.isNegative()) {
249 apInt = apInt.zext(width);
251 type = IntegerType::get(context, width, IntegerType::Signed);
252 intConstant = IntegerAttr::get(type, apInt);
254 return FIntegerConstantOp::create(builderOM, intConstant);
256 if (
auto strConstant = dyn_cast_or_null<mlir::StringAttr>(constVal))
257 return StringConstantOp::create(builderOM, strConstant);
260 auto nlaBuilder = OpBuilder::atBlockBegin(circtOp.getBodyBlock());
262 auto memPaths = instancePathCache.getAbsolutePaths(mem);
263 SmallVector<Value> memoryHierPaths;
264 SmallVector<Value> finalInstanceNames;
277 for (
auto memPath : memPaths) {
279 igraph::InstanceOpInterface finalInst = memPath.leaf();
280 finalInstanceNames.emplace_back(StringConstantOp::create(
281 builderOM, finalInst.getInstanceNameAttr()));
283 SmallVector<Attribute> namepath;
284 bool foundDut =
false;
288 igraph::InstanceOpInterface preExtractedLeafInstance;
289 for (
auto inst :
llvm::drop_end(memPath)) {
292 inst->getParentOfType<FModuleOp>()))
298 if (inst->getParentOfType<LayerBlockOp>()) {
305 return getModuleNamespace(mod);
307 preExtractedLeafInstance = inst;
310 if (!namepath.empty()) {
312 auto nla = hw::HierPathOp::create(
313 nlaBuilder, mem->getLoc(),
314 nlaBuilder.getStringAttr(circtNamespace.newName(
"memNLA")),
315 nlaBuilder.getArrayAttr(namepath));
316 nla.setVisibility(SymbolTable::Visibility::Private);
317 pathRef =
createPathRef(preExtractedLeafInstance, nla, builderOM);
326 memoryHierPaths.push_back(pathRef);
329 auto finalInstNamesList = ListCreateOp::create(
331 ListType::get(context, cast<PropertyType>(StringType::get(context))),
333 auto hierpaths = ListCreateOp::create(
335 ListType::get(context, cast<PropertyType>(PathType::get(context))),
337 SmallVector<Value> memFields;
339 auto object = ObjectOp::create(builderOM, memorySchemaClass, mem.getName());
340 SmallVector<Value> extraPortsList;
341 ClassType extraPortsType;
342 for (
auto attr : mem.getExtraPortsAttr()) {
344 auto port = cast<DictionaryAttr>(attr);
345 auto portName = createConstField(port.getAs<StringAttr>(
"name"));
346 auto direction = createConstField(port.getAs<StringAttr>(
"direction"));
347 auto width = createConstField(port.getAs<IntegerAttr>(
"width"));
349 ObjectOp::create(builderOM, extraPortsClass,
"extraPorts");
350 extraPortsType = extraPortsObj.getType();
351 auto inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 0);
352 PropAssignOp::create(builderOM, inPort, portName);
353 inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 2);
354 PropAssignOp::create(builderOM, inPort, direction);
355 inPort = ObjectSubfieldOp::create(builderOM, extraPortsObj, 4);
356 PropAssignOp::create(builderOM, inPort, width);
357 extraPortsList.push_back(extraPortsObj);
359 auto extraPorts = ListCreateOp::create(
360 builderOM, memorySchemaClass.getPortType(24), extraPortsList);
361 for (
auto field :
llvm::enumerate(memoryParamNames)) {
362 auto propVal = createConstField(
363 llvm::StringSwitch<TypedAttr>(field.value())
364 .Case(
"name", builderOM.getStringAttr(mem.getName()))
365 .Case(
"depth", mem.getDepthAttr())
366 .Case(
"width", mem.getDataWidthAttr())
367 .Case(
"maskBits", mem.getMaskBitsAttr())
368 .Case(
"readPorts", mem.getNumReadPortsAttr())
369 .Case(
"writePorts", mem.getNumWritePortsAttr())
370 .Case(
"readwritePorts", mem.getNumReadWritePortsAttr())
371 .Case(
"readLatency", mem.getReadLatencyAttr())
372 .Case(
"writeLatency", mem.getWriteLatencyAttr())
373 .Case(
"hierarchy", {})
374 .Case(
"inDut", BoolAttr::get(context, inDut))
375 .Case(
"extraPorts", {})
376 .Case(
"preExtInstName", {})
377 .Case(
"ruwBehavior", builderOM.getStringAttr(
378 stringifyRUWBehavior(mem.getRuw()))));
380 if (field.value() ==
"hierarchy")
382 else if (field.value() ==
"preExtInstName")
383 propVal = finalInstNamesList;
385 propVal = extraPorts;
394 ObjectSubfieldOp::create(builderOM,
object, 2 * field.index());
395 PropAssignOp::create(builderOM, inPort, propVal);
397 auto portIndex = memoryMetadataClass.getNumPorts();
398 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
399 {portIndex,
PortInfo(builderOM.getStringAttr(mem.getName() +
"_field"),
400 object.getType(), Direction::Out)}};
401 memoryMetadataClass.insertPorts(newPorts);
402 auto blockarg = memoryMetadataClass.getBodyBlock()->addArgument(
403 object.getType(), mem->getLoc());
404 PropAssignOp::create(builderOM, blockarg,
object);
407 ObjectOp instantiateSifiveMetadata(FModuleOp topMod) {
408 if (!blackBoxMetadataClass && !memoryMetadataClass &&
409 !retimeModulesMetadataClass && !instanceInfo.
hasDut())
411 auto builder = mlir::ImplicitLocOpBuilder::atBlockEnd(
412 mlir::UnknownLoc::get(circtOp->getContext()), circtOp.getBodyBlock());
413 SmallVector<PortInfo> mports;
414 auto sifiveMetadataClass = ClassOp::create(
415 builder, builder.getStringAttr(
"SiFive_Metadata"), mports);
416 builder.setInsertionPointToStart(sifiveMetadataClass.getBodyBlock());
418 auto addPort = [&](Value obj, StringRef fieldName) {
419 auto portIndex = sifiveMetadataClass.getNumPorts();
420 SmallVector<std::pair<unsigned, PortInfo>> newPorts = {
421 {portIndex,
PortInfo(builder.getStringAttr(fieldName +
"_field_" +
423 obj.getType(), Direction::Out)}};
424 sifiveMetadataClass.insertPorts(newPorts);
425 auto blockarg = sifiveMetadataClass.getBodyBlock()->addArgument(
426 obj.getType(), topMod->getLoc());
427 PropAssignOp::create(builder, blockarg, obj);
429 if (blackBoxMetadataClass)
430 addPort(ObjectOp::create(builder, blackBoxMetadataClass,
431 builder.getStringAttr(
"blackbox_metadata")),
434 if (memoryMetadataClass)
435 addPort(ObjectOp::create(builder, memoryMetadataClass,
436 builder.getStringAttr(
"memory_metadata")),
439 if (retimeModulesMetadataClass)
441 ObjectOp::create(builder, retimeModulesMetadataClass,
442 builder.getStringAttr(
"retime_modules_metadata")),
445 if (instanceInfo.
hasDut()) {
446 auto dutMod = instanceInfo.
getDut();
450 SmallVector<Value, 2> pathOpsToDut;
452 auto dutPaths = instancePathCache.getAbsolutePaths(dutMod);
454 for (
auto dutPath : dutPaths) {
455 SmallVector<Attribute> namepath;
457 for (
auto inst : dutPath)
460 return getModuleNamespace(mod);
462 if (namepath.empty())
466 auto leafInst = dutPath.leaf();
467 auto nlaBuilder = OpBuilder::atBlockBegin(circtOp.getBodyBlock());
468 auto nla = hw::HierPathOp::create(
469 nlaBuilder, dutMod->getLoc(),
470 nlaBuilder.getStringAttr(circtNamespace.newName(
"dutNLA")),
471 nlaBuilder.getArrayAttr(namepath));
472 nla.setVisibility(SymbolTable::Visibility::Private);
474 pathOpsToDut.emplace_back(
createPathRef(leafInst, nla, builder));
477 auto pathList = ListCreateOp::create(
479 ListType::get(context, cast<PropertyType>(PathType::get(context))),
481 addPort(pathList,
"dutModulePath");
484 builder.setInsertionPointToEnd(topMod.getBodyBlock());
485 return ObjectOp::create(builder, sifiveMetadataClass,
486 builder.getStringAttr(
"sifive_metadata"));
491 return moduleNamespaces.try_emplace(module, module).first->second;
493 MLIRContext *context;
499 DenseMap<Operation *, hw::InnerSymbolNamespace> &moduleNamespaces;
500 ClassOp memorySchemaClass, extraPortsClass;
501 ClassOp memoryMetadataClass;
502 ClassOp retimeModulesMetadataClass, retimeModulesSchemaClass;
503 ClassOp blackBoxModulesSchemaClass, blackBoxMetadataClass;
504 StringRef memoryParamNames[14] = {
505 "name",
"depth",
"width",
"maskBits",
506 "readPorts",
"writePorts",
"readwritePorts",
"writeLatency",
507 "readLatency",
"ruwBehavior",
"hierarchy",
"inDut",
508 "extraPorts",
"preExtInstName"};
509 StringRef retimeModulesParamNames[1] = {
"moduleName"};
510 StringRef blackBoxModulesParamNames[3] = {
"moduleName",
"inDut",
"libraries"};
511 llvm::SmallDenseSet<StringRef> blackboxModules;
514class CreateSiFiveMetadataPass
515 :
public circt::firrtl::impl::CreateSiFiveMetadataBase<
516 CreateSiFiveMetadataPass> {
519 LogicalResult emitRetimeModulesMetadata(ObjectModelIR &omir);
520 LogicalResult emitSitestBlackboxMetadata(ObjectModelIR &omir);
521 LogicalResult emitMemoryMetadata(ObjectModelIR &omir);
522 void runOnOperation()
override;
526 return moduleNamespaces.try_emplace(module, module).first->second;
529 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
535 CreateSiFiveMetadataPass(
bool replSeqMem, StringRef replSeqMemFile) {
536 this->replSeqMem = replSeqMem;
537 this->replSeqMemFile = replSeqMemFile.str();
545CreateSiFiveMetadataPass::emitMemoryMetadata(ObjectModelIR &omir) {
550 auto addSymbolToVerbatimOp =
552 llvm::SmallVectorImpl<Attribute> &symbols) -> SmallString<8> {
554 if (
auto module = dyn_cast<FModuleLike>(op))
555 symbol = FlatSymbolRefAttr::get(module);
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 paths = omir.instancePathCache.getAbsolutePaths(mem);
657 for (
auto p : paths) {
665 bool inDut =
false, underLayer =
false;
666 for (
auto inst : p) {
667 auto parent = inst->getParentOfType<FModuleOp>();
668 inDut |= parent == dutMod;
669 if (inst->getParentOfType<LayerBlockOp>())
672 if (!inDut || underLayer)
676 std::string hierName =
677 addSymbolToVerbatimOp(top->getParentOfType<FModuleOp>(),
680 auto finalInst = p.leaf();
681 for (
auto inst :
llvm::drop_end(p)) {
682 auto parentModule = inst->getParentOfType<FModuleOp>();
683 if (instanceInfo->
getDut() == parentModule)
685 addSymbolToVerbatimOp(parentModule, jsonSymbols).c_str();
687 hierName = hierName +
"." +
688 addSymbolToVerbatimOp(inst, jsonSymbols).c_str();
690 hierName += (
"." + finalInst.getInstanceName()).str();
692 jsonStream.value(hierName);
698 std::string dutJsonBuffer;
699 llvm::raw_string_ostream dutOs(dutJsonBuffer);
700 llvm::json::OStream dutJson(dutOs, 2);
701 SmallVector<Attribute, 8> seqMemSymbols;
702 SmallVector<Attribute, 8> jsonSymbols;
704 std::string seqMemConfStr;
706 for (
auto mem : circuitOp.getOps<FMemModuleOp>())
707 createMemMetadata(mem, dutJson, seqMemConfStr, jsonSymbols,
712 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
713 circuitOp.getBodyBlock());
715 auto dirAnno = annos.getAnnotation(metadataDirAnnoClass);
716 StringRef metadataDir =
"metadata";
718 if (
auto dir = dirAnno.getMember<StringAttr>(
"dirname"))
719 metadataDir = dir.getValue();
723 SmallString<128> seqMemsJsonPath(metadataDir);
724 llvm::sys::path::append(seqMemsJsonPath,
"seq_mems.json");
725 emit::FileOp::create(builder, seqMemsJsonPath, [&] {
726 sv::VerbatimOp::create(builder, dutJsonBuffer, ValueRange{},
727 builder.getArrayAttr(jsonSymbols));
732 if (replSeqMemFile.empty()) {
733 emitError(circuitOp->getLoc())
734 <<
"metadata emission failed, the option "
735 "`-repl-seq-mem-file=<filename>` is mandatory for specifying a "
736 "valid seq mem metadata file";
740 emit::FileOp::create(builder, replSeqMemFile, [&] {
741 sv::VerbatimOp::create(builder, seqMemConfStr, ValueRange{},
742 builder.getArrayAttr(seqMemSymbols));
756 StringRef &filename) {
761 if (error || !anno.
isClass(annoClass))
765 if (!filename.empty()) {
766 op->emitError(
"more than one ") << annoClass <<
" annotation attached";
772 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
774 op->emitError(annoClass) <<
" requires a filename";
780 filename = filenameAttr.getValue();
781 if (filename.empty()) {
782 op->emitError(annoClass) <<
" requires a non-empty filename";
791 return failure(error);
797CreateSiFiveMetadataPass::emitRetimeModulesMetadata(ObjectModelIR &omir) {
807 if (filename.empty())
812 llvm::raw_string_ostream os(buffer);
813 llvm::json::OStream j(os, 2);
817 SmallVector<Attribute> symbols;
818 SmallString<3> placeholder;
820 for (
auto module : circuitOp.
getBodyBlock()->getOps<FModuleLike>()) {
828 j.value((
"{{" + Twine(index++) +
"}}").str());
829 symbols.push_back(SymbolRefAttr::get(module.getModuleNameAttr()));
830 omir.addRetimeModule(module);
835 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
836 circuitOp.getBodyBlock());
837 emit::FileOp::create(builder, filename, [&] {
838 sv::VerbatimOp::create(builder, builder.getStringAttr(buffer), ValueRange{},
839 builder.getArrayAttr(symbols));
847CreateSiFiveMetadataPass::emitSitestBlackboxMetadata(ObjectModelIR &omir) {
851 std::array<StringRef, 3> blackListedAnnos = {
853 blackBoxInlineAnnoClass,
854 blackBoxPathAnnoClass,
860 StringRef dutFilename, testFilename;
864 circuitOp, sitestTestHarnessBlackBoxAnnoClass, testFilename)))
868 if (dutFilename.empty() && testFilename.empty())
874 SmallVector<StringRef> dutLibs;
875 SmallVector<StringRef> testLibs;
877 for (
auto extModule : circuitOp.
getBodyBlock()->getOps<FExtModuleOp>()) {
878 SmallVector<StringRef> libs;
882 if (!extModule.getDefname())
887 bool isBlacklistedBlackbox =
888 llvm::any_of(blackListedAnnos, [&](
auto blackListedAnno) {
889 return annos.hasAnnotation(blackListedAnno);
892 if (!isBlacklistedBlackbox)
893 libs.push_back(*extModule.getDefname());
895 if (
auto libsAnno = annos.getAnnotation(sitestBlackBoxLibrariesAnnoClass)) {
896 if (
auto libsAttr = libsAnno.getMember<ArrayAttr>(
"libraries")) {
897 for (
auto lib : libsAttr) {
898 if (
auto libStr = dyn_cast<StringAttr>(lib)) {
899 libs.push_back(libStr.getValue());
911 for (StringRef lib : libs)
912 dutLibs.push_back(lib);
914 for (StringRef lib : libs)
915 testLibs.push_back(lib);
917 omir.addBlackBoxModule(extModule, inDut, libs);
921 auto createOutput = [&](SmallVectorImpl<StringRef> &names,
922 StringRef filename) {
923 if (filename.empty())
927 std::sort(names.begin(), names.end());
928 names.erase(std::unique(names.begin(), names.end()), names.end());
933 llvm::raw_string_ostream os(buffer);
934 llvm::json::OStream j(os, 2);
936 for (
auto &name : names)
941 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
942 circuitOp.getBodyBlock());
944 emit::FileOp::create(builder, filename, [&] {
945 emit::VerbatimOp::create(builder, StringAttr::get(
context, buffer));
949 createOutput(testLibs, testFilename);
950 createOutput(dutLibs, dutFilename);
955void CreateSiFiveMetadataPass::runOnOperation() {
956 auto circuits = getOperation().getOps<CircuitOp>();
957 if (circuits.empty())
960 circuitOp = *circuits.begin();
962 if (!llvm::hasSingleElement(circuits)) {
963 mlir::emitError(circuitOp.getLoc(),
964 "cannot process multiple circuit operations")
965 .attachNote((*std::next(circuits.begin())).getLoc())
966 <<
"second circuit here";
967 return signalPassFailure();
970 auto &instanceGraph = getAnalysis<InstanceGraph>();
971 instanceInfo = &getAnalysis<InstanceInfo>();
972 ObjectModelIR omir(circuitOp, instanceGraph, *instanceInfo, moduleNamespaces);
974 if (failed(emitRetimeModulesMetadata(omir)) ||
975 failed(emitSitestBlackboxMetadata(omir)) ||
976 failed(emitMemoryMetadata(omir)))
977 return signalPassFailure();
979 if (FModuleOp topMod = dyn_cast<FModuleOp>(*node->getModule()))
980 if (
auto objectOp = omir.instantiateSifiveMetadata(topMod)) {
981 auto portIndex = topMod.getNumPorts();
982 SmallVector<std::pair<unsigned, PortInfo>> ports = {
984 PortInfo(StringAttr::get(objectOp->getContext(),
"metadataObj"),
985 AnyRefType::get(objectOp->getContext()), Direction::Out)}};
986 topMod.insertPorts(ports);
987 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
988 topMod->getLoc(), topMod.getBodyBlock());
989 auto objectCast = ObjectAnyRefCastOp::create(builderOM, objectOp);
990 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.
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.