28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/TypeSwitch.h"
32#include "llvm/Support/Debug.h"
33#include "llvm/Support/YAMLTraits.h"
36#define DEBUG_TYPE "gct"
40#define GEN_PASS_DEF_GRANDCENTRAL
41#include "circt/Dialect/FIRRTL/Passes.h.inc"
46using namespace firrtl;
59[[maybe_unused]]
static std::string noDefault(StringRef clazz) {
60 return (
"default '" + clazz +
61 "' construction is an intentionally *NOT* implemented "
62 "YAML feature (you should never be using this)")
66[[maybe_unused]]
static std::string deNorm(StringRef clazz) {
67 return (
"conversion from YAML to a '" + clazz +
68 "' is intentionally *NOT* implemented (you should not be "
69 "converting from YAML to an interface)")
84 DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
93struct DescribedSignal {
95 StringAttr description;
98 sv::InterfaceSignalOp signal;
106struct DescribedInstance {
110 StringAttr description;
113 ArrayAttr dimensions;
116 FlatSymbolRefAttr interface;
126LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
127LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
128LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
136using namespace ::
yaml;
149 std::string descriptionString;
150 llvm::raw_string_ostream stream(descriptionString);
151 SmallVector<StringRef> splits;
152 str.split(splits,
"\n");
156 substr.consume_front(
"//");
157 stream << substr.drop_while([](
auto c) {
return c ==
' '; });
159 [&]() { stream <<
"\n"; });
160 return descriptionString;
167struct MappingContextTraits<DescribedSignal,
Context> {
185 : name(op.signal.getSymNameAttr().getValue()) {
211 auto tpe = op.signal.getType();
212 while (
auto vector = dyn_cast<hw::UnpackedArrayType>(tpe)) {
213 dimensions.push_back(vector.getNumElements());
214 tpe = vector.getElementType();
216 dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
221 assert(isa<IntegerType>(tpe));
222 width = type_cast<IntegerType>(tpe).getWidth();
226 Field(IO &io) { llvm_unreachable(noDefault(
"Field").c_str()); }
230 llvm_unreachable(deNorm(
"DescribedSignal").c_str());
235 MappingNormalization<Field, DescribedSignal> keys(io, op);
236 io.mapRequired(
"name", keys->name);
237 io.mapOptional(
"description", keys->description);
238 io.mapRequired(
"dimensions", keys->dimensions);
239 io.mapRequired(
"width", keys->width);
167struct MappingContextTraits<DescribedSignal,
Context> { {
…};
248struct MappingContextTraits<DescribedInstance,
Context> {
255 std::optional<std::string> description = std::nullopt;
264 : name(op.name.getValue()), interface(op.interface) {
272 for (
auto &d : op.dimensions) {
273 auto dimension = dyn_cast<IntegerAttr>(d);
274 dimensions.push_back(dimension.getInt());
278 Instance(IO &io) { llvm_unreachable(noDefault(
"Instance").c_str()); }
281 llvm_unreachable(deNorm(
"DescribedInstance").c_str());
286 MappingNormalization<Instance, DescribedInstance> keys(io, op);
287 io.mapRequired(
"name", keys->name);
288 io.mapOptional(
"description", keys->description);
289 io.mapRequired(
"dimensions", keys->dimensions);
290 io.mapRequired(
"interface", ctx.interfaceMap[keys->interface], ctx);
248struct MappingContextTraits<DescribedInstance,
Context> { {
…};
337 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
340 StringAttr description = {};
342 for (
auto &op : op.getBodyBlock()->getOperations()) {
343 TypeSwitch<Operation *>(&op)
346 .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
347 auto tpe = op->getAttrOfType<StringAttr>(
348 "firrtl.grandcentral.yaml.type");
352 if (tpe.getValue() ==
"description") {
353 description = op.getFormatStringAttr();
358 if (tpe.getValue() ==
"unsupported") {
365 auto name = op->getAttrOfType<StringAttr>(
366 "firrtl.grandcentral.yaml.name");
367 auto dimensions = op->getAttrOfType<ArrayAttr>(
368 "firrtl.grandcentral.yaml.dimensions");
369 auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
370 "firrtl.grandcentral.yaml.symbol");
372 DescribedInstance({name, description, dimensions, symbol}));
376 .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
377 fields.push_back(DescribedSignal({description, op}));
337 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
…}
384 Interface(IO &io) { llvm_unreachable(noDefault(
"Interface").c_str()); }
388 llvm_unreachable(deNorm(
"sv::InterfaceOp").c_str());
301 struct Interface { {
…};
393 MappingNormalization<Interface, sv::InterfaceOp> keys(io, op);
394 io.mapRequired(
"name", keys->name);
395 io.mapRequired(
"fields", keys->fields, ctx);
396 io.mapRequired(
"instances", keys->instances, ctx);
426struct VerbatimBuilder {
428 SmallString<128> string;
429 SmallVector<Attribute> symbols;
430 VerbatimBuilder builder() {
return VerbatimBuilder(*
this); }
431 operator VerbatimBuilder() {
return builder(); }
436 VerbatimBuilder(Base &base)
437 : base(base), stringBaseSize(base.string.size()),
438 symbolsBaseSize(base.symbols.size()) {}
443 base.string.resize(stringBaseSize);
444 base.symbols.resize(symbolsBaseSize);
448 VerbatimBuilder(
const VerbatimBuilder &) =
delete;
449 VerbatimBuilder &operator=(
const VerbatimBuilder &) =
delete;
454 VerbatimBuilder snapshot() {
return VerbatimBuilder(base); }
457 StringRef getString()
const {
return base.string; }
459 ArrayRef<Attribute> getSymbols()
const {
return base.symbols; }
462 VerbatimBuilder &
append(
char c) {
463 base.string.push_back(c);
468 VerbatimBuilder &
append(
const Twine &twine) {
469 twine.toVector(base.string);
474 VerbatimBuilder &
append(Attribute symbol) {
475 unsigned id = base.symbols.size();
476 base.symbols.push_back(symbol);
477 append(
"{{" + Twine(
id) +
"}}");
481 VerbatimBuilder &operator+=(
char c) {
return append(c); }
482 VerbatimBuilder &operator+=(
const Twine &twine) {
return append(twine); }
483 VerbatimBuilder &operator+=(Attribute symbol) {
return append(symbol); }
487 size_t stringBaseSize;
488 size_t symbolsBaseSize;
503 SmallVector<int32_t, 4> dimensions = {};
506 std::string toStr(StringRef name) {
507 SmallString<64> stringType(str);
508 stringType.append(
" ");
509 stringType.append(name);
510 for (
auto d :
llvm::reverse(dimensions)) {
511 stringType.append(
"[");
512 stringType.append(Twine(d).str());
513 stringType.append(
"]");
516 stringType.append(
"()");
517 stringType.append(
";");
518 return std::string(stringType);
524using TypeSum = std::variant<VerbatimType, Type>;
527struct ExtractionInfo {
530 StringAttr directory = {};
535 StringAttr bindFilename = {};
539struct CompanionInfo {
550 FlatSymbolRefAttr nlaSym;
554struct VerbatimXMRbuilder {
558 FModuleOp companionMod;
559 VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
560 FModuleOp companionMod)
561 : val(val), str(str), syms(syms), companionMod(companionMod) {}
566struct InterfaceElemsBuilder {
567 StringAttr iFaceName;
570 StringAttr description;
573 Properties(StringAttr des, StringAttr name, TypeSum &elemType)
574 : description(des), elemName(name), elemType(elemType) {}
576 SmallVector<Properties> elementsList;
577 InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr
id)
578 : iFaceName(iFaceName), id(id) {}
593struct GrandCentralPass
594 :
public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
597 void runOnOperation()
override;
604 DenseMap<Attribute, FieldAndNLA> leafMap;
607 DenseMap<Attribute, CompanionInfo> companionIDMap;
613 StringAttr testbenchDir;
620 std::optional<Attribute> fromAttr(Attribute attr);
624 bool traverseField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
625 SmallVector<VerbatimXMRbuilder> &xmrElems,
626 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
631 std::optional<TypeSum>
632 computeField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
633 SmallVector<VerbatimXMRbuilder> &xmrElems,
634 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
639 std::optional<StringAttr>
640 traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr
id,
641 VerbatimBuilder &path,
642 SmallVector<VerbatimXMRbuilder> &xmrElems,
643 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
646 igraph::ModuleOpInterface getEnclosingModule(Value value,
647 FlatSymbolRefAttr sym = {});
653 InFlightDiagnostic emitCircuitError(StringRef message = {}) {
654 return emitError(getOperation().
getLoc(),
"'firrtl.circuit' op " + message);
662 std::optional<Attribute> fromViewAttr(ViewIntrinsicOp view, Attribute attr);
666 bool traverseViewField(Attribute field, VerbatimBuilder &path,
667 SmallVector<VerbatimXMRbuilder> &xmrElems,
668 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
669 ViewIntrinsicOp view,
size_t &idx);
674 std::optional<TypeSum>
675 computeViewField(Attribute field, VerbatimBuilder &path,
676 SmallVector<VerbatimXMRbuilder> &xmrElems,
677 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
678 ViewIntrinsicOp view,
size_t &idx);
683 std::optional<StringAttr>
684 traverseViewBundle(AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
685 SmallVector<VerbatimXMRbuilder> &xmrElems,
686 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
687 ViewIntrinsicOp view,
size_t &idx);
692 std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) {
693 return (bundleType.getDefName().getValue()).str();
698 std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
702 std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
704 StringAttr getOutputDirectory() {
705 if (maybeExtractInfo)
706 return maybeExtractInfo->directory;
725 std::optional<CircuitNamespace> circuitNamespace;
729 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
734 if (!circuitNamespace)
736 return *circuitNamespace;
741 return moduleNamespaces.try_emplace(module, module).first->second;
746 std::optional<SymbolTable *> symbolTable;
750 SymbolTable &getSymbolTable() {
752 symbolTable = &getAnalysis<SymbolTable>();
753 return **symbolTable;
761 std::string cleanupDescription(StringRef description) {
765 std::tie(head, description) = description.split(
"\n");
767 if (!description.empty())
769 }
while (!description.empty());
770 return std::string(out);
774 DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
777 void emitHierarchyYamlFile(StringRef yamlPath,
778 SmallVectorImpl<sv::InterfaceOp> &intfs);
792static std::optional<DictionaryAttr>
794 DictionaryAttr root, StringAttr name, StringAttr defName,
795 std::optional<IntegerAttr>
id,
796 std::optional<StringAttr> description, Twine clazz,
797 StringAttr companionAttr, Twine path = {}) {
799 auto *context = state.
circuit.getContext();
800 auto loc = state.
circuit.getLoc();
816 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
818 tryGetAs<StringAttr>(refTarget, refTarget,
"module", loc, clazz, path);
820 tryGetAs<ArrayAttr>(refTarget, refTarget,
"path", loc, clazz, path);
821 auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget,
"component",
823 if (!moduleAttr || !pathAttr || !componentAttr)
827 SmallString<32> strpath;
828 for (
auto p : pathAttr) {
829 auto dict = dyn_cast_or_null<DictionaryAttr>(p);
831 mlir::emitError(loc,
"annotation '" + clazz +
832 " has invalid type (expected DictionaryAttr)");
836 tryGetAs<DictionaryAttr>(dict, dict,
"_1", loc, clazz, path);
838 tryGetAs<DictionaryAttr>(dict, dict,
"_2", loc, clazz, path);
839 if (!instHolder || !modHolder) {
840 mlir::emitError(loc,
"annotation '" + clazz +
841 " has invalid type (expected DictionaryAttr)");
844 auto inst = tryGetAs<StringAttr>(instHolder, instHolder,
"value", loc,
847 tryGetAs<StringAttr>(modHolder, modHolder,
"value", loc, clazz, path);
849 mlir::emitError(loc,
"annotation '" + clazz +
850 " has invalid type (expected DictionaryAttr)");
853 strpath +=
"/" + inst.getValue().str() +
":" + mod.getValue().str();
856 SmallVector<Attribute> componentAttrs;
857 SmallString<32> componentStr;
858 for (
size_t i = 0, e = componentAttr.size(); i != e; ++i) {
859 auto cPath = (path +
".component[" + Twine(i) +
"]").str();
860 auto component = componentAttr[i];
861 auto dict = dyn_cast_or_null<DictionaryAttr>(component);
863 mlir::emitError(loc,
"annotation '" + clazz +
"' with path '" + cPath +
864 " has invalid type (expected DictionaryAttr)");
868 tryGetAs<StringAttr>(dict, refTarget,
"class", loc, clazz, cPath);
872 auto value = dict.get(
"value");
875 if (
auto field = dyn_cast<StringAttr>(value)) {
876 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Field" &&
877 "A StringAttr target token must be found with a subfield target "
879 componentStr.append((Twine(
".") + field.getValue()).str());
884 if (
auto index = dyn_cast<IntegerAttr>(value)) {
885 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Index" &&
886 "An IntegerAttr target token must be found with a subindex "
889 (Twine(
"[") + Twine(index.getValue().getZExtValue()) +
"]").str());
894 "Annotation '" + clazz +
"' with path '" + cPath +
895 ".value has unexpected type (should be StringAttr "
896 "for subfield or IntegerAttr for subindex).")
898 <<
"The value received was: " << value <<
"\n";
903 tryGetAs<StringAttr>(refTarget, refTarget,
"ref", loc, clazz, path);
907 return (Twine(
"~|" + moduleAttr.getValue() + strpath +
">" +
908 refAttr.getValue()) +
914 tryGetAs<StringAttr>(augmentedType, root,
"class", loc, clazz, path);
917 StringRef classBase = classAttr.getValue();
918 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented")) {
920 "the 'class' was expected to start with "
921 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
922 classAttr.getValue() +
"' (Did you misspell it?)")
924 <<
"see annotation: " << augmentedType;
931 if (classBase ==
"BundleType") {
933 tryGetAs<StringAttr>(augmentedType, root,
"defName", loc, clazz, path);
941 SmallVector<Attribute> elements;
943 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
946 for (
size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
947 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
951 "Annotation '" + Twine(clazz) +
"' with path '.elements[" +
953 "]' contained an unexpected type (expected a DictionaryAttr).")
955 <<
"The received element was: " << elementsAttr[i] <<
"\n";
958 auto ePath = (path +
".elements[" + Twine(i) +
"]").str();
959 auto name = tryGetAs<StringAttr>(field, root,
"name", loc, clazz, ePath);
961 tryGetAs<DictionaryAttr>(field, root,
"tpe", loc, clazz, ePath);
964 std::optional<StringAttr> description;
965 if (
auto maybeDescription = field.get(
"description"))
966 description = cast<StringAttr>(maybeDescription);
968 state, tpe, root, name, defName, std::nullopt, description, clazz,
969 companionAttr, path +
"_" + name.getValue());
976 if (
auto maybeDescription = field.get(
"description"))
977 attrs.append(
"description", cast<StringAttr>(maybeDescription));
978 attrs.append(
"name", name);
979 auto tpeClass = tpe.getAs<StringAttr>(
"class");
981 mlir::emitError(loc,
"missing 'class' key in") << tpe;
984 attrs.append(
"tpe", tpeClass);
985 elements.push_back(*eltAttr);
991 attrs.append(
"class", classAttr);
992 attrs.append(
"defName", defName);
994 attrs.append(
"description", *description);
995 attrs.append(
"elements", ArrayAttr::get(context, elements));
997 attrs.append(
"id", *
id);
998 attrs.append(
"name", name);
999 return DictionaryAttr::getWithSorted(context, attrs);
1008 if (classBase ==
"GroundType") {
1009 auto augRef = augmentedType.getAs<DictionaryAttr>(
"ref");
1011 mlir::emitError(loc,
"missing 'ref' key in ") << augmentedType;
1012 return std::nullopt;
1014 auto maybeTarget = refToTarget(augRef);
1016 mlir::emitError(loc,
"Failed to parse ReferenceTarget").attachNote()
1017 <<
"See the full Annotation here: " << root;
1018 return std::nullopt;
1021 auto id = state.
newID();
1023 auto target = *maybeTarget;
1025 NamedAttrList elementIface, elementScattered;
1028 elementIface.append(
"class", classAttr);
1030 elementIface.append(
"description", *description);
1031 elementIface.append(
"id",
id);
1032 elementIface.append(
"name", name);
1034 elementScattered.append(
"class", classAttr);
1035 elementScattered.append(
"id",
id);
1037 auto targetAttr = StringAttr::get(context, target);
1040 if (!xmrSrcTarget) {
1041 mlir::emitError(loc,
"Failed to resolve target ") << targetAttr;
1042 return std::nullopt;
1048 auto sourceRef = xmrSrcTarget->ref;
1049 ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
1050 std::optional<Value> source =
1051 TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1054 .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1055 -> std::optional<Value> {
1056 auto portNo = sourceRef.getImpl().getPortNo();
1057 if (xmrSrcTarget->instances.empty()) {
1059 if (paths.size() > 1) {
1061 "cannot resolve a unique instance path from the "
1062 "external module '")
1063 << targetAttr <<
"'";
1064 return std::nullopt;
1066 auto *it = xmrSrcTarget->instances.begin();
1067 for (
auto inst : paths.back()) {
1068 xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1072 auto lastInst = xmrSrcTarget->instances.pop_back_val();
1073 builder.setInsertionPointAfter(lastInst);
1075 xmrSrcTarget->fieldIdx);
1079 .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1080 builder.setInsertionPointToEnd(module.getBodyBlock());
1081 auto portNum = sourceRef.getImpl().getPortNo();
1083 xmrSrcTarget->fieldIdx);
1086 .Default([&](Operation *op) -> std::optional<Value> {
1087 auto module = cast<FModuleOp>(sourceRef.getModule());
1088 builder.setInsertionPointToEnd(module.getBodyBlock());
1089 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1091 if (is && is.getTargetResult())
1093 xmrSrcTarget->fieldIdx);
1094 if (sourceRef.getOp()->getNumResults() != 1) {
1096 <<
"cannot be used as a target of the Grand Central View \""
1097 << defName.getValue()
1098 <<
"\" because it does not have exactly one result";
1099 return std::nullopt;
1102 xmrSrcTarget->fieldIdx);
1107 return std::nullopt;
1119 builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1123 auto sinkType = source->getType();
1124 if (
auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1125 sinkType = baseSinkType.getPassiveType();
1126 auto sink = WireOp::create(builder, sinkType, name);
1129 annotations.addAnnotations(
1130 {DictionaryAttr::getWithSorted(context, elementScattered)});
1131 annotations.applyToOperation(sink);
1136 (path +
"__bore").str(),
1137 WiringProblem::RefTypeUsage::Prefer});
1139 return DictionaryAttr::getWithSorted(context, elementIface);
1144 if (classBase ==
"VectorType") {
1146 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
1148 return std::nullopt;
1149 SmallVector<Attribute> elements;
1150 for (
auto [i, elt] :
llvm::enumerate(elementsAttr)) {
1153 StringAttr::get(context,
""),
id, std::nullopt,
1154 clazz, companionAttr, path +
"_" + Twine(i));
1156 return std::nullopt;
1157 elements.push_back(*eltAttr);
1159 NamedAttrList attrs;
1160 attrs.append(
"class", classAttr);
1162 attrs.append(
"description", *description);
1163 attrs.append(
"elements", ArrayAttr::get(context, elements));
1164 attrs.append(
"name", name);
1165 return DictionaryAttr::getWithSorted(context, attrs);
1170 mlir::emitError(loc,
"found unknown AugmentedType '" + classAttr.getValue() +
1171 "' (Did you misspell it?)")
1173 <<
"see annotation: " << augmentedType;
1174 return std::nullopt;
1178 DictionaryAttr anno,
1181 auto id = state.
newID();
1182 auto *context = state.
circuit.getContext();
1183 auto loc = state.
circuit.getLoc();
1184 NamedAttrList companionAttrs;
1186 companionAttrs.append(
"id",
id);
1188 tryGetAs<DictionaryAttr>(anno, anno,
"view", loc,
viewAnnoClass);
1191 auto name = tryGetAs<StringAttr>(anno, anno,
"name", loc,
viewAnnoClass);
1194 companionAttrs.append(
"name", name);
1195 auto companionAttr =
1196 tryGetAs<StringAttr>(anno, anno,
"companion", loc,
viewAnnoClass);
1199 companionAttrs.append(
"target", companionAttr);
1204 companionAttr, Twine(name));
1219std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1220 auto dict = dyn_cast<DictionaryAttr>(attr);
1222 emitCircuitError() <<
"attribute is not a dictionary: " << attr <<
"\n";
1223 return std::nullopt;
1226 auto clazz = dict.getAs<StringAttr>(
"class");
1228 emitCircuitError() <<
"missing 'class' key in " << dict <<
"\n";
1229 return std::nullopt;
1232 auto classBase = clazz.getValue();
1233 classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented");
1235 if (classBase ==
"BundleType") {
1236 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1237 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1238 emitCircuitError() <<
"has an invalid AugmentedBundleType that does not "
1239 "contain 'defName' and 'elements' fields: "
1241 }
else if (classBase ==
"VectorType") {
1242 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1243 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1244 emitCircuitError() <<
"has an invalid AugmentedVectorType that does not "
1245 "contain 'name' and 'elements' fields: "
1247 }
else if (classBase ==
"GroundType") {
1248 auto id = dict.getAs<IntegerAttr>(
"id");
1249 auto name = dict.getAs<StringAttr>(
"name");
1250 if (
id && leafMap.count(
id) && name)
1251 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1253 emitCircuitError() <<
"has an invalid AugmentedGroundType that does not "
1254 "contain 'id' and 'name' fields: "
1256 if (
id && !leafMap.count(
id))
1257 emitCircuitError() <<
"has an AugmentedGroundType with 'id == "
1258 <<
id.getValue().getZExtValue()
1259 <<
"' that does not have a scattered leaf to connect "
1260 "to in the circuit "
1261 "(was the leaf deleted or constant prop'd away?)";
1263 emitCircuitError() <<
"has an invalid AugmentedType";
1265 return std::nullopt;
1268std::optional<Attribute> GrandCentralPass::fromViewAttr(ViewIntrinsicOp view,
1270 auto dict = dyn_cast<DictionaryAttr>(attr);
1272 view.emitError() <<
"attribute is not a dictionary: " << attr;
1273 return std::nullopt;
1276 auto clazz = dict.getAs<StringAttr>(
"class");
1278 view.emitError() <<
"missing 'class' key in " << dict;
1279 return std::nullopt;
1282 auto classBase = clazz.getValue();
1283 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented"))
1284 view.emitOpError() <<
"has an invalid AugmentedType class '" << classBase
1286 else if (classBase ==
"BundleType") {
1287 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1288 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1289 view.emitError() <<
"has an invalid AugmentedBundleType that does not "
1290 "contain 'defName' and 'elements' fields: "
1292 }
else if (classBase ==
"VectorType") {
1293 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1294 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1295 view.emitError() <<
"has an invalid AugmentedVectorType that does not "
1296 "contain 'name' and 'elements' fields: "
1298 }
else if (classBase ==
"GroundType") {
1299 auto id = dict.getAs<IntegerAttr>(
"id");
1303 <<
"has 'id' field which is only for old annotation encoding")
1305 <<
"id within GroundType attribute: " << dict;
1306 return std::nullopt;
1308 auto name = dict.getAs<StringAttr>(
"name");
1310 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1311 view.emitError() <<
"has an invalid AugmentedGroundType that does not "
1312 "contain 'name' field: "
1315 view.emitOpError() <<
"has an invalid AugmentedType '" << classBase <<
"'";
1317 return std::nullopt;
1320bool GrandCentralPass::traverseField(
1321 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1322 SmallVector<VerbatimXMRbuilder> &xmrElems,
1323 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1324 return TypeSwitch<Attribute, bool>(field)
1325 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1326 auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1329 nla = nlaTable->
getNLA(sym.getAttr());
1330 Value leafValue = fieldRef.getValue();
1331 assert(leafValue &&
"leafValue not found");
1333 auto companionModule = companionIDMap.lookup(
id).companion;
1334 igraph::ModuleOpInterface enclosing =
1335 getEnclosingModule(leafValue, sym);
1337 auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1340 if (!tpe.getBitWidthOrSentinel())
1352 auto *nodeOp = leafValue.getDefiningOp();
1353 if (companionModule != enclosing) {
1354 auto diag = companionModule->emitError()
1355 <<
"Grand Central View \""
1356 << companionIDMap.lookup(
id).name
1357 <<
"\" is invalid because a leaf is not inside the "
1359 diag.attachNote(leafValue.getLoc())
1360 <<
"the leaf value is declared here";
1362 auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1363 diag.attachNote(leafModule.getLoc())
1364 <<
"the leaf value is inside this module";
1369 if (!isa<NodeOp>(nodeOp)) {
1370 emitError(leafValue.getLoc())
1371 <<
"Grand Central View \"" << companionIDMap.lookup(
id).name
1372 <<
"\" has an invalid leaf value (this must be a node)";
1379 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1380 SmallString<128> replStr;
1381 StringRef begin =
"{{";
1382 StringRef
end =
"}}";
1385 while (from < base.size()) {
1387 size_t beginAt = base.find(begin, from);
1388 size_t endAt = base.find(end, from);
1390 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1391 (beginAt > endAt)) {
1392 replStr.append(base.substr(from));
1396 replStr.append(base.substr(from, beginAt - from));
1399 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1401 bool failed = idChar.getAsInteger(10, idNum);
1403 assert(!failed &&
"failed to parse integer from verbatim string");
1405 replStr.append(
"{{");
1406 Twine(idNum + 1).toVector(replStr);
1407 replStr.append(
"}}");
1409 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1416 path +=
" = {{-1}}";
1419 xmrElems.emplace_back(
1420 nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1421 ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1424 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1425 bool notFailed =
true;
1426 auto elements = vector.getElements();
1427 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1428 auto field = fromAttr(elements[i]);
1431 notFailed &= traverseField(
1432 *field,
id, path.snapshot().append(
"[" + Twine(i) +
"]"),
1433 xmrElems, interfaceBuilder);
1437 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1438 bool anyFailed =
true;
1439 for (
auto element : bundle.getElements()) {
1440 auto field = fromAttr(element);
1443 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1445 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1446 anyFailed &= traverseField(
1447 *field,
id, path.snapshot().append(
"." + name.getValue()),
1448 xmrElems, interfaceBuilder);
1453 .Default([](
auto a) {
return true; });
1456bool GrandCentralPass::traverseViewField(
1457 Attribute field, VerbatimBuilder &path,
1458 SmallVector<VerbatimXMRbuilder> &xmrElems,
1459 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1461 return TypeSwitch<Attribute, bool>(field)
1462 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1465 if (index >= view.getNumOperands()) {
1466 view.emitOpError(
"more ground types needed (")
1467 << idx <<
" so far) than view has operands ("
1468 << view.getNumOperands() <<
")";
1472 auto val = view.getOperand(index);
1473 auto tpe = type_cast<FIRRTLBaseType>(val.getType());
1476 if (!tpe.getBitWidthOrSentinel())
1482 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1483 SmallString<128> replStr;
1484 StringRef begin =
"{{";
1485 StringRef
end =
"}}";
1488 while (from < base.size()) {
1490 size_t beginAt = base.find(begin, from);
1491 size_t endAt = base.find(end, from);
1493 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1494 (beginAt > endAt)) {
1495 replStr.append(base.substr(from));
1499 replStr.append(base.substr(from, beginAt - from));
1502 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1504 bool failed = idChar.getAsInteger(10, idNum);
1506 assert(!failed &&
"failed to parse integer from verbatim string");
1508 replStr.append(
"{{");
1509 Twine(idNum + 1).toVector(replStr);
1510 replStr.append(
"}}");
1512 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1518 path +=
" = {{-1}}";
1521 auto mod = view->getParentOfType<FModuleOp>();
1522 xmrElems.emplace_back(val, getStrAndIncrementIds(path.getString()),
1523 ArrayAttr::get(&getContext(), path.getSymbols()),
1527 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1528 bool notFailed =
true;
1529 auto elements = vector.getElements();
1530 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1531 auto field = fromViewAttr(view, elements[i]);
1534 notFailed &= traverseViewField(
1535 *field, path.snapshot().append(
"[" + Twine(i) +
"]"), xmrElems,
1536 interfaceBuilder, view, idx);
1540 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1541 bool anyFailed =
true;
1542 for (
auto element : bundle.getElements()) {
1543 auto field = fromViewAttr(view, element);
1546 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1548 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1549 anyFailed &= traverseViewField(
1550 *field, path.snapshot().append(
"." + name.getValue()), xmrElems,
1551 interfaceBuilder, view, idx);
1556 .Default([](
auto a) {
return true; });
1559std::optional<TypeSum> GrandCentralPass::computeField(
1560 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1561 SmallVector<VerbatimXMRbuilder> &xmrElems,
1562 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1563 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1564 .Case<AugmentedGroundTypeAttr>(
1565 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1567 if (!traverseField(field,
id, path, xmrElems, interfaceBuilder))
1568 return std::nullopt;
1569 FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1572 auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1575 if (!tpe.isGround()) {
1576 value.getDefiningOp()->emitOpError()
1577 <<
"cannot be added to interface with id '"
1578 <<
id.getValue().getZExtValue()
1579 <<
"' because it is not a ground type";
1580 return std::nullopt;
1582 return TypeSum(IntegerType::get(getOperation().getContext(),
1583 tpe.getBitWidthOrSentinel()));
1585 .Case<AugmentedVectorTypeAttr>(
1586 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1587 auto elements = vector.getElements();
1588 if (elements.empty())
1589 llvm::report_fatal_error(
1590 "unexpected empty augmented vector in GrandCentral View");
1591 auto firstElement = fromAttr(elements[0]);
1593 return std::nullopt;
1595 *firstElement,
id, path.snapshot().append(
"[" + Twine(0) +
"]"),
1596 xmrElems, interfaceBuilder);
1598 return std::nullopt;
1600 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1601 auto subField = fromAttr(elements[i]);
1603 return std::nullopt;
1604 (void)traverseField(*subField,
id,
1605 path.snapshot().append(
"[" + Twine(i) +
"]"),
1606 xmrElems, interfaceBuilder);
1611 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1613 str.dimensions.push_back(elements.getValue().size());
1614 return TypeSum(str);
1616 .Case<AugmentedBundleTypeAttr>(
1617 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1619 traverseBundle(bundle,
id, path, xmrElems, interfaceBuilder);
1620 assert(ifaceName && *ifaceName);
1621 return VerbatimType({ifaceName->str(),
true});
1625std::optional<TypeSum> GrandCentralPass::computeViewField(
1626 Attribute field, VerbatimBuilder &path,
1627 SmallVector<VerbatimXMRbuilder> &xmrElems,
1628 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1630 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1631 .Case<AugmentedGroundTypeAttr>(
1632 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1635 if (!traverseViewField(field, path, xmrElems, interfaceBuilder,
1637 return std::nullopt;
1639 auto val = view.getOperand(index);
1640 auto tpe = dyn_cast<FIRRTLBaseType>(val.getType());
1641 if (!tpe || !tpe.isGround()) {
1642 mlir::emitError(val.getLoc(),
"cannot be added to interface, "
1643 "because it is not a ground type")
1644 .attachNote(view.getLoc())
1645 .append(
"interface part of view");
1646 return std::nullopt;
1650 IntegerType::get(&getContext(), tpe.getBitWidthOrSentinel()));
1652 .Case<AugmentedVectorTypeAttr>(
1653 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1654 auto elements = vector.getElements();
1655 if (elements.empty())
1656 llvm::report_fatal_error(
1657 "unexpected empty augmented vector in GrandCentral View");
1658 auto firstElement = fromViewAttr(view, elements[0]);
1660 return std::nullopt;
1662 *firstElement, path.snapshot().append(
"[" + Twine(0) +
"]"),
1663 xmrElems, interfaceBuilder, view, idx);
1665 return std::nullopt;
1667 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1668 auto subField = fromViewAttr(view, elements[i]);
1670 return std::nullopt;
1671 (void)traverseViewField(
1672 *subField, path.snapshot().append(
"[" + Twine(i) +
"]"),
1673 xmrElems, interfaceBuilder, view, idx);
1678 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1680 str.dimensions.push_back(elements.getValue().size());
1681 return TypeSum(str);
1683 .Case<AugmentedBundleTypeAttr>(
1684 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1685 auto ifaceName = traverseViewBundle(bundle, path, xmrElems,
1686 interfaceBuilder, view, idx);
1687 assert(ifaceName && *ifaceName);
1688 return VerbatimType({ifaceName->str(),
true});
1698std::optional<StringAttr> GrandCentralPass::traverseBundle(
1699 AugmentedBundleTypeAttr bundle, IntegerAttr
id, VerbatimBuilder &path,
1700 SmallVector<VerbatimXMRbuilder> &xmrElems,
1701 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1703 unsigned lastIndex = interfaceBuilder.size();
1704 auto iFaceName = StringAttr::get(
1705 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1706 interfaceBuilder.emplace_back(iFaceName,
id);
1708 for (
auto element : bundle.getElements()) {
1709 auto field = fromAttr(element);
1711 return std::nullopt;
1713 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1724 *field,
id, path.snapshot().append(
".").append(name.getValue()),
1725 xmrElems, interfaceBuilder);
1727 return std::nullopt;
1728 StringAttr description =
1729 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1730 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1742std::optional<StringAttr> GrandCentralPass::traverseViewBundle(
1743 AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
1744 SmallVector<VerbatimXMRbuilder> &xmrElems,
1745 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1749 if (!bundle.getDefName()) {
1750 view.emitOpError(
"missing 'defName' at top-level");
1751 return std::nullopt;
1753 if (!bundle.getElements()) {
1754 view.emitOpError(
"missing 'elements' at top-level");
1755 return std::nullopt;
1758 unsigned lastIndex = interfaceBuilder.size();
1759 auto iFaceName = StringAttr::get(
1760 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1761 interfaceBuilder.emplace_back(iFaceName, IntegerAttr() );
1763 for (
auto element : bundle.getElements()) {
1764 auto field = fromViewAttr(view, element);
1766 return std::nullopt;
1768 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1770 view.emitError(
"missing 'name' field in element of bundle: ") << element;
1771 return std::nullopt;
1783 *field, path.snapshot().append(
".").append(name.getValue()), xmrElems,
1784 interfaceBuilder, view, idx);
1786 return std::nullopt;
1787 StringAttr description =
1788 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1789 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1797igraph::ModuleOpInterface
1798GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1799 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1800 return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1802 auto *op = value.getDefiningOp();
1803 if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1804 return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1805 instance.getModuleNameAttr().getValue());
1807 return op->getParentOfType<igraph::ModuleOpInterface>();
1811void GrandCentralPass::runOnOperation() {
1814 CircuitOp circuitOp = getOperation();
1823 SmallVector<Annotation> worklist;
1824 bool removalError =
false;
1833 if (companionMode != CompanionMode::Instantiate)
1834 worklist.push_back(anno);
1839 if (maybeExtractInfo) {
1840 emitCircuitError(
"more than one 'ExtractGrandCentralAnnotation' was "
1841 "found, but exactly one must be provided");
1842 removalError = true;
1846 auto directory = anno.
getMember<StringAttr>(
"directory");
1847 auto filename = anno.
getMember<StringAttr>(
"filename");
1848 if (!directory || !filename) {
1850 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1851 "not contain 'directory' and 'filename' fields: "
1853 removalError =
true;
1856 if (directory.getValue().empty())
1857 directory = StringAttr::get(circuitOp.getContext(),
".");
1859 maybeExtractInfo = {directory, filename};
1864 if (maybeHierarchyFileYAML) {
1865 emitCircuitError(
"more than one 'GrandCentralHierarchyFileAnnotation' "
1866 "was found, but zero or one may be provided");
1867 removalError =
true;
1871 auto filename = anno.
getMember<StringAttr>(
"filename");
1874 <<
"contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1875 "that does not contain 'directory' and 'filename' fields: "
1877 removalError =
true;
1881 maybeHierarchyFileYAML = filename;
1886 testbenchDir = anno.
getMember<StringAttr>(
"dirname");
1893 return signalPassFailure();
1896 llvm::SmallMapVector<StringAttr, SmallVector<sv::InterfaceOp, 0>, 1>
1898 if (maybeHierarchyFileYAML.has_value())
1899 interfaceYAMLMap[maybeHierarchyFileYAML.value()] = {};
1902 SmallVector<ViewIntrinsicOp> views;
1903 circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); });
1907 instancePaths = &instancePathCache;
1908 instanceInfo = &getAnalysis<InstanceInfo>();
1911 llvm::dbgs() <<
"Extraction Info:\n";
1912 if (maybeExtractInfo)
1913 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1914 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1916 llvm::dbgs() <<
" <none>\n";
1917 llvm::dbgs() <<
"DUT: ";
1918 if (
auto dut = instanceInfo->
getDut())
1919 llvm::dbgs() << dut.getModuleName() <<
"\n";
1921 llvm::dbgs() <<
"<none>\n";
1923 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1925 if (maybeHierarchyFileYAML)
1926 llvm::dbgs() << *maybeHierarchyFileYAML;
1928 llvm::dbgs() <<
"<none>";
1929 llvm::dbgs() <<
"\n";
1935 if (worklist.empty() && views.empty()) {
1936 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
1937 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
1938 return markAllAnalysesPreserved();
1945 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1949 auto getID = [&](Operation *op,
1950 Annotation annotation) -> std::optional<IntegerAttr> {
1951 auto id = annotation.getMember<IntegerAttr>(
"id");
1954 <<
"contained a malformed "
1955 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1956 "that did not contain an 'id' field";
1957 removalError =
true;
1958 return std::nullopt;
1963 nlaTable = &getAnalysis<NLATable>();
1969 DenseSet<Operation *> modulesToDelete;
1970 circuitOp.walk([&](Operation *op) {
1971 TypeSwitch<Operation *>(op)
1972 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
1976 auto maybeID = getID(op, annotation);
1980 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1981 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
1988 .Case<InstanceOp>([&](
auto op) {
1994 <<
"is marked as an interface element, but this should be "
1995 "impossible due to how the Chisel Grand Central API works";
1996 removalError =
true;
2000 .Case<MemOp>([&](
auto op) {
2005 <<
"is marked as an interface element, but this does not make "
2006 "sense (is there a scattering bug or do you have a "
2007 "malformed hand-crafted MLIR circuit?)";
2008 removalError =
true;
2016 <<
"has port '" << i
2017 <<
"' marked as an interface element, but this does not "
2018 "make sense (is there a scattering bug or do you have a "
2019 "malformed hand-crafted MLIR circuit?)";
2020 removalError =
true;
2024 .Case<FModuleOp>([&](FModuleOp op) {
2030 auto maybeID = getID(op, annotation);
2034 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2035 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
2045 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
2046 "circt.nonlocal") !=
nullptr;
2047 auto name = annotation.
getMember<StringAttr>(
"name");
2048 auto id = annotation.
getMember<IntegerAttr>(
"id");
2051 <<
"has a malformed "
2052 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2053 "not contain an 'id' field with an 'IntegerAttr' value";
2054 goto FModuleOp_error;
2058 <<
"has a malformed "
2059 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2060 "not contain a 'name' field with a 'StringAttr' value";
2061 goto FModuleOp_error;
2070 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
2072 companionIDMap[id] = {name.getValue(), op, isNonlocal};
2079 for (
auto port : op.getPorts()) {
2082 auto ty = port.type;
2083 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
2086 <<
"companion instance cannot have output ports";
2091 if (!maybeExtractInfo) {
2097 for (
auto *i :
llvm::make_early_inc_range(
2098 instancePaths->instanceGraph.lookup(op)->uses())) {
2099 auto instance = cast<InstanceOp>(i->getInstance());
2102 if (instance->getParentOfType<LayerBlockOp>())
2105 i->getParent()->getModule()))
2108 if (companionMode == CompanionMode::Drop) {
2110 OpBuilder builder(&getContext());
2111 for (
auto port : instance->getResults()) {
2112 builder.setInsertionPointAfterValue(port);
2114 WireOp::create(builder, port.getLoc(), port.getType());
2115 port.replaceAllUsesWith(wire.getResult());
2122 if (companionMode == CompanionMode::Bind)
2123 instance->setAttr(
"lowerToBind", builder.getUnitAttr());
2127 hw::OutputFileAttr::getFromFilename(
2129 maybeExtractInfo->bindFilename.getValue(),
2149 <<
"Found companion module: "
2150 << companionNode->
getModule().getModuleName() <<
"\n"
2151 <<
" submodules exclusively instantiated "
2152 "(including companion):\n";
2155 for (
auto &node :
llvm::depth_first(companionNode)) {
2164 if (modNode != companionNode &&
2166 modNode->getModule()))
2171 <<
" - module: " << mod.getModuleName() <<
"\n";
2174 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
2176 if (companionMode == CompanionMode::Drop) {
2177 modulesToDelete.insert(mod);
2183 if (extmodule->hasAttr(
"output_file"))
2187 hw::OutputFileAttr::getAsDirectory(
2189 maybeExtractInfo->directory.getValue()));
2195 if (companionMode == CompanionMode::Drop) {
2196 modulesToDelete.insert(mod);
2200 if (!mod->hasAttr(
"output_file")) {
2201 mod->setAttr(
"output_file",
2202 hw::OutputFileAttr::getAsDirectory(
2204 maybeExtractInfo->directory.getValue(),
2207 mod->setAttr(
"comment", builder.getStringAttr(
2208 "VCS coverage exclude_file"));
2218 <<
"unknown annotation class: " << annotation.
getDict();
2221 removalError =
true;
2228 return signalPassFailure();
2230 if (companionMode == CompanionMode::Drop) {
2231 for (
auto *mod : modulesToDelete) {
2232 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2234 DenseSet<hw::HierPathOp> nlas;
2237 for (
auto nla : nlas) {
2238 if (nla.root() == name)
2242 cast<igraph::ModuleOpInterface>(*mod));
2247 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2248 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2255 SmallVector<IntegerAttr> ids;
2256 auto sort = [&ids]() {
2257 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2258 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2261 for (
auto tuple : companionIDMap)
2262 ids.push_back(cast<IntegerAttr>(tuple.first));
2264 llvm::dbgs() <<
"companionIDMap:\n";
2265 for (
auto id : ids) {
2266 auto value = companionIDMap.lookup(
id);
2267 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2268 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2271 for (
auto tuple : leafMap)
2272 ids.push_back(cast<IntegerAttr>(tuple.first));
2274 llvm::dbgs() <<
"leafMap:\n";
2275 for (
auto id : ids) {
2276 auto fieldRef = leafMap.lookup(
id).field;
2279 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2280 FModuleOp
module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2281 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2282 <<
module.getName() + ">" +
2283 module.getPortName(blockArg.getArgNumber());
2285 llvm::dbgs() <<
", fieldID=" << fieldID;
2286 llvm::dbgs() <<
"\n";
2288 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2289 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2292 llvm::dbgs() <<
", fieldID=" << fieldID;
2293 llvm::dbgs() <<
"\n";
2305 companionToInterfaceMap;
2306 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2307 InterfaceElemsBuilder &rhs) {
2308 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2309 InterfaceElemsBuilder::Properties &rhs) {
2313 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2315 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2319 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2320 rhs.elementsList.begin(), compareProps);
2322 for (
auto anno : worklist) {
2323 auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.
getDict());
2327 if (!bundle.isRoot()) {
2328 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2330 removalError =
true;
2334 if (companionIDMap.count(bundle.getID()) == 0) {
2335 emitCircuitError() <<
"no companion found with 'id' value '"
2336 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2337 removalError =
true;
2343 auto companionIter = companionIDMap.lookup(bundle.getID());
2344 auto companionModule = companionIter.companion;
2345 auto symbolName = getNamespace().newName(
2346 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2347 getInterfaceName(bundle) +
"__");
2353 auto instanceSymbol =
2354 hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2355 StringAttr::get(&getContext(), symbolName));
2356 VerbatimBuilder::Base verbatimData;
2357 VerbatimBuilder verbatim(verbatimData);
2358 verbatim += instanceSymbol;
2361 SmallVector<VerbatimXMRbuilder> xmrElems;
2362 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2364 auto ifaceName = traverseBundle(bundle, bundle.getID(), verbatim, xmrElems,
2367 removalError =
true;
2371 if (companionIter.isNonlocal) {
2375 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2376 if (viewMapIter != companionToInterfaceMap.end())
2377 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2378 viewMapIter->getSecond().begin(),
2379 compareInterfaceSignal)) {
2383 companionToInterfaceMap[companionModule] = interfaceBuilder;
2386 if (interfaceBuilder.empty())
2388 auto companionBuilder =
2389 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2392 for (
auto xmrElem : xmrElems) {
2393 auto uloc = companionBuilder.getUnknownLoc();
2394 sv::VerbatimOp::create(companionBuilder, uloc, xmrElem.str, xmrElem.val,
2397 numXMRs += xmrElems.size();
2399 sv::InterfaceOp topIface;
2400 for (
const auto &ifaceBuilder : interfaceBuilder) {
2401 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2402 auto loc = getOperation().getLoc();
2403 sv::InterfaceOp iface =
2404 sv::InterfaceOp::create(builder, loc, ifaceBuilder.iFaceName);
2409 companionIDMap[ifaceBuilder.id].companion) &&
2411 iface->setAttr(
"output_file",
2412 hw::OutputFileAttr::getAsDirectory(
2413 &getContext(), testbenchDir.getValue(),
2415 else if (maybeExtractInfo)
2416 iface->setAttr(
"output_file",
2417 hw::OutputFileAttr::getAsDirectory(
2418 &getContext(), getOutputDirectory().getValue(),
2420 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2421 builder.setInsertionPointToEnd(
2423 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2424 ifaceBuilder.iFaceName)] = iface;
2425 for (
auto elem : ifaceBuilder.elementsList) {
2427 auto uloc = builder.getUnknownLoc();
2429 auto description = elem.description;
2432 auto descriptionOp = sv::VerbatimOp::create(
2434 (
"// " + cleanupDescription(description.getValue())));
2439 if (maybeHierarchyFileYAML)
2440 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2441 builder.getStringAttr(
"description"));
2443 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2444 auto instanceOp = sv::VerbatimOp::create(
2445 builder, uloc, str->toStr(elem.elemName.getValue()));
2449 if (maybeHierarchyFileYAML) {
2450 if (str->instantiation)
2451 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2452 builder.getStringAttr(
"instance"));
2454 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2455 builder.getStringAttr(
"unsupported"));
2456 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2457 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2458 builder.getI32ArrayAttr(str->dimensions));
2459 instanceOp->setAttr(
2460 "firrtl.grandcentral.yaml.symbol",
2461 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2466 auto tpe = std::get<Type>(elem.elemType);
2467 sv::InterfaceSignalOp::create(builder, uloc, elem.elemName.getValue(),
2474 if (maybeHierarchyFileYAML.has_value())
2475 interfaceYAMLMap[maybeHierarchyFileYAML.value()].push_back(topIface);
2478 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2479 sv::InterfaceInstanceOp::create(
2480 builder, getOperation().
getLoc(), topIface.getInterfaceType(),
2481 companionIDMap.lookup(bundle.getID()).name,
2482 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2486 if (!maybeExtractInfo)
2492 companionIDMap[bundle.getID()].companion))
2496 for (
auto view : views) {
2497 auto bundle = view.getAugmentedType();
2499 assert(!bundle.isRoot() &&
"'id' found in firrtl.view");
2501 if (!bundle.getDefName()) {
2502 view.emitOpError(
"missing 'defName' at top-level");
2503 removalError =
true;
2506 if (!bundle.getElements()) {
2507 view.emitOpError(
"missing 'elements' at top-level");
2508 removalError =
true;
2512 auto viewParentMod = view->getParentOfType<FModuleLike>();
2513 auto symbolName = getModuleNamespace(viewParentMod)
2514 .newName(
"__" + getInterfaceName(bundle) +
"__");
2520 auto instanceSymbol =
2521 hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod),
2522 StringAttr::get(&getContext(), symbolName));
2523 VerbatimBuilder::Base verbatimData;
2524 VerbatimBuilder verbatim(verbatimData);
2525 verbatim += instanceSymbol;
2528 SmallVector<VerbatimXMRbuilder> xmrElems;
2529 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2532 auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems,
2533 interfaceBuilder, view, index);
2535 removalError =
true;
2538 if (index != view.getNumOperands()) {
2539 assert(index < view.getNumOperands() &&
2540 "this should error while consuming");
2541 removalError =
true;
2542 view.emitOpError() <<
"has too many operands: " << view.getNumOperands()
2543 <<
" operands but only " << index <<
" were needed";
2547 if (interfaceBuilder.empty())
2549 ImplicitLocOpBuilder viewBuilder(view.getLoc(), view);
2550 viewBuilder.setInsertionPointAfter(view);
2553 for (
auto xmrElem : xmrElems)
2554 sv::VerbatimOp::create(viewBuilder, xmrElem.str, xmrElem.val,
2556 numXMRs += xmrElems.size();
2558 sv::InterfaceOp topIface;
2559 auto containingOutputFileAttr =
2560 viewParentMod->getAttrOfType<hw::OutputFileAttr>(
"output_file");
2561 auto yamlPath = view.getYamlFileAttr();
2562 for (
const auto &ifaceBuilder : interfaceBuilder) {
2563 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2564 auto loc = getOperation().getLoc();
2565 sv::InterfaceOp iface =
2566 sv::InterfaceOp::create(builder, loc, ifaceBuilder.iFaceName);
2573 if (containingOutputFileAttr)
2574 iface->setAttr(
"output_file", containingOutputFileAttr);
2576 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2577 builder.setInsertionPointToEnd(
2579 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2580 ifaceBuilder.iFaceName)] = iface;
2581 for (
auto elem : ifaceBuilder.elementsList) {
2583 auto uloc = builder.getUnknownLoc();
2585 auto description = elem.description;
2588 auto descriptionOp = sv::VerbatimOp::create(
2590 (
"// " + cleanupDescription(description.getValue())));
2596 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2597 builder.getStringAttr(
"description"));
2599 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2600 auto instanceOp = sv::VerbatimOp::create(
2601 builder, uloc, str->toStr(elem.elemName.getValue()));
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 sv::InterfaceSignalOp::create(builder, uloc, elem.elemName.getValue(),
2631 interfaceYAMLMap[yamlPath].push_back(topIface);
2635 viewBuilder.setInsertionPoint(view);
2636 sv::InterfaceInstanceOp::create(
2637 viewBuilder, topIface.getInterfaceType(), view.getName(),
2638 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2643 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2644 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2649 return signalPassFailure();
2650 markAnalysesPreserved<NLATable>();
2653void GrandCentralPass::emitHierarchyYamlFile(
2654 StringRef yamlPath, SmallVectorImpl<sv::InterfaceOp> &intfs) {
2655 CircuitOp circuitOp = getOperation();
2657 std::string yamlString;
2658 llvm::raw_string_ostream stream(yamlString);
2659 ::yaml::Context yamlContext({interfaceMap});
2660 llvm::yaml::Output yout(stream);
2661 yamlize(yout, intfs,
true, yamlContext);
2663 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2664 sv::VerbatimOp::create(builder, builder.getUnknownLoc(), yamlString)
2665 ->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
2666 &getContext(), yamlPath,
2668 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
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 Location getLoc(DefSlot slot)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static Block * getBodyBlock(FModuleLike mod)
This class provides a thread-safe interface to access the analysis results.
This class represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if all instances are under (or transitively under) the effective design-under-test.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
This table tracks nlas and what modules participate in them.
void removeNLAsfromModule(const DenseSet< hw::HierPathOp > &nlas, StringAttr mod)
Remove all the nlas in the set nlas from the module.
void getNLAsInModule(StringAttr modName, DenseSet< hw::HierPathOp > &nlas)
Get the NLAs that the module modName particiaptes in, and insert them into the DenseSet nlas.
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
virtual void erase(InstanceGraphNode *node)
Remove this module from the instance graph.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
constexpr const char * testBenchDirAnnoClass
constexpr const char * augmentedGroundTypeClass
constexpr const char * viewAnnoClass
constexpr const char * blackBoxPathAnnoClass
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
constexpr const char * companionAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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 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)