28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/Debug.h"
32#include "llvm/Support/YAMLTraits.h"
35#define DEBUG_TYPE "gct"
39#define GEN_PASS_DEF_GRANDCENTRAL
40#include "circt/Dialect/FIRRTL/Passes.h.inc"
45using namespace firrtl;
58[[maybe_unused]]
static std::string noDefault(StringRef clazz) {
59 return (
"default '" + clazz +
60 "' construction is an intentionally *NOT* implemented "
61 "YAML feature (you should never be using this)")
65[[maybe_unused]]
static std::string deNorm(StringRef clazz) {
66 return (
"conversion from YAML to a '" + clazz +
67 "' is intentionally *NOT* implemented (you should not be "
68 "converting from YAML to an interface)")
83 DenseMap<Attribute, sv::InterfaceOp> &interfaceMap;
92struct DescribedSignal {
94 StringAttr description;
97 sv::InterfaceSignalOp signal;
105struct DescribedInstance {
109 StringAttr description;
112 ArrayAttr dimensions;
115 FlatSymbolRefAttr interface;
125LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedSignal)
126LLVM_YAML_IS_SEQUENCE_VECTOR(::yaml::DescribedInstance)
127LLVM_YAML_IS_SEQUENCE_VECTOR(sv::InterfaceOp)
135using namespace ::
yaml;
148 std::string descriptionString;
149 llvm::raw_string_ostream stream(descriptionString);
150 SmallVector<StringRef> splits;
151 str.split(splits,
"\n");
155 substr.consume_front(
"//");
156 stream << substr.drop_while([](
auto c) {
return c ==
' '; });
158 [&]() { stream <<
"\n"; });
159 return descriptionString;
166struct MappingContextTraits<DescribedSignal, Context> {
184 : name(op.signal.getSymNameAttr().getValue()) {
210 auto tpe = op.signal.getType();
211 while (
auto vector = dyn_cast<hw::UnpackedArrayType>(tpe)) {
212 dimensions.push_back(vector.getNumElements());
213 tpe = vector.getElementType();
215 dimensions = SmallVector<unsigned>(llvm::reverse(dimensions));
220 assert(isa<IntegerType>(tpe));
221 width = type_cast<IntegerType>(tpe).getWidth();
225 Field(IO &io) { llvm_unreachable(noDefault(
"Field").c_str()); }
229 llvm_unreachable(deNorm(
"DescribedSignal").c_str());
233 static void mapping(IO &io, DescribedSignal &op, Context &ctx) {
234 MappingNormalization<Field, DescribedSignal> keys(io, op);
235 io.mapRequired(
"name", keys->name);
236 io.mapOptional(
"description", keys->description);
237 io.mapRequired(
"dimensions", keys->dimensions);
238 io.mapRequired(
"width", keys->width);
233 static void mapping(IO &io, DescribedSignal &op, Context &ctx) {
…}
166struct MappingContextTraits<DescribedSignal, Context> { {
…};
247struct MappingContextTraits<DescribedInstance, Context> {
254 std::optional<std::string> description = std::nullopt;
263 : name(op.name.getValue()), interface(op.interface) {
271 for (
auto &d : op.dimensions) {
272 auto dimension = dyn_cast<IntegerAttr>(d);
273 dimensions.push_back(dimension.getInt());
277 Instance(IO &io) { llvm_unreachable(noDefault(
"Instance").c_str()); }
280 llvm_unreachable(deNorm(
"DescribedInstance").c_str());
284 static void mapping(IO &io, DescribedInstance &op, Context &ctx) {
285 MappingNormalization<Instance, DescribedInstance> keys(io, op);
286 io.mapRequired(
"name", keys->name);
287 io.mapOptional(
"description", keys->description);
288 io.mapRequired(
"dimensions", keys->dimensions);
289 io.mapRequired(
"interface", ctx.interfaceMap[keys->interface], ctx);
284 static void mapping(IO &io, DescribedInstance &op, Context &ctx) {
…}
247struct MappingContextTraits<DescribedInstance, Context> { {
…};
297struct MappingContextTraits<
sv::InterfaceOp, Context> {
336 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
339 StringAttr description = {};
341 for (
auto &op : op.getBodyBlock()->getOperations()) {
342 TypeSwitch<Operation *>(&op)
345 .Case<sv::VerbatimOp>([&](sv::VerbatimOp op) {
346 auto tpe = op->getAttrOfType<StringAttr>(
347 "firrtl.grandcentral.yaml.type");
351 if (tpe.getValue() ==
"description") {
352 description = op.getFormatStringAttr();
357 if (tpe.getValue() ==
"unsupported") {
364 auto name = op->getAttrOfType<StringAttr>(
365 "firrtl.grandcentral.yaml.name");
366 auto dimensions = op->getAttrOfType<ArrayAttr>(
367 "firrtl.grandcentral.yaml.dimensions");
368 auto symbol = op->getAttrOfType<FlatSymbolRefAttr>(
369 "firrtl.grandcentral.yaml.symbol");
371 DescribedInstance({name, description, dimensions, symbol}));
375 .Case<sv::InterfaceSignalOp>([&](sv::InterfaceSignalOp op) {
376 fields.push_back(DescribedSignal({description, op}));
336 Interface(IO &io, sv::InterfaceOp &op) : name(op.getName()) {
…}
383 Interface(IO &io) { llvm_unreachable(noDefault(
"Interface").c_str()); }
387 llvm_unreachable(deNorm(
"sv::InterfaceOp").c_str());
300 struct Interface { {
…};
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);
391 static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx) {
…}
297struct MappingContextTraits<
sv::InterfaceOp, Context> { {
…};
425struct VerbatimBuilder {
427 SmallString<128> string;
428 SmallVector<Attribute> symbols;
429 VerbatimBuilder builder() {
return VerbatimBuilder(*
this); }
430 operator VerbatimBuilder() {
return builder(); }
435 VerbatimBuilder(Base &base)
436 : base(base), stringBaseSize(base.string.size()),
437 symbolsBaseSize(base.symbols.size()) {}
442 base.string.resize(stringBaseSize);
443 base.symbols.resize(symbolsBaseSize);
447 VerbatimBuilder(
const VerbatimBuilder &) =
delete;
448 VerbatimBuilder &operator=(
const VerbatimBuilder &) =
delete;
453 VerbatimBuilder snapshot() {
return VerbatimBuilder(base); }
456 StringRef getString()
const {
return base.string; }
458 ArrayRef<Attribute> getSymbols()
const {
return base.symbols; }
461 VerbatimBuilder &
append(
char c) {
462 base.string.push_back(c);
467 VerbatimBuilder &
append(
const Twine &twine) {
468 twine.toVector(base.string);
473 VerbatimBuilder &
append(Attribute symbol) {
474 unsigned id = base.symbols.size();
475 base.symbols.push_back(symbol);
476 append(
"{{" + Twine(
id) +
"}}");
480 VerbatimBuilder &operator+=(
char c) {
return append(c); }
481 VerbatimBuilder &operator+=(
const Twine &twine) {
return append(twine); }
482 VerbatimBuilder &operator+=(Attribute symbol) {
return append(symbol); }
486 size_t stringBaseSize;
487 size_t symbolsBaseSize;
502 SmallVector<int32_t, 4> dimensions = {};
505 std::string toStr(StringRef name) {
506 SmallString<64> stringType(str);
507 stringType.append(
" ");
508 stringType.append(name);
509 for (
auto d :
llvm::reverse(dimensions)) {
510 stringType.append(
"[");
511 stringType.append(Twine(d).str());
512 stringType.append(
"]");
515 stringType.append(
"()");
516 stringType.append(
";");
517 return std::string(stringType);
523using TypeSum = std::variant<VerbatimType, Type>;
526struct ExtractionInfo {
529 StringAttr directory = {};
534 StringAttr bindFilename = {};
538struct CompanionInfo {
549 FlatSymbolRefAttr nlaSym;
553struct VerbatimXMRbuilder {
557 FModuleOp companionMod;
558 VerbatimXMRbuilder(Value val, StringAttr str, ArrayAttr syms,
559 FModuleOp companionMod)
560 : val(val), str(str), syms(syms), companionMod(companionMod) {}
565struct InterfaceElemsBuilder {
566 StringAttr iFaceName;
569 StringAttr description;
572 Properties(StringAttr des, StringAttr name, TypeSum &elemType)
573 : description(des), elemName(name), elemType(elemType) {}
575 SmallVector<Properties> elementsList;
576 InterfaceElemsBuilder(StringAttr iFaceName, IntegerAttr
id)
577 : iFaceName(iFaceName), id(id) {}
592struct GrandCentralPass
593 :
public circt::firrtl::impl::GrandCentralBase<GrandCentralPass> {
594 using GrandCentralBase::companionMode;
596 void runOnOperation()
override;
603 DenseMap<Attribute, FieldAndNLA> leafMap;
606 DenseMap<Attribute, CompanionInfo> companionIDMap;
612 StringAttr testbenchDir;
619 std::optional<Attribute> fromAttr(Attribute attr);
623 bool traverseField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
624 SmallVector<VerbatimXMRbuilder> &xmrElems,
625 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
630 std::optional<TypeSum>
631 computeField(Attribute field, IntegerAttr
id, VerbatimBuilder &path,
632 SmallVector<VerbatimXMRbuilder> &xmrElems,
633 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
638 std::optional<StringAttr>
639 traverseBundle(AugmentedBundleTypeAttr bundle, IntegerAttr
id,
640 VerbatimBuilder &path,
641 SmallVector<VerbatimXMRbuilder> &xmrElems,
642 SmallVector<InterfaceElemsBuilder> &interfaceBuilder);
645 igraph::ModuleOpInterface getEnclosingModule(Value value,
646 FlatSymbolRefAttr sym = {});
652 InFlightDiagnostic emitCircuitError(StringRef message = {}) {
653 return emitError(getOperation().
getLoc(),
"'firrtl.circuit' op " + message);
661 std::optional<Attribute> fromViewAttr(ViewIntrinsicOp view, Attribute attr);
665 bool traverseViewField(Attribute field, VerbatimBuilder &path,
666 SmallVector<VerbatimXMRbuilder> &xmrElems,
667 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
668 ViewIntrinsicOp view,
size_t &idx);
673 std::optional<TypeSum>
674 computeViewField(Attribute field, VerbatimBuilder &path,
675 SmallVector<VerbatimXMRbuilder> &xmrElems,
676 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
677 ViewIntrinsicOp view,
size_t &idx);
682 std::optional<StringAttr>
683 traverseViewBundle(AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
684 SmallVector<VerbatimXMRbuilder> &xmrElems,
685 SmallVector<InterfaceElemsBuilder> &interfaceBuilder,
686 ViewIntrinsicOp view,
size_t &idx);
691 std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) {
692 return (bundleType.getDefName().getValue()).str();
697 std::optional<ExtractionInfo> maybeExtractInfo = std::nullopt;
701 std::optional<StringAttr> maybeHierarchyFileYAML = std::nullopt;
703 StringAttr getOutputDirectory() {
704 if (maybeExtractInfo)
705 return maybeExtractInfo->directory;
724 std::optional<CircuitNamespace> circuitNamespace;
728 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
733 if (!circuitNamespace)
735 return *circuitNamespace;
740 return moduleNamespaces.try_emplace(module, module).first->second;
745 std::optional<SymbolTable *> symbolTable;
749 SymbolTable &getSymbolTable() {
751 symbolTable = &getAnalysis<SymbolTable>();
752 return **symbolTable;
760 std::string cleanupDescription(StringRef description) {
764 std::tie(head, description) = description.split(
"\n");
766 if (!description.empty())
768 }
while (!description.empty());
769 return std::string(out);
773 DenseMap<Attribute, sv::InterfaceOp> interfaceMap;
776 void emitHierarchyYamlFile(StringRef yamlPath,
777 SmallVectorImpl<sv::InterfaceOp> &intfs);
791static std::optional<DictionaryAttr>
793 DictionaryAttr root, StringAttr name, StringAttr defName,
794 std::optional<IntegerAttr>
id,
795 std::optional<StringAttr> description, Twine clazz,
796 StringAttr companionAttr, Twine path = {}) {
798 auto *context = state.
circuit.getContext();
799 auto loc = state.
circuit.getLoc();
815 [&](DictionaryAttr refTarget) -> std::optional<std::string> {
817 tryGetAs<StringAttr>(refTarget, refTarget,
"module", loc, clazz, path);
819 tryGetAs<ArrayAttr>(refTarget, refTarget,
"path", loc, clazz, path);
820 auto componentAttr = tryGetAs<ArrayAttr>(refTarget, refTarget,
"component",
822 if (!moduleAttr || !pathAttr || !componentAttr)
826 SmallString<32> strpath;
827 for (
auto p : pathAttr) {
828 auto dict = dyn_cast_or_null<DictionaryAttr>(p);
830 mlir::emitError(loc,
"annotation '" + clazz +
831 " has invalid type (expected DictionaryAttr)");
835 tryGetAs<DictionaryAttr>(dict, dict,
"_1", loc, clazz, path);
837 tryGetAs<DictionaryAttr>(dict, dict,
"_2", loc, clazz, path);
838 if (!instHolder || !modHolder) {
839 mlir::emitError(loc,
"annotation '" + clazz +
840 " has invalid type (expected DictionaryAttr)");
843 auto inst = tryGetAs<StringAttr>(instHolder, instHolder,
"value", loc,
846 tryGetAs<StringAttr>(modHolder, modHolder,
"value", loc, clazz, path);
848 mlir::emitError(loc,
"annotation '" + clazz +
849 " has invalid type (expected DictionaryAttr)");
852 strpath +=
"/" + inst.getValue().str() +
":" + mod.getValue().str();
855 SmallVector<Attribute> componentAttrs;
856 SmallString<32> componentStr;
857 for (
size_t i = 0, e = componentAttr.size(); i != e; ++i) {
858 auto cPath = (path +
".component[" + Twine(i) +
"]").str();
859 auto component = componentAttr[i];
860 auto dict = dyn_cast_or_null<DictionaryAttr>(component);
862 mlir::emitError(loc,
"annotation '" + clazz +
"' with path '" + cPath +
863 " has invalid type (expected DictionaryAttr)");
867 tryGetAs<StringAttr>(dict, refTarget,
"class", loc, clazz, cPath);
871 auto value = dict.get(
"value");
874 if (
auto field = dyn_cast<StringAttr>(value)) {
875 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Field" &&
876 "A StringAttr target token must be found with a subfield target "
878 componentStr.append((Twine(
".") + field.getValue()).str());
883 if (
auto index = dyn_cast<IntegerAttr>(value)) {
884 assert(classAttr.getValue() ==
"firrtl.annotations.TargetToken$Index" &&
885 "An IntegerAttr target token must be found with a subindex "
888 (Twine(
"[") + Twine(index.getValue().getZExtValue()) +
"]").str());
893 "Annotation '" + clazz +
"' with path '" + cPath +
894 ".value has unexpected type (should be StringAttr "
895 "for subfield or IntegerAttr for subindex).")
897 <<
"The value received was: " << value <<
"\n";
902 tryGetAs<StringAttr>(refTarget, refTarget,
"ref", loc, clazz, path);
906 return (Twine(
"~|" + moduleAttr.getValue() + strpath +
">" +
907 refAttr.getValue()) +
913 tryGetAs<StringAttr>(augmentedType, root,
"class", loc, clazz, path);
916 StringRef classBase = classAttr.getValue();
917 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented")) {
919 "the 'class' was expected to start with "
920 "'sifive.enterprise.grandCentral.Augmented*', but was '" +
921 classAttr.getValue() +
"' (Did you misspell it?)")
923 <<
"see annotation: " << augmentedType;
930 if (classBase ==
"BundleType") {
932 tryGetAs<StringAttr>(augmentedType, root,
"defName", loc, clazz, path);
940 SmallVector<Attribute> elements;
942 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
945 for (
size_t i = 0, e = elementsAttr.size(); i != e; ++i) {
946 auto field = dyn_cast_or_null<DictionaryAttr>(elementsAttr[i]);
950 "Annotation '" + Twine(clazz) +
"' with path '.elements[" +
952 "]' contained an unexpected type (expected a DictionaryAttr).")
954 <<
"The received element was: " << elementsAttr[i] <<
"\n";
957 auto ePath = (path +
".elements[" + Twine(i) +
"]").str();
958 auto name = tryGetAs<StringAttr>(field, root,
"name", loc, clazz, ePath);
960 tryGetAs<DictionaryAttr>(field, root,
"tpe", loc, clazz, ePath);
963 std::optional<StringAttr> description;
964 if (
auto maybeDescription = field.get(
"description"))
965 description = cast<StringAttr>(maybeDescription);
967 state, tpe, root, name, defName, std::nullopt, description, clazz,
968 companionAttr, path +
"_" + name.getValue());
975 if (
auto maybeDescription = field.get(
"description"))
976 attrs.append(
"description", cast<StringAttr>(maybeDescription));
977 attrs.append(
"name", name);
978 auto tpeClass = tpe.getAs<StringAttr>(
"class");
980 mlir::emitError(loc,
"missing 'class' key in") << tpe;
983 attrs.append(
"tpe", tpeClass);
984 elements.push_back(*eltAttr);
990 attrs.append(
"class", classAttr);
991 attrs.append(
"defName", defName);
993 attrs.append(
"description", *description);
994 attrs.append(
"elements", ArrayAttr::get(context, elements));
996 attrs.append(
"id", *
id);
997 attrs.append(
"name", name);
998 return DictionaryAttr::getWithSorted(context, attrs);
1007 if (classBase ==
"GroundType") {
1008 auto augRef = augmentedType.getAs<DictionaryAttr>(
"ref");
1010 mlir::emitError(loc,
"missing 'ref' key in ") << augmentedType;
1011 return std::nullopt;
1013 auto maybeTarget = refToTarget(augRef);
1015 mlir::emitError(loc,
"Failed to parse ReferenceTarget").attachNote()
1016 <<
"See the full Annotation here: " << root;
1017 return std::nullopt;
1020 auto id = state.
newID();
1022 auto target = *maybeTarget;
1024 NamedAttrList elementIface, elementScattered;
1027 elementIface.append(
"class", classAttr);
1029 elementIface.append(
"description", *description);
1030 elementIface.append(
"id",
id);
1031 elementIface.append(
"name", name);
1033 elementScattered.append(
"class", classAttr);
1034 elementScattered.append(
"id",
id);
1036 auto targetAttr = StringAttr::get(context, target);
1039 if (!xmrSrcTarget) {
1040 mlir::emitError(loc,
"Failed to resolve target ") << targetAttr;
1041 return std::nullopt;
1047 auto sourceRef = xmrSrcTarget->ref;
1048 ImplicitLocOpBuilder builder(sourceRef.getOp()->getLoc(), context);
1049 std::optional<Value> source =
1050 TypeSwitch<Operation *, std::optional<Value>>(sourceRef.getOp())
1053 .Case<FExtModuleOp>([&](FExtModuleOp extMod)
1054 -> std::optional<Value> {
1055 auto portNo = sourceRef.getImpl().getPortNo();
1056 if (xmrSrcTarget->instances.empty()) {
1058 if (paths.size() > 1) {
1060 "cannot resolve a unique instance path from the "
1061 "external module '")
1062 << targetAttr <<
"'";
1063 return std::nullopt;
1065 auto *it = xmrSrcTarget->instances.begin();
1066 for (
auto inst : paths.back()) {
1067 xmrSrcTarget->instances.insert(it, cast<InstanceOp>(inst));
1071 auto lastInst = xmrSrcTarget->instances.pop_back_val();
1072 builder.setInsertionPointAfter(lastInst);
1074 xmrSrcTarget->fieldIdx);
1078 .Case<FModuleOp>([&](FModuleOp module) -> std::optional<Value> {
1079 builder.setInsertionPointToEnd(module.getBodyBlock());
1080 auto portNum = sourceRef.getImpl().getPortNo();
1082 xmrSrcTarget->fieldIdx);
1085 .Default([&](Operation *op) -> std::optional<Value> {
1086 auto module = cast<FModuleOp>(sourceRef.getModule());
1087 builder.setInsertionPointToEnd(module.getBodyBlock());
1088 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
1090 if (is && is.getTargetResult())
1092 xmrSrcTarget->fieldIdx);
1093 if (sourceRef.getOp()->getNumResults() != 1) {
1095 <<
"cannot be used as a target of the Grand Central View \""
1096 << defName.getValue()
1097 <<
"\" because it does not have exactly one result";
1098 return std::nullopt;
1101 xmrSrcTarget->fieldIdx);
1106 return std::nullopt;
1118 builder.setInsertionPointToEnd(companionMod.getBodyBlock());
1122 auto sinkType = source->getType();
1123 if (
auto baseSinkType = type_dyn_cast<FIRRTLBaseType>(sinkType))
1124 sinkType = baseSinkType.getPassiveType();
1125 auto sink = builder.create<WireOp>(sinkType, name);
1128 annotations.addAnnotations(
1129 {DictionaryAttr::getWithSorted(context, elementScattered)});
1130 annotations.applyToOperation(sink);
1135 (path +
"__bore").str(),
1136 WiringProblem::RefTypeUsage::Prefer});
1138 return DictionaryAttr::getWithSorted(context, elementIface);
1143 if (classBase ==
"VectorType") {
1145 tryGetAs<ArrayAttr>(augmentedType, root,
"elements", loc, clazz, path);
1147 return std::nullopt;
1148 SmallVector<Attribute> elements;
1149 for (
auto [i, elt] :
llvm::enumerate(elementsAttr)) {
1152 StringAttr::get(context,
""),
id, std::nullopt,
1153 clazz, companionAttr, path +
"_" + Twine(i));
1155 return std::nullopt;
1156 elements.push_back(*eltAttr);
1158 NamedAttrList attrs;
1159 attrs.append(
"class", classAttr);
1161 attrs.append(
"description", *description);
1162 attrs.append(
"elements", ArrayAttr::get(context, elements));
1163 attrs.append(
"name", name);
1164 return DictionaryAttr::getWithSorted(context, attrs);
1169 mlir::emitError(loc,
"found unknown AugmentedType '" + classAttr.getValue() +
1170 "' (Did you misspell it?)")
1172 <<
"see annotation: " << augmentedType;
1173 return std::nullopt;
1177 DictionaryAttr anno,
1180 auto id = state.
newID();
1181 auto *context = state.
circuit.getContext();
1182 auto loc = state.
circuit.getLoc();
1183 NamedAttrList companionAttrs;
1185 companionAttrs.append(
"id",
id);
1187 tryGetAs<DictionaryAttr>(anno, anno,
"view", loc,
viewAnnoClass);
1190 auto name = tryGetAs<StringAttr>(anno, anno,
"name", loc,
viewAnnoClass);
1193 companionAttrs.append(
"name", name);
1194 auto companionAttr =
1195 tryGetAs<StringAttr>(anno, anno,
"companion", loc,
viewAnnoClass);
1198 companionAttrs.append(
"target", companionAttr);
1203 companionAttr, Twine(name));
1218std::optional<Attribute> GrandCentralPass::fromAttr(Attribute attr) {
1219 auto dict = dyn_cast<DictionaryAttr>(attr);
1221 emitCircuitError() <<
"attribute is not a dictionary: " << attr <<
"\n";
1222 return std::nullopt;
1225 auto clazz = dict.getAs<StringAttr>(
"class");
1227 emitCircuitError() <<
"missing 'class' key in " << dict <<
"\n";
1228 return std::nullopt;
1231 auto classBase = clazz.getValue();
1232 classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented");
1234 if (classBase ==
"BundleType") {
1235 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1236 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1237 emitCircuitError() <<
"has an invalid AugmentedBundleType that does not "
1238 "contain 'defName' and 'elements' fields: "
1240 }
else if (classBase ==
"VectorType") {
1241 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1242 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1243 emitCircuitError() <<
"has an invalid AugmentedVectorType that does not "
1244 "contain 'name' and 'elements' fields: "
1246 }
else if (classBase ==
"GroundType") {
1247 auto id = dict.getAs<IntegerAttr>(
"id");
1248 auto name = dict.getAs<StringAttr>(
"name");
1249 if (
id && leafMap.count(
id) && name)
1250 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1252 emitCircuitError() <<
"has an invalid AugmentedGroundType that does not "
1253 "contain 'id' and 'name' fields: "
1255 if (
id && !leafMap.count(
id))
1256 emitCircuitError() <<
"has an AugmentedGroundType with 'id == "
1257 <<
id.getValue().getZExtValue()
1258 <<
"' that does not have a scattered leaf to connect "
1259 "to in the circuit "
1260 "(was the leaf deleted or constant prop'd away?)";
1262 emitCircuitError() <<
"has an invalid AugmentedType";
1264 return std::nullopt;
1267std::optional<Attribute> GrandCentralPass::fromViewAttr(ViewIntrinsicOp view,
1269 auto dict = dyn_cast<DictionaryAttr>(attr);
1271 view.emitError() <<
"attribute is not a dictionary: " << attr;
1272 return std::nullopt;
1275 auto clazz = dict.getAs<StringAttr>(
"class");
1277 view.emitError() <<
"missing 'class' key in " << dict;
1278 return std::nullopt;
1281 auto classBase = clazz.getValue();
1282 if (!classBase.consume_front(
"sifive.enterprise.grandcentral.Augmented"))
1283 view.emitOpError() <<
"has an invalid AugmentedType class '" << classBase
1285 else if (classBase ==
"BundleType") {
1286 if (dict.getAs<StringAttr>(
"defName") && dict.getAs<ArrayAttr>(
"elements"))
1287 return AugmentedBundleTypeAttr::get(&getContext(), dict);
1288 view.emitError() <<
"has an invalid AugmentedBundleType that does not "
1289 "contain 'defName' and 'elements' fields: "
1291 }
else if (classBase ==
"VectorType") {
1292 if (dict.getAs<StringAttr>(
"name") && dict.getAs<ArrayAttr>(
"elements"))
1293 return AugmentedVectorTypeAttr::get(&getContext(), dict);
1294 view.emitError() <<
"has an invalid AugmentedVectorType that does not "
1295 "contain 'name' and 'elements' fields: "
1297 }
else if (classBase ==
"GroundType") {
1298 auto id = dict.getAs<IntegerAttr>(
"id");
1302 <<
"has 'id' field which is only for old annotation encoding")
1304 <<
"id within GroundType attribute: " << dict;
1305 return std::nullopt;
1307 auto name = dict.getAs<StringAttr>(
"name");
1309 return AugmentedGroundTypeAttr::get(&getContext(), dict);
1310 view.emitError() <<
"has an invalid AugmentedGroundType that does not "
1311 "contain 'name' field: "
1314 view.emitOpError() <<
"has an invalid AugmentedType '" << classBase <<
"'";
1316 return std::nullopt;
1319bool GrandCentralPass::traverseField(
1320 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1321 SmallVector<VerbatimXMRbuilder> &xmrElems,
1322 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1323 return TypeSwitch<Attribute, bool>(field)
1324 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1325 auto [fieldRef, sym] = leafMap.lookup(ground.getID());
1328 nla = nlaTable->
getNLA(sym.getAttr());
1329 Value leafValue = fieldRef.getValue();
1330 assert(leafValue &&
"leafValue not found");
1332 auto companionModule = companionIDMap.lookup(
id).companion;
1333 igraph::ModuleOpInterface enclosing =
1334 getEnclosingModule(leafValue, sym);
1336 auto tpe = type_cast<FIRRTLBaseType>(leafValue.getType());
1339 if (!tpe.getBitWidthOrSentinel())
1351 auto *nodeOp = leafValue.getDefiningOp();
1352 if (companionModule != enclosing) {
1353 auto diag = companionModule->emitError()
1354 <<
"Grand Central View \""
1355 << companionIDMap.lookup(
id).name
1356 <<
"\" is invalid because a leaf is not inside the "
1358 diag.attachNote(leafValue.getLoc())
1359 <<
"the leaf value is declared here";
1361 auto leafModule = nodeOp->getParentOfType<FModuleOp>();
1362 diag.attachNote(leafModule.getLoc())
1363 <<
"the leaf value is inside this module";
1368 if (!isa<NodeOp>(nodeOp)) {
1369 emitError(leafValue.getLoc())
1370 <<
"Grand Central View \"" << companionIDMap.lookup(
id).name
1371 <<
"\" has an invalid leaf value (this must be a node)";
1378 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1379 SmallString<128> replStr;
1380 StringRef begin =
"{{";
1381 StringRef
end =
"}}";
1384 while (from < base.size()) {
1386 size_t beginAt = base.find(begin, from);
1387 size_t endAt = base.find(end, from);
1389 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1390 (beginAt > endAt)) {
1391 replStr.append(base.substr(from));
1395 replStr.append(base.substr(from, beginAt - from));
1398 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1400 bool failed = idChar.getAsInteger(10, idNum);
1402 assert(!failed &&
"failed to parse integer from verbatim string");
1404 replStr.append(
"{{");
1405 Twine(idNum + 1).toVector(replStr);
1406 replStr.append(
"}}");
1408 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1415 path +=
" = {{-1}}";
1418 xmrElems.emplace_back(
1419 nodeOp->getOperand(0), getStrAndIncrementIds(path.getString()),
1420 ArrayAttr::get(&getContext(), path.getSymbols()), companionModule);
1423 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1424 bool notFailed =
true;
1425 auto elements = vector.getElements();
1426 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1427 auto field = fromAttr(elements[i]);
1430 notFailed &= traverseField(
1431 *field,
id, path.snapshot().append(
"[" + Twine(i) +
"]"),
1432 xmrElems, interfaceBuilder);
1436 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1437 bool anyFailed =
true;
1438 for (
auto element : bundle.getElements()) {
1439 auto field = fromAttr(element);
1442 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1444 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1445 anyFailed &= traverseField(
1446 *field,
id, path.snapshot().append(
"." + name.getValue()),
1447 xmrElems, interfaceBuilder);
1452 .Default([](
auto a) {
return true; });
1455bool GrandCentralPass::traverseViewField(
1456 Attribute field, VerbatimBuilder &path,
1457 SmallVector<VerbatimXMRbuilder> &xmrElems,
1458 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1460 return TypeSwitch<Attribute, bool>(field)
1461 .Case<AugmentedGroundTypeAttr>([&](AugmentedGroundTypeAttr ground) {
1464 if (index >= view.getNumOperands()) {
1465 view.emitOpError(
"more ground types needed (")
1466 << idx <<
" so far) than view has operands ("
1467 << view.getNumOperands() <<
")";
1471 auto val = view.getOperand(index);
1472 auto tpe = type_cast<FIRRTLBaseType>(val.getType());
1475 if (!tpe.getBitWidthOrSentinel())
1481 auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr {
1482 SmallString<128> replStr;
1483 StringRef begin =
"{{";
1484 StringRef
end =
"}}";
1487 while (from < base.size()) {
1489 size_t beginAt = base.find(begin, from);
1490 size_t endAt = base.find(end, from);
1492 if (beginAt == StringRef::npos || endAt == StringRef::npos ||
1493 (beginAt > endAt)) {
1494 replStr.append(base.substr(from));
1498 replStr.append(base.substr(from, beginAt - from));
1501 auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2);
1503 bool failed = idChar.getAsInteger(10, idNum);
1505 assert(!failed &&
"failed to parse integer from verbatim string");
1507 replStr.append(
"{{");
1508 Twine(idNum + 1).toVector(replStr);
1509 replStr.append(
"}}");
1511 return StringAttr::get(&getContext(),
"assign " + replStr +
";");
1517 path +=
" = {{-1}}";
1520 auto mod = view->getParentOfType<FModuleOp>();
1521 xmrElems.emplace_back(val, getStrAndIncrementIds(path.getString()),
1522 ArrayAttr::get(&getContext(), path.getSymbols()),
1526 .Case<AugmentedVectorTypeAttr>([&](
auto vector) {
1527 bool notFailed =
true;
1528 auto elements = vector.getElements();
1529 for (
size_t i = 0, e = elements.size(); i != e; ++i) {
1530 auto field = fromViewAttr(view, elements[i]);
1533 notFailed &= traverseViewField(
1534 *field, path.snapshot().append(
"[" + Twine(i) +
"]"), xmrElems,
1535 interfaceBuilder, view, idx);
1539 .Case<AugmentedBundleTypeAttr>([&](AugmentedBundleTypeAttr bundle) {
1540 bool anyFailed =
true;
1541 for (
auto element : bundle.getElements()) {
1542 auto field = fromViewAttr(view, element);
1545 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1547 name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"defName");
1548 anyFailed &= traverseViewField(
1549 *field, path.snapshot().append(
"." + name.getValue()), xmrElems,
1550 interfaceBuilder, view, idx);
1555 .Default([](
auto a) {
return true; });
1558std::optional<TypeSum> GrandCentralPass::computeField(
1559 Attribute field, IntegerAttr
id, VerbatimBuilder &path,
1560 SmallVector<VerbatimXMRbuilder> &xmrElems,
1561 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1562 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1563 .Case<AugmentedGroundTypeAttr>(
1564 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1566 if (!traverseField(field,
id, path, xmrElems, interfaceBuilder))
1567 return std::nullopt;
1568 FieldRef fieldRef = leafMap.lookup(ground.getID()).field;
1571 auto tpe = firrtl::type_cast<FIRRTLBaseType>(
1574 if (!tpe.isGround()) {
1575 value.getDefiningOp()->emitOpError()
1576 <<
"cannot be added to interface with id '"
1577 <<
id.getValue().getZExtValue()
1578 <<
"' because it is not a ground type";
1579 return std::nullopt;
1581 return TypeSum(IntegerType::get(getOperation().getContext(),
1582 tpe.getBitWidthOrSentinel()));
1584 .Case<AugmentedVectorTypeAttr>(
1585 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1586 auto elements = vector.getElements();
1587 if (elements.empty())
1588 llvm::report_fatal_error(
1589 "unexpected empty augmented vector in GrandCentral View");
1590 auto firstElement = fromAttr(elements[0]);
1592 return std::nullopt;
1594 *firstElement,
id, path.snapshot().append(
"[" + Twine(0) +
"]"),
1595 xmrElems, interfaceBuilder);
1597 return std::nullopt;
1599 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1600 auto subField = fromAttr(elements[i]);
1602 return std::nullopt;
1603 (void)traverseField(*subField,
id,
1604 path.snapshot().append(
"[" + Twine(i) +
"]"),
1605 xmrElems, interfaceBuilder);
1610 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1612 str.dimensions.push_back(elements.getValue().size());
1613 return TypeSum(str);
1615 .Case<AugmentedBundleTypeAttr>(
1616 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1618 traverseBundle(bundle,
id, path, xmrElems, interfaceBuilder);
1619 assert(ifaceName && *ifaceName);
1620 return VerbatimType({ifaceName->str(),
true});
1624std::optional<TypeSum> GrandCentralPass::computeViewField(
1625 Attribute field, VerbatimBuilder &path,
1626 SmallVector<VerbatimXMRbuilder> &xmrElems,
1627 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1629 return TypeSwitch<Attribute, std::optional<TypeSum>>(field)
1630 .Case<AugmentedGroundTypeAttr>(
1631 [&](AugmentedGroundTypeAttr ground) -> std::optional<TypeSum> {
1634 if (!traverseViewField(field, path, xmrElems, interfaceBuilder,
1636 return std::nullopt;
1638 auto val = view.getOperand(index);
1639 auto tpe = dyn_cast<FIRRTLBaseType>(val.getType());
1640 if (!tpe || !tpe.isGround()) {
1641 mlir::emitError(val.getLoc(),
"cannot be added to interface, "
1642 "because it is not a ground type")
1643 .attachNote(view.getLoc())
1644 .append(
"interface part of view");
1645 return std::nullopt;
1649 IntegerType::get(&getContext(), tpe.getBitWidthOrSentinel()));
1651 .Case<AugmentedVectorTypeAttr>(
1652 [&](AugmentedVectorTypeAttr vector) -> std::optional<TypeSum> {
1653 auto elements = vector.getElements();
1654 if (elements.empty())
1655 llvm::report_fatal_error(
1656 "unexpected empty augmented vector in GrandCentral View");
1657 auto firstElement = fromViewAttr(view, elements[0]);
1659 return std::nullopt;
1661 *firstElement, path.snapshot().append(
"[" + Twine(0) +
"]"),
1662 xmrElems, interfaceBuilder, view, idx);
1664 return std::nullopt;
1666 for (
size_t i = 1, e = elements.size(); i != e; ++i) {
1667 auto subField = fromViewAttr(view, elements[i]);
1669 return std::nullopt;
1670 (void)traverseViewField(
1671 *subField, path.snapshot().append(
"[" + Twine(i) +
"]"),
1672 xmrElems, interfaceBuilder, view, idx);
1677 hw::UnpackedArrayType::get(*tpe, elements.getValue().size()));
1679 str.dimensions.push_back(elements.getValue().size());
1680 return TypeSum(str);
1682 .Case<AugmentedBundleTypeAttr>(
1683 [&](AugmentedBundleTypeAttr bundle) -> TypeSum {
1684 auto ifaceName = traverseViewBundle(bundle, path, xmrElems,
1685 interfaceBuilder, view, idx);
1686 assert(ifaceName && *ifaceName);
1687 return VerbatimType({ifaceName->str(),
true});
1697std::optional<StringAttr> GrandCentralPass::traverseBundle(
1698 AugmentedBundleTypeAttr bundle, IntegerAttr
id, VerbatimBuilder &path,
1699 SmallVector<VerbatimXMRbuilder> &xmrElems,
1700 SmallVector<InterfaceElemsBuilder> &interfaceBuilder) {
1702 unsigned lastIndex = interfaceBuilder.size();
1703 auto iFaceName = StringAttr::get(
1704 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1705 interfaceBuilder.emplace_back(iFaceName,
id);
1707 for (
auto element : bundle.getElements()) {
1708 auto field = fromAttr(element);
1710 return std::nullopt;
1712 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1723 *field,
id, path.snapshot().append(
".").append(name.getValue()),
1724 xmrElems, interfaceBuilder);
1726 return std::nullopt;
1727 StringAttr description =
1728 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1729 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1741std::optional<StringAttr> GrandCentralPass::traverseViewBundle(
1742 AugmentedBundleTypeAttr bundle, VerbatimBuilder &path,
1743 SmallVector<VerbatimXMRbuilder> &xmrElems,
1744 SmallVector<InterfaceElemsBuilder> &interfaceBuilder, ViewIntrinsicOp view,
1748 if (!bundle.getDefName()) {
1749 view.emitOpError(
"missing 'defName' at top-level");
1750 return std::nullopt;
1752 if (!bundle.getElements()) {
1753 view.emitOpError(
"missing 'elements' at top-level");
1754 return std::nullopt;
1757 unsigned lastIndex = interfaceBuilder.size();
1758 auto iFaceName = StringAttr::get(
1759 &getContext(), getNamespace().newName(getInterfaceName(bundle)));
1760 interfaceBuilder.emplace_back(iFaceName, IntegerAttr() );
1762 for (
auto element : bundle.getElements()) {
1763 auto field = fromViewAttr(view, element);
1765 return std::nullopt;
1767 auto name = cast<DictionaryAttr>(element).getAs<StringAttr>(
"name");
1769 view.emitError(
"missing 'name' field in element of bundle: ") << element;
1770 return std::nullopt;
1782 *field, path.snapshot().append(
".").append(name.getValue()), xmrElems,
1783 interfaceBuilder, view, idx);
1785 return std::nullopt;
1786 StringAttr description =
1787 cast<DictionaryAttr>(element).getAs<StringAttr>(
"description");
1788 interfaceBuilder[lastIndex].elementsList.emplace_back(description, name,
1796igraph::ModuleOpInterface
1797GrandCentralPass::getEnclosingModule(Value value, FlatSymbolRefAttr sym) {
1798 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1799 return cast<igraph::ModuleOpInterface>(blockArg.getOwner()->getParentOp());
1801 auto *op = value.getDefiningOp();
1802 if (InstanceOp instance = dyn_cast<InstanceOp>(op))
1803 return getSymbolTable().lookup<igraph::ModuleOpInterface>(
1804 instance.getModuleNameAttr().getValue());
1806 return op->getParentOfType<igraph::ModuleOpInterface>();
1810void GrandCentralPass::runOnOperation() {
1813 CircuitOp circuitOp = getOperation();
1822 SmallVector<Annotation> worklist;
1823 bool removalError =
false;
1832 if (companionMode != CompanionMode::Instantiate)
1833 worklist.push_back(anno);
1838 if (maybeExtractInfo) {
1839 emitCircuitError(
"more than one 'ExtractGrandCentralAnnotation' was "
1840 "found, but exactly one must be provided");
1841 removalError = true;
1845 auto directory = anno.
getMember<StringAttr>(
"directory");
1846 auto filename = anno.
getMember<StringAttr>(
"filename");
1847 if (!directory || !filename) {
1849 <<
"contained an invalid 'ExtractGrandCentralAnnotation' that does "
1850 "not contain 'directory' and 'filename' fields: "
1852 removalError =
true;
1855 if (directory.getValue().empty())
1856 directory = StringAttr::get(circuitOp.getContext(),
".");
1858 maybeExtractInfo = {directory, filename};
1863 if (maybeHierarchyFileYAML) {
1864 emitCircuitError(
"more than one 'GrandCentralHierarchyFileAnnotation' "
1865 "was found, but zero or one may be provided");
1866 removalError =
true;
1870 auto filename = anno.
getMember<StringAttr>(
"filename");
1873 <<
"contained an invalid 'GrandCentralHierarchyFileAnnotation' "
1874 "that does not contain 'directory' and 'filename' fields: "
1876 removalError =
true;
1880 maybeHierarchyFileYAML = filename;
1885 testbenchDir = anno.
getMember<StringAttr>(
"dirname");
1892 return signalPassFailure();
1895 llvm::dbgs() <<
"Extraction Info:\n";
1896 if (maybeExtractInfo)
1897 llvm::dbgs() <<
" directory: " << maybeExtractInfo->directory <<
"\n"
1898 <<
" filename: " << maybeExtractInfo->bindFilename <<
"\n";
1900 llvm::dbgs() <<
" <none>\n";
1901 llvm::dbgs() <<
"DUT: ";
1902 if (
auto dut = instanceInfo->
getDut())
1903 llvm::dbgs() << dut.getModuleName() <<
"\n";
1905 llvm::dbgs() <<
"<none>\n";
1907 <<
"Hierarchy File Info (from GrandCentralHierarchyFileAnnotation):\n"
1909 if (maybeHierarchyFileYAML)
1910 llvm::dbgs() << *maybeHierarchyFileYAML;
1912 llvm::dbgs() <<
"<none>";
1913 llvm::dbgs() <<
"\n";
1917 llvm::SmallMapVector<StringAttr, SmallVector<sv::InterfaceOp, 0>, 1>
1919 if (maybeHierarchyFileYAML.has_value())
1920 interfaceYAMLMap[maybeHierarchyFileYAML.value()] = {};
1923 SmallVector<ViewIntrinsicOp> views;
1924 circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); });
1929 if (worklist.empty() && views.empty()) {
1930 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
1931 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
1932 return markAllAnalysesPreserved();
1939 auto builder = OpBuilder::atBlockEnd(circuitOp.getBodyBlock());
1943 auto getID = [&](Operation *op,
1944 Annotation annotation) -> std::optional<IntegerAttr> {
1945 auto id = annotation.getMember<IntegerAttr>(
"id");
1948 <<
"contained a malformed "
1949 "'sifive.enterprise.grandcentral.AugmentedGroundType' annotation "
1950 "that did not contain an 'id' field";
1951 removalError =
true;
1952 return std::nullopt;
1959 instancePaths = &instancePathCache;
1960 instanceInfo = &getAnalysis<InstanceInfo>();
1964 auto exactlyOneInstance = [&](FModuleOp op,
1965 StringRef msg) -> std::optional<InstanceOp> {
1968 switch (node->getNumUses()) {
1970 op->emitOpError() <<
"is marked as a GrandCentral '" << msg
1971 <<
"', but is never instantiated";
1972 return std::nullopt;
1974 return cast<InstanceOp>(*(*node->uses().begin())->getInstance());
1976 auto diag = op->emitOpError()
1977 <<
"is marked as a GrandCentral '" << msg
1978 <<
"', but it is instantiated more than once";
1979 for (
auto *instance : node->uses())
1980 diag.attachNote(instance->getInstance()->
getLoc())
1981 <<
"it is instantiated here";
1982 return std::nullopt;
1986 nlaTable = &getAnalysis<NLATable>();
1992 DenseSet<Operation *> modulesToDelete;
1993 circuitOp.walk([&](Operation *op) {
1994 TypeSwitch<Operation *>(op)
1995 .Case<RegOp, RegResetOp, WireOp, NodeOp>([&](
auto op) {
1999 auto maybeID = getID(op, annotation);
2003 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2004 leafMap[*maybeID] = {{op.getResult(), annotation.
getFieldID()},
2011 .Case<InstanceOp>([&](
auto op) {
2017 <<
"is marked as an interface element, but this should be "
2018 "impossible due to how the Chisel Grand Central API works";
2019 removalError =
true;
2023 .Case<MemOp>([&](
auto op) {
2028 <<
"is marked as an interface element, but this does not make "
2029 "sense (is there a scattering bug or do you have a "
2030 "malformed hand-crafted MLIR circuit?)";
2031 removalError =
true;
2039 <<
"has port '" << i
2040 <<
"' marked as an interface element, but this does not "
2041 "make sense (is there a scattering bug or do you have a "
2042 "malformed hand-crafted MLIR circuit?)";
2043 removalError =
true;
2047 .Case<FModuleOp>([&](FModuleOp op) {
2053 auto maybeID = getID(op, annotation);
2057 annotation.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
2058 leafMap[*maybeID] = {{op.getArgument(i), annotation.
getFieldID()},
2068 auto isNonlocal = annotation.
getMember<FlatSymbolRefAttr>(
2069 "circt.nonlocal") !=
nullptr;
2070 auto name = annotation.
getMember<StringAttr>(
"name");
2071 auto id = annotation.
getMember<IntegerAttr>(
"id");
2074 <<
"has a malformed "
2075 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2076 "not contain an 'id' field with an 'IntegerAttr' value";
2077 goto FModuleOp_error;
2081 <<
"has a malformed "
2082 "'sifive.enterprise.grandcentral.ViewAnnotation' that did "
2083 "not contain a 'name' field with a 'StringAttr' value";
2084 goto FModuleOp_error;
2097 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
2099 companionIDMap[id] = {name.getValue(), op, isNonlocal};
2102 auto instance = exactlyOneInstance(op,
"companion");
2104 goto FModuleOp_error;
2107 for (
auto [i, result] :
llvm::enumerate(instance->getResults())) {
2108 if (instance->getPortDirection(i) == Direction::In)
2111 auto ty = result.getType();
2112 if (isa<RefType>(ty) && companionMode != CompanionMode::Drop)
2115 <<
"companion instance cannot have output ports";
2116 goto FModuleOp_error;
2121 if (!maybeExtractInfo) {
2141 <<
"Found companion module: "
2142 << companionNode->
getModule().getModuleName() <<
"\n"
2143 <<
" submodules exclusively instantiated "
2144 "(including companion):\n";
2147 if (companionMode == CompanionMode::Drop) {
2149 OpBuilder builder(&getContext());
2150 for (
auto port : instance->getResults()) {
2151 builder.setInsertionPointAfterValue(port);
2153 builder.create<WireOp>(port.getLoc(), port.getType());
2154 port.replaceAllUsesWith(wire.getResult());
2160 if (companionMode == CompanionMode::Bind)
2161 (*instance)->setAttr(
"lowerToBind", builder.getUnitAttr());
2163 (*instance)->setAttr(
2165 hw::OutputFileAttr::getFromFilename(
2167 maybeExtractInfo->bindFilename.getValue(),
2171 for (
auto &node :
llvm::depth_first(companionNode)) {
2172 auto mod = node->getModule();
2180 if (modNode != companionNode &&
2182 modNode->getModule()))
2187 <<
" - module: " << mod.getModuleName() <<
"\n";
2190 if (
auto extmodule = dyn_cast<FExtModuleOp>(*mod)) {
2192 if (companionMode == CompanionMode::Drop) {
2193 modulesToDelete.insert(mod);
2199 if (extmodule->hasAttr(
"output_file"))
2203 hw::OutputFileAttr::getAsDirectory(
2205 maybeExtractInfo->directory.getValue()));
2211 if (companionMode == CompanionMode::Drop) {
2212 modulesToDelete.insert(mod);
2216 if (!mod->hasAttr(
"output_file")) {
2217 mod->setAttr(
"output_file",
2218 hw::OutputFileAttr::getAsDirectory(
2220 maybeExtractInfo->directory.getValue(),
2223 mod->setAttr(
"comment", builder.getStringAttr(
2224 "VCS coverage exclude_file"));
2234 <<
"unknown annotation class: " << annotation.
getDict();
2237 removalError =
true;
2244 return signalPassFailure();
2246 if (companionMode == CompanionMode::Drop) {
2247 for (
auto *mod : modulesToDelete) {
2248 auto name = cast<FModuleLike>(mod).getModuleNameAttr();
2250 DenseSet<hw::HierPathOp> nlas;
2253 for (
auto nla : nlas) {
2254 if (nla.root() == name)
2261 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2262 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2269 SmallVector<IntegerAttr> ids;
2270 auto sort = [&ids]() {
2271 llvm::sort(ids, [](IntegerAttr a, IntegerAttr b) {
2272 return a.getValue().getZExtValue() < b.getValue().getZExtValue();
2275 for (
auto tuple : companionIDMap)
2276 ids.push_back(cast<IntegerAttr>(tuple.first));
2278 llvm::dbgs() <<
"companionIDMap:\n";
2279 for (
auto id : ids) {
2280 auto value = companionIDMap.lookup(
id);
2281 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2282 << value.companion.getName() <<
" -> " << value.name <<
"\n";
2285 for (
auto tuple : leafMap)
2286 ids.push_back(cast<IntegerAttr>(tuple.first));
2288 llvm::dbgs() <<
"leafMap:\n";
2289 for (
auto id : ids) {
2290 auto fieldRef = leafMap.lookup(
id).field;
2293 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
2294 FModuleOp
module = cast<FModuleOp>(blockArg.getOwner()->getParentOp());
2295 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2296 <<
module.getName() + ">" +
2297 module.getPortName(blockArg.getArgNumber());
2299 llvm::dbgs() <<
", fieldID=" << fieldID;
2300 llvm::dbgs() <<
"\n";
2302 llvm::dbgs() <<
" - " <<
id.getValue() <<
": "
2303 << cast<StringAttr>(value.getDefiningOp()->getAttr(
"name"))
2306 llvm::dbgs() <<
", fieldID=" << fieldID;
2307 llvm::dbgs() <<
"\n";
2319 companionToInterfaceMap;
2320 auto compareInterfaceSignal = [&](InterfaceElemsBuilder &lhs,
2321 InterfaceElemsBuilder &rhs) {
2322 auto compareProps = [&](InterfaceElemsBuilder::Properties &lhs,
2323 InterfaceElemsBuilder::Properties &rhs) {
2327 if (lhs.elemType.index() == 0 && rhs.elemType.index() == 0)
2329 if (std::get<Type>(lhs.elemType) == std::get<Type>(rhs.elemType))
2333 return std::equal(lhs.elementsList.begin(), lhs.elementsList.end(),
2334 rhs.elementsList.begin(), compareProps);
2336 for (
auto anno : worklist) {
2337 auto bundle = AugmentedBundleTypeAttr::get(&getContext(), anno.
getDict());
2341 if (!bundle.isRoot()) {
2342 emitCircuitError() <<
"missing 'id' in root-level BundleType: "
2344 removalError =
true;
2348 if (companionIDMap.count(bundle.getID()) == 0) {
2349 emitCircuitError() <<
"no companion found with 'id' value '"
2350 << bundle.getID().getValue().getZExtValue() <<
"'\n";
2351 removalError =
true;
2357 auto companionIter = companionIDMap.lookup(bundle.getID());
2358 auto companionModule = companionIter.companion;
2359 auto symbolName = getNamespace().newName(
2360 "__" + companionIDMap.lookup(bundle.getID()).name +
"_" +
2361 getInterfaceName(bundle) +
"__");
2367 auto instanceSymbol =
2368 hw::InnerRefAttr::get(SymbolTable::getSymbolName(companionModule),
2369 StringAttr::get(&getContext(), symbolName));
2370 VerbatimBuilder::Base verbatimData;
2371 VerbatimBuilder verbatim(verbatimData);
2372 verbatim += instanceSymbol;
2375 SmallVector<VerbatimXMRbuilder> xmrElems;
2376 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2378 auto ifaceName = traverseBundle(bundle, bundle.getID(), verbatim, xmrElems,
2381 removalError =
true;
2385 if (companionIter.isNonlocal) {
2389 auto viewMapIter = companionToInterfaceMap.find(companionModule);
2390 if (viewMapIter != companionToInterfaceMap.end())
2391 if (std::equal(interfaceBuilder.begin(), interfaceBuilder.end(),
2392 viewMapIter->getSecond().begin(),
2393 compareInterfaceSignal)) {
2397 companionToInterfaceMap[companionModule] = interfaceBuilder;
2400 if (interfaceBuilder.empty())
2402 auto companionBuilder =
2403 OpBuilder::atBlockEnd(companionModule.getBodyBlock());
2406 for (
auto xmrElem : xmrElems) {
2407 auto uloc = companionBuilder.getUnknownLoc();
2408 companionBuilder.create<sv::VerbatimOp>(uloc, xmrElem.str, xmrElem.val,
2411 numXMRs += xmrElems.size();
2413 sv::InterfaceOp topIface;
2414 for (
const auto &ifaceBuilder : interfaceBuilder) {
2415 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2416 auto loc = getOperation().getLoc();
2417 sv::InterfaceOp iface =
2418 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2423 companionIDMap[ifaceBuilder.id].companion) &&
2425 iface->setAttr(
"output_file",
2426 hw::OutputFileAttr::getAsDirectory(
2427 &getContext(), testbenchDir.getValue(),
2429 else if (maybeExtractInfo)
2430 iface->setAttr(
"output_file",
2431 hw::OutputFileAttr::getAsDirectory(
2432 &getContext(), getOutputDirectory().getValue(),
2434 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2435 builder.setInsertionPointToEnd(
2436 cast<sv::InterfaceOp>(iface).getBodyBlock());
2437 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2438 ifaceBuilder.iFaceName)] = iface;
2439 for (
auto elem : ifaceBuilder.elementsList) {
2441 auto uloc = builder.getUnknownLoc();
2443 auto description = elem.description;
2446 auto descriptionOp = builder.create<sv::VerbatimOp>(
2447 uloc, (
"// " + cleanupDescription(description.getValue())));
2452 if (maybeHierarchyFileYAML)
2453 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2454 builder.getStringAttr(
"description"));
2456 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2457 auto instanceOp = builder.create<sv::VerbatimOp>(
2458 uloc, str->toStr(elem.elemName.getValue()));
2462 if (maybeHierarchyFileYAML) {
2463 if (str->instantiation)
2464 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2465 builder.getStringAttr(
"instance"));
2467 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2468 builder.getStringAttr(
"unsupported"));
2469 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2470 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2471 builder.getI32ArrayAttr(str->dimensions));
2472 instanceOp->setAttr(
2473 "firrtl.grandcentral.yaml.symbol",
2474 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2479 auto tpe = std::get<Type>(elem.elemType);
2480 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2487 if (maybeHierarchyFileYAML.has_value())
2488 interfaceYAMLMap[maybeHierarchyFileYAML.value()].push_back(topIface);
2491 builder.setInsertionPointToStart(companionModule.getBodyBlock());
2492 builder.create<sv::InterfaceInstanceOp>(
2493 getOperation().getLoc(), topIface.getInterfaceType(),
2494 companionIDMap.lookup(bundle.getID()).name,
2495 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2499 if (!maybeExtractInfo)
2505 companionIDMap[bundle.getID()].companion))
2509 for (
auto view : views) {
2510 auto bundle = view.getAugmentedType();
2512 assert(!bundle.isRoot() &&
"'id' found in firrtl.view");
2514 if (!bundle.getDefName()) {
2515 view.emitOpError(
"missing 'defName' at top-level");
2516 removalError =
true;
2519 if (!bundle.getElements()) {
2520 view.emitOpError(
"missing 'elements' at top-level");
2521 removalError =
true;
2525 auto viewParentMod = view->getParentOfType<FModuleLike>();
2526 auto symbolName = getModuleNamespace(viewParentMod)
2527 .newName(
"__" + getInterfaceName(bundle) +
"__");
2533 auto instanceSymbol =
2534 hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod),
2535 StringAttr::get(&getContext(), symbolName));
2536 VerbatimBuilder::Base verbatimData;
2537 VerbatimBuilder verbatim(verbatimData);
2538 verbatim += instanceSymbol;
2541 SmallVector<VerbatimXMRbuilder> xmrElems;
2542 SmallVector<InterfaceElemsBuilder> interfaceBuilder;
2545 auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems,
2546 interfaceBuilder, view, index);
2548 removalError =
true;
2551 if (index != view.getNumOperands()) {
2552 assert(index < view.getNumOperands() &&
2553 "this should error while consuming");
2554 removalError =
true;
2555 view.emitOpError() <<
"has too many operands: " << view.getNumOperands()
2556 <<
" operands but only " << index <<
" were needed";
2560 if (interfaceBuilder.empty())
2562 ImplicitLocOpBuilder viewBuilder(view.getLoc(), view);
2563 viewBuilder.setInsertionPointAfter(view);
2566 for (
auto xmrElem : xmrElems)
2567 viewBuilder.create<
sv::VerbatimOp>(xmrElem.str, xmrElem.val,
2569 numXMRs += xmrElems.size();
2571 sv::InterfaceOp topIface;
2572 auto containingOutputFileAttr =
2573 viewParentMod->getAttrOfType<hw::OutputFileAttr>(
"output_file");
2574 auto yamlPath = view.getYamlFileAttr();
2575 for (
const auto &ifaceBuilder : interfaceBuilder) {
2576 auto builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
2577 auto loc = getOperation().getLoc();
2578 sv::InterfaceOp iface =
2579 builder.create<sv::InterfaceOp>(loc, ifaceBuilder.iFaceName);
2586 if (containingOutputFileAttr)
2587 iface->setAttr(
"output_file", containingOutputFileAttr);
2589 iface.setCommentAttr(builder.getStringAttr(
"VCS coverage exclude_file"));
2590 builder.setInsertionPointToEnd(
2591 cast<sv::InterfaceOp>(iface).getBodyBlock());
2592 interfaceMap[FlatSymbolRefAttr::get(builder.getContext(),
2593 ifaceBuilder.iFaceName)] = iface;
2594 for (
auto elem : ifaceBuilder.elementsList) {
2596 auto uloc = builder.getUnknownLoc();
2598 auto description = elem.description;
2601 auto descriptionOp = builder.create<sv::VerbatimOp>(
2602 uloc, (
"// " + cleanupDescription(description.getValue())));
2608 descriptionOp->setAttr(
"firrtl.grandcentral.yaml.type",
2609 builder.getStringAttr(
"description"));
2611 if (
auto *str = std::get_if<VerbatimType>(&elem.elemType)) {
2612 auto instanceOp = builder.create<sv::VerbatimOp>(
2613 uloc, str->toStr(elem.elemName.getValue()));
2618 if (str->instantiation)
2619 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2620 builder.getStringAttr(
"instance"));
2622 instanceOp->setAttr(
"firrtl.grandcentral.yaml.type",
2623 builder.getStringAttr(
"unsupported"));
2624 instanceOp->setAttr(
"firrtl.grandcentral.yaml.name", elem.elemName);
2625 instanceOp->setAttr(
"firrtl.grandcentral.yaml.dimensions",
2626 builder.getI32ArrayAttr(str->dimensions));
2627 instanceOp->setAttr(
2628 "firrtl.grandcentral.yaml.symbol",
2629 FlatSymbolRefAttr::get(builder.getContext(), str->str));
2634 auto tpe = std::get<Type>(elem.elemType);
2635 builder.create<sv::InterfaceSignalOp>(uloc, elem.elemName.getValue(),
2643 interfaceYAMLMap[yamlPath].push_back(topIface);
2647 viewBuilder.setInsertionPoint(view);
2648 viewBuilder.create<sv::InterfaceInstanceOp>(
2649 topIface.getInterfaceType(), view.getName(),
2650 hw::InnerSymAttr::get(builder.getStringAttr(symbolName)));
2655 for (
auto &[yamlPath, intfs] : interfaceYAMLMap)
2656 emitHierarchyYamlFile(yamlPath.getValue(), intfs);
2661 return signalPassFailure();
2662 markAnalysesPreserved<NLATable>();
2665void GrandCentralPass::emitHierarchyYamlFile(
2666 StringRef yamlPath, SmallVectorImpl<sv::InterfaceOp> &intfs) {
2667 CircuitOp circuitOp = getOperation();
2669 std::string yamlString;
2670 llvm::raw_string_ostream stream(yamlString);
2671 ::yaml::Context yamlContext({interfaceMap});
2672 llvm::yaml::Output yout(stream);
2673 yamlize(yout, intfs,
true, yamlContext);
2675 auto builder = OpBuilder::atBlockBegin(circuitOp.getBodyBlock());
2676 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), yamlString)
2677 ->setAttr(
"output_file", hw::OutputFileAttr::getFromFilename(
2678 &getContext(), yamlPath,
2680 LLVM_DEBUG({ llvm::dbgs() <<
"Generated YAML:" << yamlString <<
"\n"; });
2687std::unique_ptr<mlir::Pass>
2689 auto pass = std::make_unique<GrandCentralPass>();
2690 pass->companionMode = companionMode;
assert(baseType &&"element must be base type")
static std::optional< DictionaryAttr > parseAugmentedType(ApplyState &state, DictionaryAttr augmentedType, DictionaryAttr root, StringAttr name, StringAttr defName, std::optional< IntegerAttr > id, std::optional< StringAttr > description, Twine clazz, StringAttr companionAttr, Twine path={})
Recursively walk a sifive.enterprise.grandcentral.AugmentedType to extract any annotations it may con...
static 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 represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if all instances are under (or transitively under) the effective design-under-test.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
This table tracks nlas and what modules participate in them.
void removeNLAsfromModule(const DenseSet< hw::HierPathOp > &nlas, StringAttr mod)
Remove all the nlas in the set nlas from the module.
void getNLAsInModule(StringAttr modName, DenseSet< hw::HierPathOp > &nlas)
Get the NLAs that the module modName particiaptes in, and insert them into the DenseSet nlas.
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
constexpr const char * augmentedBundleTypeClass
igraph::InstancePathCache InstancePathCache
constexpr const char * extractGrandCentralClass
constexpr const char * testBenchDirAnnoClass
constexpr const char * augmentedGroundTypeClass
std::unique_ptr< mlir::Pass > createGrandCentralPass(CompanionMode companionMode=CompanionMode::Bind)
constexpr const char * viewAnnoClass
constexpr const char * blackBoxPathAnnoClass
Value getValueByFieldID(ImplicitLocOpBuilder builder, Value value, unsigned fieldID)
This gets the value targeted by a field id.
constexpr const char * companionAnnoClass
constexpr const char * blackBoxInlineAnnoClass
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * grandCentralHierarchyFileAnnoClass
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
static std::string stripComment(StringRef str)
Convert newlines and comments to remove the comments.
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
AddToWorklistFn addToWorklistFn
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
The namespace of a CircuitOp, generally inhabited by modules.
void insertOp(Operation *op)
Add a new op to the target cache.
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
InstanceGraph & instanceGraph
The instance graph of the IR.
StringRef name
The name of the interface.
FlatSymbolRefAttr interface
The underlying interface.
Instance(IO &io, DescribedInstance &op)
DescribedInstance denormalize(IO &)
SmallVector< int64_t, 2 > dimensions
An array describing the dimensionality of the interface.
static void mapping(IO &io, DescribedInstance &op, Context &ctx)
DescribedSignal denormalize(IO &)
This cannot be denormalized back to an interface op.
StringRef name
The name of the field.
std::optional< std::string > description
An optional, textual description of what the field is.
Field(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
unsigned width
The width of the underlying type.
Field(IO &io, DescribedSignal &op)
Construct a Field from a DescribedSignal (an sv::InterfaceSignalOp with an optional description).
SmallVector< unsigned, 2 > dimensions
The dimensions of the field.
static void mapping(IO &io, DescribedSignal &op, Context &ctx)
Interface(IO &io)
A no-argument constructor is necessary to work with LLVM's YAML library.
std::vector< DescribedInstance > instances
Instantiations of other interfaces.
StringRef name
The name of the interface.
Interface(IO &io, sv::InterfaceOp &op)
Construct an Interface from an sv::InterfaceOp.
sv::InterfaceOp denormalize(IO &)
This cannot be denormalized back to an interface op.
std::vector< DescribedSignal > fields
All ground or vectors that make up the interface.
static void mapping(IO &io, sv::InterfaceOp &op, Context &ctx)