28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/Debug.h"
32#include "llvm/Support/YAMLTraits.h"
35#define DEBUG_TYPE "gct"
39#define GEN_PASS_DEF_GRANDCENTRAL
40#include "circt/Dialect/FIRRTL/Passes.h.inc"
45using namespace firrtl;
58[[maybe_unused]]
static std::string noDefault(StringRef clazz) {
59 return (
"default '" + clazz +
60 "' construction is an intentionally *NOT* implemented "
61 "YAML feature (you should never be using this)")
65[[maybe_unused]]
static std::string deNorm(StringRef clazz) {
66 return (
"conversion from YAML to a '" + clazz +
67 "' is intentionally *NOT* implemented (you should not be "
68 "converting from YAML to an interface)")
83 DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
92struct DescribedSignal {
94 StringAttr description;
97 sv::InterfaceSignalOp signal;
105struct DescribedInstance {
109 StringAttr description;
112 ArrayAttr dimensions;
115 FlatSymbolRefAttr interface;
125LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
126LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
127LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
135using namespace ::
yaml;
148 std::string descriptionString;
149 llvm::raw_string_ostream stream(descriptionString);
150 SmallVector<StringRef> splits;
151 str.split(splits,
"\n");
155 substr.consume_front(
"//");
156 stream << substr.drop_while([](
auto c) {
return c ==
' '; });
158 [&]() { stream <<
"\n"; });
159 return descriptionString;
166struct MappingContextTraits<DescribedSignal, Context> {
184 : name(op.signal.getSymNameAttr().getValue()) {
210 auto tpe = op.signal.getType();
211 while (
auto vector = dyn_cast<hw::UnpackedArrayType>(tpe)) {
212 dimensions.push_back(vector.getNumElements());
213 tpe = vector.getElementType();
215 dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
220 assert(isa<IntegerType>(tpe));
221 width = type_cast<IntegerType>(tpe).getWidth();
225 Field(IO &io) { llvm_unreachable(noDefault(
"Field").c_str()); }
229 llvm_unreachable(deNorm(
"DescribedSignal").c_str());
233 static void mapping(IO &io, DescribedSignal &op, Context &ctx) {
234 MappingNormalization<Field, DescribedSignal> keys(io, op);
235 io.mapRequired(
"name", keys->name);
236 io.mapOptional(
"description", keys->description);
237 io.mapRequired(
"dimensions", keys->dimensions);
238 io.mapRequired(
"width", keys->width);
247struct MappingContextTraits<DescribedInstance, Context> {
254 std::optional<std::string> description = std::nullopt;
263 : name(op.name.getValue()), interface(op.interface) {
271 for (
auto &d : op.dimensions) {
272 auto dimension = dyn_cast<IntegerAttr>(d);
273 dimensions.push_back(dimension.getInt());
277 Instance(IO &io) { llvm_unreachable(noDefault(
"Instance").c_str()); }
280 llvm_unreachable(deNorm(
"DescribedInstance").c_str());
284 static void mapping(IO &io, DescribedInstance &op, Context &ctx) {
285 MappingNormalization<Instance, DescribedInstance> keys(io, op);
286 io.mapRequired(
"name", keys->name);
287 io.mapOptional(
"description", keys->description);
288 io.mapRequired(
"dimensions", keys->dimensions);
289 io.mapRequired(
"interface", ctx.interfaceMap[keys->interface], ctx);
297struct MappingContextTraits<
sv::InterfaceOp, Context> {
336 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
339 StringAttr description = {};
341 for (
auto &op : op.getBodyBlock()->getOperations()) {
342 TypeSwitch<Operation *>(&op)
345 .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
346 auto tpe = op->getAttrOfType<StringAttr>(
347 "firrtl.grandcentral.yaml.type");
351 if (tpe.getValue() ==
"description") {
352 description = op.getFormatStringAttr();
357 if (tpe.getValue() ==
"unsupported") {
364 auto name = op->getAttrOfType<StringAttr>(
365 "firrtl.grandcentral.yaml.name");
366 auto dimensions = op->getAttrOfType<ArrayAttr>(
367 "firrtl.grandcentral.yaml.dimensions");
368 auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
369 "firrtl.grandcentral.yaml.symbol");
371 DescribedInstance({name, description, dimensions, symbol}));
375 .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
376 fields.push_back(DescribedSignal({description, op}));
383 Interface(IO &io) { llvm_unreachable(noDefault(
"Interface").c_str()); }
387 llvm_unreachable(deNorm(
"sv::InterfaceOp").c_str());
391 static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx) {
392 MappingNormalization<Interface, sv::InterfaceOp> keys(io, op);
393 io.mapRequired(
"name", keys->name);
394 io.mapRequired(
"fields", keys->fields, ctx);
395 io.mapRequired(
"instances", keys->instances, ctx);
425struct VerbatimBuilder {
427 SmallString<128> string;
428 SmallVector<Attribute> symbols;
429 VerbatimBuilder builder() {
return VerbatimBuilder(*
this); }
430 operator VerbatimBuilder() {
return builder(); }
435 VerbatimBuilder(Base &base)
436 : base(base), stringBaseSize(base.string.size()),
437 symbolsBaseSize(base.symbols.size()) {}
442 base.string.resize(stringBaseSize);
443 base.symbols.resize(symbolsBaseSize);
447 VerbatimBuilder(
const VerbatimBuilder &) =
delete;
448 VerbatimBuilder &operator=(
const VerbatimBuilder &) =
delete;
453 VerbatimBuilder snapshot() {
return VerbatimBuilder(base); }
456 StringRef getString()
const {
return base.string; }
458 ArrayRef<Attribute> getSymbols()
const {
return base.symbols; }
461 VerbatimBuilder &
append(
char c) {
462 base.string.push_back(c);
467 VerbatimBuilder &
append(
const Twine &twine) {
468 twine.toVector(base.string);
473 VerbatimBuilder &
append(Attribute symbol) {
474 unsigned id = base.symbols.size();
475 base.symbols.push_back(symbol);
476 append(
"{{" + Twine(
id) +
"}}");
480 VerbatimBuilder &operator+=(
char c) {
return append(c); }
481 VerbatimBuilder &operator+=(
const Twine &twine) {
return append(twine); }
482 VerbatimBuilder &operator+=(Attribute symbol) {
return append(symbol); }
486 size_t stringBaseSize;
487 size_t symbolsBaseSize;
502 SmallVector<int32_t, 4> dimensions = {};
505 std::string toStr(StringRef name) {
506 SmallString<64> stringType(str);
507 stringType.append(
" ");
508 stringType.append(name);
509 for (
auto d :
llvm::reverse(dimensions)) {
510 stringType.append(
"[");
511 stringType.append(Twine(d).str());
512 stringType.append(
"]");
515 stringType.append(
"()");
516 stringType.append(
";");
517 return std::string(stringType);
523using TypeSum = std::variant<VerbatimType, Type>;
526struct ExtractionInfo {
529 StringAttr directory = {};
534 StringAttr bindFilename = {};
538struct CompanionInfo {
549 FlatSymbolRefAttr nlaSym;
553struct VerbatimXMRbuilder {
557 FModuleOp companionMod;
558 VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
559 FModuleOp companionMod)
560 : val(val), str(str), syms(syms), companionMod(companionMod) {}
565struct InterfaceElemsBuilder {
566 StringAttr iFaceName;
569 StringAttr description;
572 Properties(StringAttr des, StringAttr name, TypeSum &elemType)
573 : description(des), elemName(name), elemType(elemType) {}
575 SmallVector<Properties> elementsList;
576 InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr
id)
577 : iFaceName(iFaceName), id(id) {}
592struct GrandCentralPass
593 :
public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
594 using GrandCentralBase::companionMode;
596 void runOnOperation()
override;
603 DenseMap<Attribute, FieldAndNLA> leafMap;
606 DenseMap<Attribute, CompanionInfo> companionIDMap;
612 StringAttr testbenchDir;
619 std::optional<Attribute> fromAttr(Attribute attr);
623 bool traverseField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
624 SmallVector<VerbatimXMRbuilder> &xmrElems,
625 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
630 std::optional<TypeSum>
631 computeField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
632 SmallVector<VerbatimXMRbuilder> &xmrElems,
633 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
638 std::optional<StringAttr>
639 traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr
id,
640 VerbatimBuilder &path,
641 SmallVector<VerbatimXMRbuilder> &xmrElems,
642 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
645 igraph::ModuleOpInterface getEnclosingModule(Value value,
646 FlatSymbolRefAttr sym = {});
652 InFlightDiagnostic emitCircuitError(StringRef message = {}) {
653 return emitError(getOperation().getLoc(),
"'firrtl.circuit' op " + message);
661 std::optional<Attribute> fromViewAttr(ViewIntrinsicOp view, Attribute attr);
665 bool traverseViewField(Attribute field, VerbatimBuilder &path,
666 SmallVector<VerbatimXMRbuilder> &xmrElems,
667 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
668 ViewIntrinsicOp view,
size_t &idx);
673 std::optional<TypeSum>
674 computeViewField(Attribute field, VerbatimBuilder &path,
675 SmallVector<VerbatimXMRbuilder> &xmrElems,
676 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
677 ViewIntrinsicOp view,
size_t &idx);
682 std::optional<StringAttr>
683 traverseViewBundle(AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
684 SmallVector<VerbatimXMRbuilder> &xmrElems,
685 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
686 ViewIntrinsicOp view,
size_t &idx);
691 std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) {
692 return (bundleType.getDefName().getValue()).str();
697 std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
701 std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
703 StringAttr getOutputDirectory() {
704 if (maybeExtractInfo)
705 return maybeExtractInfo->directory;
724 std::optional<CircuitNamespace> circuitNamespace;
728 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
733 if (!circuitNamespace)
735 return *circuitNamespace;
740 return moduleNamespaces.try_emplace(module, module).first->second;
745 std::optional<SymbolTable *> symbolTable;
749 SymbolTable &getSymbolTable() {
751 symbolTable = &getAnalysis<SymbolTable>();
752 return **symbolTable;
760 std::string cleanupDescription(StringRef description) {
764 std::tie(head, description) = description.split(
"\n");
766 if (!description.empty())
768 }
while (!description.empty());
769 return std::string(out);
773 DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
776 void emitHierarchyYamlFile(StringRef yamlPath,
777 SmallVectorImpl<sv::InterfaceOp> &intfs);
791static std::optional<DictionaryAttr>
793 DictionaryAttr root, StringAttr name, StringAttr defName,
794 std::optional<IntegerAttr>
id,
795 std::optional<StringAttr> description, Twine clazz,
796 StringAttr companionAttr, Twine path = {}) {
798 auto *context = state.
circuit.getContext();
799 auto loc = state.
circuit.getLoc();
816 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
818 tryGetAs<StringAttr>(refTarget, refTarget,
"circuit", loc, clazz, path);
820 tryGetAs<StringAttr>(refTarget, refTarget,
"module", loc, clazz, path);
822 tryGetAs<ArrayAttr>(refTarget, refTarget,
"path", loc, clazz, path);
823 auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget,
"component",
825 if (!circuitAttr || !moduleAttr || !pathAttr || !componentAttr)
829 SmallString<32> strpath;
830 for (
auto p : pathAttr) {
831 auto dict = dyn_cast_or_null<DictionaryAttr>(p);
833 mlir::emitError(loc,
"annotation '" + clazz +
834 " has invalid type (expected DictionaryAttr)");
838 tryGetAs<DictionaryAttr>(dict, dict,
"_1", loc, clazz, path);
840 tryGetAs<DictionaryAttr>(dict, dict,
"_2", loc, clazz, path);
841 if (!instHolder || !modHolder) {
842 mlir::emitError(loc,
"annotation '" + clazz +
843 " has invalid type (expected DictionaryAttr)");
846 auto inst = tryGetAs<StringAttr>(instHolder, instHolder,
"value", loc,
849 tryGetAs<StringAttr>(modHolder, modHolder,
"value", loc, clazz, path);
851 mlir::emitError(loc,
"annotation '" + clazz +
852 " has invalid type (expected DictionaryAttr)");
855 strpath +=
"/" + inst.getValue().str() +
":" + mod.getValue().str();
858 SmallVector<Attribute> componentAttrs;
859 SmallString<32> componentStr;
860 for (
size_t i = 0, e = componentAttr.size(); i != e; ++i) {
861 auto cPath = (path +
".component[" + Twine(i) +
"]").str();
862 auto component = componentAttr[i];
863 auto dict = dyn_cast_or_null<DictionaryAttr>(component);
865 mlir::emitError(loc,
"annotation '" + clazz +
"' with path '" + cPath +
866 " has invalid type (expected DictionaryAttr)");
870 tryGetAs<StringAttr>(dict, refTarget,
"class", loc, clazz, cPath);
874 auto value = dict.get(
"value");
877 if (
auto field = dyn_cast<StringAttr>(value)) {
878 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Field" &&
879 "A StringAttr target token must be found with a subfield target "
881 componentStr.append((Twine(
".") + field.getValue()).str());
886 if (
auto index = dyn_cast<IntegerAttr>(value)) {
887 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Index" &&
888 "An IntegerAttr target token must be found with a subindex "
891 (Twine(
"[") + Twine(index.getValue().getZExtValue()) +
"]").str());
896 "Annotation '" + clazz +
"' with path '" + cPath +
897 ".value has unexpected type (should be StringAttr "
898 "for subfield or IntegerAttr for subindex).")
900 <<
"The value received was: " << value <<
"\n";
905 tryGetAs<StringAttr>(refTarget, refTarget,
"ref", loc, clazz, path);
909 return (Twine(
"~" + circuitAttr.getValue() +
"|" + moduleAttr.getValue() +
910 strpath +
">" + refAttr.getValue()) +
916 tryGetAs<StringAttr>(augmentedType, root,
"class", loc, clazz, path);
919 StringRef classBase = classAttr.getValue();
920 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented")) {
922 "the 'class' was expected to start with "
923 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
924 classAttr.getValue() +
"' (Did you misspell it?)")
926 <<
"see annotation: " << augmentedType;
933 if (classBase ==
"BundleType") {
935 tryGetAs<StringAttr>(augmentedType, root,
"defName", loc, clazz, path);
943 SmallVector<Attribute> elements;
945 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
948 for (
size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
949 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
953 "Annotation '" + Twine(clazz) +
"' with path '.elements[" +
955 "]' contained an unexpected type (expected a DictionaryAttr).")
957 <<
"The received element was: " << elementsAttr[i] <<
"\n";
960 auto ePath = (path +
".elements[" + Twine(i) +
"]").str();
961 auto name = tryGetAs<StringAttr>(field, root,
"name", loc, clazz, ePath);
963 tryGetAs<DictionaryAttr>(field, root,
"tpe", loc, clazz, ePath);
966 std::optional<StringAttr> description;
967 if (
auto maybeDescription = field.get(
"description"))
968 description = cast<StringAttr>(maybeDescription);
970 state, tpe, root, name, defName, std::nullopt, description, clazz,
971 companionAttr, path +
"_" + name.getValue());
978 if (
auto maybeDescription = field.get(
"description"))
979 attrs.append(
"description", cast<StringAttr>(maybeDescription));
980 attrs.append(
"name", name);
981 auto tpeClass = tpe.getAs<StringAttr>(
"class");
983 mlir::emitError(loc,
"missing 'class' key in") << tpe;
986 attrs.append(
"tpe", tpeClass);
987 elements.push_back(*eltAttr);
993 attrs.append(
"class", classAttr);
994 attrs.append(
"defName", defName);
996 attrs.append(
"description", *description);
997 attrs.append(
"elements", ArrayAttr::get(context, elements));
999 attrs.append(
"id", *
id);
1000 attrs.append(
"name", name);
1001 return DictionaryAttr::getWithSorted(context, attrs);
1010 if (classBase ==
"GroundType") {
1011 auto augRef = augmentedType.getAs<DictionaryAttr>(
"ref");
1013 mlir::emitError(loc,
"missing 'ref' key in ") << augmentedType;
1014 return std::nullopt;
1016 auto maybeTarget = refToTarget(augRef);
1018 mlir::emitError(loc,
"Failed to parse ReferenceTarget").attachNote()
1019 <<
"See the full Annotation here: " << root;
1020 return std::nullopt;
1023 auto id = state.
newID();
1025 auto target = *maybeTarget;
1027 NamedAttrList elementIface, elementScattered;
1030 elementIface.append(
"class", classAttr);
1032 elementIface.append(
"description", *description);
1033 elementIface.append(
"id",
id);
1034 elementIface.append(
"name", name);
1036 elementScattered.append(
"class", classAttr);
1037 elementScattered.append(
"id",
id);
1039 auto targetAttr = StringAttr::get(context, target);
1042 if (!xmrSrcTarget) {
1043 mlir::emitError(loc,
"Failed to resolve target ") << targetAttr;
1044 return std::nullopt;
1050 auto sourceRef = xmrSrcTarget->ref;
1051 ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
1052 std::optional<Value> source =
1053 TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1056 .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1057 -> std::optional<Value> {
1058 auto portNo = sourceRef.getImpl().getPortNo();
1059 if (xmrSrcTarget->instances.empty()) {
1061 if (paths.size() > 1) {
1063 "cannot resolve a unique instance path from the "
1064 "external module '")
1065 << targetAttr <<
"'";
1066 return std::nullopt;
1068 auto *it = xmrSrcTarget->instances.begin();
1069 for (
auto inst : paths.back()) {
1070 xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1074 auto lastInst = xmrSrcTarget->instances.pop_back_val();
1075 builder.setInsertionPointAfter(lastInst);
1077 xmrSrcTarget->fieldIdx);
1081 .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1082 builder.setInsertionPointToEnd(module.getBodyBlock());
1083 auto portNum = sourceRef.getImpl().getPortNo();
1085 xmrSrcTarget->fieldIdx);
1088 .Default([&](Operation *op) -> std::optional<Value> {
1089 auto module = cast<FModuleOp>(sourceRef.getModule());
1090 builder.setInsertionPointToEnd(module.getBodyBlock());
1091 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1093 if (is && is.getTargetResult())
1095 xmrSrcTarget->fieldIdx);
1096 if (sourceRef.getOp()->getNumResults() != 1) {
1098 <<
"cannot be used as a target of the Grand Central View \""
1099 << defName.getValue()
1100 <<
"\" because it does not have exactly one result";
1101 return std::nullopt;
1104 xmrSrcTarget->fieldIdx);
1109 return std::nullopt;
1121 builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1125 auto sinkType = source->getType();
1126 if (
auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1127 sinkType = baseSinkType.getPassiveType();
1128 auto sink = builder.create<WireOp>(sinkType, name);
1131 annotations.addAnnotations(
1132 {DictionaryAttr::getWithSorted(context, elementScattered)});
1133 annotations.applyToOperation(sink);
1138 (path +
"__bore").str(),
1139 WiringProblem::RefTypeUsage::Prefer});
1141 return DictionaryAttr::getWithSorted(context, elementIface);
1146 if (classBase ==
"VectorType") {
1148 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
1150 return std::nullopt;
1151 SmallVector<Attribute> elements;
1152 for (
auto [i, elt] :
llvm::enumerate(elementsAttr)) {
1155 StringAttr::get(context,
""),
id, std::nullopt,
1156 clazz, companionAttr, path +
"_" + Twine(i));
1158 return std::nullopt;
1159 elements.push_back(*eltAttr);
1161 NamedAttrList attrs;
1162 attrs.append(
"class", classAttr);
1164 attrs.append(
"description", *description);
1165 attrs.append(
"elements", ArrayAttr::get(context, elements));
1166 attrs.append(
"name", name);
1167 return DictionaryAttr::getWithSorted(context, attrs);
1172 mlir::emitError(loc,
"found unknown AugmentedType '" + classAttr.getValue() +
1173 "' (Did you misspell it?)")
1175 <<
"see annotation: " << augmentedType;
1176 return std::nullopt;
1180 DictionaryAttr anno,
1183 auto id = state.
newID();
1184 auto *context = state.
circuit.getContext();
1185 auto loc = state.
circuit.getLoc();
1186 NamedAttrList companionAttrs;
1188 companionAttrs.append(
"id",
id);
1190 tryGetAs<DictionaryAttr>(anno, anno,
"view", loc,
viewAnnoClass);
1193 auto name = tryGetAs<StringAttr>(anno, anno,
"name", loc,
viewAnnoClass);
1196 companionAttrs.append(
"name", name);
1197 auto companionAttr =
1198 tryGetAs<StringAttr>(anno, anno,
"companion", loc,
viewAnnoClass);
1201 companionAttrs.append(
"target", companionAttr);
1206 companionAttr, Twine(name));
1221std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1222 auto dict = dyn_cast<DictionaryAttr>(attr);
1224 emitCircuitError() <<
"attribute is not a dictionary: " << attr <<
"\n";
1225 return std::nullopt;
1228 auto clazz = dict.getAs<StringAttr>(
"class");
1230 emitCircuitError() <<
"missing 'class' key in " << dict <<
"\n";
1231 return std::nullopt;
1234 auto classBase = clazz.getValue();
1235 classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented");
1237 if (classBase ==
"BundleType") {
1238 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1239 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1240 emitCircuitError() <<
"has an invalid AugmentedBundleType that does not "
1241 "contain 'defName' and 'elements' fields: "
1243 }
else if (classBase ==
"VectorType") {
1244 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1245 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1246 emitCircuitError() <<
"has an invalid AugmentedVectorType that does not "
1247 "contain 'name' and 'elements' fields: "
1249 }
else if (classBase ==
"GroundType") {
1250 auto id = dict.getAs<IntegerAttr>(
"id");
1251 auto name = dict.getAs<StringAttr>(
"name");
1252 if (
id && leafMap.count(
id) && name)
1253 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1255 emitCircuitError() <<
"has an invalid AugmentedGroundType that does not "
1256 "contain 'id' and 'name' fields: "
1258 if (
id && !leafMap.count(
id))
1259 emitCircuitError() <<
"has an AugmentedGroundType with 'id == "
1260 <<
id.getValue().getZExtValue()
1261 <<
"' that does not have a scattered leaf to connect "
1262 "to in the circuit "
1263 "(was the leaf deleted or constant prop'd away?)";
1265 emitCircuitError() <<
"has an invalid AugmentedType";
1267 return std::nullopt;
1270std::optional<Attribute> GrandCentralPass::fromViewAttr(ViewIntrinsicOp view,
1272 auto dict = dyn_cast<DictionaryAttr>(attr);
1274 view.emitError() <<
"attribute is not a dictionary: " << attr;
1275 return std::nullopt;
1278 auto clazz = dict.getAs<StringAttr>(
"class");
1280 view.emitError() <<
"missing 'class' key in " << dict;
1281 return std::nullopt;
1284 auto classBase = clazz.getValue();
1285 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented"))
1286 view.emitOpError() <<
"has an invalid AugmentedType class '" << classBase
1288 else if (classBase ==
"BundleType") {
1289 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1290 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1291 view.emitError() <<
"has an invalid AugmentedBundleType that does not "
1292 "contain 'defName' and 'elements' fields: "
1294 }
else if (classBase ==
"VectorType") {
1295 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1296 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1297 view.emitError() <<
"has an invalid AugmentedVectorType that does not "
1298 "contain 'name' and 'elements' fields: "
1300 }
else if (classBase ==
"GroundType") {
1301 auto id = dict.getAs<IntegerAttr>(
"id");
1305 <<
"has 'id' field which is only for old annotation encoding")
1307 <<
"id within GroundType attribute: " << dict;
1308 return std::nullopt;
1310 auto name = dict.getAs<StringAttr>(
"name");
1312 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1313 view.emitError() <<
"has an invalid AugmentedGroundType that does not "
1314 "contain 'name' field: "
1317 view.emitOpError() <<
"has an invalid AugmentedType '" << classBase <<
"'";
1319 return std::nullopt;
1322bool GrandCentralPass::traverseField(
1323 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1324 SmallVector<VerbatimXMRbuilder> &xmrElems,
1325 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1326 return TypeSwitch<Attribute, bool>(field)
1327 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1328 auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1331 nla = nlaTable->
getNLA(sym.getAttr());
1332 Value leafValue = fieldRef.getValue();
1333 assert(leafValue &&
"leafValue not found");
1335 auto companionModule = companionIDMap.lookup(
id).companion;
1336 igraph::ModuleOpInterface enclosing =
1337 getEnclosingModule(leafValue, sym);
1339 auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1342 if (!tpe.getBitWidthOrSentinel())
1354 auto *nodeOp = leafValue.getDefiningOp();
1355 if (companionModule != enclosing) {
1356 auto diag = companionModule->emitError()
1357 <<
"Grand Central View \""
1358 << companionIDMap.lookup(
id).name
1359 <<
"\" is invalid because a leaf is not inside the "
1361 diag.attachNote(leafValue.getLoc())
1362 <<
"the leaf value is declared here";
1364 auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1365 diag.attachNote(leafModule.getLoc())
1366 <<
"the leaf value is inside this module";
1371 if (!isa<NodeOp>(nodeOp)) {
1372 emitError(leafValue.getLoc())
1373 <<
"Grand Central View \"" << companionIDMap.lookup(
id).name
1374 <<
"\" has an invalid leaf value (this must be a node)";
1381 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1382 SmallString<128> replStr;
1383 StringRef begin =
"{{";
1384 StringRef
end =
"}}";
1387 while (from < base.size()) {
1389 size_t beginAt = base.find(begin, from);
1390 size_t endAt = base.find(end, from);
1392 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1393 (beginAt > endAt)) {
1394 replStr.append(base.substr(from));
1398 replStr.append(base.substr(from, beginAt - from));
1401 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1403 bool failed = idChar.getAsInteger(10, idNum);
1405 assert(!failed &&
"failed to parse integer from verbatim string");
1407 replStr.append(
"{{");
1408 Twine(idNum + 1).toVector(replStr);
1409 replStr.append(
"}}");
1411 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1418 path +=
" = {{-1}}";
1421 xmrElems.emplace_back(
1422 nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1423 ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1426 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1427 bool notFailed =
true;
1428 auto elements = vector.getElements();
1429 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1430 auto field = fromAttr(elements[i]);
1433 notFailed &= traverseField(
1434 *field,
id, path.snapshot().append(
"[" + Twine(i) +
"]"),
1435 xmrElems, interfaceBuilder);
1439 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1440 bool anyFailed =
true;
1441 for (
auto element : bundle.getElements()) {
1442 auto field = fromAttr(element);
1445 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1447 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1448 anyFailed &= traverseField(
1449 *field,
id, path.snapshot().append(
"." + name.getValue()),
1450 xmrElems, interfaceBuilder);
1455 .Default([](
auto a) {
return true; });
1458bool GrandCentralPass::traverseViewField(
1459 Attribute field, VerbatimBuilder &path,
1460 SmallVector<VerbatimXMRbuilder> &xmrElems,
1461 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1463 return TypeSwitch<Attribute, bool>(field)
1464 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1467 if (index >= view.getNumOperands()) {
1468 view.emitOpError(
"more ground types needed (")
1469 << idx <<
" so far) than view has operands ("
1470 << view.getNumOperands() <<
")";
1474 auto val = view.getOperand(index);
1475 auto tpe = type_cast<FIRRTLBaseType>(val.getType());
1478 if (!tpe.getBitWidthOrSentinel())
1484 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1485 SmallString<128> replStr;
1486 StringRef begin =
"{{";
1487 StringRef
end =
"}}";
1490 while (from < base.size()) {
1492 size_t beginAt = base.find(begin, from);
1493 size_t endAt = base.find(end, from);
1495 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1496 (beginAt > endAt)) {
1497 replStr.append(base.substr(from));
1501 replStr.append(base.substr(from, beginAt - from));
1504 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1506 bool failed = idChar.getAsInteger(10, idNum);
1508 assert(!failed &&
"failed to parse integer from verbatim string");
1510 replStr.append(
"{{");
1511 Twine(idNum + 1).toVector(replStr);
1512 replStr.append(
"}}");
1514 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1520 path +=
" = {{-1}}";
1523 auto mod = view->getParentOfType<FModuleOp>();
1524 xmrElems.emplace_back(val, getStrAndIncrementIds(path.getString()),
1525 ArrayAttr::get(&getContext(), path.getSymbols()),
1529 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1530 bool notFailed =
true;
1531 auto elements = vector.getElements();
1532 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1533 auto field = fromViewAttr(view, elements[i]);
1536 notFailed &= traverseViewField(
1537 *field, path.snapshot().append(
"[" + Twine(i) +
"]"), xmrElems,
1538 interfaceBuilder, view, idx);
1542 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1543 bool anyFailed =
true;
1544 for (
auto element : bundle.getElements()) {
1545 auto field = fromViewAttr(view, element);
1548 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1550 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1551 anyFailed &= traverseViewField(
1552 *field, path.snapshot().append(
"." + name.getValue()), xmrElems,
1553 interfaceBuilder, view, idx);
1558 .Default([](
auto a) {
return true; });
1561std::optional<TypeSum> GrandCentralPass::computeField(
1562 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1563 SmallVector<VerbatimXMRbuilder> &xmrElems,
1564 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1565 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1566 .Case<AugmentedGroundTypeAttr>(
1567 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1569 if (!traverseField(field,
id, path, xmrElems, interfaceBuilder))
1570 return std::nullopt;
1571 FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1574 auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1577 if (!tpe.isGround()) {
1578 value.getDefiningOp()->emitOpError()
1579 <<
"cannot be added to interface with id '"
1580 <<
id.getValue().getZExtValue()
1581 <<
"' because it is not a ground type";
1582 return std::nullopt;
1584 return TypeSum(IntegerType::get(getOperation().getContext(),
1585 tpe.getBitWidthOrSentinel()));
1587 .Case<AugmentedVectorTypeAttr>(
1588 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1589 auto elements = vector.getElements();
1590 if (elements.empty())
1591 llvm::report_fatal_error(
1592 "unexpected empty augmented vector in GrandCentral View");
1593 auto firstElement = fromAttr(elements[0]);
1595 return std::nullopt;
1597 *firstElement,
id, path.snapshot().append(
"[" + Twine(0) +
"]"),
1598 xmrElems, interfaceBuilder);
1600 return std::nullopt;
1602 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1603 auto subField = fromAttr(elements[i]);
1605 return std::nullopt;
1606 (void)traverseField(*subField,
id,
1607 path.snapshot().append(
"[" + Twine(i) +
"]"),
1608 xmrElems, interfaceBuilder);
1613 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1615 str.dimensions.push_back(elements.getValue().size());
1616 return TypeSum(str);
1618 .Case<AugmentedBundleTypeAttr>(
1619 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1621 traverseBundle(bundle,
id, path, xmrElems, interfaceBuilder);
1622 assert(ifaceName && *ifaceName);
1623 return VerbatimType({ifaceName->str(),
true});
1627std::optional<TypeSum> GrandCentralPass::computeViewField(
1628 Attribute field, VerbatimBuilder &path,
1629 SmallVector<VerbatimXMRbuilder> &xmrElems,
1630 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1632 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1633 .Case<AugmentedGroundTypeAttr>(
1634 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1637 if (!traverseViewField(field, path, xmrElems, interfaceBuilder,
1639 return std::nullopt;
1641 auto val = view.getOperand(index);
1642 auto tpe = dyn_cast<FIRRTLBaseType>(val.getType());
1643 if (!tpe || !tpe.isGround()) {
1644 mlir::emitError(val.getLoc(),
"cannot be added to interface, "
1645 "because it is not a ground type")
1646 .attachNote(view.getLoc())
1647 .append(
"interface part of view");
1648 return std::nullopt;
1652 IntegerType::get(&getContext(), tpe.getBitWidthOrSentinel()));
1654 .Case<AugmentedVectorTypeAttr>(
1655 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1656 auto elements = vector.getElements();
1657 if (elements.empty())
1658 llvm::report_fatal_error(
1659 "unexpected empty augmented vector in GrandCentral View");
1660 auto firstElement = fromViewAttr(view, elements[0]);
1662 return std::nullopt;
1664 *firstElement, path.snapshot().append(
"[" + Twine(0) +
"]"),
1665 xmrElems, interfaceBuilder, view, idx);
1667 return std::nullopt;
1669 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1670 auto subField = fromViewAttr(view, elements[i]);
1672 return std::nullopt;
1673 (void)traverseViewField(
1674 *subField, path.snapshot().append(
"[" + Twine(i) +
"]"),
1675 xmrElems, interfaceBuilder, view, idx);
1680 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1682 str.dimensions.push_back(elements.getValue().size());
1683 return TypeSum(str);
1685 .Case<AugmentedBundleTypeAttr>(
1686 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1687 auto ifaceName = traverseViewBundle(bundle, path, xmrElems,
1688 interfaceBuilder, view, idx);
1689 assert(ifaceName && *ifaceName);
1690 return VerbatimType({ifaceName->str(),
true});
1700std::optional<StringAttr> GrandCentralPass::traverseBundle(
1701 AugmentedBundleTypeAttr bundle, IntegerAttr
id, VerbatimBuilder &path,
1702 SmallVector<VerbatimXMRbuilder> &xmrElems,
1703 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1705 unsigned lastIndex = interfaceBuilder.size();
1706 auto iFaceName = StringAttr::get(
1707 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1708 interfaceBuilder.emplace_back(iFaceName,
id);
1710 for (
auto element : bundle.getElements()) {
1711 auto field = fromAttr(element);
1713 return std::nullopt;
1715 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1726 *field,
id, path.snapshot().append(
".").append(name.getValue()),
1727 xmrElems, interfaceBuilder);
1729 return std::nullopt;
1730 StringAttr description =
1731 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1732 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1744std::optional<StringAttr> GrandCentralPass::traverseViewBundle(
1745 AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
1746 SmallVector<VerbatimXMRbuilder> &xmrElems,
1747 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1751 if (!bundle.getDefName()) {
1752 view.emitOpError(
"missing 'defName' at top-level");
1753 return std::nullopt;
1755 if (!bundle.getElements()) {
1756 view.emitOpError(
"missing 'elements' at top-level");
1757 return std::nullopt;
1760 unsigned lastIndex = interfaceBuilder.size();
1761 auto iFaceName = StringAttr::get(
1762 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1763 interfaceBuilder.emplace_back(iFaceName, IntegerAttr() );
1765 for (
auto element : bundle.getElements()) {
1766 auto field = fromViewAttr(view, element);
1768 return std::nullopt;
1770 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1772 view.emitError(
"missing 'name' field in element of bundle: ") << element;
1773 return std::nullopt;
1785 *field, path.snapshot().append(
".").append(name.getValue()), xmrElems,
1786 interfaceBuilder, view, idx);
1788 return std::nullopt;
1789 StringAttr description =
1790 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1791 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1799igraph::ModuleOpInterface
1800GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1801 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1802 return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1804 auto *op = value.getDefiningOp();
1805 if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1806 return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1807 instance.getModuleNameAttr().getValue());
1809 return op->getParentOfType<igraph::ModuleOpInterface>();
1813void GrandCentralPass::runOnOperation() {
1816 CircuitOp circuitOp = getOperation();
1825 SmallVector<Annotation> worklist;
1826 bool removalError =
false;
1835 if (companionMode != CompanionMode::Instantiate)
1836 worklist.push_back(anno);
1841 if (maybeExtractInfo) {
1842 emitCircuitError(
"more than one 'ExtractGrandCentralAnnotation' was "
1843 "found, but exactly one must be provided");
1844 removalError = true;
1848 auto directory = anno.
getMember<StringAttr>(
"directory");
1849 auto filename = anno.
getMember<StringAttr>(
"filename");
1850 if (!directory || !filename) {
1852 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1853 "not contain 'directory' and 'filename' fields: "
1855 removalError =
true;
1858 if (directory.getValue().empty())
1859 directory = StringAttr::get(circuitOp.getContext(),
".");
1861 maybeExtractInfo = {directory, filename};
1866 if (maybeHierarchyFileYAML) {
1867 emitCircuitError(
"more than one 'GrandCentralHierarchyFileAnnotation' "
1868 "was found, but zero or one may be provided");
1869 removalError =
true;
1873 auto filename = anno.
getMember<StringAttr>(
"filename");
1876 <<
"contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1877 "that does not contain 'directory' and 'filename' fields: "
1879 removalError =
true;
1883 maybeHierarchyFileYAML = filename;
1888 testbenchDir = anno.
getMember<StringAttr>(
"dirname");
1895 return signalPassFailure();
1898 llvm::dbgs() <<
"Extraction Info:\n";
1899 if (maybeExtractInfo)
1900 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1901 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1903 llvm::dbgs() <<
" <none>\n";
1904 llvm::dbgs() <<
"DUT: ";
1905 if (
auto dut = instanceInfo->
getDut())
1906 llvm::dbgs() << dut.getModuleName() <<
"\n";
1908 llvm::dbgs() <<
"<none>\n";
1910 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1912 if (maybeHierarchyFileYAML)
1913 llvm::dbgs() << *maybeHierarchyFileYAML;
1915 llvm::dbgs() <<
"<none>";
1916 llvm::dbgs() <<
"\n";
1920 llvm::SmallMapVector<StringAttr, SmallVector<sv::InterfaceOp, 0>, 1>
1922 if (maybeHierarchyFileYAML.has_value())
1923 interfaceYAMLMap[maybeHierarchyFileYAML.value()] = {};
1926 SmallVector<ViewIntrinsicOp> views;
1927 circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); });
1932 if (worklist.empty() && views.empty()) {
1933 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
1934 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
1935 return markAllAnalysesPreserved();
1942 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1946 auto getID = [&](Operation *op,
1947 Annotation annotation) -> std::optional<IntegerAttr> {
1948 auto id = annotation.getMember<IntegerAttr>(
"id");
1951 <<
"contained a malformed "
1952 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1953 "that did not contain an 'id' field";
1954 removalError =
true;
1955 return std::nullopt;
1962 instancePaths = &instancePathCache;
1963 instanceInfo = &getAnalysis<InstanceInfo>();
1967 auto exactlyOneInstance = [&](FModuleOp op,
1968 StringRef msg) -> std::optional<InstanceOp> {
1971 switch (node->getNumUses()) {
1973 op->emitOpError() <<
"is marked as a GrandCentral '" << msg
1974 <<
"', but is never instantiated";
1975 return std::nullopt;
1977 return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1979 auto diag = op->emitOpError()
1980 <<
"is marked as a GrandCentral '" << msg
1981 <<
"', but it is instantiated more than once";
1982 for (
auto *instance : node->uses())
1983 diag.attachNote(instance->getInstance()->getLoc())
1984 <<
"it is instantiated here";
1985 return std::nullopt;
1989 nlaTable = &getAnalysis<NLATable>();
1995 DenseSet<Operation *> modulesToDelete;
1996 circuitOp.walk([&](Operation *op) {
1997 TypeSwitch<Operation *>(op)
1998 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
2002 auto maybeID = getID(op, annotation);
2006 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2007 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
2014 .Case<InstanceOp>([&](
auto op) {
2020 <<
"is marked as an interface element, but this should be "
2021 "impossible due to how the Chisel Grand Central API works";
2022 removalError =
true;
2026 .Case<MemOp>([&](
auto op) {
2031 <<
"is marked as an interface element, but this does not make "
2032 "sense (is there a scattering bug or do you have a "
2033 "malformed hand-crafted MLIR circuit?)";
2034 removalError =
true;
2042 <<
"has port '" << i
2043 <<
"' marked as an interface element, but this does not "
2044 "make sense (is there a scattering bug or do you have a "
2045 "malformed hand-crafted MLIR circuit?)";
2046 removalError =
true;
2050 .Case<FModuleOp>([&](FModuleOp op) {
2056 auto maybeID = getID(op, annotation);
2060 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2061 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
2071 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
2072 "circt.nonlocal") !=
nullptr;
2073 auto name = annotation.
getMember<StringAttr>(
"name");
2074 auto id = annotation.
getMember<IntegerAttr>(
"id");
2077 <<
"has a malformed "
2078 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2079 "not contain an 'id' field with an 'IntegerAttr' value";
2080 goto FModuleOp_error;
2084 <<
"has a malformed "
2085 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2086 "not contain a 'name' field with a 'StringAttr' value";
2087 goto FModuleOp_error;
2100 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
2102 companionIDMap[id] = {name.getValue(), op, isNonlocal};
2105 auto instance = exactlyOneInstance(op,
"companion");
2107 goto FModuleOp_error;
2110 for (
auto [i, result] :
llvm::enumerate(instance->getResults())) {
2111 if (instance->getPortDirection(i) == Direction::In)
2114 auto ty = result.getType();
2115 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
2118 <<
"companion instance cannot have output ports";
2119 goto FModuleOp_error;
2124 if (!maybeExtractInfo) {
2144 <<
"Found companion module: "
2145 << companionNode->
getModule().getModuleName() <<
"\n"
2146 <<
" submodules exclusively instantiated "
2147 "(including companion):\n";
2150 if (companionMode == CompanionMode::Drop) {
2152 OpBuilder builder(&getContext());
2153 for (
auto port : instance->getResults()) {
2154 builder.setInsertionPointAfterValue(port);
2156 builder.create<WireOp>(port.getLoc(), port.getType());
2157 port.replaceAllUsesWith(wire.getResult());
2163 if (companionMode == CompanionMode::Bind)
2164 (*instance)->setAttr(
"lowerToBind", builder.getUnitAttr());
2166 (*instance)->setAttr(
2168 hw::OutputFileAttr::getFromFilename(
2170 maybeExtractInfo->bindFilename.getValue(),
2174 for (
auto &node :
llvm::depth_first(companionNode)) {
2175 auto mod = node->getModule();
2183 if (modNode != companionNode &&
2185 modNode->getModule()))
2190 <<
" - module: " << mod.getModuleName() <<
"\n";
2193 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
2195 if (companionMode == CompanionMode::Drop) {
2196 modulesToDelete.insert(mod);
2202 if (extmodule->hasAttr(
"output_file"))
2206 hw::OutputFileAttr::getAsDirectory(
2208 maybeExtractInfo->directory.getValue()));
2214 if (companionMode == CompanionMode::Drop) {
2215 modulesToDelete.insert(mod);
2219 if (!mod->hasAttr(
"output_file")) {
2220 mod->setAttr(
"output_file",
2221 hw::OutputFileAttr::getAsDirectory(
2223 maybeExtractInfo->directory.getValue(),
2226 mod->setAttr(
"comment", builder.getStringAttr(
2227 "VCS coverage exclude_file"));
2237 <<
"unknown annotation class: " << annotation.
getDict();
2240 removalError =
true;
2247 return signalPassFailure();
2249 if (companionMode == CompanionMode::Drop) {
2250 for (
auto *mod : modulesToDelete) {
2251 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2253 DenseSet<hw::HierPathOp> nlas;
2256 for (
auto nla : nlas) {
2257 if (nla.root() == name)
2264 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2265 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2272 SmallVector<IntegerAttr> ids;
2273 auto sort = [&ids]() {
2274 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2275 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2278 for (
auto tuple : companionIDMap)
2279 ids.push_back(cast<IntegerAttr>(tuple.first));
2281 llvm::dbgs() <<
"companionIDMap:\n";
2282 for (
auto id : ids) {
2283 auto value = companionIDMap.lookup(
id);
2284 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2285 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2288 for (
auto tuple : leafMap)
2289 ids.push_back(cast<IntegerAttr>(tuple.first));
2291 llvm::dbgs() <<
"leafMap:\n";
2292 for (
auto id : ids) {
2293 auto fieldRef = leafMap.lookup(
id).field;
2296 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2297 FModuleOp
module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2298 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2299 <<
module.getName() + ">" +
2300 module.getPortName(blockArg.getArgNumber());
2302 llvm::dbgs() <<
", fieldID=" << fieldID;
2303 llvm::dbgs() <<
"\n";
2305 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2306 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2309 llvm::dbgs() <<
", fieldID=" << fieldID;
2310 llvm::dbgs() <<
"\n";
2322 companionToInterfaceMap;
2323 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2324 InterfaceElemsBuilder &rhs) {
2325 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2326 InterfaceElemsBuilder::Properties &rhs) {
2330 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2332 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2336 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2337 rhs.elementsList.begin(), compareProps);
2339 for (
auto anno : worklist) {
2340 auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.
getDict());
2344 if (!bundle.isRoot()) {
2345 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2347 removalError =
true;
2351 if (companionIDMap.count(bundle.getID()) == 0) {
2352 emitCircuitError() <<
"no companion found with 'id' value '"
2353 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2354 removalError =
true;
2360 auto companionIter = companionIDMap.lookup(bundle.getID());
2361 auto companionModule = companionIter.companion;
2362 auto symbolName = getNamespace().newName(
2363 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2364 getInterfaceName(bundle) +
"__");
2370 auto instanceSymbol =
2371 hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2372 StringAttr::get(&getContext(), symbolName));
2373 VerbatimBuilder::Base verbatimData;
2374 VerbatimBuilder verbatim(verbatimData);
2375 verbatim += instanceSymbol;
2378 SmallVector<VerbatimXMRbuilder> xmrElems;
2379 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2381 auto ifaceName = traverseBundle(bundle, bundle.getID(), verbatim, xmrElems,
2384 removalError =
true;
2388 if (companionIter.isNonlocal) {
2392 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2393 if (viewMapIter != companionToInterfaceMap.end())
2394 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2395 viewMapIter->getSecond().begin(),
2396 compareInterfaceSignal)) {
2400 companionToInterfaceMap[companionModule] = interfaceBuilder;
2403 if (interfaceBuilder.empty())
2405 auto companionBuilder =
2406 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2409 for (
auto xmrElem : xmrElems) {
2410 auto uloc = companionBuilder.getUnknownLoc();
2411 companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2414 numXMRs += xmrElems.size();
2416 sv::InterfaceOp topIface;
2417 for (
const auto &ifaceBuilder : interfaceBuilder) {
2418 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2419 auto loc = getOperation().getLoc();
2420 sv::InterfaceOp iface =
2421 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2426 companionIDMap[ifaceBuilder.id].companion) &&
2428 iface->setAttr(
"output_file",
2429 hw::OutputFileAttr::getAsDirectory(
2430 &getContext(), testbenchDir.getValue(),
2432 else if (maybeExtractInfo)
2433 iface->setAttr(
"output_file",
2434 hw::OutputFileAttr::getAsDirectory(
2435 &getContext(), getOutputDirectory().getValue(),
2437 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2438 builder.setInsertionPointToEnd(
2439 cast<sv::InterfaceOp>(iface).getBodyBlock());
2440 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2441 ifaceBuilder.iFaceName)] = iface;
2442 for (
auto elem : ifaceBuilder.elementsList) {
2444 auto uloc = builder.getUnknownLoc();
2446 auto description = elem.description;
2449 auto descriptionOp = builder.create<sv::VerbatimOp>(
2450 uloc, (
"// " + cleanupDescription(description.getValue())));
2455 if (maybeHierarchyFileYAML)
2456 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2457 builder.getStringAttr(
"description"));
2459 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2460 auto instanceOp = builder.create<sv::VerbatimOp>(
2461 uloc, str->toStr(elem.elemName.getValue()));
2465 if (maybeHierarchyFileYAML) {
2466 if (str->instantiation)
2467 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2468 builder.getStringAttr(
"instance"));
2470 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2471 builder.getStringAttr(
"unsupported"));
2472 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2473 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2474 builder.getI32ArrayAttr(str->dimensions));
2475 instanceOp->setAttr(
2476 "firrtl.grandcentral.yaml.symbol",
2477 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2482 auto tpe = std::get<Type>(elem.elemType);
2483 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2490 if (maybeHierarchyFileYAML.has_value())
2491 interfaceYAMLMap[maybeHierarchyFileYAML.value()].push_back(topIface);
2494 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2495 builder.create<sv::InterfaceInstanceOp>(
2496 getOperation().getLoc(), topIface.getInterfaceType(),
2497 companionIDMap.lookup(bundle.getID()).name,
2498 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2502 if (!maybeExtractInfo)
2508 companionIDMap[bundle.getID()].companion))
2512 for (
auto view : views) {
2513 auto bundle = view.getAugmentedType();
2515 assert(!bundle.isRoot() &&
"'id' found in firrtl.view");
2517 if (!bundle.getDefName()) {
2518 view.emitOpError(
"missing 'defName' at top-level");
2519 removalError =
true;
2522 if (!bundle.getElements()) {
2523 view.emitOpError(
"missing 'elements' at top-level");
2524 removalError =
true;
2528 auto viewParentMod = view->getParentOfType<FModuleLike>();
2529 auto symbolName = getModuleNamespace(viewParentMod)
2530 .newName(
"__" + getInterfaceName(bundle) +
"__");
2536 auto instanceSymbol =
2537 hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod),
2538 StringAttr::get(&getContext(), symbolName));
2539 VerbatimBuilder::Base verbatimData;
2540 VerbatimBuilder verbatim(verbatimData);
2541 verbatim += instanceSymbol;
2544 SmallVector<VerbatimXMRbuilder> xmrElems;
2545 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2548 auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems,
2549 interfaceBuilder, view, index);
2551 removalError =
true;
2554 if (index != view.getNumOperands()) {
2555 assert(index < view.getNumOperands() &&
2556 "this should error while consuming");
2557 removalError =
true;
2558 view.emitOpError() <<
"has too many operands: " << view.getNumOperands()
2559 <<
" operands but only " << index <<
" were needed";
2563 if (interfaceBuilder.empty())
2565 ImplicitLocOpBuilder viewBuilder(view.getLoc(), view);
2566 viewBuilder.setInsertionPointAfter(view);
2569 for (
auto xmrElem : xmrElems)
2570 viewBuilder.create<
sv::VerbatimOp>(xmrElem.str, xmrElem.val,
2572 numXMRs += xmrElems.size();
2574 sv::InterfaceOp topIface;
2575 auto containingOutputFileAttr =
2576 viewParentMod->getAttrOfType<hw::OutputFileAttr>(
"output_file");
2577 auto yamlPath = view.getYamlFileAttr();
2578 for (
const auto &ifaceBuilder : interfaceBuilder) {
2579 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2580 auto loc = getOperation().getLoc();
2581 sv::InterfaceOp iface =
2582 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2589 if (containingOutputFileAttr)
2590 iface->setAttr(
"output_file", containingOutputFileAttr);
2592 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2593 builder.setInsertionPointToEnd(
2594 cast<sv::InterfaceOp>(iface).getBodyBlock());
2595 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2596 ifaceBuilder.iFaceName)] = iface;
2597 for (
auto elem : ifaceBuilder.elementsList) {
2599 auto uloc = builder.getUnknownLoc();
2601 auto description = elem.description;
2604 auto descriptionOp = builder.create<sv::VerbatimOp>(
2605 uloc, (
"// " + cleanupDescription(description.getValue())));
2611 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2612 builder.getStringAttr(
"description"));
2614 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2615 auto instanceOp = builder.create<sv::VerbatimOp>(
2616 uloc, str->toStr(elem.elemName.getValue()));
2621 if (str->instantiation)
2622 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2623 builder.getStringAttr(
"instance"));
2625 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2626 builder.getStringAttr(
"unsupported"));
2627 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2628 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2629 builder.getI32ArrayAttr(str->dimensions));
2630 instanceOp->setAttr(
2631 "firrtl.grandcentral.yaml.symbol",
2632 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2637 auto tpe = std::get<Type>(elem.elemType);
2638 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2646 interfaceYAMLMap[yamlPath].push_back(topIface);
2650 viewBuilder.setInsertionPoint(view);
2651 viewBuilder.create<sv::InterfaceInstanceOp>(
2652 topIface.getInterfaceType(), view.getName(),
2653 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2658 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2659 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2664 return signalPassFailure();
2665 markAnalysesPreserved<NLATable>();
2668void GrandCentralPass::emitHierarchyYamlFile(
2669 StringRef yamlPath, SmallVectorImpl<sv::InterfaceOp> &intfs) {
2670 CircuitOp circuitOp = getOperation();
2672 std::string yamlString;
2673 llvm::raw_string_ostream stream(yamlString);
2674 ::yaml::Context yamlContext({interfaceMap});
2675 llvm::yaml::Output yout(stream);
2676 yamlize(yout, intfs,
true, yamlContext);
2678 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2679 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2680 ->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
2681 &getContext(), yamlPath,
2683 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
2690std::unique_ptr<mlir::Pass>
2692 auto pass = std::make_unique<GrandCentralPass>();
2693 pass->companionMode = companionMode;
assert(baseType &&"element must be base type")
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 StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static Block * getBodyBlock(FModuleLike mod)
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.
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
std::unique_ptr< mlir::Pass > createGrandCentralPass(CompanionMode companionMode=CompanionMode::Bind)
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.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
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 absolute 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)