27 #include "mlir/IR/ImplicitLocOpBuilder.h"
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"
44 using namespace circt;
45 using 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;
92 struct DescribedSignal {
94 StringAttr description;
97 sv::InterfaceSignalOp signal;
105 struct DescribedInstance {
109 StringAttr description;
112 ArrayAttr dimensions;
115 FlatSymbolRefAttr interface;
125 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
126 LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
127 LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
135 using 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;
166 struct 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);
247 struct 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);
297 struct MappingContextTraits<
sv::InterfaceOp, Context> {
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);
425 struct 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;
494 struct VerbatimType {
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);
524 typedef std::variant<VerbatimType, Type> TypeSum;
527 struct ExtractionInfo {
530 StringAttr directory = {};
535 StringAttr bindFilename = {};
539 struct CompanionInfo {
550 FlatSymbolRefAttr nlaSym;
554 struct VerbatimXMRbuilder {
558 FModuleOp companionMod;
559 VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
560 FModuleOp companionMod)
561 : val(val), str(str), syms(syms), companionMod(companionMod) {}
566 struct 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) {}
593 struct GrandCentralPass
594 :
public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
595 using GrandCentralBase::companionMode;
597 void runOnOperation()
override;
603 std::optional<Attribute> fromAttr(Attribute attr);
607 DenseMap<Attribute, FieldAndNLA> leafMap;
610 DenseMap<Attribute, CompanionInfo> companionIDMap;
614 StringRef interfacePrefix;
625 StringAttr testbenchDir;
629 std::string getInterfaceName(StringAttr prefix,
630 AugmentedBundleTypeAttr bundleType) {
633 return (prefix.getValue() + interfacePrefix +
634 bundleType.getDefName().getValue())
636 return (interfacePrefix + bundleType.getDefName().getValue()).str();
641 bool traverseField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
642 SmallVector<VerbatimXMRbuilder> &xmrElems,
643 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
648 std::optional<TypeSum>
649 computeField(Attribute field, IntegerAttr
id, StringAttr prefix,
650 VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
651 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
656 std::optional<StringAttr>
657 traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr
id,
658 StringAttr prefix, VerbatimBuilder &path,
659 SmallVector<VerbatimXMRbuilder> &xmrElems,
660 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
663 igraph::ModuleOpInterface getEnclosingModule(Value value,
664 FlatSymbolRefAttr sym = {});
668 std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
672 std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
674 StringAttr getOutputDirectory() {
675 if (maybeExtractInfo)
676 return maybeExtractInfo->directory;
690 std::optional<CircuitNamespace> circuitNamespace;
694 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
699 if (!circuitNamespace)
701 return *circuitNamespace;
705 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
706 return moduleNamespaces.try_emplace(module, module).first->second;
711 std::optional<SymbolTable *> symbolTable;
715 SymbolTable &getSymbolTable() {
717 symbolTable = &getAnalysis<SymbolTable>();
718 return **symbolTable;
725 InFlightDiagnostic emitCircuitError(StringRef message = {}) {
726 return emitError(getOperation().getLoc(),
"'firrtl.circuit' op " + message);
734 std::string cleanupDescription(StringRef description) {
738 std::tie(head, description) = description.split(
"\n");
740 if (!description.empty())
742 }
while (!description.empty());
743 return std::string(out);
747 DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
750 void emitHierarchyYamlFile(SmallVectorImpl<sv::InterfaceOp> &intfs);
764 static std::optional<DictionaryAttr>
766 DictionaryAttr root, StringRef companion, StringAttr name,
767 StringAttr defName, std::optional<IntegerAttr>
id,
768 std::optional<StringAttr> description, Twine clazz,
769 StringAttr companionAttr, Twine path = {}) {
771 auto *context = state.
circuit.getContext();
772 auto loc = state.
circuit.getLoc();
789 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
791 tryGetAs<StringAttr>(refTarget, refTarget,
"circuit", loc, clazz, path);
793 tryGetAs<StringAttr>(refTarget, refTarget,
"module", loc, clazz, path);
795 tryGetAs<ArrayAttr>(refTarget, refTarget,
"path", loc, clazz, path);
796 auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget,
"component",
798 if (!circuitAttr || !moduleAttr || !pathAttr || !componentAttr)
802 SmallString<32> strpath;
803 for (
auto p : pathAttr) {
804 auto dict = dyn_cast_or_null<DictionaryAttr>(p);
806 mlir::emitError(loc,
"annotation '" + clazz +
807 " has invalid type (expected DictionaryAttr)");
811 tryGetAs<DictionaryAttr>(dict, dict,
"_1", loc, clazz, path);
813 tryGetAs<DictionaryAttr>(dict, dict,
"_2", loc, clazz, path);
814 if (!instHolder || !modHolder) {
815 mlir::emitError(loc,
"annotation '" + clazz +
816 " has invalid type (expected DictionaryAttr)");
819 auto inst = tryGetAs<StringAttr>(instHolder, instHolder,
"value", loc,
822 tryGetAs<StringAttr>(modHolder, modHolder,
"value", loc, clazz, path);
824 mlir::emitError(loc,
"annotation '" + clazz +
825 " has invalid type (expected DictionaryAttr)");
828 strpath +=
"/" + inst.getValue().str() +
":" + mod.getValue().str();
831 SmallVector<Attribute> componentAttrs;
832 SmallString<32> componentStr;
833 for (
size_t i = 0, e = componentAttr.size(); i != e; ++i) {
834 auto cPath = (path +
".component[" + Twine(i) +
"]").str();
835 auto component = componentAttr[i];
836 auto dict = dyn_cast_or_null<DictionaryAttr>(component);
838 mlir::emitError(loc,
"annotation '" + clazz +
"' with path '" + cPath +
839 " has invalid type (expected DictionaryAttr)");
843 tryGetAs<StringAttr>(dict, refTarget,
"class", loc, clazz, cPath);
847 auto value = dict.get(
"value");
850 if (
auto field = dyn_cast<StringAttr>(value)) {
851 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Field" &&
852 "A StringAttr target token must be found with a subfield target "
854 componentStr.append((Twine(
".") + field.getValue()).str());
859 if (
auto index = dyn_cast<IntegerAttr>(value)) {
860 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Index" &&
861 "An IntegerAttr target token must be found with a subindex "
864 (Twine(
"[") + Twine(index.getValue().getZExtValue()) +
"]").str());
869 "Annotation '" + clazz +
"' with path '" + cPath +
870 ".value has unexpected type (should be StringAttr "
871 "for subfield or IntegerAttr for subindex).")
873 <<
"The value received was: " << value <<
"\n";
878 tryGetAs<StringAttr>(refTarget, refTarget,
"ref", loc, clazz, path);
880 return (Twine(
"~" + circuitAttr.getValue() +
"|" + moduleAttr.getValue() +
881 strpath +
">" + refAttr.getValue()) +
887 tryGetAs<StringAttr>(augmentedType, root,
"class", loc, clazz, path);
890 StringRef classBase = classAttr.getValue();
891 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented")) {
893 "the 'class' was expected to start with "
894 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
895 classAttr.getValue() +
"' (Did you misspell it?)")
897 <<
"see annotation: " << augmentedType;
904 if (classBase ==
"BundleType") {
906 tryGetAs<StringAttr>(augmentedType, root,
"defName", loc, clazz, path);
914 SmallVector<Attribute> elements;
916 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
919 for (
size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
920 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
924 "Annotation '" + Twine(clazz) +
"' with path '.elements[" +
926 "]' contained an unexpected type (expected a DictionaryAttr).")
928 <<
"The received element was: " << elementsAttr[i] <<
"\n";
931 auto ePath = (path +
".elements[" + Twine(i) +
"]").str();
932 auto name = tryGetAs<StringAttr>(field, root,
"name", loc, clazz, ePath);
934 tryGetAs<DictionaryAttr>(field, root,
"tpe", loc, clazz, ePath);
935 std::optional<StringAttr> description;
936 if (
auto maybeDescription = field.get(
"description"))
937 description = cast<StringAttr>(maybeDescription);
939 state, tpe, root, companion, name, defName, std::nullopt, description,
940 clazz, companionAttr, path +
"_" + name.getValue());
941 if (!name || !tpe || !eltAttr)
947 if (
auto maybeDescription = field.get(
"description"))
948 attrs.append(
"description", cast<StringAttr>(maybeDescription));
949 attrs.append(
"name", name);
950 attrs.append(
"tpe", tpe.getAs<StringAttr>(
"class"));
951 elements.push_back(*eltAttr);
957 attrs.append(
"class", classAttr);
958 attrs.append(
"defName", defName);
960 attrs.append(
"description", *description);
963 attrs.append(
"id", *
id);
964 attrs.append(
"name", name);
965 return DictionaryAttr::getWithSorted(context, attrs);
974 if (classBase ==
"GroundType") {
975 auto maybeTarget = refToTarget(augmentedType.getAs<DictionaryAttr>(
"ref"));
977 mlir::emitError(loc,
"Failed to parse ReferenceTarget").attachNote()
978 <<
"See the full Annotation here: " << root;
982 auto id = state.
newID();
984 auto target = *maybeTarget;
986 NamedAttrList elementIface, elementScattered;
989 elementIface.append(
"class", classAttr);
991 elementIface.append(
"description", *description);
992 elementIface.append(
"id",
id);
993 elementIface.append(
"name", name);
995 elementScattered.append(
"class", classAttr);
996 elementScattered.append(
"id",
id);
1001 if (!xmrSrcTarget) {
1002 mlir::emitError(loc,
"Failed to resolve target ") << targetAttr;
1003 return std::nullopt;
1009 auto sourceRef = xmrSrcTarget->ref;
1010 ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
1011 std::optional<Value> source =
1012 TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1015 .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1016 -> std::optional<Value> {
1017 auto portNo = sourceRef.getImpl().getPortNo();
1018 if (xmrSrcTarget->instances.empty()) {
1020 if (paths.size() > 1) {
1022 "cannot resolve a unique instance path from the "
1023 "external module '")
1024 << targetAttr <<
"'";
1025 return std::nullopt;
1027 auto *it = xmrSrcTarget->instances.begin();
1028 for (
auto inst : paths.back()) {
1029 xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1033 auto lastInst = xmrSrcTarget->instances.pop_back_val();
1034 builder.setInsertionPointAfter(lastInst);
1036 xmrSrcTarget->fieldIdx);
1040 .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1041 builder.setInsertionPointToEnd(module.getBodyBlock());
1042 auto portNum = sourceRef.getImpl().getPortNo();
1044 xmrSrcTarget->fieldIdx);
1047 .Default([&](Operation *op) -> std::optional<Value> {
1048 auto module = cast<FModuleOp>(sourceRef.getModule());
1049 builder.setInsertionPointToEnd(module.getBodyBlock());
1050 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1052 if (is && is.getTargetResult())
1054 xmrSrcTarget->fieldIdx);
1055 if (sourceRef.getOp()->getNumResults() != 1) {
1057 <<
"cannot be used as a target of the Grand Central View \""
1058 << defName.getValue()
1059 <<
"\" because it does not have exactly one result";
1060 return std::nullopt;
1063 xmrSrcTarget->fieldIdx);
1068 return std::nullopt;
1080 builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1084 auto sinkType = source->getType();
1085 if (
auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1086 sinkType = baseSinkType.getPassiveType();
1087 auto sink = builder.create<WireOp>(sinkType, name);
1090 annotations.addAnnotations(
1091 {DictionaryAttr::getWithSorted(context, elementScattered)});
1092 annotations.applyToOperation(sink);
1097 (path +
"__bore").str(),
1100 return DictionaryAttr::getWithSorted(context, elementIface);
1105 if (classBase ==
"VectorType") {
1107 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
1109 return std::nullopt;
1110 SmallVector<Attribute> elements;
1111 for (
auto [i, elt] : llvm::enumerate(elementsAttr)) {
1113 state, cast<DictionaryAttr>(elt), root, companion, name,
1115 path +
"_" + Twine(i));
1117 return std::nullopt;
1118 elements.push_back(*eltAttr);
1120 NamedAttrList attrs;
1121 attrs.append(
"class", classAttr);
1123 attrs.append(
"description", *description);
1125 attrs.append(
"name", name);
1126 return DictionaryAttr::getWithSorted(context, attrs);
1136 llvm::StringSwitch<bool>(classBase)
1137 .Cases(
"StringType",
"BooleanType",
"IntegerType",
"DoubleType",
true)
1140 NamedAttrList attrs;
1141 attrs.append(
"class", classAttr);
1142 attrs.append(
"name", name);
1144 tryGetAs<Attribute>(augmentedType, root,
"value", loc, clazz, path);
1146 return std::nullopt;
1147 attrs.append(
"value", value);
1148 return DictionaryAttr::getWithSorted(context, attrs);
1153 mlir::emitError(loc,
"found unknown AugmentedType '" + classAttr.getValue() +
1154 "' (Did you misspell it?)")
1156 <<
"see annotation: " << augmentedType;
1157 return std::nullopt;
1161 DictionaryAttr anno,
1164 auto id = state.
newID();
1165 auto *context = state.
circuit.getContext();
1166 auto loc = state.
circuit.getLoc();
1167 NamedAttrList companionAttrs;
1169 companionAttrs.append(
"id",
id);
1171 tryGetAs<DictionaryAttr>(anno, anno,
"view", loc,
viewAnnoClass);
1174 auto name = tryGetAs<StringAttr>(anno, anno,
"name", loc,
viewAnnoClass);
1177 companionAttrs.append(
"name", name);
1178 auto companionAttr =
1179 tryGetAs<StringAttr>(anno, anno,
"companion", loc,
viewAnnoClass);
1182 companionAttrs.append(
"target", companionAttr);
1202 std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1203 auto dict = dyn_cast<DictionaryAttr>(attr);
1205 emitCircuitError() <<
"attribute is not a dictionary: " << attr <<
"\n";
1206 return std::nullopt;
1209 auto clazz = dict.getAs<StringAttr>(
"class");
1211 emitCircuitError() <<
"missing 'class' key in " << dict <<
"\n";
1212 return std::nullopt;
1215 auto classBase = clazz.getValue();
1216 classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented");
1218 if (classBase ==
"BundleType") {
1219 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1221 emitCircuitError() <<
"has an invalid AugmentedBundleType that does not "
1222 "contain 'defName' and 'elements' fields: "
1224 }
else if (classBase ==
"VectorType") {
1225 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1227 emitCircuitError() <<
"has an invalid AugmentedVectorType that does not "
1228 "contain 'name' and 'elements' fields: "
1230 }
else if (classBase ==
"GroundType") {
1231 auto id = dict.getAs<IntegerAttr>(
"id");
1232 auto name = dict.getAs<StringAttr>(
"name");
1233 if (
id && leafMap.count(
id) && name)
1236 emitCircuitError() <<
"has an invalid AugmentedGroundType that does not "
1237 "contain 'id' and 'name' fields: "
1239 if (
id && !leafMap.count(
id))
1240 emitCircuitError() <<
"has an AugmentedGroundType with 'id == "
1241 <<
id.getValue().getZExtValue()
1242 <<
"' that does not have a scattered leaf to connect "
1243 "to in the circuit "
1244 "(was the leaf deleted or constant prop'd away?)";
1245 }
else if (classBase ==
"StringType") {
1246 if (
auto name = dict.getAs<StringAttr>(
"name"))
1248 }
else if (classBase ==
"BooleanType") {
1249 if (
auto name = dict.getAs<StringAttr>(
"name"))
1251 }
else if (classBase ==
"IntegerType") {
1252 if (
auto name = dict.getAs<StringAttr>(
"name"))
1254 }
else if (classBase ==
"DoubleType") {
1255 if (
auto name = dict.getAs<StringAttr>(
"name"))
1257 }
else if (classBase ==
"LiteralType") {
1258 if (
auto name = dict.getAs<StringAttr>(
"name"))
1260 }
else if (classBase ==
"DeletedType") {
1261 if (
auto name = dict.getAs<StringAttr>(
"name"))
1264 emitCircuitError() <<
"has an invalid AugmentedType";
1266 return std::nullopt;
1269 bool GrandCentralPass::traverseField(
1270 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1271 SmallVector<VerbatimXMRbuilder> &xmrElems,
1272 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1273 return TypeSwitch<Attribute, bool>(field)
1274 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1275 auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1278 nla = nlaTable->
getNLA(sym.getAttr());
1279 Value leafValue = fieldRef.getValue();
1280 assert(leafValue &&
"leafValue not found");
1282 auto companionModule = companionIDMap.lookup(
id).companion;
1283 igraph::ModuleOpInterface enclosing =
1284 getEnclosingModule(leafValue, sym);
1286 auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1289 if (!tpe.getBitWidthOrSentinel())
1301 auto *nodeOp = leafValue.getDefiningOp();
1302 if (companionModule != enclosing) {
1303 auto diag = companionModule->emitError()
1304 <<
"Grand Central View \""
1305 << companionIDMap.lookup(
id).name
1306 <<
"\" is invalid because a leaf is not inside the "
1308 diag.attachNote(leafValue.getLoc())
1309 <<
"the leaf value is declared here";
1311 auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1312 diag.attachNote(leafModule.getLoc())
1313 <<
"the leaf value is inside this module";
1318 if (!isa<NodeOp>(nodeOp)) {
1319 emitError(leafValue.getLoc())
1320 <<
"Grand Central View \"" << companionIDMap.lookup(
id).name
1321 <<
"\" has an invalid leaf value (this must be a node)";
1328 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1329 SmallString<128> replStr;
1330 StringRef begin =
"{{";
1331 StringRef
end =
"}}";
1334 while (from < base.size()) {
1336 size_t beginAt = base.find(begin, from);
1337 size_t endAt = base.find(end, from);
1339 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1340 (beginAt > endAt)) {
1341 replStr.append(base.substr(from));
1345 replStr.append(base.substr(from, beginAt - from));
1348 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1350 bool failed = idChar.getAsInteger(10, idNum);
1352 assert(!failed &&
"failed to parse integer from verbatim string");
1354 replStr.append(
"{{");
1355 Twine(idNum + 1).toVector(replStr);
1356 replStr.append(
"}}");
1365 path +=
" = {{-1}}";
1368 xmrElems.emplace_back(
1369 nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1370 ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1373 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1374 bool notFailed =
true;
1375 auto elements = vector.getElements();
1376 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1377 auto field = fromAttr(elements[i]);
1380 notFailed &= traverseField(
1381 *field,
id, path.snapshot().append(
"[" + Twine(i) +
"]"),
1382 xmrElems, interfaceBuilder);
1386 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1387 bool anyFailed =
true;
1388 for (
auto element : bundle.getElements()) {
1389 auto field = fromAttr(element);
1392 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1394 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1395 anyFailed &= traverseField(
1396 *field,
id, path.snapshot().append(
"." + name.getValue()),
1397 xmrElems, interfaceBuilder);
1402 .Case<AugmentedStringTypeAttr>([&](
auto a) {
return false; })
1403 .Case<AugmentedBooleanTypeAttr>([&](
auto a) {
return false; })
1404 .Case<AugmentedIntegerTypeAttr>([&](
auto a) {
return false; })
1405 .Case<AugmentedDoubleTypeAttr>([&](
auto a) {
return false; })
1406 .Case<AugmentedLiteralTypeAttr>([&](
auto a) {
return false; })
1407 .Case<AugmentedDeletedTypeAttr>([&](
auto a) {
return false; })
1408 .Default([](
auto a) {
return true; });
1411 std::optional<TypeSum> GrandCentralPass::computeField(
1412 Attribute field, IntegerAttr
id, StringAttr prefix, VerbatimBuilder &path,
1413 SmallVector<VerbatimXMRbuilder> &xmrElems,
1414 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1416 auto unsupported = [&](StringRef name, StringRef kind) {
1417 return VerbatimType({(
"// <unsupported " + kind +
" type>").str(),
false});
1420 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1421 .Case<AugmentedGroundTypeAttr>(
1422 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1424 if (!traverseField(field,
id, path, xmrElems, interfaceBuilder))
1425 return std::nullopt;
1426 FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1429 auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1432 if (!tpe.isGround()) {
1433 value.getDefiningOp()->emitOpError()
1434 <<
"cannot be added to interface with id '"
1435 <<
id.getValue().getZExtValue()
1436 <<
"' because it is not a ground type";
1437 return std::nullopt;
1440 tpe.getBitWidthOrSentinel()));
1442 .Case<AugmentedVectorTypeAttr>(
1443 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1444 auto elements = vector.getElements();
1445 auto firstElement = fromAttr(elements[0]);
1447 computeField(*firstElement,
id, prefix,
1448 path.snapshot().append(
"[" + Twine(0) +
"]"),
1449 xmrElems, interfaceBuilder);
1451 return std::nullopt;
1453 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1454 auto subField = fromAttr(elements[i]);
1456 return std::nullopt;
1457 (void)traverseField(*subField,
id,
1458 path.snapshot().append(
"[" + Twine(i) +
"]"),
1459 xmrElems, interfaceBuilder);
1466 str.dimensions.push_back(elements.getValue().size());
1467 return TypeSum(str);
1469 .Case<AugmentedBundleTypeAttr>(
1470 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1471 auto ifaceName = traverseBundle(bundle,
id, prefix, path, xmrElems,
1473 assert(ifaceName && *ifaceName);
1474 return VerbatimType({ifaceName->str(),
true});
1476 .Case<AugmentedStringTypeAttr>([&](
auto field) -> TypeSum {
1477 return unsupported(field.getName().getValue(),
"string");
1479 .Case<AugmentedBooleanTypeAttr>([&](
auto field) -> TypeSum {
1480 return unsupported(field.getName().getValue(),
"boolean");
1482 .Case<AugmentedIntegerTypeAttr>([&](
auto field) -> TypeSum {
1483 return unsupported(field.getName().getValue(),
"integer");
1485 .Case<AugmentedDoubleTypeAttr>([&](
auto field) -> TypeSum {
1486 return unsupported(field.getName().getValue(),
"double");
1488 .Case<AugmentedLiteralTypeAttr>([&](
auto field) -> TypeSum {
1489 return unsupported(field.getName().getValue(),
"literal");
1491 .Case<AugmentedDeletedTypeAttr>([&](
auto field) -> TypeSum {
1492 return unsupported(field.getName().getValue(),
"deleted");
1502 std::optional<StringAttr> GrandCentralPass::traverseBundle(
1503 AugmentedBundleTypeAttr bundle, IntegerAttr
id, StringAttr prefix,
1504 VerbatimBuilder &path, SmallVector<VerbatimXMRbuilder> &xmrElems,
1505 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1507 unsigned lastIndex = interfaceBuilder.size();
1509 &getContext(), getNamespace().newName(getInterfaceName(prefix, bundle)));
1510 interfaceBuilder.emplace_back(iFaceName,
id);
1512 for (
auto element : bundle.getElements()) {
1513 auto field = fromAttr(element);
1515 return std::nullopt;
1517 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1528 *field,
id, prefix, path.snapshot().append(
".").append(name.getValue()),
1529 xmrElems, interfaceBuilder);
1531 return std::nullopt;
1532 StringAttr description =
1533 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1534 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1542 igraph::ModuleOpInterface
1543 GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1544 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1545 return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1547 auto *op = value.getDefiningOp();
1548 if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1549 return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1550 instance.getModuleNameAttr().getValue());
1552 return op->getParentOfType<igraph::ModuleOpInterface>();
1556 void GrandCentralPass::runOnOperation() {
1559 CircuitOp circuitOp = getOperation();
1568 SmallVector<Annotation> worklist;
1569 bool removalError =
false;
1578 if (companionMode != CompanionMode::Instantiate)
1579 worklist.push_back(anno);
1584 if (maybeExtractInfo) {
1585 emitCircuitError(
"more than one 'ExtractGrandCentralAnnotation' was "
1586 "found, but exactly one must be provided");
1587 removalError = true;
1591 auto directory = anno.
getMember<StringAttr>(
"directory");
1592 auto filename = anno.
getMember<StringAttr>(
"filename");
1593 if (!directory || !filename) {
1595 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1596 "not contain 'directory' and 'filename' fields: "
1598 removalError =
true;
1601 if (directory.getValue().empty())
1604 maybeExtractInfo = {directory, filename};
1609 if (maybeHierarchyFileYAML) {
1610 emitCircuitError(
"more than one 'GrandCentralHierarchyFileAnnotation' "
1611 "was found, but zero or one may be provided");
1612 removalError =
true;
1616 auto filename = anno.
getMember<StringAttr>(
"filename");
1619 <<
"contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1620 "that does not contain 'directory' and 'filename' fields: "
1622 removalError =
true;
1626 maybeHierarchyFileYAML = filename;
1631 if (!interfacePrefix.empty()) {
1632 emitCircuitError(
"more than one 'PrefixInterfacesAnnotation' was "
1633 "found, but zero or one may be provided");
1634 removalError =
true;
1638 auto prefix = anno.
getMember<StringAttr>(
"prefix");
1641 <<
"contained an invalid 'PrefixInterfacesAnnotation' that does "
1642 "not contain a 'prefix' field: "
1644 removalError =
true;
1648 interfacePrefix = prefix.getValue();
1653 testbenchDir = anno.
getMember<StringAttr>(
"dirname");
1661 for (
auto mod : circuitOp.getOps<FModuleOp>()) {
1663 removalError =
true;
1667 return signalPassFailure();
1670 llvm::dbgs() <<
"Extraction Info:\n";
1671 if (maybeExtractInfo)
1672 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1673 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1675 llvm::dbgs() <<
" <none>\n";
1676 llvm::dbgs() <<
"DUT: ";
1678 llvm::dbgs() << dut.getModuleName() <<
"\n";
1680 llvm::dbgs() <<
"<none>\n";
1682 <<
"Prefix Info (from PrefixInterfacesAnnotation):\n"
1683 <<
" prefix: " << interfacePrefix <<
"\n"
1684 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1686 if (maybeHierarchyFileYAML)
1687 llvm::dbgs() << *maybeHierarchyFileYAML;
1689 llvm::dbgs() <<
"<none>";
1690 llvm::dbgs() <<
"\n";
1696 if (worklist.empty()) {
1697 SmallVector<sv::InterfaceOp, 0> interfaceVec;
1698 emitHierarchyYamlFile(interfaceVec);
1699 return markAllAnalysesPreserved();
1706 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1710 auto getID = [&](Operation *op,
1711 Annotation annotation) -> std::optional<IntegerAttr> {
1712 auto id = annotation.getMember<IntegerAttr>(
"id");
1715 <<
"contained a malformed "
1716 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1717 "that did not contain an 'id' field";
1718 removalError =
true;
1719 return std::nullopt;
1726 instancePaths = &instancePathCache;
1734 DenseSet<InstanceGraphNode *> dutModules;
1741 SmallVector<InstanceGraphNode *> modules({effectiveDUT});
1742 while (!modules.empty()) {
1743 auto *m = modules.pop_back_val();
1745 auto *mod = a->getTarget();
1748 if (
auto block = a->getInstance()->getParentOfType<LayerBlockOp>()) {
1749 auto diag = a->getInstance().emitOpError()
1750 <<
"is instantiated under a '" << block.getOperationName()
1751 <<
"' op which is unexpected by GrandCentral (did you "
1752 "forget to run the LowerLayers pass?)";
1753 diag.attachNote(block.getLoc())
1754 <<
"the '" << block.getOperationName() <<
"' op is here";
1755 removalError =
true;
1757 auto instOp = dyn_cast<InstanceOp>(*a->getInstance());
1758 if (dutModules.contains(mod) ||
1761 (instOp && instOp.getLowerToBind()))
1763 modules.push_back(mod);
1764 dutModules.insert(mod);
1771 auto exactlyOneInstance = [&](FModuleOp op,
1772 StringRef msg) -> std::optional<InstanceOp> {
1775 switch (node->getNumUses()) {
1777 op->emitOpError() <<
"is marked as a GrandCentral '" << msg
1778 <<
"', but is never instantiated";
1779 return std::nullopt;
1781 return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1783 auto diag = op->emitOpError()
1784 <<
"is marked as a GrandCentral '" << msg
1785 <<
"', but it is instantiated more than once";
1786 for (
auto *instance : node->uses())
1787 diag.attachNote(instance->getInstance()->getLoc())
1788 <<
"it is instantiated here";
1789 return std::nullopt;
1793 nlaTable = &getAnalysis<NLATable>();
1799 DenseSet<Operation *> modulesToDelete;
1800 circuitOp.walk([&](Operation *op) {
1801 TypeSwitch<Operation *>(op)
1802 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
1806 auto maybeID = getID(op, annotation);
1810 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1811 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
1818 .Case<InstanceOp>([&](
auto op) {
1824 <<
"is marked as an interface element, but this should be "
1825 "impossible due to how the Chisel Grand Central API works";
1826 removalError =
true;
1830 .Case<MemOp>([&](
auto op) {
1835 <<
"is marked as an interface element, but this does not make "
1836 "sense (is there a scattering bug or do you have a "
1837 "malformed hand-crafted MLIR circuit?)";
1838 removalError =
true;
1846 <<
"has port '" << i
1847 <<
"' marked as an interface element, but this does not "
1848 "make sense (is there a scattering bug or do you have a "
1849 "malformed hand-crafted MLIR circuit?)";
1850 removalError =
true;
1854 .Case<FModuleOp>([&](FModuleOp op) {
1860 auto maybeID = getID(op, annotation);
1864 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1865 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
1875 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
1876 "circt.nonlocal") !=
nullptr;
1877 auto name = annotation.
getMember<StringAttr>(
"name");
1878 auto id = annotation.
getMember<IntegerAttr>(
"id");
1881 <<
"has a malformed "
1882 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1883 "not contain an 'id' field with an 'IntegerAttr' value";
1884 goto FModuleOp_error;
1888 <<
"has a malformed "
1889 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
1890 "not contain a 'name' field with a 'StringAttr' value";
1891 goto FModuleOp_error;
1904 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
1906 companionIDMap[id] = {name.getValue(), op, isNonlocal};
1909 auto instance = exactlyOneInstance(op,
"companion");
1911 goto FModuleOp_error;
1914 for (
auto [i, result] : llvm::enumerate(instance->getResults())) {
1915 if (instance->getPortDirection(i) == Direction::In)
1918 auto ty = result.getType();
1919 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
1922 <<
"companion instance cannot have output ports";
1923 goto FModuleOp_error;
1928 if (!maybeExtractInfo) {
1948 <<
"Found companion module: "
1949 << companionNode->getModule().getModuleName() <<
"\n"
1950 <<
" submodules exclusively instantiated "
1951 "(including companion):\n";
1956 OpBuilder builder(&getContext());
1957 for (
auto port : instance->getResults()) {
1958 builder.setInsertionPointAfterValue(port);
1960 builder.create<WireOp>(port.getLoc(), port.getType());
1961 port.replaceAllUsesWith(wire.getResult());
1968 (*instance)->setAttr(
"lowerToBind", builder.getUnitAttr());
1970 (*instance)->setAttr(
1972 hw::OutputFileAttr::getFromFilename(
1974 maybeExtractInfo->bindFilename.getValue(),
1978 for (
auto &node : llvm::depth_first(companionNode)) {
1979 auto mod = node->getModule();
1987 SmallVector<InstanceRecord *> instances(modNode->uses());
1988 if (modNode != companionNode && dutModules.count(modNode))
1993 <<
" - module: " << mod.getModuleName() <<
"\n";
1996 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
1999 modulesToDelete.insert(mod);
2005 if (extmodule->hasAttr(
"output_file"))
2009 hw::OutputFileAttr::getAsDirectory(
2011 maybeExtractInfo->directory.getValue()));
2018 modulesToDelete.insert(mod);
2022 if (!mod->hasAttr(
"output_file")) {
2023 mod->setAttr(
"output_file",
2024 hw::OutputFileAttr::getAsDirectory(
2026 maybeExtractInfo->directory.getValue(),
2029 mod->setAttr(
"comment", builder.getStringAttr(
2030 "VCS coverage exclude_file"));
2040 <<
"unknown annotation class: " << annotation.
getDict();
2043 removalError =
true;
2050 return signalPassFailure();
2052 if (companionMode == CompanionMode::Drop) {
2053 for (
auto *mod : modulesToDelete) {
2054 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2056 DenseSet<hw::HierPathOp> nlas;
2059 for (
auto nla : nlas) {
2060 if (nla.root() == name)
2067 SmallVector<sv::InterfaceOp, 0> interfaceVec;
2068 emitHierarchyYamlFile(interfaceVec);
2075 SmallVector<IntegerAttr> ids;
2076 auto sort = [&ids]() {
2077 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2078 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2081 for (
auto tuple : companionIDMap)
2082 ids.push_back(cast<IntegerAttr>(tuple.first));
2084 llvm::dbgs() <<
"companionIDMap:\n";
2085 for (
auto id : ids) {
2086 auto value = companionIDMap.lookup(
id);
2087 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2088 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2091 for (
auto tuple : leafMap)
2092 ids.push_back(cast<IntegerAttr>(tuple.first));
2094 llvm::dbgs() <<
"leafMap:\n";
2095 for (
auto id : ids) {
2096 auto fieldRef = leafMap.lookup(
id).field;
2099 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2100 FModuleOp module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2101 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2102 << module.getName() +
">" +
2103 module.getPortName(blockArg.getArgNumber());
2105 llvm::dbgs() <<
", fieldID=" << fieldID;
2106 llvm::dbgs() <<
"\n";
2108 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2109 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2112 llvm::dbgs() <<
", fieldID=" << fieldID;
2113 llvm::dbgs() <<
"\n";
2124 SmallVector<sv::InterfaceOp, 2> interfaceVec;
2126 companionToInterfaceMap;
2127 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2128 InterfaceElemsBuilder &rhs) {
2129 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2130 InterfaceElemsBuilder::Properties &rhs) {
2134 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2136 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2140 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2141 rhs.elementsList.begin(), compareProps);
2143 for (
auto anno : worklist) {
2148 if (!bundle.isRoot()) {
2149 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2151 removalError =
true;
2155 if (companionIDMap.count(bundle.getID()) == 0) {
2156 emitCircuitError() <<
"no companion found with 'id' value '"
2157 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2158 removalError =
true;
2164 auto companionIter = companionIDMap.lookup(bundle.getID());
2165 auto companionModule = companionIter.companion;
2166 auto symbolName = getNamespace().newName(
2167 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2168 getInterfaceName(bundle.getPrefix(), bundle) +
"__");
2174 auto instanceSymbol =
2177 VerbatimBuilder::Base verbatimData;
2178 VerbatimBuilder verbatim(verbatimData);
2179 verbatim += instanceSymbol;
2182 SmallVector<VerbatimXMRbuilder> xmrElems;
2183 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2185 auto ifaceName = traverseBundle(bundle, bundle.getID(), bundle.getPrefix(),
2186 verbatim, xmrElems, interfaceBuilder);
2188 removalError =
true;
2192 if (companionIter.isNonlocal) {
2196 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2197 if (viewMapIter != companionToInterfaceMap.end())
2198 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2199 viewMapIter->getSecond().begin(),
2200 compareInterfaceSignal)) {
2204 companionToInterfaceMap[companionModule] = interfaceBuilder;
2207 if (interfaceBuilder.empty())
2209 auto companionBuilder =
2210 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2213 for (
auto xmrElem : xmrElems) {
2214 auto uloc = companionBuilder.getUnknownLoc();
2215 companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2218 numXMRs += xmrElems.size();
2220 sv::InterfaceOp topIface;
2221 for (
const auto &ifaceBuilder : interfaceBuilder) {
2222 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2223 auto loc = getOperation().getLoc();
2224 sv::InterfaceOp iface =
2225 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2231 companionIDMap[ifaceBuilder.id].companion, dut) &&
2233 iface->setAttr(
"output_file",
2234 hw::OutputFileAttr::getAsDirectory(
2235 &getContext(), testbenchDir.getValue(),
2237 else if (maybeExtractInfo)
2238 iface->setAttr(
"output_file",
2239 hw::OutputFileAttr::getAsDirectory(
2240 &getContext(), getOutputDirectory().getValue(),
2242 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2243 builder.setInsertionPointToEnd(
2244 cast<sv::InterfaceOp>(iface).getBodyBlock());
2246 ifaceBuilder.iFaceName)] = iface;
2247 for (
auto elem : ifaceBuilder.elementsList) {
2249 auto uloc = builder.getUnknownLoc();
2251 auto description = elem.description;
2254 auto descriptionOp = builder.create<sv::VerbatimOp>(
2255 uloc, (
"// " + cleanupDescription(description.getValue())));
2260 if (maybeHierarchyFileYAML)
2261 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2262 builder.getStringAttr(
"description"));
2264 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2265 auto instanceOp = builder.create<sv::VerbatimOp>(
2266 uloc, str->toStr(elem.elemName.getValue()));
2270 if (maybeHierarchyFileYAML) {
2271 if (str->instantiation)
2272 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2273 builder.getStringAttr(
"instance"));
2275 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2276 builder.getStringAttr(
"unsupported"));
2277 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2278 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2279 builder.getI32ArrayAttr(str->dimensions));
2280 instanceOp->setAttr(
2281 "firrtl.grandcentral.yaml.symbol",
2287 auto tpe = std::get<Type>(elem.elemType);
2288 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2295 interfaceVec.push_back(topIface);
2298 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2299 builder.create<sv::InterfaceInstanceOp>(
2300 getOperation().getLoc(), topIface.getInterfaceType(),
2301 companionIDMap.lookup(bundle.getID()).name,
2306 if (!maybeExtractInfo)
2312 companionIDMap[bundle.getID()].companion, dut))
2316 emitHierarchyYamlFile(interfaceVec);
2321 return signalPassFailure();
2322 markAnalysesPreserved<NLATable>();
2325 void GrandCentralPass::emitHierarchyYamlFile(
2326 SmallVectorImpl<sv::InterfaceOp> &intfs) {
2330 if (!maybeHierarchyFileYAML)
2333 CircuitOp circuitOp = getOperation();
2335 std::string yamlString;
2336 llvm::raw_string_ostream stream(yamlString);
2337 ::yaml::Context yamlContext({interfaceMap});
2339 yamlize(yout, intfs,
true, yamlContext);
2341 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2342 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2343 ->setAttr(
"output_file",
2344 hw::OutputFileAttr::getFromFilename(
2345 &getContext(), maybeHierarchyFileYAML->getValue(),
2347 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
2354 std::unique_ptr<mlir::Pass>
2356 auto pass = std::make_unique<GrandCentralPass>();
2357 pass->companionMode = companionMode;
assert(baseType &&"element must be base type")
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringRef companion, 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.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
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::InstanceGraphNode * getTopLevelNode() override
Get the node corresponding to the top-level module of a circuit.
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.
bool isAncestor(ModuleOpInterface child, ModuleOpInterface parent, llvm::function_ref< bool(InstanceRecord *)> skipInstance=[](InstanceRecord *_) { return false;})
Check if child is instantiated by a parent.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
This is an edge in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
LogicalResult extractDUT(FModuleOp mod, FModuleOp &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
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 * prefixInterfacesAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
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)