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(SmallVectorImpl<sv::InterfaceOp> &intfs);
790static std::optional<DictionaryAttr>
792 DictionaryAttr root, StringAttr name, StringAttr defName,
793 std::optional<IntegerAttr>
id,
794 std::optional<StringAttr> description, Twine clazz,
795 StringAttr companionAttr, Twine path = {}) {
797 auto *context = state.
circuit.getContext();
798 auto loc = state.
circuit.getLoc();
815 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
817 tryGetAs<StringAttr>(refTarget, refTarget,
"circuit", loc, clazz, path);
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 (!circuitAttr || !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(
"~" + circuitAttr.getValue() +
"|" + moduleAttr.getValue() +
909 strpath +
">" + 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 = builder.create<WireOp>(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();
1183 auto *context = state.
circuit.getContext();
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");
1849 if (!directory || !filename) {
1851 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1852 "not contain 'directory' and 'filename' fields: "
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::dbgs() <<
"Extraction Info:\n";
1898 if (maybeExtractInfo)
1899 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1900 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1902 llvm::dbgs() <<
" <none>\n";
1903 llvm::dbgs() <<
"DUT: ";
1904 if (
auto dut = instanceInfo->
getDut())
1905 llvm::dbgs() << dut.getModuleName() <<
"\n";
1907 llvm::dbgs() <<
"<none>\n";
1909 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1911 if (maybeHierarchyFileYAML)
1912 llvm::dbgs() << *maybeHierarchyFileYAML;
1914 llvm::dbgs() <<
"<none>";
1915 llvm::dbgs() <<
"\n";
1919 SmallVector<ViewIntrinsicOp> views;
1920 circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); });
1925 if (worklist.empty() && views.empty()) {
1926 SmallVector<sv::InterfaceOp, 0> interfaceVec;
1927 emitHierarchyYamlFile(interfaceVec);
1928 return markAllAnalysesPreserved();
1935 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1939 auto getID = [&](Operation *op,
1940 Annotation annotation) -> std::optional<IntegerAttr> {
1941 auto id = annotation.getMember<IntegerAttr>(
"id");
1944 <<
"contained a malformed "
1945 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1946 "that did not contain an 'id' field";
1947 removalError =
true;
1948 return std::nullopt;
1955 instancePaths = &instancePathCache;
1956 instanceInfo = &getAnalysis<InstanceInfo>();
1960 auto exactlyOneInstance = [&](FModuleOp op,
1961 StringRef msg) -> std::optional<InstanceOp> {
1964 switch (node->getNumUses()) {
1966 op->emitOpError() <<
"is marked as a GrandCentral '" << msg
1967 <<
"', but is never instantiated";
1968 return std::nullopt;
1970 return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1972 auto diag = op->emitOpError()
1973 <<
"is marked as a GrandCentral '" << msg
1974 <<
"', but it is instantiated more than once";
1975 for (
auto *instance : node->uses())
1976 diag.attachNote(instance->getInstance()->getLoc())
1977 <<
"it is instantiated here";
1978 return std::nullopt;
1982 nlaTable = &getAnalysis<NLATable>();
1988 DenseSet<Operation *> modulesToDelete;
1989 circuitOp.walk([&](Operation *op) {
1990 TypeSwitch<Operation *>(op)
1991 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
1995 auto maybeID = getID(op, annotation);
1999 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2000 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
2007 .Case<InstanceOp>([&](
auto op) {
2013 <<
"is marked as an interface element, but this should be "
2014 "impossible due to how the Chisel Grand Central API works";
2015 removalError =
true;
2019 .Case<MemOp>([&](
auto op) {
2024 <<
"is marked as an interface element, but this does not make "
2025 "sense (is there a scattering bug or do you have a "
2026 "malformed hand-crafted MLIR circuit?)";
2027 removalError =
true;
2035 <<
"has port '" << i
2036 <<
"' marked as an interface element, but this does not "
2037 "make sense (is there a scattering bug or do you have a "
2038 "malformed hand-crafted MLIR circuit?)";
2039 removalError =
true;
2043 .Case<FModuleOp>([&](FModuleOp op) {
2049 auto maybeID = getID(op, annotation);
2053 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2054 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
2064 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
2065 "circt.nonlocal") !=
nullptr;
2066 auto name = annotation.
getMember<StringAttr>(
"name");
2067 auto id = annotation.
getMember<IntegerAttr>(
"id");
2070 <<
"has a malformed "
2071 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2072 "not contain an 'id' field with an 'IntegerAttr' value";
2073 goto FModuleOp_error;
2077 <<
"has a malformed "
2078 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2079 "not contain a 'name' field with a 'StringAttr' value";
2080 goto FModuleOp_error;
2093 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
2095 companionIDMap[id] = {name.getValue(), op, isNonlocal};
2098 auto instance = exactlyOneInstance(op,
"companion");
2100 goto FModuleOp_error;
2103 for (
auto [i, result] :
llvm::enumerate(instance->getResults())) {
2104 if (instance->getPortDirection(i) == Direction::In)
2107 auto ty = result.getType();
2108 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
2111 <<
"companion instance cannot have output ports";
2112 goto FModuleOp_error;
2117 if (!maybeExtractInfo) {
2137 <<
"Found companion module: "
2138 << companionNode->
getModule().getModuleName() <<
"\n"
2139 <<
" submodules exclusively instantiated "
2140 "(including companion):\n";
2143 if (companionMode == CompanionMode::Drop) {
2145 OpBuilder builder(&getContext());
2146 for (
auto port : instance->getResults()) {
2147 builder.setInsertionPointAfterValue(port);
2149 builder.create<WireOp>(port.getLoc(), port.getType());
2150 port.replaceAllUsesWith(wire.getResult());
2156 if (companionMode == CompanionMode::Bind)
2157 (*instance)->setAttr(
"lowerToBind", builder.getUnitAttr());
2159 (*instance)->setAttr(
2161 hw::OutputFileAttr::getFromFilename(
2163 maybeExtractInfo->bindFilename.getValue(),
2167 for (
auto &node :
llvm::depth_first(companionNode)) {
2168 auto mod = node->getModule();
2176 if (modNode != companionNode &&
2178 modNode->getModule()))
2183 <<
" - module: " << mod.getModuleName() <<
"\n";
2186 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
2188 if (companionMode == CompanionMode::Drop) {
2189 modulesToDelete.insert(mod);
2195 if (extmodule->hasAttr(
"output_file"))
2199 hw::OutputFileAttr::getAsDirectory(
2201 maybeExtractInfo->directory.getValue()));
2207 if (companionMode == CompanionMode::Drop) {
2208 modulesToDelete.insert(mod);
2212 if (!mod->hasAttr(
"output_file")) {
2213 mod->setAttr(
"output_file",
2214 hw::OutputFileAttr::getAsDirectory(
2216 maybeExtractInfo->directory.getValue(),
2219 mod->setAttr(
"comment", builder.getStringAttr(
2220 "VCS coverage exclude_file"));
2230 <<
"unknown annotation class: " << annotation.
getDict();
2233 removalError =
true;
2240 return signalPassFailure();
2242 if (companionMode == CompanionMode::Drop) {
2243 for (
auto *mod : modulesToDelete) {
2244 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2246 DenseSet<hw::HierPathOp> nlas;
2249 for (
auto nla : nlas) {
2250 if (nla.root() == name)
2257 SmallVector<sv::InterfaceOp, 0> interfaceVec;
2258 emitHierarchyYamlFile(interfaceVec);
2265 SmallVector<IntegerAttr> ids;
2266 auto sort = [&ids]() {
2267 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2268 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2271 for (
auto tuple : companionIDMap)
2272 ids.push_back(cast<IntegerAttr>(tuple.first));
2274 llvm::dbgs() <<
"companionIDMap:\n";
2275 for (
auto id : ids) {
2276 auto value = companionIDMap.lookup(
id);
2277 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2278 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2281 for (
auto tuple : leafMap)
2282 ids.push_back(cast<IntegerAttr>(tuple.first));
2284 llvm::dbgs() <<
"leafMap:\n";
2285 for (
auto id : ids) {
2286 auto fieldRef = leafMap.lookup(
id).field;
2289 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2290 FModuleOp
module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2291 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2292 <<
module.getName() + ">" +
2293 module.getPortName(blockArg.getArgNumber());
2295 llvm::dbgs() <<
", fieldID=" << fieldID;
2296 llvm::dbgs() <<
"\n";
2298 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2299 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2302 llvm::dbgs() <<
", fieldID=" << fieldID;
2303 llvm::dbgs() <<
"\n";
2314 SmallVector<sv::InterfaceOp, 2> interfaceVec;
2316 companionToInterfaceMap;
2317 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2318 InterfaceElemsBuilder &rhs) {
2319 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2320 InterfaceElemsBuilder::Properties &rhs) {
2324 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2326 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2330 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2331 rhs.elementsList.begin(), compareProps);
2333 for (
auto anno : worklist) {
2334 auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.
getDict());
2338 if (!bundle.isRoot()) {
2339 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2341 removalError =
true;
2345 if (companionIDMap.count(bundle.getID()) == 0) {
2346 emitCircuitError() <<
"no companion found with 'id' value '"
2347 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2348 removalError =
true;
2354 auto companionIter = companionIDMap.lookup(bundle.getID());
2355 auto companionModule = companionIter.companion;
2356 auto symbolName = getNamespace().newName(
2357 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2358 getInterfaceName(bundle) +
"__");
2364 auto instanceSymbol =
2365 hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2366 StringAttr::get(&getContext(), symbolName));
2367 VerbatimBuilder::Base verbatimData;
2368 VerbatimBuilder verbatim(verbatimData);
2369 verbatim += instanceSymbol;
2372 SmallVector<VerbatimXMRbuilder> xmrElems;
2373 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2375 auto ifaceName = traverseBundle(bundle, bundle.getID(), verbatim, xmrElems,
2378 removalError =
true;
2382 if (companionIter.isNonlocal) {
2386 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2387 if (viewMapIter != companionToInterfaceMap.end())
2388 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2389 viewMapIter->getSecond().begin(),
2390 compareInterfaceSignal)) {
2394 companionToInterfaceMap[companionModule] = interfaceBuilder;
2397 if (interfaceBuilder.empty())
2399 auto companionBuilder =
2400 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2403 for (
auto xmrElem : xmrElems) {
2404 auto uloc = companionBuilder.getUnknownLoc();
2405 companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2408 numXMRs += xmrElems.size();
2410 sv::InterfaceOp topIface;
2411 for (
const auto &ifaceBuilder : interfaceBuilder) {
2412 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2413 auto loc = getOperation().getLoc();
2414 sv::InterfaceOp iface =
2415 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2420 companionIDMap[ifaceBuilder.id].companion) &&
2422 iface->setAttr(
"output_file",
2423 hw::OutputFileAttr::getAsDirectory(
2424 &getContext(), testbenchDir.getValue(),
2426 else if (maybeExtractInfo)
2427 iface->setAttr(
"output_file",
2428 hw::OutputFileAttr::getAsDirectory(
2429 &getContext(), getOutputDirectory().getValue(),
2431 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2432 builder.setInsertionPointToEnd(
2433 cast<sv::InterfaceOp>(iface).getBodyBlock());
2434 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2435 ifaceBuilder.iFaceName)] = iface;
2436 for (
auto elem : ifaceBuilder.elementsList) {
2438 auto uloc = builder.getUnknownLoc();
2440 auto description = elem.description;
2443 auto descriptionOp = builder.create<sv::VerbatimOp>(
2444 uloc, (
"// " + cleanupDescription(description.getValue())));
2449 if (maybeHierarchyFileYAML)
2450 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2451 builder.getStringAttr(
"description"));
2453 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2454 auto instanceOp = builder.create<sv::VerbatimOp>(
2455 uloc, str->toStr(elem.elemName.getValue()));
2459 if (maybeHierarchyFileYAML) {
2460 if (str->instantiation)
2461 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2462 builder.getStringAttr(
"instance"));
2464 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2465 builder.getStringAttr(
"unsupported"));
2466 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2467 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2468 builder.getI32ArrayAttr(str->dimensions));
2469 instanceOp->setAttr(
2470 "firrtl.grandcentral.yaml.symbol",
2471 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2476 auto tpe = std::get<Type>(elem.elemType);
2477 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2484 interfaceVec.push_back(topIface);
2487 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2488 builder.create<sv::InterfaceInstanceOp>(
2489 getOperation().getLoc(), topIface.getInterfaceType(),
2490 companionIDMap.lookup(bundle.getID()).name,
2491 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2495 if (!maybeExtractInfo)
2501 companionIDMap[bundle.getID()].companion))
2505 for (
auto view : views) {
2506 auto bundle = view.getAugmentedType();
2508 assert(!bundle.isRoot() &&
"'id' found in firrtl.view");
2510 if (!bundle.getDefName()) {
2511 view.emitOpError(
"missing 'defName' at top-level");
2512 removalError =
true;
2515 if (!bundle.getElements()) {
2516 view.emitOpError(
"missing 'elements' at top-level");
2517 removalError =
true;
2521 auto viewParentMod = view->getParentOfType<FModuleLike>();
2522 auto symbolName = getModuleNamespace(viewParentMod)
2523 .newName(
"__" + getInterfaceName(bundle) +
"__");
2529 auto instanceSymbol =
2530 hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod),
2531 StringAttr::get(&getContext(), symbolName));
2532 VerbatimBuilder::Base verbatimData;
2533 VerbatimBuilder verbatim(verbatimData);
2534 verbatim += instanceSymbol;
2537 SmallVector<VerbatimXMRbuilder> xmrElems;
2538 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2541 auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems,
2542 interfaceBuilder, view, index);
2544 removalError =
true;
2547 if (index != view.getNumOperands()) {
2548 assert(index < view.getNumOperands() &&
2549 "this should error while consuming");
2550 removalError =
true;
2551 view.emitOpError() <<
"has too many operands: " << view.getNumOperands()
2552 <<
" operands but only " << index <<
" were needed";
2556 if (interfaceBuilder.empty())
2558 ImplicitLocOpBuilder viewBuilder(view.getLoc(), view);
2559 viewBuilder.setInsertionPointAfter(view);
2562 for (
auto xmrElem : xmrElems)
2563 viewBuilder.create<
sv::VerbatimOp>(xmrElem.str, xmrElem.val,
2565 numXMRs += xmrElems.size();
2567 sv::InterfaceOp topIface;
2568 for (
const auto &ifaceBuilder : interfaceBuilder) {
2569 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2570 auto loc = getOperation().getLoc();
2571 sv::InterfaceOp iface =
2572 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2577 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2578 builder.setInsertionPointToEnd(
2579 cast<sv::InterfaceOp>(iface).getBodyBlock());
2580 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2581 ifaceBuilder.iFaceName)] = iface;
2582 for (
auto elem : ifaceBuilder.elementsList) {
2584 auto uloc = builder.getUnknownLoc();
2586 auto description = elem.description;
2589 auto descriptionOp = builder.create<sv::VerbatimOp>(
2590 uloc, (
"// " + cleanupDescription(description.getValue())));
2595 if (maybeHierarchyFileYAML)
2596 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2597 builder.getStringAttr(
"description"));
2599 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2600 auto instanceOp = builder.create<sv::VerbatimOp>(
2601 uloc, str->toStr(elem.elemName.getValue()));
2605 if (maybeHierarchyFileYAML) {
2606 if (str->instantiation)
2607 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2608 builder.getStringAttr(
"instance"));
2610 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2611 builder.getStringAttr(
"unsupported"));
2612 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2613 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2614 builder.getI32ArrayAttr(str->dimensions));
2615 instanceOp->setAttr(
2616 "firrtl.grandcentral.yaml.symbol",
2617 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2622 auto tpe = std::get<Type>(elem.elemType);
2623 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2630 interfaceVec.push_back(topIface);
2634 viewBuilder.setInsertionPoint(view);
2635 viewBuilder.create<sv::InterfaceInstanceOp>(
2636 topIface.getInterfaceType(), view.getName(),
2637 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2642 emitHierarchyYamlFile(interfaceVec);
2647 return signalPassFailure();
2648 markAnalysesPreserved<NLATable>();
2651void GrandCentralPass::emitHierarchyYamlFile(
2652 SmallVectorImpl<sv::InterfaceOp> &intfs) {
2656 if (!maybeHierarchyFileYAML)
2659 CircuitOp circuitOp = getOperation();
2661 std::string yamlString;
2662 llvm::raw_string_ostream stream(yamlString);
2663 ::yaml::Context yamlContext({interfaceMap});
2664 llvm::yaml::Output yout(stream);
2665 yamlize(yout, intfs,
true, yamlContext);
2667 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2668 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2669 ->setAttr(
"output_file",
2670 hw::OutputFileAttr::getFromFilename(
2671 &getContext(), maybeHierarchyFileYAML->getValue(),
2673 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
2680std::unique_ptr<mlir::Pass>
2682 auto pass = std::make_unique<GrandCentralPass>();
2683 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)