diff --git a/lib/Dialect/FIRRTL/Transforms/GrandCentral.cpp b/lib/Dialect/FIRRTL/Transforms/GrandCentral.cpp index 2c83a843d78c..4453570a3f35 100644 --- a/lib/Dialect/FIRRTL/Transforms/GrandCentral.cpp +++ b/lib/Dialect/FIRRTL/Transforms/GrandCentral.cpp @@ -596,10 +596,7 @@ struct GrandCentralPass void runOnOperation() override; private: - /// Optionally build an AugmentedType from an attribute. Return none if the - /// attribute is not a dictionary or if it does not match any of the known - /// templates for AugmentedTypes. - std::optional fromAttr(Attribute attr); + //===- Annotation handling data -----------------------------------------===// /// Mapping of ID to leaf ground type and an optional non-local annotation /// associated with that ID. @@ -614,10 +611,12 @@ struct GrandCentralPass /// "TestBenchDirAnnotation" is found. StringAttr testbenchDir; - /// Return a string containing the name of an interface. - std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) { - return (bundleType.getDefName().getValue()).str(); - } + //===- Annotation handling functions ------------------------------------===// + + /// Optionally build an AugmentedType from an attribute. Return none if the + /// attribute is not a dictionary or if it does not match any of the known + /// templates for AugmentedTypes. + std::optional fromAttr(Attribute attr); /// Recursively examine an AugmentedType to populate the "mappings" file /// (generate XMRs) for this interface. This does not build new interfaces. @@ -646,6 +645,53 @@ struct GrandCentralPass igraph::ModuleOpInterface getEnclosingModule(Value value, FlatSymbolRefAttr sym = {}); + // Utility that acts like emitOpError, but does _not_ include a note. The + // note in emitOpError includes the entire op which means the **ENTIRE** + // FIRRTL circuit. This doesn't communicate anything useful to the user + // other than flooding their terminal. + InFlightDiagnostic emitCircuitError(StringRef message = {}) { + return emitError(getOperation().getLoc(), "'firrtl.circuit' op " + message); + } + + //===- Intrinsic Handling Functions -------------------------------------===// + + /// Optionally build an AugmentedType from an attribute. Return none if the + /// attribute is not a dictionary or if it does not match any of the known + /// templates for AugmentedTypes. + std::optional fromViewAttr(ViewIntrinsicOp view, Attribute attr); + + /// Recursively examine an AugmentedType to populate the "mappings" file + /// (generate XMRs) for this interface. This does not build new interfaces. + bool traverseViewField(Attribute field, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, + ViewIntrinsicOp view, size_t &idx); + + /// Recursively examine an AugmentedType to both build new interfaces and + /// populate a "mappings" file (generate XMRs) using `traverseField`. Return + /// the type of the field examined. + std::optional + computeViewField(Attribute field, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, + ViewIntrinsicOp view, size_t &idx); + + /// Recursively examine an AugmentedBundleType to both build new interfaces + /// and populate a "mappings" file (generate XMRs). Return none if the + /// interface is invalid. + std::optional + traverseViewBundle(AugmentedBundleTypeAttr bundle, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, + ViewIntrinsicOp view, size_t &idx); + + //===- Common helpers and state -----------------------------------------===// + + /// Return a string containing the name of an interface. + std::string getInterfaceName(AugmentedBundleTypeAttr bundleType) { + return (bundleType.getDefName().getValue()).str(); + } + /// Information about how the circuit should be extracted. This will be /// non-empty if an extraction annotation is found. std::optional maybeExtractInfo = std::nullopt; @@ -666,6 +712,7 @@ struct GrandCentralPass /// /// TODO: Investigate a way to not use a pointer here like how `getNamespace` /// works below. + /// Only used for annotation handling / companions. InstancePathCache *instancePaths = nullptr; /// An instance info analysis that is used to query if modules are in the @@ -705,14 +752,6 @@ struct GrandCentralPass return **symbolTable; } - // Utility that acts like emitOpError, but does _not_ include a note. The - // note in emitOpError includes the entire op which means the **ENTIRE** - // FIRRTL circuit. This doesn't communicate anything useful to the user - // other than flooding their terminal. - InFlightDiagnostic emitCircuitError(StringRef message = {}) { - return emitError(getOperation().getLoc(), "'firrtl.circuit' op " + message); - } - // Insert comment delimiters ("// ") after newlines in the description string. // This is necessary to prevent introducing invalid verbatim Verilog. // @@ -1227,6 +1266,58 @@ std::optional GrandCentralPass::fromAttr(Attribute attr) { return std::nullopt; } +std::optional GrandCentralPass::fromViewAttr(ViewIntrinsicOp view, + Attribute attr) { + auto dict = dyn_cast(attr); + if (!dict) { + view.emitError() << "attribute is not a dictionary: " << attr; + return std::nullopt; + } + + auto clazz = dict.getAs("class"); + if (!clazz) { + view.emitError() << "missing 'class' key in " << dict; + return std::nullopt; + } + + auto classBase = clazz.getValue(); + if (!classBase.consume_front("sifive.enterprise.grandcentral.Augmented")) + view.emitOpError() << "has an invalid AugmentedType class '" << classBase + << "'"; + else if (classBase == "BundleType") { + if (dict.getAs("defName") && dict.getAs("elements")) + return AugmentedBundleTypeAttr::get(&getContext(), dict); + view.emitError() << "has an invalid AugmentedBundleType that does not " + "contain 'defName' and 'elements' fields: " + << dict; + } else if (classBase == "VectorType") { + if (dict.getAs("name") && dict.getAs("elements")) + return AugmentedVectorTypeAttr::get(&getContext(), dict); + view.emitError() << "has an invalid AugmentedVectorType that does not " + "contain 'name' and 'elements' fields: " + << dict; + } else if (classBase == "GroundType") { + auto id = dict.getAs("id"); + if (id) { + ( + view.emitOpError() + << "has 'id' field which is only for old annotation encoding") + .attachNote() + << "id within GroundType attribute: " << dict; + return std::nullopt; + } + auto name = dict.getAs("name"); + if (name) + return AugmentedGroundTypeAttr::get(&getContext(), dict); + view.emitError() << "has an invalid AugmentedGroundType that does not " + "contain 'name' field: " + << dict; + } else { + view.emitOpError() << "has an invalid AugmentedType '" << classBase << "'"; + } + return std::nullopt; +} + bool GrandCentralPass::traverseField( Attribute field, IntegerAttr id, VerbatimBuilder &path, SmallVector &xmrElems, @@ -1363,6 +1454,109 @@ bool GrandCentralPass::traverseField( .Default([](auto a) { return true; }); } +bool GrandCentralPass::traverseViewField( + Attribute field, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, ViewIntrinsicOp view, + size_t &idx) { + return TypeSwitch(field) + .Case([&](AugmentedGroundTypeAttr ground) { + // Grab next signal, increment index counter. + auto index = idx++; + if (index >= view.getNumOperands()) { + view.emitOpError("more ground types needed (") + << idx << " so far) than view has operands (" + << view.getNumOperands() << ")"; + return false; + } + + auto val = view.getOperand(index); + auto tpe = type_cast(val.getType()); + + // If the type is zero-width then do not emit an XMR. + if (!tpe.getBitWidthOrSentinel()) + return true; + + /// Increment all the indices inside `{{`, `}}` by one. This is to + /// indicate that a value is added to the `substitutions` of the + /// verbatim op, other than the symbols. + auto getStrAndIncrementIds = [&](StringRef base) -> StringAttr { + SmallString<128> replStr; + StringRef begin = "{{"; + StringRef end = "}}"; + // The replacement string. + size_t from = 0; + while (from < base.size()) { + // Search for the first `{{` and `}}`. + size_t beginAt = base.find(begin, from); + size_t endAt = base.find(end, from); + // If not found, then done. + if (beginAt == StringRef::npos || endAt == StringRef::npos || + (beginAt > endAt)) { + replStr.append(base.substr(from)); + break; + } + // Copy the string as is, until the `{{`. + replStr.append(base.substr(from, beginAt - from)); + // Advance `from` to the character after the `}}`. + from = endAt + 2; + auto idChar = base.substr(beginAt + 2, endAt - beginAt - 2); + int idNum; + bool failed = idChar.getAsInteger(10, idNum); + (void)failed; + assert(!failed && "failed to parse integer from verbatim string"); + // Now increment the id and append. + replStr.append("{{"); + Twine(idNum + 1).toVector(replStr); + replStr.append("}}"); + } + return StringAttr::get(&getContext(), "assign " + replStr + ";"); + }; + + // This is the new style of XMRs using RefTypes. The value substitution + // index is set to -1, as it will be incremented when generating the + // string. + path += " = {{-1}}"; + + // Assemble the verbatim op. + auto mod = view->getParentOfType(); + xmrElems.emplace_back(val, getStrAndIncrementIds(path.getString()), + ArrayAttr::get(&getContext(), path.getSymbols()), + mod); + return true; + }) + .Case([&](auto vector) { + bool notFailed = true; + auto elements = vector.getElements(); + for (size_t i = 0, e = elements.size(); i != e; ++i) { + auto field = fromViewAttr(view, elements[i]); + if (!field) + return false; + notFailed &= traverseViewField( + *field, path.snapshot().append("[" + Twine(i) + "]"), xmrElems, + interfaceBuilder, view, idx); + } + return notFailed; + }) + .Case([&](AugmentedBundleTypeAttr bundle) { + bool anyFailed = true; + for (auto element : bundle.getElements()) { + auto field = fromViewAttr(view, element); + if (!field) + return false; + auto name = cast(element).getAs("name"); + if (!name) + name = cast(element).getAs("defName"); + anyFailed &= traverseViewField( + *field, path.snapshot().append("." + name.getValue()), xmrElems, + interfaceBuilder, view, idx); + } + + return anyFailed; + }) + .Default([](auto a) { return true; }); +} + std::optional GrandCentralPass::computeField( Attribute field, IntegerAttr id, VerbatimBuilder &path, SmallVector &xmrElems, @@ -1429,6 +1623,73 @@ std::optional GrandCentralPass::computeField( }); } +std::optional GrandCentralPass::computeViewField( + Attribute field, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, ViewIntrinsicOp view, + size_t &idx) { + return TypeSwitch>(field) + .Case( + [&](AugmentedGroundTypeAttr ground) -> std::optional { + // Traverse to generate mappings. + auto index = idx; + if (!traverseViewField(field, path, xmrElems, interfaceBuilder, + view, idx)) + return std::nullopt; + + auto val = view.getOperand(index); + auto tpe = dyn_cast(val.getType()); + if (!tpe || !tpe.isGround()) { + mlir::emitError(val.getLoc(), "cannot be added to interface, " + "because it is not a ground type") + .attachNote(view.getLoc()) + .append("interface part of view"); + return std::nullopt; + } + + return TypeSum( + IntegerType::get(&getContext(), tpe.getBitWidthOrSentinel())); + }) + .Case( + [&](AugmentedVectorTypeAttr vector) -> std::optional { + auto elements = vector.getElements(); + if (elements.empty()) + llvm::report_fatal_error( + "unexpected empty augmented vector in GrandCentral View"); + auto firstElement = fromViewAttr(view, elements[0]); + if (!firstElement) + return std::nullopt; + auto elementType = computeViewField( + *firstElement, path.snapshot().append("[" + Twine(0) + "]"), + xmrElems, interfaceBuilder, view, idx); + if (!elementType) + return std::nullopt; + + for (size_t i = 1, e = elements.size(); i != e; ++i) { + auto subField = fromViewAttr(view, elements[i]); + if (!subField) + return std::nullopt; + (void)traverseViewField( + *subField, path.snapshot().append("[" + Twine(i) + "]"), + xmrElems, interfaceBuilder, view, idx); + } + + if (auto *tpe = std::get_if(&*elementType)) + return TypeSum( + hw::UnpackedArrayType::get(*tpe, elements.getValue().size())); + auto str = std::get(*elementType); + str.dimensions.push_back(elements.getValue().size()); + return TypeSum(str); + }) + .Case( + [&](AugmentedBundleTypeAttr bundle) -> TypeSum { + auto ifaceName = traverseViewBundle(bundle, path, xmrElems, + interfaceBuilder, view, idx); + assert(ifaceName && *ifaceName); + return VerbatimType({ifaceName->str(), true}); + }); +} + /// Traverse an Annotation that is an AugmentedBundleType. During traversal, /// construct any discovered SystemVerilog interfaces. If this is the root /// interface, instantiate that interface in the companion. Recurse into fields @@ -1473,6 +1734,65 @@ std::optional GrandCentralPass::traverseBundle( return iFaceName; } +/// Traverse an attribute that is an AugmentedBundleType. During traversal, +/// construct any discovered SystemVerilog interfaces. If this is the root +/// interface, instantiate that interface in the companion. Recurse into fields +/// of the AugmentedBundleType to construct nested interfaces and generate +/// stringy-typed SystemVerilog hierarchical references to drive the +/// interface. Returns false on any failure and true on success. +std::optional GrandCentralPass::traverseViewBundle( + AugmentedBundleTypeAttr bundle, VerbatimBuilder &path, + SmallVector &xmrElems, + SmallVector &interfaceBuilder, ViewIntrinsicOp view, + size_t &idx) { + + // TODO: Require as part of attribute, structurally. + if (!bundle.getDefName()) { + view.emitOpError("missing 'defName' at top-level"); + return std::nullopt; + } + if (!bundle.getElements()) { + view.emitOpError("missing 'elements' at top-level"); + return std::nullopt; + } + + unsigned lastIndex = interfaceBuilder.size(); + auto iFaceName = StringAttr::get( + &getContext(), getNamespace().newName(getInterfaceName(bundle))); + interfaceBuilder.emplace_back(iFaceName, IntegerAttr() /* XXX */); + + for (auto element : bundle.getElements()) { + auto field = fromViewAttr(view, element); + if (!field) + return std::nullopt; + + auto name = cast(element).getAs("name"); + if (!name) { + view.emitError("missing 'name' field in element of bundle: ") << element; + return std::nullopt; + } + // auto signalSym = hw::InnerRefAttr::get(iface.sym_nameAttr(), name); + // TODO: The `append(name.getValue())` in the following should actually be + // `append(signalSym)`, but this requires that `computeField` and the + // functions it calls always return a type for which we can construct an + // `InterfaceSignalOp`. Since nested interface instances are currently + // busted (due to the interface being a symbol table), this doesn't work at + // the moment. Passing a `name` works most of the time, but can be brittle + // if the interface field requires renaming in the output (e.g. due to + // naming conflicts). + auto elementType = computeViewField( + *field, path.snapshot().append(".").append(name.getValue()), xmrElems, + interfaceBuilder, view, idx); + if (!elementType) + return std::nullopt; + StringAttr description = + cast(element).getAs("description"); + interfaceBuilder[lastIndex].elementsList.emplace_back(description, name, + *elementType); + } + return iFaceName; +} + /// Return the module that is associated with this value. Use the cached/lazily /// constructed symbol table to make this fast. igraph::ModuleOpInterface @@ -1595,10 +1915,14 @@ void GrandCentralPass::runOnOperation() { llvm::dbgs() << "\n"; }); + // Scan entire design for View operations. + SmallVector views; + circuitOp.walk([&views](ViewIntrinsicOp view) { views.push_back(view); }); + // Exit immediately if no annotations indicative of interfaces that need to be // built exist. However, still generate the YAML file if the annotation for // this was passed in because some flows expect this. - if (worklist.empty()) { + if (worklist.empty() && views.empty()) { SmallVector interfaceVec; emitHierarchyYamlFile(interfaceVec); return markAllAnalysesPreserved(); @@ -2178,6 +2502,143 @@ void GrandCentralPass::runOnOperation() { continue; } + for (auto view : views) { + auto bundle = view.getAugmentedType(); + + assert(!bundle.isRoot() && "'id' found in firrtl.view"); + + if (!bundle.getDefName()) { + view.emitOpError("missing 'defName' at top-level"); + removalError = true; + continue; + } + if (!bundle.getElements()) { + view.emitOpError("missing 'elements' at top-level"); + removalError = true; + continue; + } + + auto viewParentMod = view->getParentOfType(); + auto symbolName = getModuleNamespace(viewParentMod) + .newName("__" + getInterfaceName(bundle) + "__"); + + // Recursively walk the AugmentedBundleType to generate interfaces and XMRs. + // Error out if this returns None (indicating that the view is + // malformed in some way). A good error message is generated inside + // `traverseViewBundle` or the functions it calls. + auto instanceSymbol = + hw::InnerRefAttr::get(SymbolTable::getSymbolName(viewParentMod), + StringAttr::get(&getContext(), symbolName)); + VerbatimBuilder::Base verbatimData; + VerbatimBuilder verbatim(verbatimData); + verbatim += instanceSymbol; + // List of interface elements. + + SmallVector xmrElems; + SmallVector interfaceBuilder; + + size_t index = 0; + auto ifaceName = traverseViewBundle(bundle, verbatim, xmrElems, + interfaceBuilder, view, index); + if (!ifaceName) { + removalError = true; + continue; + } + if (index != view.getNumOperands()) { + assert(index < view.getNumOperands() && + "this should error while consuming"); + removalError = true; + view.emitOpError() << "has too many operands: " << view.getNumOperands() + << " operands but only " << index << " were needed"; + continue; + } + + if (interfaceBuilder.empty()) + continue; + ImplicitLocOpBuilder viewBuilder(view.getLoc(), view); + viewBuilder.setInsertionPointAfter(view); + + // Generate gathered XMR's. + for (auto xmrElem : xmrElems) + viewBuilder.create(xmrElem.str, xmrElem.val, + xmrElem.syms); + numXMRs += xmrElems.size(); + + sv::InterfaceOp topIface; + for (const auto &ifaceBuilder : interfaceBuilder) { + auto builder = OpBuilder::atBlockEnd(getOperation().getBodyBlock()); + auto loc = getOperation().getLoc(); + sv::InterfaceOp iface = + builder.create(loc, ifaceBuilder.iFaceName); + if (!topIface) + topIface = iface; + ++numInterfaces; + + iface.setCommentAttr(builder.getStringAttr("VCS coverage exclude_file")); + builder.setInsertionPointToEnd( + cast(iface).getBodyBlock()); + interfaceMap[FlatSymbolRefAttr::get(builder.getContext(), + ifaceBuilder.iFaceName)] = iface; + for (auto elem : ifaceBuilder.elementsList) { + + auto uloc = builder.getUnknownLoc(); + + auto description = elem.description; + + if (description) { + auto descriptionOp = builder.create( + uloc, ("// " + cleanupDescription(description.getValue()))); + + // If we need to generate a YAML representation of this interface, + // then add an attribute indicating that this `sv::VerbatimOp` is + // actually a description. + if (maybeHierarchyFileYAML) + descriptionOp->setAttr("firrtl.grandcentral.yaml.type", + builder.getStringAttr("description")); + } + if (auto *str = std::get_if(&elem.elemType)) { + auto instanceOp = builder.create( + uloc, str->toStr(elem.elemName.getValue())); + + // If we need to generate a YAML representation of the interface, then + // add attributes that describe what this `sv::VerbatimOp` is. + if (maybeHierarchyFileYAML) { + if (str->instantiation) + instanceOp->setAttr("firrtl.grandcentral.yaml.type", + builder.getStringAttr("instance")); + else + instanceOp->setAttr("firrtl.grandcentral.yaml.type", + builder.getStringAttr("unsupported")); + instanceOp->setAttr("firrtl.grandcentral.yaml.name", elem.elemName); + instanceOp->setAttr("firrtl.grandcentral.yaml.dimensions", + builder.getI32ArrayAttr(str->dimensions)); + instanceOp->setAttr( + "firrtl.grandcentral.yaml.symbol", + FlatSymbolRefAttr::get(builder.getContext(), str->str)); + } + continue; + } + + auto tpe = std::get(elem.elemType); + builder.create(uloc, elem.elemName.getValue(), + tpe); + } + } + + ++numViews; + + interfaceVec.push_back(topIface); + + // Instantiate the interface before the view and the XMR's we inserted + // above. + viewBuilder.setInsertionPoint(view); + viewBuilder.create( + topIface.getInterfaceType(), view.getName(), + hw::InnerSymAttr::get(builder.getStringAttr(symbolName))); + + view.erase(); + } + emitHierarchyYamlFile(interfaceVec); // Signal pass failure if any errors were found while examining circuit diff --git a/test/Dialect/FIRRTL/grand-central-view-errors.mlir b/test/Dialect/FIRRTL/grand-central-view-errors.mlir new file mode 100644 index 000000000000..387d91af5b8d --- /dev/null +++ b/test/Dialect/FIRRTL/grand-central-view-errors.mlir @@ -0,0 +1,255 @@ +// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-grand-central))' -split-input-file -verify-diagnostics %s + + +// View has insufficient operands. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "TooFewOperands" { + firrtl.module @TooFewOperands() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op more ground types needed (2 so far) than view has operands (1)}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of foo", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "multi\nline\ndescription\nof\nbar", + name = "bar" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// View has too many operands. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "TooManyOperands" { + firrtl.module @TooManyOperands() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op has too many operands: 3 operands but only 2 were needed}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of foo", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "multi\nline\ndescription\nof\nbar", + name = "bar" + } + ] + }>, %c0_ui1, %c0_ui1, %c0_ui1 : !firrtl.uint<1>, !firrtl.uint<1>, !firrtl.uint<1> + } +} + +// ----- +// View has "id" field on ground type. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "HasID" { + firrtl.module @HasID() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op has 'id' field which is only for old annotation encoding}} + // expected-note @below {{id within GroundType attribute: {class = "sifive.enterprise.grandcentral.AugmentedGroundType", description = "description of foo", id = 1 : i64, name = "foo"}}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of foo", + id = 1, + name = "foo" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Attribute missing "class" field. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "MissingClass" { + firrtl.module @MissingClass() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{missing 'class' key in {description = "description of foo", name = "foo"}}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + // (Missing class key) + description = "description of foo", + name = "foo" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Top-level attribute missing "defName" field. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "MissingDefName" { + firrtl.module @MissingDefName() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op missing 'defName' at top-level}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of foo", + name = "foo" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Top-level attribute missing "elements" field. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "MissingElements" { + firrtl.module @MissingElements() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op missing 'elements' at top-level}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView" + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Invalid / unsupported "class" in element. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "InvalidClass" { + firrtl.module @InvalidClass() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op has an invalid AugmentedType class 'invalid class'}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "invalid class", + name = "foo" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Invalid augmented "class" in element. + +// In the future, verifier on firrtl.view can catch this. +firrtl.circuit "InvalidAug" { + firrtl.module @InvalidAug() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + + // expected-error @below {{op has an invalid AugmentedType 'GroundTorp'}} + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundTorp", + name = "foo" + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} + +// ----- +// Bundle missing 'defName' or `element + +firrtl.circuit "InvalidBundleDefNameElements" { + firrtl.module @InvalidBundleDefNameElements() { + // expected-error @below {{has an invalid AugmentedBundleType that does not contain 'defName' and 'elements' fields: {class = "sifive.enterprise.grandcentral.AugmentedBundleType", description = "description of foo"}} + firrtl.view "View", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + description = "description of foo", + name = "foo" + } + ] + }> + } +} + +// ----- +// Bundle missing 'name'. + +firrtl.circuit "InvalidBundleNoName" { + firrtl.module @InvalidBundleNoName() { + // expected-error @below {{missing 'name' field in element of bundle: {class = "sifive.enterprise.grandcentral.AugmentedBundleType", defName = "foo", description = "description of foo", elements = []}} + firrtl.view "View", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + description = "description of foo", + defName = "foo", + elements = [] + } + ] + }> + } +} + +// ----- +// Vector missing 'name'. + +firrtl.circuit "InvalidVectorNoName" { + firrtl.module @InvalidVectorNoName() { + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + // expected-error @below {{has an invalid AugmentedVectorType that does not contain 'name' and 'elements' fields: {class = "sifive.enterprise.grandcentral.AugmentedVectorType", description = "description of foo", elements = [{class = "sifive.enterprise.grandcentral.AugmentedGroundType", description = "description of bar", name = "UNUSED"}]}} + firrtl.view "View", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + description = "description of foo", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of bar", + name = "UNUSED" + } + ] + } + ] + }>, %c0_ui1 : !firrtl.uint<1> + } +} diff --git a/test/Dialect/FIRRTL/grand-central-view.mlir b/test/Dialect/FIRRTL/grand-central-view.mlir new file mode 100644 index 000000000000..988cb7fb7c8f --- /dev/null +++ b/test/Dialect/FIRRTL/grand-central-view.mlir @@ -0,0 +1,513 @@ +// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-grand-central,symbol-dce))' -split-input-file %s | FileCheck %s + +// This is the main test that includes different interfaces of different +// types. All the interfaces share a common, simple circuit that provides two +// signals, "foo" and "bar". + +firrtl.circuit "InterfaceGroundType" attributes { + annotations = [ + { + class = "sifive.enterprise.grandcentral.ExtractGrandCentralAnnotation", + directory = "gct-dir", + filename = "bindings.sv" + }, + { + class = "sifive.enterprise.grandcentral.GrandCentralHierarchyFileAnnotation", + filename = "gct.yaml" + } + ] +} { + firrtl.module @Companion() { + // These are dummy references created for the purposes of the test. + %_ui0 = firrtl.verbatim.expr "???" : () -> !firrtl.uint<0> + %_ui1 = firrtl.verbatim.expr "???" : () -> !firrtl.uint<1> + %_ui2 = firrtl.verbatim.expr "???" : () -> !firrtl.uint<2> + %ref_ui0 = firrtl.ref.send %_ui0 : !firrtl.uint<0> + %ref_ui1 = firrtl.ref.send %_ui1 : !firrtl.uint<1> + %ref_ui2 = firrtl.ref.send %_ui2 : !firrtl.uint<2> + + %ui1 = firrtl.ref.resolve %ref_ui1 : !firrtl.probe> + %foo = firrtl.node %ui1 : !firrtl.uint<1> + + %ui2 = firrtl.ref.resolve %ref_ui2 : !firrtl.probe> + %bar = firrtl.node %ui2 : !firrtl.uint<2> + + %ui0 = firrtl.ref.resolve %ref_ui0 : !firrtl.probe> + %baz = firrtl.node %ui0 : !firrtl.uint<0> + + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + %c-1_si2 = firrtl.constant -1 : !firrtl.sint<2> + + firrtl.view "GroundView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "GroundView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "description of foo", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + description = "multi\nline\ndescription\nof\nbar", + name = "bar" + } + ] + }>, %foo, %bar : !firrtl.uint<1>, !firrtl.uint<2> + + firrtl.view "VectorView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "VectorView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + } + ], + name = "vector" + } + ] + }>, %foo, %foo : !firrtl.uint<1>, !firrtl.uint<1> + + + firrtl.view "BundleView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "BundleView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "Bundle", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + } + ], + name = "bundle" + } + ] + }>, %foo, %bar : !firrtl.uint<1>, !firrtl.uint<2> + + firrtl.view "VectorOfBundleView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "VectorOfBundleView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "Bundle2", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + } + ], + name = "bundle2" + } + ], + name = "vector" + } + ] + }>, %foo, %bar : !firrtl.uint<1>, !firrtl.uint<2> + + firrtl.view "VectorOfVectorView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "VectorOfVectorView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + defName = "Vector2", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "baz" + } + ], + name = "vector2" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedVectorType", + defName = "Vector2", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "baz" + } + ], + name = "vector2" + } + ], + name = "vector" + } + ] + }>, %bar, %bar, %bar, %bar, %bar, %bar : !firrtl.uint<2>, !firrtl.uint<2>, !firrtl.uint<2>, !firrtl.uint<2>, !firrtl.uint<2>, !firrtl.uint<2> + + firrtl.view "ConstantView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "ConstantView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + + name = "foo" + }, + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "bar" + } + ] + }>, %c0_ui1, %c-1_si2 : !firrtl.uint<1>, !firrtl.sint<2> + + firrtl.view "ZeroWidthView", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "ZeroWidthView", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "zerowidth" + } + ] + }>, %baz : !firrtl.uint<0> + } + firrtl.module @InterfaceGroundType() { + firrtl.instance companion @Companion() + } +} + +// All AugmentedBundleType annotations are removed from the circuit. +// +// CHECK-LABEL: firrtl.circuit "InterfaceGroundType" {{.+}} {annotations = +// CHECK-SAME: class = "sifive.enterprise.grandcentral.ExtractGrandCentralAnnotation" +// CHECK-NOT: class = "sifive.enterprise.grandcentral.AugmentedBundleType" +// CHECK-SAME: { + +// Check YAML Output. +// +// Note: Built-in vector serialization works slightly differently than +// user-defined vector serialization. This results in the verbose "[ ]" for the +// empty dimensions vector, and the terse "[]" for the empty instances vector. +// +// CHECK: sv.verbatim +// CHECK-SAME: - name: GroundView +// CHECK-SAME: fields: +// CHECK-SAME: - name: foo +// CHECK-SAME: description: description of foo +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 1 +// CHECK-SAME: - name: bar +// CHECK-SAME: description: \22multi\\nline\\ndescription\\nof\\nbar\22 +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 2 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: VectorView +// CHECK-SAME: fields: +// CHECK-SAME: - name: vector +// CHECK-SAME: dimensions: [ 2 ] +// CHECK-SAME: width: 1 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: BundleView +// CHECK-SAME: fields: [] +// CHECK-SAME: instances: +// CHECK-SAME: - name: bundle +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: interface: +// CHECK-SAME: name: Bundle +// CHECK-SAME: fields: +// CHECK-SAME: - name: foo +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 1 +// CHECK-SAME: - name: bar +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 2 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: VectorOfBundleView +// CHECK-SAME: fields: [] +// CHECK-SAME: instances: +// CHECK-SAME: - name: vector +// CHECK-SAME: dimensions: [ 1 ] +// CHECK-SAME: interface: +// CHECK-SAME: name: Bundle2 +// CHECK-SAME: fields: +// CHECK-SAME: - name: foo +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 1 +// CHECK-SAME: - name: bar +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 2 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: VectorOfVectorView +// CHECK-SAME: fields: +// CHECK-SAME: - name: vector +// CHECK-SAME: dimensions: [ 3, 2 ] +// CHECK-SAME: width: 2 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: ConstantView +// CHECK-SAME: fields: +// CHECK-SAME: - name: foo +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 1 +// CHECK-SAME: - name: bar +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 2 +// CHECK-SAME: instances: [] +// CHECK-SAME: - name: ZeroWidthView +// CHECK-SAME: fields: +// CHECK-SAME: - name: zerowidth +// CHECK-SAME: dimensions: [ ] +// CHECK-SAME: width: 0 +// CHECK-SAME: instances: [] + +// The shared companion contains all instantiated interfaces. +// AugmentedGroundType annotations are removed. Interface is driven via XMRs +// directly from ref resolve ops. +// +// CHECK: firrtl.module @Companion +// +// CHECK: %[[foo_ref:[a-zA-Z0-9_]+]] = firrtl.ref.resolve {{.+}} : !firrtl.probe> +// CHECK-NOT: sifive.enterprise.grandcentral.AugmentedGroundType +// CHECK: %[[bar_ref:[a-zA-Z0-9_]+]] = firrtl.ref.resolve {{.+}} : !firrtl.probe> +// CHECK-NOT: sifive.enterprise.grandcentral.AugmentedGroundType + +// CHECK: %GroundView = sv.interface.instance sym @[[groundSym:[a-zA-Z0-9_]+]] : !sv.interface<@GroundView> +// +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.foo = {{0}};" +// CHECK-SAME: (%foo) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[groundSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.bar = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[groundSym]]>]} + +// CHECK-NEXT: %VectorView = sv.interface.instance sym @[[vectorSym:[a-zA-Z0-9_]+]] : !sv.interface<@VectorView> +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0] = {{0}};" +// CHECK-SAME: (%foo) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[1] = {{0}};" +// CHECK-SAME: (%foo) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorSym]]>]} + +// CHECK-NEXT: %BundleView = sv.interface.instance sym @[[bundleSym:[a-zA-Z0-9_]+]] : !sv.interface<@BundleView> +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.bundle.foo = {{0}};" +// CHECK-SAME: (%foo) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[bundleSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.bundle.bar = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[bundleSym]]>]} + +// CHECK-NEXT: %VectorOfBundleView = sv.interface.instance sym @[[vectorOfBundleSym:[a-zA-Z0-9_]+]] : !sv.interface<@VectorOfBundleView> +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0].foo = {{0}};" +// CHECK-SAME: (%foo) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfBundleSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0].bar = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfBundleSym]]>]} + +// CHECK-NEXT: %VectorOfVectorView = sv.interface.instance sym @[[vectorOfVectorSym:[a-zA-Z0-9_]+]] : !sv.interface<@VectorOfVectorView> +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0][0] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0][1] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[0][2] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[1][0] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[1][1] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.vector[1][2] = {{0}};" +// CHECK-SAME: (%bar) : !firrtl.uint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[vectorOfVectorSym]]>]} + +// CHECK-NEXT: %ConstantView = sv.interface.instance sym @[[constantSym:[a-zA-Z0-9_]+]] : !sv.interface<@ConstantView> +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.foo = {{0}};" +// CHECK-SAME: (%c0_ui1) : !firrtl.uint<1> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[constantSym]]>]} +// CHECK{LITERAL}: sv.verbatim "assign {{1}}.bar = {{0}};" +// CHECK-SAME: (%c-1_si2) : !firrtl.sint<2> +// CHECK-SAME: {symbols = [#hw.innerNameRef<@Companion::@[[constantSym]]>]} + +// There are no more verbatim assigns after this. +// Zero-width views are not given XMR's. +// +// CHECK-NEXT: %ZeroWidthView = sv.interface.instance sym @[[zeroWidthSym:[a-zA-Z0-9_]+]] : !sv.interface<@ZeroWidthView> +// CHECK-NOT: sv.verbatim "assign + +// These views do not have a notion of "companion" module. +// +// CHECK: firrtl.module @InterfaceGroundType() +// CHECK: firrtl.instance companion +// CHECK-NOT: lowerToBind +// CHECK-NOT: output_file = #hw.output_file<"bindings.sv", excludeFromFileList>} + +// The body of all interfaces are populated with the correct signals, names, +// comments, and types. +// +// CHECK: sv.interface @GroundView +// CHECK-SAME: comment = "VCS coverage exclude_file" + +// CHECK-NOT: output_file = #hw.output_file<"gct-dir{{/|\\\\}}" + +// CHECK-NEXT: sv.verbatim "// description of foo" +// CHECK-NEXT: sv.interface.signal @foo : i1 +// CHECK-NEXT: sv.verbatim "// multi\0A// line\0A// description\0A// of\0A// bar" +// CHECK-NEXT: sv.interface.signal @bar : i2 +// +// CHECK: sv.interface @VectorView +// CHECK-NEXT: sv.interface.signal @vector : !hw.uarray<2xi1> +// +// CHECK: sv.interface @BundleView +// CHECK-NEXT: sv.verbatim "Bundle bundle();" +// +// CHECK: sv.interface @Bundle +// CHECK-NEXT: sv.interface.signal @foo : i1 +// CHECK-NEXT: sv.interface.signal @bar : i2 +// +// CHECK: sv.interface @VectorOfBundleView +// CHECK-NEXT: sv.verbatim "Bundle2 vector[1]();" +// +// CHECK: sv.interface @Bundle2 +// CHECK-NEXT: sv.interface.signal @foo : i1 +// CHECK-NEXT: sv.interface.signal @bar : i2 +// +// CHECK: sv.interface @VectorOfVectorView +// CHECK-NEXT: sv.interface.signal @vector : !hw.uarray<2xuarray<3xi2>> +// +// CHECK: sv.interface @ConstantView +// CHECK-NEXT: sv.interface.signal @foo : i1 +// CHECK-NEXT: sv.interface.signal @bar : i2 +// +// CHECK: sv.interface @ZeroWidthView +// CHECK-NEXT: sv.interface.signal @zerowidth : i0 + +// ----- + +firrtl.circuit "Top" attributes { + annotations = [ + { + class = "sifive.enterprise.grandcentral.ExtractGrandCentralAnnotation", + directory = ".", + filename = "bindings.sv" + } + ] +} { + firrtl.module private @Companion_w1(in %_gen_uint: !firrtl.uint<1>) { + %view_uintrefPort = firrtl.node %_gen_uint : !firrtl.uint<1> + firrtl.view "View_w1", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "MyInterface_w1", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "SameName", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "uint" + } + ], + name = "SameName" + } + ] + }>, %view_uintrefPort : !firrtl.uint<1> + } + firrtl.module private @Companion_w2(in %_gen_uint: !firrtl.uint<2>) { + %view_uintrefPort = firrtl.node %_gen_uint : !firrtl.uint<2> + firrtl.view "View_w2", <{ + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "MyInterface_w2", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedBundleType", + defName = "SameName", + elements = [ + { + class = "sifive.enterprise.grandcentral.AugmentedGroundType", + name = "uint" + } + ], + name = "SameName" + } + ] + }>, %view_uintrefPort : !firrtl.uint<2> + } + firrtl.module private @DUT() { + %c0_ui2 = firrtl.constant 0 : !firrtl.uint<2> + %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1> + %a_w1 = firrtl.wire {annotations = [{class = "firrtl.transforms.DontTouchAnnotation"}]} : !firrtl.uint<1> + firrtl.matchingconnect %a_w1, %c0_ui1 : !firrtl.uint<1> + %a_w2 = firrtl.wire {annotations = [{class = "firrtl.transforms.DontTouchAnnotation"}]} : !firrtl.uint<2> + firrtl.matchingconnect %a_w2, %c0_ui2 : !firrtl.uint<2> + %companion_w1__gen_uint = firrtl.instance companion_w1 @Companion_w1(in _gen_uint: !firrtl.uint<1>) + %companion_w2__gen_uint = firrtl.instance companion_w2 @Companion_w2(in _gen_uint: !firrtl.uint<2>) + firrtl.matchingconnect %companion_w1__gen_uint, %a_w1 : !firrtl.uint<1> + firrtl.matchingconnect %companion_w2__gen_uint, %a_w2 : !firrtl.uint<2> + } + firrtl.module @Top() { + firrtl.instance dut @DUT() + } +} + +// Check that the correct subinterface name is used when aliasing is possible. +// Here, SameName is used twice as a sub-interface name and we need to make sure +// that MyInterface_w2 uses the uniqued name of SameName. +// +// See: https://github.com/llvm/circt/issues/4234 + +// CHECK-LABEL: sv.interface @MyInterface_w1 {{.+}} { +// CHECK-NEXT: sv.verbatim "SameName SameName();" +// CHECK-NEXT: } +// CHECK-LABEL: sv.interface @MyInterface_w2 {{.+}} { +// CHECK-NEXT: sv.verbatim "SameName_0 SameName();" +// CHECK-NEXT: } + +// ----- + +firrtl.circuit "NoInterfaces" attributes { + annotations = [ + {class = "sifive.enterprise.grandcentral.GrandCentralHierarchyFileAnnotation", + filename = "gct.yaml"}]} { + firrtl.module @NoInterfaces() {} +} + +// CHECK-LABEL: module { +// CHECK: sv.verbatim +// CHECK-SAME: []