28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/TypeSwitch.h"
32#include "llvm/Support/Debug.h"
33#include "llvm/Support/Path.h"
34#include "llvm/Support/YAMLTraits.h"
37#define DEBUG_TYPE "firrtl-grand-central"
41#define GEN_PASS_DEF_GRANDCENTRAL
42#include "circt/Dialect/FIRRTL/Passes.h.inc"
47using namespace firrtl;
60[[maybe_unused]]
static std::string noDefault(StringRef clazz) {
61 return (
"default '" + clazz +
62 "' construction is an intentionally *NOT* implemented "
63 "YAML feature (you should never be using this)")
67[[maybe_unused]]
static std::string deNorm(StringRef clazz) {
68 return (
"conversion from YAML to a '" + clazz +
69 "' is intentionally *NOT* implemented (you should not be "
70 "converting from YAML to an interface)")
85 DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
94struct DescribedSignal {
96 StringAttr description;
99 sv::InterfaceSignalOp signal;
107struct DescribedInstance {
111 StringAttr description;
114 ArrayAttr dimensions;
117 FlatSymbolRefAttr interface;
127LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
128LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
129LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
137using namespace ::
yaml;
150 std::string descriptionString;
151 llvm::raw_string_ostream stream(descriptionString);
152 SmallVector<StringRef> splits;
153 str.split(splits,
"\n");
157 substr.consume_front(
"//");
158 stream << substr.drop_while([](
auto c) {
return c ==
' '; });
160 [&]() { stream <<
"\n"; });
161 return descriptionString;
168struct MappingContextTraits<DescribedSignal,
Context> {
186 : name(op.signal.getSymNameAttr().getValue()) {
212 auto tpe = op.signal.getType();
213 while (
auto vector = dyn_cast<hw::UnpackedArrayType>(tpe)) {
214 dimensions.push_back(vector.getNumElements());
215 tpe = vector.getElementType();
217 dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
222 assert(isa<IntegerType>(tpe));
223 width = type_cast<IntegerType>(tpe).getWidth();
227 Field(IO &io) { llvm_unreachable(noDefault(
"Field").c_str()); }
231 llvm_unreachable(deNorm(
"DescribedSignal").c_str());
236 MappingNormalization<Field, DescribedSignal> keys(io, op);
237 io.mapRequired(
"name", keys->name);
238 io.mapOptional(
"description", keys->description);
239 io.mapRequired(
"dimensions", keys->dimensions);
240 io.mapRequired(
"width", keys->width);
249struct MappingContextTraits<DescribedInstance,
Context> {
256 std::optional<std::string> description = std::nullopt;
265 : name(op.name.getValue()), interface(op.interface) {
273 for (
auto &d : op.dimensions) {
274 auto dimension = dyn_cast<IntegerAttr>(d);
275 dimensions.push_back(dimension.getInt());
279 Instance(IO &io) { llvm_unreachable(noDefault(
"Instance").c_str()); }
282 llvm_unreachable(deNorm(
"DescribedInstance").c_str());
287 MappingNormalization<Instance, DescribedInstance> keys(io, op);
288 io.mapRequired(
"name", keys->name);
289 io.mapOptional(
"description", keys->description);
290 io.mapRequired(
"dimensions", keys->dimensions);
291 io.mapRequired(
"interface", ctx.interfaceMap[keys->interface], ctx);
338 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
341 StringAttr description = {};
343 for (
auto &op : op.getBodyBlock()->getOperations()) {
344 TypeSwitch<Operation *>(&op)
347 .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
348 auto tpe = op->getAttrOfType<StringAttr>(
349 "firrtl.grandcentral.yaml.type");
353 if (tpe.getValue() ==
"description") {
354 description = op.getFormatStringAttr();
359 if (tpe.getValue() ==
"unsupported") {
366 auto name = op->getAttrOfType<StringAttr>(
367 "firrtl.grandcentral.yaml.name");
368 auto dimensions = op->getAttrOfType<ArrayAttr>(
369 "firrtl.grandcentral.yaml.dimensions");
370 auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
371 "firrtl.grandcentral.yaml.symbol");
373 DescribedInstance({name, description, dimensions, symbol}));
377 .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
378 fields.push_back(DescribedSignal({description, op}));
385 Interface(IO &io) { llvm_unreachable(noDefault(
"Interface").c_str()); }
389 llvm_unreachable(deNorm(
"sv::InterfaceOp").c_str());
394 MappingNormalization<Interface, sv::InterfaceOp> keys(io, op);
395 io.mapRequired(
"name", keys->name);
396 io.mapRequired(
"fields", keys->fields, ctx);
397 io.mapRequired(
"instances", keys->instances, ctx);
427struct VerbatimBuilder {
429 SmallString<128> string;
430 SmallVector<Attribute> symbols;
431 VerbatimBuilder builder() {
return VerbatimBuilder(*
this); }
432 operator VerbatimBuilder() {
return builder(); }
437 VerbatimBuilder(Base &base)
438 : base(base), stringBaseSize(base.string.size()),
439 symbolsBaseSize(base.symbols.size()) {}
444 base.string.resize(stringBaseSize);
445 base.symbols.resize(symbolsBaseSize);
449 VerbatimBuilder(
const VerbatimBuilder &) =
delete;
450 VerbatimBuilder &operator=(
const VerbatimBuilder &) =
delete;
455 VerbatimBuilder snapshot() {
return VerbatimBuilder(base); }
458 StringRef getString()
const {
return base.string; }
460 ArrayRef<Attribute> getSymbols()
const {
return base.symbols; }
463 VerbatimBuilder &
append(
char c) {
464 base.string.push_back(c);
469 VerbatimBuilder &
append(
const Twine &twine) {
470 twine.toVector(base.string);
475 VerbatimBuilder &
append(Attribute symbol) {
476 unsigned id = base.symbols.size();
477 base.symbols.push_back(symbol);
478 append(
"{{" + Twine(
id) +
"}}");
482 VerbatimBuilder &operator+=(
char c) {
return append(c); }
483 VerbatimBuilder &operator+=(
const Twine &twine) {
return append(twine); }
484 VerbatimBuilder &operator+=(Attribute symbol) {
return append(symbol); }
488 size_t stringBaseSize;
489 size_t symbolsBaseSize;
504 SmallVector<int32_t, 4> dimensions = {};
507 std::string toStr(StringRef name) {
508 SmallString<64> stringType(str);
509 stringType.append(
" ");
510 stringType.append(name);
511 for (
auto d :
llvm::reverse(dimensions)) {
512 stringType.append(
"[");
513 stringType.append(Twine(d).str());
514 stringType.append(
"]");
517 stringType.append(
"()");
518 stringType.append(
";");
519 return std::string(stringType);
525using TypeSum = std::variant<VerbatimType, Type>;
528struct ExtractionInfo {
531 StringAttr directory = {};
536 StringAttr bindFilename = {};
540struct CompanionInfo {
551 FlatSymbolRefAttr nlaSym;
555struct VerbatimXMRbuilder {
559 FModuleOp companionMod;
560 VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
561 FModuleOp companionMod)
562 : val(val), str(str), syms(syms), companionMod(companionMod) {}
567struct InterfaceElemsBuilder {
568 StringAttr iFaceName;
571 StringAttr description;
574 Properties(StringAttr des, StringAttr name, TypeSum &elemType)
575 : description(des), elemName(name), elemType(elemType) {}
577 SmallVector<Properties> elementsList;
578 InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr
id)
579 : iFaceName(iFaceName), id(id) {}
594struct GrandCentralPass
595 :
public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
598 void runOnOperation()
override;
605 DenseMap<Attribute, FieldAndNLA> leafMap;
608 DenseMap<Attribute, CompanionInfo> companionIDMap;
614 StringAttr testbenchDir;
621 std::optional<Attribute> fromAttr(Attribute attr);
625 bool traverseField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
626 SmallVector<VerbatimXMRbuilder> &xmrElems,
627 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
632 std::optional<TypeSum>
633 computeField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
634 SmallVector<VerbatimXMRbuilder> &xmrElems,
635 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
640 std::optional<StringAttr>
641 traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr
id,
642 VerbatimBuilder &path,
643 SmallVector<VerbatimXMRbuilder> &xmrElems,
644 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
647 igraph::ModuleOpInterface getEnclosingModule(Value value,
648 FlatSymbolRefAttr sym = {});
654 InFlightDiagnostic emitCircuitError(StringRef message = {}) {
655 return emitError(getOperation().
getLoc(),
"'firrtl.circuit' op " + message);
663 std::optional<Attribute> fromViewAttr(ViewIntrinsicOp view, Attribute attr);
667 bool traverseViewField(Attribute field, VerbatimBuilder &path,
668 SmallVector<VerbatimXMRbuilder> &xmrElems,
669 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
670 ViewIntrinsicOp view,
size_t &idx);
675 std::optional<TypeSum>
676 computeViewField(Attribute field, VerbatimBuilder &path,
677 SmallVector<VerbatimXMRbuilder> &xmrElems,
678 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
679 ViewIntrinsicOp view,
size_t &idx);
684 std::optional<StringAttr>
685 traverseViewBundle(AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
686 SmallVector<VerbatimXMRbuilder> &xmrElems,
687 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
688 ViewIntrinsicOp view,
size_t &idx);
693 std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) {
694 return (bundleType.getDefName().getValue()).str();
699 std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
703 std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
705 StringAttr getOutputDirectory() {
706 if (maybeExtractInfo)
707 return maybeExtractInfo->directory;
726 std::optional<CircuitNamespace> circuitNamespace;
730 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
735 if (!circuitNamespace)
737 return *circuitNamespace;
742 return moduleNamespaces.try_emplace(module, module).first->second;
747 std::optional<SymbolTable *> symbolTable;
751 SymbolTable &getSymbolTable() {
753 symbolTable = &getAnalysis<SymbolTable>();
754 return **symbolTable;
762 std::string cleanupDescription(StringRef description) {
766 std::tie(head, description) = description.split(
"\n");
768 if (!description.empty())
770 }
while (!description.empty());
771 return std::string(out);
775 DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
778 void emitHierarchyYamlFile(StringRef yamlPath,
779 SmallVectorImpl<sv::InterfaceOp> &intfs);
793static std::optional<DictionaryAttr>
795 DictionaryAttr root, StringAttr name, StringAttr defName,
796 std::optional<IntegerAttr>
id,
797 std::optional<StringAttr> description, Twine clazz,
798 StringAttr companionAttr, Twine path = {}) {
801 auto loc = state.
circuit.getLoc();
817 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
819 tryGetAs<StringAttr>(refTarget, refTarget,
"module", loc, clazz, path);
821 tryGetAs<ArrayAttr>(refTarget, refTarget,
"path", loc, clazz, path);
822 auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget,
"component",
824 if (!moduleAttr || !pathAttr || !componentAttr)
828 SmallString<32> strpath;
829 for (
auto p : pathAttr) {
830 auto dict = dyn_cast_or_null<DictionaryAttr>(p);
832 mlir::emitError(loc,
"annotation '" + clazz +
833 " has invalid type (expected DictionaryAttr)");
837 tryGetAs<DictionaryAttr>(dict, dict,
"_1", loc, clazz, path);
839 tryGetAs<DictionaryAttr>(dict, dict,
"_2", loc, clazz, path);
840 if (!instHolder || !modHolder) {
841 mlir::emitError(loc,
"annotation '" + clazz +
842 " has invalid type (expected DictionaryAttr)");
845 auto inst = tryGetAs<StringAttr>(instHolder, instHolder,
"value", loc,
848 tryGetAs<StringAttr>(modHolder, modHolder,
"value", loc, clazz, path);
850 mlir::emitError(loc,
"annotation '" + clazz +
851 " has invalid type (expected DictionaryAttr)");
854 strpath +=
"/" + inst.getValue().str() +
":" + mod.getValue().str();
857 SmallVector<Attribute> componentAttrs;
858 SmallString<32> componentStr;
859 for (
size_t i = 0, e = componentAttr.size(); i != e; ++i) {
860 auto cPath = (path +
".component[" + Twine(i) +
"]").str();
861 auto component = componentAttr[i];
862 auto dict = dyn_cast_or_null<DictionaryAttr>(component);
864 mlir::emitError(loc,
"annotation '" + clazz +
"' with path '" + cPath +
865 " has invalid type (expected DictionaryAttr)");
869 tryGetAs<StringAttr>(dict, refTarget,
"class", loc, clazz, cPath);
873 auto value = dict.get(
"value");
876 if (
auto field = dyn_cast<StringAttr>(value)) {
877 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Field" &&
878 "A StringAttr target token must be found with a subfield target "
880 componentStr.append((Twine(
".") + field.getValue()).str());
885 if (
auto index = dyn_cast<IntegerAttr>(value)) {
886 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Index" &&
887 "An IntegerAttr target token must be found with a subindex "
890 (Twine(
"[") + Twine(index.getValue().getZExtValue()) +
"]").str());
895 "Annotation '" + clazz +
"' with path '" + cPath +
896 ".value has unexpected type (should be StringAttr "
897 "for subfield or IntegerAttr for subindex).")
899 <<
"The value received was: " << value <<
"\n";
904 tryGetAs<StringAttr>(refTarget, refTarget,
"ref", loc, clazz, path);
908 return (Twine(
"~|" + moduleAttr.getValue() + strpath +
">" +
909 refAttr.getValue()) +
915 tryGetAs<StringAttr>(augmentedType, root,
"class", loc, clazz, path);
918 StringRef classBase = classAttr.getValue();
919 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented")) {
921 "the 'class' was expected to start with "
922 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
923 classAttr.getValue() +
"' (Did you misspell it?)")
925 <<
"see annotation: " << augmentedType;
932 if (classBase ==
"BundleType") {
934 tryGetAs<StringAttr>(augmentedType, root,
"defName", loc, clazz, path);
942 SmallVector<Attribute> elements;
944 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
947 for (
size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
948 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
952 "Annotation '" + Twine(clazz) +
"' with path '.elements[" +
954 "]' contained an unexpected type (expected a DictionaryAttr).")
956 <<
"The received element was: " << elementsAttr[i] <<
"\n";
959 auto ePath = (path +
".elements[" + Twine(i) +
"]").str();
960 auto name = tryGetAs<StringAttr>(field, root,
"name", loc, clazz, ePath);
962 tryGetAs<DictionaryAttr>(field, root,
"tpe", loc, clazz, ePath);
965 std::optional<StringAttr> description;
966 if (
auto maybeDescription = field.get(
"description"))
967 description = cast<StringAttr>(maybeDescription);
969 state, tpe, root, name, defName, std::nullopt, description, clazz,
970 companionAttr, path +
"_" + name.getValue());
977 if (
auto maybeDescription = field.get(
"description"))
978 attrs.append(
"description", cast<StringAttr>(maybeDescription));
979 attrs.append(
"name", name);
980 auto tpeClass = tpe.getAs<StringAttr>(
"class");
982 mlir::emitError(loc,
"missing 'class' key in") << tpe;
985 attrs.append(
"tpe", tpeClass);
986 elements.push_back(*eltAttr);
992 attrs.append(
"class", classAttr);
993 attrs.append(
"defName", defName);
995 attrs.append(
"description", *description);
996 attrs.append(
"elements", ArrayAttr::get(
context, elements));
998 attrs.append(
"id", *
id);
999 attrs.append(
"name", name);
1000 return DictionaryAttr::getWithSorted(
context, attrs);
1009 if (classBase ==
"GroundType") {
1010 auto augRef = augmentedType.getAs<DictionaryAttr>(
"ref");
1012 mlir::emitError(loc,
"missing 'ref' key in ") << augmentedType;
1013 return std::nullopt;
1015 auto maybeTarget = refToTarget(augRef);
1017 mlir::emitError(loc,
"Failed to parse ReferenceTarget").attachNote()
1018 <<
"See the full Annotation here: " << root;
1019 return std::nullopt;
1022 auto id = state.
newID();
1024 auto target = *maybeTarget;
1026 NamedAttrList elementIface, elementScattered;
1029 elementIface.append(
"class", classAttr);
1031 elementIface.append(
"description", *description);
1032 elementIface.append(
"id",
id);
1033 elementIface.append(
"name", name);
1035 elementScattered.append(
"class", classAttr);
1036 elementScattered.append(
"id",
id);
1038 auto targetAttr = StringAttr::get(
context, target);
1041 if (!xmrSrcTarget) {
1042 mlir::emitError(loc,
"Failed to resolve target ") << targetAttr;
1043 return std::nullopt;
1049 auto sourceRef = xmrSrcTarget->ref;
1050 ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(),
context);
1051 std::optional<Value> source =
1052 TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1055 .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1056 -> std::optional<Value> {
1057 auto portNo = sourceRef.getImpl().getPortNo();
1058 if (xmrSrcTarget->instances.empty()) {
1060 if (paths.size() > 1) {
1062 "cannot resolve a unique instance path from the "
1063 "external module '")
1064 << targetAttr <<
"'";
1065 return std::nullopt;
1067 auto *it = xmrSrcTarget->instances.begin();
1068 for (
auto inst : paths.back()) {
1069 xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1073 auto lastInst = xmrSrcTarget->instances.pop_back_val();
1074 builder.setInsertionPointAfter(lastInst);
1076 xmrSrcTarget->fieldIdx);
1080 .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1081 builder.setInsertionPointToEnd(module.getBodyBlock());
1082 auto portNum = sourceRef.getImpl().getPortNo();
1084 xmrSrcTarget->fieldIdx);
1087 .Default([&](Operation *op) -> std::optional<Value> {
1088 auto module = cast<FModuleOp>(sourceRef.getModule());
1089 builder.setInsertionPointToEnd(module.getBodyBlock());
1090 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1092 if (is && is.getTargetResult())
1094 xmrSrcTarget->fieldIdx);
1095 if (sourceRef.getOp()->getNumResults() != 1) {
1097 <<
"cannot be used as a target of the Grand Central View \""
1098 << defName.getValue()
1099 <<
"\" because it does not have exactly one result";
1100 return std::nullopt;
1103 xmrSrcTarget->fieldIdx);
1108 return std::nullopt;
1120 builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1124 auto sinkType = source->getType();
1125 if (
auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1126 sinkType = baseSinkType.getPassiveType();
1127 auto sink = WireOp::create(builder, sinkType, name);
1130 annotations.addAnnotations(
1131 {DictionaryAttr::getWithSorted(
context, elementScattered)});
1132 annotations.applyToOperation(sink);
1137 (path +
"__bore").str(),
1138 WiringProblem::RefTypeUsage::Prefer});
1140 return DictionaryAttr::getWithSorted(
context, elementIface);
1145 if (classBase ==
"VectorType") {
1147 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
1149 return std::nullopt;
1150 SmallVector<Attribute> elements;
1151 for (
auto [i, elt] :
llvm::enumerate(elementsAttr)) {
1154 StringAttr::get(
context,
""),
id, std::nullopt,
1155 clazz, companionAttr, path +
"_" + Twine(i));
1157 return std::nullopt;
1158 elements.push_back(*eltAttr);
1160 NamedAttrList attrs;
1161 attrs.append(
"class", classAttr);
1163 attrs.append(
"description", *description);
1164 attrs.append(
"elements", ArrayAttr::get(
context, elements));
1165 attrs.append(
"name", name);
1166 return DictionaryAttr::getWithSorted(
context, attrs);
1171 mlir::emitError(loc,
"found unknown AugmentedType '" + classAttr.getValue() +
1172 "' (Did you misspell it?)")
1174 <<
"see annotation: " << augmentedType;
1175 return std::nullopt;
1179 DictionaryAttr anno,
1182 auto id = state.
newID();
1184 auto loc = state.
circuit.getLoc();
1185 NamedAttrList companionAttrs;
1187 companionAttrs.append(
"id",
id);
1189 tryGetAs<DictionaryAttr>(anno, anno,
"view", loc,
viewAnnoClass);
1192 auto name = tryGetAs<StringAttr>(anno, anno,
"name", loc,
viewAnnoClass);
1195 companionAttrs.append(
"name", name);
1196 auto companionAttr =
1197 tryGetAs<StringAttr>(anno, anno,
"companion", loc,
viewAnnoClass);
1200 companionAttrs.append(
"target", companionAttr);
1205 companionAttr, Twine(name));
1220std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1221 auto dict = dyn_cast<DictionaryAttr>(attr);
1223 emitCircuitError() <<
"attribute is not a dictionary: " << attr <<
"\n";
1224 return std::nullopt;
1227 auto clazz = dict.getAs<StringAttr>(
"class");
1229 emitCircuitError() <<
"missing 'class' key in " << dict <<
"\n";
1230 return std::nullopt;
1233 auto classBase = clazz.getValue();
1234 classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented");
1236 if (classBase ==
"BundleType") {
1237 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1238 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1239 emitCircuitError() <<
"has an invalid AugmentedBundleType that does not "
1240 "contain 'defName' and 'elements' fields: "
1242 }
else if (classBase ==
"VectorType") {
1243 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1244 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1245 emitCircuitError() <<
"has an invalid AugmentedVectorType that does not "
1246 "contain 'name' and 'elements' fields: "
1248 }
else if (classBase ==
"GroundType") {
1249 auto id = dict.getAs<IntegerAttr>(
"id");
1250 auto name = dict.getAs<StringAttr>(
"name");
1251 if (
id && leafMap.count(
id) && name)
1252 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1254 emitCircuitError() <<
"has an invalid AugmentedGroundType that does not "
1255 "contain 'id' and 'name' fields: "
1257 if (
id && !leafMap.count(
id))
1258 emitCircuitError() <<
"has an AugmentedGroundType with 'id == "
1259 <<
id.getValue().getZExtValue()
1260 <<
"' that does not have a scattered leaf to connect "
1261 "to in the circuit "
1262 "(was the leaf deleted or constant prop'd away?)";
1264 emitCircuitError() <<
"has an invalid AugmentedType";
1266 return std::nullopt;
1269std::optional<Attribute> GrandCentralPass::fromViewAttr(ViewIntrinsicOp view,
1271 auto dict = dyn_cast<DictionaryAttr>(attr);
1273 view.emitError() <<
"attribute is not a dictionary: " << attr;
1274 return std::nullopt;
1277 auto clazz = dict.getAs<StringAttr>(
"class");
1279 view.emitError() <<
"missing 'class' key in " << dict;
1280 return std::nullopt;
1283 auto classBase = clazz.getValue();
1284 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented"))
1285 view.emitOpError() <<
"has an invalid AugmentedType class '" << classBase
1287 else if (classBase ==
"BundleType") {
1288 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1289 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1290 view.emitError() <<
"has an invalid AugmentedBundleType that does not "
1291 "contain 'defName' and 'elements' fields: "
1293 }
else if (classBase ==
"VectorType") {
1294 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1295 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1296 view.emitError() <<
"has an invalid AugmentedVectorType that does not "
1297 "contain 'name' and 'elements' fields: "
1299 }
else if (classBase ==
"GroundType") {
1300 auto id = dict.getAs<IntegerAttr>(
"id");
1304 <<
"has 'id' field which is only for old annotation encoding")
1306 <<
"id within GroundType attribute: " << dict;
1307 return std::nullopt;
1309 auto name = dict.getAs<StringAttr>(
"name");
1311 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1312 view.emitError() <<
"has an invalid AugmentedGroundType that does not "
1313 "contain 'name' field: "
1316 view.emitOpError() <<
"has an invalid AugmentedType '" << classBase <<
"'";
1318 return std::nullopt;
1321bool GrandCentralPass::traverseField(
1322 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1323 SmallVector<VerbatimXMRbuilder> &xmrElems,
1324 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1325 return TypeSwitch<Attribute, bool>(field)
1326 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1327 auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1330 nla = nlaTable->
getNLA(sym.getAttr());
1331 Value leafValue = fieldRef.getValue();
1332 assert(leafValue &&
"leafValue not found");
1334 auto companionModule = companionIDMap.lookup(
id).companion;
1335 igraph::ModuleOpInterface enclosing =
1336 getEnclosingModule(leafValue, sym);
1338 auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1341 if (!tpe.getBitWidthOrSentinel())
1353 auto *nodeOp = leafValue.getDefiningOp();
1354 if (companionModule != enclosing) {
1355 auto diag = companionModule->emitError()
1356 <<
"Grand Central View \""
1357 << companionIDMap.lookup(
id).name
1358 <<
"\" is invalid because a leaf is not inside the "
1360 diag.attachNote(leafValue.getLoc())
1361 <<
"the leaf value is declared here";
1363 auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1364 diag.attachNote(leafModule.getLoc())
1365 <<
"the leaf value is inside this module";
1370 if (!isa<NodeOp>(nodeOp)) {
1371 emitError(leafValue.getLoc())
1372 <<
"Grand Central View \"" << companionIDMap.lookup(
id).name
1373 <<
"\" has an invalid leaf value (this must be a node)";
1380 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1381 SmallString<128> replStr;
1382 StringRef begin =
"{{";
1383 StringRef
end =
"}}";
1386 while (from < base.size()) {
1388 size_t beginAt = base.find(begin, from);
1389 size_t endAt = base.find(end, from);
1391 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1392 (beginAt > endAt)) {
1393 replStr.append(base.substr(from));
1397 replStr.append(base.substr(from, beginAt - from));
1400 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1402 bool failed = idChar.getAsInteger(10, idNum);
1404 assert(!failed &&
"failed to parse integer from verbatim string");
1406 replStr.append(
"{{");
1407 Twine(idNum + 1).toVector(replStr);
1408 replStr.append(
"}}");
1410 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1417 path +=
" = {{-1}}";
1420 xmrElems.emplace_back(
1421 nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1422 ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1425 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1426 bool notFailed =
true;
1427 auto elements = vector.getElements();
1428 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1429 auto field = fromAttr(elements[i]);
1432 notFailed &= traverseField(
1433 *field,
id, path.snapshot().append(
"[" + Twine(i) +
"]"),
1434 xmrElems, interfaceBuilder);
1438 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1439 bool anyFailed =
true;
1440 for (
auto element : bundle.getElements()) {
1441 auto field = fromAttr(element);
1444 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1446 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1447 anyFailed &= traverseField(
1448 *field,
id, path.snapshot().append(
"." + name.getValue()),
1449 xmrElems, interfaceBuilder);
1454 .Default([](
auto a) {
return true; });
1457bool GrandCentralPass::traverseViewField(
1458 Attribute field, VerbatimBuilder &path,
1459 SmallVector<VerbatimXMRbuilder> &xmrElems,
1460 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1462 return TypeSwitch<Attribute, bool>(field)
1463 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1466 if (index >= view.getNumOperands()) {
1467 view.emitOpError(
"more ground types needed (")
1468 << idx <<
" so far) than view has operands ("
1469 << view.getNumOperands() <<
")";
1473 auto val = view.getOperand(index);
1474 auto tpe = type_cast<FIRRTLBaseType>(val.getType());
1477 if (!tpe.getBitWidthOrSentinel())
1483 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1484 SmallString<128> replStr;
1485 StringRef begin =
"{{";
1486 StringRef
end =
"}}";
1489 while (from < base.size()) {
1491 size_t beginAt = base.find(begin, from);
1492 size_t endAt = base.find(end, from);
1494 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1495 (beginAt > endAt)) {
1496 replStr.append(base.substr(from));
1500 replStr.append(base.substr(from, beginAt - from));
1503 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1505 bool failed = idChar.getAsInteger(10, idNum);
1507 assert(!failed &&
"failed to parse integer from verbatim string");
1509 replStr.append(
"{{");
1510 Twine(idNum + 1).toVector(replStr);
1511 replStr.append(
"}}");
1513 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1519 path +=
" = {{-1}}";
1522 auto mod = view->getParentOfType<FModuleOp>();
1523 xmrElems.emplace_back(val, getStrAndIncrementIds(path.getString()),
1524 ArrayAttr::get(&getContext(), path.getSymbols()),
1528 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1529 bool notFailed =
true;
1530 auto elements = vector.getElements();
1531 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1532 auto field = fromViewAttr(view, elements[i]);
1535 notFailed &= traverseViewField(
1536 *field, path.snapshot().append(
"[" + Twine(i) +
"]"), xmrElems,
1537 interfaceBuilder, view, idx);
1541 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1542 bool anyFailed =
true;
1543 for (
auto element : bundle.getElements()) {
1544 auto field = fromViewAttr(view, element);
1547 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1549 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1550 anyFailed &= traverseViewField(
1551 *field, path.snapshot().append(
"." + name.getValue()), xmrElems,
1552 interfaceBuilder, view, idx);
1557 .Default([](
auto a) {
return true; });
1560std::optional<TypeSum> GrandCentralPass::computeField(
1561 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1562 SmallVector<VerbatimXMRbuilder> &xmrElems,
1563 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1564 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1565 .Case<AugmentedGroundTypeAttr>(
1566 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1568 if (!traverseField(field,
id, path, xmrElems, interfaceBuilder))
1569 return std::nullopt;
1570 FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1573 auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1576 if (!tpe.isGround()) {
1577 value.getDefiningOp()->emitOpError()
1578 <<
"cannot be added to interface with id '"
1579 <<
id.getValue().getZExtValue()
1580 <<
"' because it is not a ground type";
1581 return std::nullopt;
1583 return TypeSum(IntegerType::get(getOperation().getContext(),
1584 tpe.getBitWidthOrSentinel()));
1586 .Case<AugmentedVectorTypeAttr>(
1587 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1588 auto elements = vector.getElements();
1589 if (elements.empty())
1590 llvm::report_fatal_error(
1591 "unexpected empty augmented vector in GrandCentral View");
1592 auto firstElement = fromAttr(elements[0]);
1594 return std::nullopt;
1596 *firstElement,
id, path.snapshot().append(
"[" + Twine(0) +
"]"),
1597 xmrElems, interfaceBuilder);
1599 return std::nullopt;
1601 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1602 auto subField = fromAttr(elements[i]);
1604 return std::nullopt;
1605 (void)traverseField(*subField,
id,
1606 path.snapshot().append(
"[" + Twine(i) +
"]"),
1607 xmrElems, interfaceBuilder);
1612 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1614 str.dimensions.push_back(elements.getValue().size());
1615 return TypeSum(str);
1617 .Case<AugmentedBundleTypeAttr>(
1618 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1620 traverseBundle(bundle,
id, path, xmrElems, interfaceBuilder);
1621 assert(ifaceName && *ifaceName);
1622 return VerbatimType({ifaceName->str(),
true});
1626std::optional<TypeSum> GrandCentralPass::computeViewField(
1627 Attribute field, VerbatimBuilder &path,
1628 SmallVector<VerbatimXMRbuilder> &xmrElems,
1629 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1631 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1632 .Case<AugmentedGroundTypeAttr>(
1633 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1636 if (!traverseViewField(field, path, xmrElems, interfaceBuilder,
1638 return std::nullopt;
1640 auto val = view.getOperand(index);
1641 auto tpe = dyn_cast<FIRRTLBaseType>(val.getType());
1642 if (!tpe || !tpe.isGround()) {
1643 mlir::emitError(val.getLoc(),
"cannot be added to interface, "
1644 "because it is not a ground type")
1645 .attachNote(view.getLoc())
1646 .append(
"interface part of view");
1647 return std::nullopt;
1651 IntegerType::get(&getContext(), tpe.getBitWidthOrSentinel()));
1653 .Case<AugmentedVectorTypeAttr>(
1654 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1655 auto elements = vector.getElements();
1656 if (elements.empty())
1657 llvm::report_fatal_error(
1658 "unexpected empty augmented vector in GrandCentral View");
1659 auto firstElement = fromViewAttr(view, elements[0]);
1661 return std::nullopt;
1663 *firstElement, path.snapshot().append(
"[" + Twine(0) +
"]"),
1664 xmrElems, interfaceBuilder, view, idx);
1666 return std::nullopt;
1668 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1669 auto subField = fromViewAttr(view, elements[i]);
1671 return std::nullopt;
1672 (void)traverseViewField(
1673 *subField, path.snapshot().append(
"[" + Twine(i) +
"]"),
1674 xmrElems, interfaceBuilder, view, idx);
1679 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1681 str.dimensions.push_back(elements.getValue().size());
1682 return TypeSum(str);
1684 .Case<AugmentedBundleTypeAttr>(
1685 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1686 auto ifaceName = traverseViewBundle(bundle, path, xmrElems,
1687 interfaceBuilder, view, idx);
1688 assert(ifaceName && *ifaceName);
1689 return VerbatimType({ifaceName->str(),
true});
1699std::optional<StringAttr> GrandCentralPass::traverseBundle(
1700 AugmentedBundleTypeAttr bundle, IntegerAttr
id, VerbatimBuilder &path,
1701 SmallVector<VerbatimXMRbuilder> &xmrElems,
1702 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1704 unsigned lastIndex = interfaceBuilder.size();
1705 auto iFaceName = StringAttr::get(
1706 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1707 interfaceBuilder.emplace_back(iFaceName,
id);
1709 for (
auto element : bundle.getElements()) {
1710 auto field = fromAttr(element);
1712 return std::nullopt;
1714 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1725 *field,
id, path.snapshot().append(
".").append(name.getValue()),
1726 xmrElems, interfaceBuilder);
1728 return std::nullopt;
1729 StringAttr description =
1730 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1731 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1743std::optional<StringAttr> GrandCentralPass::traverseViewBundle(
1744 AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
1745 SmallVector<VerbatimXMRbuilder> &xmrElems,
1746 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1750 if (!bundle.getDefName()) {
1751 view.emitOpError(
"missing 'defName' at top-level");
1752 return std::nullopt;
1754 if (!bundle.getElements()) {
1755 view.emitOpError(
"missing 'elements' at top-level");
1756 return std::nullopt;
1759 unsigned lastIndex = interfaceBuilder.size();
1760 auto iFaceName = StringAttr::get(
1761 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1762 interfaceBuilder.emplace_back(iFaceName, IntegerAttr() );
1764 for (
auto element : bundle.getElements()) {
1765 auto field = fromViewAttr(view, element);
1767 return std::nullopt;
1769 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1771 view.emitError(
"missing 'name' field in element of bundle: ") << element;
1772 return std::nullopt;
1784 *field, path.snapshot().append(
".").append(name.getValue()), xmrElems,
1785 interfaceBuilder, view, idx);
1787 return std::nullopt;
1788 StringAttr description =
1789 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1790 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1798igraph::ModuleOpInterface
1799GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1800 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1801 return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1803 auto *op = value.getDefiningOp();
1804 if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1805 return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1806 instance.getModuleNameAttr().getValue());
1808 return op->getParentOfType<igraph::ModuleOpInterface>();
1812void GrandCentralPass::runOnOperation() {
1815 CircuitOp circuitOp = getOperation();
1824 SmallVector<Annotation> worklist;
1825 bool removalError =
false;
1834 if (companionMode != CompanionMode::Instantiate)
1835 worklist.push_back(anno);
1840 if (maybeExtractInfo) {
1841 emitCircuitError(
"more than one 'ExtractGrandCentralAnnotation' was "
1842 "found, but exactly one must be provided");
1843 removalError = true;
1847 auto directory = anno.
getMember<StringAttr>(
"directory");
1848 auto filename = anno.
getMember<StringAttr>(
"filename");
1851 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1852 "not contain 'directory' field: "
1854 removalError =
true;
1857 if (directory.getValue().empty())
1858 directory = StringAttr::get(circuitOp.getContext(),
".");
1860 maybeExtractInfo = {directory, filename};
1865 if (maybeHierarchyFileYAML) {
1866 emitCircuitError(
"more than one 'GrandCentralHierarchyFileAnnotation' "
1867 "was found, but zero or one may be provided");
1868 removalError =
true;
1872 auto filename = anno.
getMember<StringAttr>(
"filename");
1875 <<
"contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1876 "that does not contain 'directory' and 'filename' fields: "
1878 removalError =
true;
1882 maybeHierarchyFileYAML = filename;
1887 testbenchDir = anno.
getMember<StringAttr>(
"dirname");
1894 return signalPassFailure();
1897 llvm::SmallMapVector<StringAttr, SmallVector<sv::InterfaceOp, 0>, 1>
1899 if (maybeHierarchyFileYAML.has_value())
1900 interfaceYAMLMap[maybeHierarchyFileYAML.value()] = {};
1903 SmallVector<ViewIntrinsicOp> views;
1904 circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); });
1908 instancePaths = &instancePathCache;
1909 instanceInfo = &getAnalysis<InstanceInfo>();
1912 llvm::dbgs() <<
"Extraction Info:\n";
1913 if (maybeExtractInfo)
1914 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1915 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1917 llvm::dbgs() <<
" <none>\n";
1918 llvm::dbgs() <<
"DUT: ";
1919 if (
auto dut = instanceInfo->
getDut())
1920 llvm::dbgs() << dut.getModuleName() <<
"\n";
1922 llvm::dbgs() <<
"<none>\n";
1924 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1926 if (maybeHierarchyFileYAML)
1927 llvm::dbgs() << *maybeHierarchyFileYAML;
1929 llvm::dbgs() <<
"<none>";
1930 llvm::dbgs() <<
"\n";
1933 bool changed =
false;
1934 if (noViews && !views.empty()) {
1936 for (
auto &view : views)
1944 if (worklist.empty() && views.empty()) {
1945 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
1946 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
1948 markAllAnalysesPreserved();
1956 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1960 auto getID = [&](Operation *op,
1961 Annotation annotation) -> std::optional<IntegerAttr> {
1962 auto id = annotation.getMember<IntegerAttr>(
"id");
1965 <<
"contained a malformed "
1966 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1967 "that did not contain an 'id' field";
1968 removalError =
true;
1969 return std::nullopt;
1974 nlaTable = &getAnalysis<NLATable>();
1980 DenseSet<Operation *> modulesToDelete;
1981 circuitOp.walk([&](Operation *op) {
1982 TypeSwitch<Operation *>(op)
1983 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
1987 auto maybeID = getID(op, annotation);
1991 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1992 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
1999 .Case<InstanceOp>([&](
auto op) {
2005 <<
"is marked as an interface element, but this should be "
2006 "impossible due to how the Chisel Grand Central API works";
2007 removalError =
true;
2011 .Case<MemOp>([&](
auto op) {
2016 <<
"is marked as an interface element, but this does not make "
2017 "sense (is there a scattering bug or do you have a "
2018 "malformed hand-crafted MLIR circuit?)";
2019 removalError =
true;
2027 <<
"has port '" << i
2028 <<
"' marked as an interface element, but this does not "
2029 "make sense (is there a scattering bug or do you have a "
2030 "malformed hand-crafted MLIR circuit?)";
2031 removalError =
true;
2035 .Case<FModuleOp>([&](FModuleOp op) {
2041 auto maybeID = getID(op, annotation);
2045 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2046 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
2056 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
2057 "circt.nonlocal") !=
nullptr;
2058 auto name = annotation.
getMember<StringAttr>(
"name");
2059 auto id = annotation.
getMember<IntegerAttr>(
"id");
2062 <<
"has a malformed "
2063 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2064 "not contain an 'id' field with an 'IntegerAttr' value";
2065 goto FModuleOp_error;
2069 <<
"has a malformed "
2070 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2071 "not contain a 'name' field with a 'StringAttr' value";
2072 goto FModuleOp_error;
2081 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
2083 companionIDMap[id] = {name.getValue(), op, isNonlocal};
2090 for (
auto port : op.getPorts()) {
2093 auto ty = port.type;
2094 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
2097 <<
"companion instance cannot have output ports";
2102 if (!maybeExtractInfo) {
2108 for (
auto *i :
llvm::make_early_inc_range(
2109 instancePaths->instanceGraph.lookup(op)->uses())) {
2110 auto instance = cast<InstanceOp>(i->getInstance());
2113 if (instance->getParentOfType<LayerBlockOp>())
2116 i->getParent()->getModule()))
2119 if (companionMode == CompanionMode::Drop) {
2121 OpBuilder builder(&getContext());
2122 for (
auto port : instance->getResults()) {
2123 builder.setInsertionPointAfterValue(port);
2125 WireOp::create(builder, port.getLoc(), port.getType());
2126 port.replaceAllUsesWith(wire.getResult());
2133 if (companionMode == CompanionMode::Bind)
2134 instance->setAttr(
"lowerToBind", builder.getUnitAttr());
2138 SmallString<128> bindFilename;
2139 if (maybeExtractInfo->bindFilename) {
2140 bindFilename = maybeExtractInfo->bindFilename.getValue();
2142 bindFilename = maybeExtractInfo->directory.getValue();
2143 llvm::sys::path::append(
2145 i->getParent()->getModule().getModuleName().str() +
2149 instance->setAttr(
"output_file",
2150 hw::OutputFileAttr::getFromFilename(
2151 &getContext(), bindFilename,
2171 <<
"Found companion module: "
2172 << companionNode->
getModule().getModuleName() <<
"\n"
2173 <<
" submodules exclusively instantiated "
2174 "(including companion):\n";
2177 for (
auto &node :
llvm::depth_first(companionNode)) {
2186 if (modNode != companionNode &&
2188 modNode->getModule()))
2193 <<
" - module: " << mod.getModuleName() <<
"\n";
2196 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
2198 if (companionMode == CompanionMode::Drop) {
2199 modulesToDelete.insert(mod);
2205 if (extmodule->hasAttr(
"output_file"))
2209 hw::OutputFileAttr::getAsDirectory(
2211 maybeExtractInfo->directory.getValue()));
2217 if (companionMode == CompanionMode::Drop) {
2218 modulesToDelete.insert(mod);
2222 if (!mod->hasAttr(
"output_file")) {
2223 mod->setAttr(
"output_file",
2224 hw::OutputFileAttr::getAsDirectory(
2226 maybeExtractInfo->directory.getValue(),
2229 mod->setAttr(
"comment", builder.getStringAttr(
2230 "VCS coverage exclude_file"));
2240 <<
"unknown annotation class: " << annotation.
getDict();
2243 removalError =
true;
2250 return signalPassFailure();
2252 if (companionMode == CompanionMode::Drop) {
2253 for (
auto *mod : modulesToDelete) {
2254 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2256 DenseSet<hw::HierPathOp> nlas;
2259 for (
auto nla : nlas) {
2260 if (nla.root() == name)
2264 cast<igraph::ModuleOpInterface>(*mod));
2269 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2270 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2277 SmallVector<IntegerAttr> ids;
2278 auto sort = [&ids]() {
2279 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2280 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2283 for (
auto tuple : companionIDMap)
2284 ids.push_back(cast<IntegerAttr>(tuple.first));
2286 llvm::dbgs() <<
"companionIDMap:\n";
2287 for (
auto id : ids) {
2288 auto value = companionIDMap.lookup(
id);
2289 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2290 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2293 for (
auto tuple : leafMap)
2294 ids.push_back(cast<IntegerAttr>(tuple.first));
2296 llvm::dbgs() <<
"leafMap:\n";
2297 for (
auto id : ids) {
2298 auto fieldRef = leafMap.lookup(
id).field;
2301 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2302 FModuleOp
module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2303 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2304 <<
module.getName() + ">" +
2305 module.getPortName(blockArg.getArgNumber());
2307 llvm::dbgs() <<
", fieldID=" << fieldID;
2308 llvm::dbgs() <<
"\n";
2310 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2311 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2314 llvm::dbgs() <<
", fieldID=" << fieldID;
2315 llvm::dbgs() <<
"\n";
2327 companionToInterfaceMap;
2328 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2329 InterfaceElemsBuilder &rhs) {
2330 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2331 InterfaceElemsBuilder::Properties &rhs) {
2335 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2337 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2341 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2342 rhs.elementsList.begin(), compareProps);
2344 for (
auto anno : worklist) {
2345 auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.
getDict());
2349 if (!bundle.isRoot()) {
2350 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2352 removalError =
true;
2356 if (companionIDMap.count(bundle.getID()) == 0) {
2357 emitCircuitError() <<
"no companion found with 'id' value '"
2358 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2359 removalError =
true;
2365 auto companionIter = companionIDMap.lookup(bundle.getID());
2366 auto companionModule = companionIter.companion;
2367 auto symbolName = getNamespace().newName(
2368 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2369 getInterfaceName(bundle) +
"__");
2375 auto instanceSymbol =
2376 hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2377 StringAttr::get(&getContext(), symbolName));
2378 VerbatimBuilder::Base verbatimData;
2379 VerbatimBuilder verbatim(verbatimData);
2380 verbatim += instanceSymbol;
2383 SmallVector<VerbatimXMRbuilder> xmrElems;
2384 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2386 auto ifaceName = traverseBundle(bundle, bundle.getID(), verbatim, xmrElems,
2389 removalError =
true;
2393 if (companionIter.isNonlocal) {
2397 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2398 if (viewMapIter != companionToInterfaceMap.end())
2399 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2400 viewMapIter->getSecond().begin(),
2401 compareInterfaceSignal)) {
2405 companionToInterfaceMap[companionModule] = interfaceBuilder;
2408 if (interfaceBuilder.empty())
2410 auto companionBuilder =
2411 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2414 for (
auto xmrElem : xmrElems) {
2415 auto uloc = companionBuilder.getUnknownLoc();
2416 sv::VerbatimOp::create(companionBuilder, uloc, xmrElem.str, xmrElem.val,
2419 numXMRs += xmrElems.size();
2421 sv::InterfaceOp topIface;
2422 for (
const auto &ifaceBuilder : interfaceBuilder) {
2423 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2424 auto loc = getOperation().getLoc();
2425 sv::InterfaceOp iface =
2426 sv::InterfaceOp::create(builder, loc, ifaceBuilder.iFaceName);
2431 companionIDMap[ifaceBuilder.id].companion) &&
2433 iface->setAttr(
"output_file",
2434 hw::OutputFileAttr::getAsDirectory(
2435 &getContext(), testbenchDir.getValue(),
2437 else if (maybeExtractInfo)
2438 iface->setAttr(
"output_file",
2439 hw::OutputFileAttr::getAsDirectory(
2440 &getContext(), getOutputDirectory().getValue(),
2442 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2443 builder.setInsertionPointToEnd(
2445 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2446 ifaceBuilder.iFaceName)] = iface;
2447 for (
auto elem : ifaceBuilder.elementsList) {
2449 auto uloc = builder.getUnknownLoc();
2451 auto description = elem.description;
2454 auto descriptionOp = sv::VerbatimOp::create(
2456 (
"// " + cleanupDescription(description.getValue())));
2461 if (maybeHierarchyFileYAML)
2462 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2463 builder.getStringAttr(
"description"));
2465 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2466 auto instanceOp = sv::VerbatimOp::create(
2467 builder, uloc, str->toStr(elem.elemName.getValue()));
2471 if (maybeHierarchyFileYAML) {
2472 if (str->instantiation)
2473 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2474 builder.getStringAttr(
"instance"));
2476 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2477 builder.getStringAttr(
"unsupported"));
2478 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2479 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2480 builder.getI32ArrayAttr(str->dimensions));
2481 instanceOp->setAttr(
2482 "firrtl.grandcentral.yaml.symbol",
2483 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2488 auto tpe = std::get<Type>(elem.elemType);
2489 sv::InterfaceSignalOp::create(builder, uloc, elem.elemName.getValue(),
2496 if (maybeHierarchyFileYAML.has_value())
2497 interfaceYAMLMap[maybeHierarchyFileYAML.value()].push_back(topIface);
2500 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2501 sv::InterfaceInstanceOp::create(
2502 builder, getOperation().
getLoc(), topIface.getInterfaceType(),
2503 companionIDMap.lookup(bundle.getID()).name,
2504 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2508 if (!maybeExtractInfo)
2514 companionIDMap[bundle.getID()].companion))
2518 for (
auto view : views) {
2519 auto bundle = view.getAugmentedType();
2521 assert(!bundle.isRoot() &&
"'id' found in firrtl.view");
2523 if (!bundle.getDefName()) {
2524 view.emitOpError(
"missing 'defName' at top-level");
2525 removalError =
true;
2528 if (!bundle.getElements()) {
2529 view.emitOpError(
"missing 'elements' at top-level");
2530 removalError =
true;
2534 auto viewParentMod = view->getParentOfType<FModuleLike>();
2535 auto symbolName = getModuleNamespace(viewParentMod)
2536 .newName(
"__" + getInterfaceName(bundle) +
"__");
2542 auto instanceSymbol =
2543 hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod),
2544 StringAttr::get(&getContext(), symbolName));
2545 VerbatimBuilder::Base verbatimData;
2546 VerbatimBuilder verbatim(verbatimData);
2547 verbatim += instanceSymbol;
2550 SmallVector<VerbatimXMRbuilder> xmrElems;
2551 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2554 auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems,
2555 interfaceBuilder, view, index);
2557 removalError =
true;
2560 if (index != view.getNumOperands()) {
2561 assert(index < view.getNumOperands() &&
2562 "this should error while consuming");
2563 removalError =
true;
2564 view.emitOpError() <<
"has too many operands: " << view.getNumOperands()
2565 <<
" operands but only " << index <<
" were needed";
2569 if (interfaceBuilder.empty())
2571 ImplicitLocOpBuilder viewBuilder(view.getLoc(), view);
2572 viewBuilder.setInsertionPointAfter(view);
2575 for (
auto xmrElem : xmrElems)
2576 sv::VerbatimOp::create(viewBuilder, xmrElem.str, xmrElem.val,
2578 numXMRs += xmrElems.size();
2580 sv::InterfaceOp topIface;
2581 auto containingOutputFileAttr =
2582 viewParentMod->getAttrOfType<hw::OutputFileAttr>(
"output_file");
2583 auto yamlPath = view.getYamlFileAttr();
2584 for (
const auto &ifaceBuilder : interfaceBuilder) {
2585 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2586 auto loc = getOperation().getLoc();
2587 sv::InterfaceOp iface =
2588 sv::InterfaceOp::create(builder, loc, ifaceBuilder.iFaceName);
2595 if (containingOutputFileAttr)
2596 iface->setAttr(
"output_file", containingOutputFileAttr);
2598 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2599 builder.setInsertionPointToEnd(
2601 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2602 ifaceBuilder.iFaceName)] = iface;
2603 for (
auto elem : ifaceBuilder.elementsList) {
2605 auto uloc = builder.getUnknownLoc();
2607 auto description = elem.description;
2610 auto descriptionOp = sv::VerbatimOp::create(
2612 (
"// " + cleanupDescription(description.getValue())));
2618 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2619 builder.getStringAttr(
"description"));
2621 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2622 auto instanceOp = sv::VerbatimOp::create(
2623 builder, uloc, str->toStr(elem.elemName.getValue()));
2628 if (str->instantiation)
2629 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2630 builder.getStringAttr(
"instance"));
2632 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2633 builder.getStringAttr(
"unsupported"));
2634 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2635 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2636 builder.getI32ArrayAttr(str->dimensions));
2637 instanceOp->setAttr(
2638 "firrtl.grandcentral.yaml.symbol",
2639 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2644 auto tpe = std::get<Type>(elem.elemType);
2645 sv::InterfaceSignalOp::create(builder, uloc, elem.elemName.getValue(),
2653 interfaceYAMLMap[yamlPath].push_back(topIface);
2657 viewBuilder.setInsertionPoint(view);
2658 sv::InterfaceInstanceOp::create(
2659 viewBuilder, topIface.getInterfaceType(), view.getName(),
2660 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2665 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2666 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2671 return signalPassFailure();
2672 markAnalysesPreserved<NLATable>();
2675void GrandCentralPass::emitHierarchyYamlFile(
2676 StringRef yamlPath, SmallVectorImpl<sv::InterfaceOp> &intfs) {
2677 CircuitOp circuitOp = getOperation();
2679 std::string yamlString;
2680 llvm::raw_string_ostream stream(yamlString);
2681 ::yaml::Context yamlContext({interfaceMap});
2682 llvm::yaml::Output yout(stream);
2683 yamlize(yout, intfs,
true, yamlContext);
2685 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2686 sv::VerbatimOp::create(builder, builder.getUnknownLoc(), yamlString)
2687 ->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
2688 &getContext(), yamlPath,
2690 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringAttr name, StringAttr defName, std::optional< IntegerAttr > id, std::optional< StringAttr > description, Twine clazz, StringAttr companionAttr, Twine path={})
Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract any annotations it may con...
static Location getLoc(DefSlot slot)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static Block * getBodyBlock(FModuleLike mod)
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
This class provides a thread-safe interface to access the analysis results.
This class represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
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.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if all instances are under (or transitively under) 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.
This table tracks nlas and what modules participate in them.
void removeNLAsfromModule(const DenseSet< hw::HierPathOp > &nlas, StringAttr mod)
Remove all the nlas in the set nlas from the module.
void getNLAsInModule(StringAttr modName, DenseSet< hw::HierPathOp > &nlas)
Get the NLAs that the module modName particiaptes in, and insert them into the DenseSet nlas.
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
virtual void erase(InstanceGraphNode *node)
Remove this module from the instance graph.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
constexpr const char * testBenchDirAnnoClass
constexpr const char * augmentedGroundTypeClass
constexpr const char * viewAnnoClass
constexpr const char * blackBoxPathAnnoClass
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
constexpr const char * companionAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static std::string stripComment(StringRef str)
Convert newlines and comments to remove the comments.
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
AddToWorklistFn addToWorklistFn
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
The namespace of a CircuitOp, generally inhabited by modules.
void insertOp(Operation *op)
Add a new op to the target cache.
A data structure that caches and provides paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
InstanceGraph & instanceGraph
The instance graph of the IR.
StringRef name
The name of the interface.
FlatSymbolRefAttr interface
The underlying interface.
Instance(IO &io, DescribedInstance &op)
DescribedInstance denormalize(IO &)
SmallVector< int64_t, 2 > dimensions
An array describing the dimensionality of the interface.
static void mapping(IO &io, DescribedInstance &op, Context &ctx)
DescribedSignal denormalize(IO &)
This cannot be denormalized back to an interface op.
StringRef name
The name of the field.
std::optional< std::string > description
An optional, textual description of what the field is.
Field(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
unsigned width
The width of the underlying type.
Field(IO &io, DescribedSignal &op)
Construct a Field from a DescribedSignal (an sv::InterfaceSignalOp with an optional description).
SmallVector< unsigned, 2 > dimensions
The dimensions of the field.
static void mapping(IO &io, DescribedSignal &op, Context &ctx)
Interface(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
std::vector< DescribedInstance > instances
Instantiations of other interfaces.
StringRef name
The name of the interface.
Interface(IO &io, sv::InterfaceOp &op)
Construct an Interface from an sv::InterfaceOp.
sv::InterfaceOp denormalize(IO &)
This cannot be denormalized back to an interface op.
std::vector< DescribedSignal > fields
All ground or vectors that make up the interface.
static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx)