diff --git a/include/circt/Dialect/FIRRTL/Passes.h b/include/circt/Dialect/FIRRTL/Passes.h index 1b8779f192..5d2b54e61b 100644 --- a/include/circt/Dialect/FIRRTL/Passes.h +++ b/include/circt/Dialect/FIRRTL/Passes.h @@ -24,6 +24,7 @@ namespace firrtl { std::unique_ptr createLowerFIRRTLToRTLModulePass(); std::unique_ptr createLowerFIRRTLToRTLPass(); +std::unique_ptr createLowerFIRRTLTypesPass(); } // namespace firrtl } // namespace circt diff --git a/include/circt/Dialect/FIRRTL/Passes.td b/include/circt/Dialect/FIRRTL/Passes.td index 61ecb3b734..be450f3c29 100644 --- a/include/circt/Dialect/FIRRTL/Passes.td +++ b/include/circt/Dialect/FIRRTL/Passes.td @@ -9,6 +9,12 @@ include "mlir/Pass/PassBase.td" +/// Lower firrtl.module port types to ground types. +def LowerFIRRTLTypes : Pass<"lower-firrtl-types", "firrtl::FModuleOp"> { + let summary = "Lower FIRRTL types to ground types"; + let constructor = "circt::firrtl::createLowerFIRRTLTypesPass()"; +} + /// Lower firrtl.module's to rtl.module. def LowerFIRRTLToRTLModule : Pass<"lower-firrtl-to-rtl-module", "ModuleOp"> { diff --git a/lib/Dialect/FIRRTL/LowerToRTL.cpp b/lib/Dialect/FIRRTL/LowerToRTL.cpp index 41a8a48bdd..d8a837075f 100644 --- a/lib/Dialect/FIRRTL/LowerToRTL.cpp +++ b/lib/Dialect/FIRRTL/LowerToRTL.cpp @@ -2,6 +2,7 @@ // //===----------------------------------------------------------------------===// +#include "PassDetails.h" #include "circt/Dialect/FIRRTL/Ops.h" #include "circt/Dialect/FIRRTL/Passes.h" #include "circt/Dialect/FIRRTL/Visitors.h" @@ -19,9 +20,6 @@ static T getTypeOf(Value v) { return v.getType().cast(); } -#define GEN_PASS_CLASSES -#include "circt/Dialect/FIRRTL/FIRRTLPasses.h.inc" - /// Given a FIRRTL type, return the corresponding type for the RTL dialect. /// This returns a null type if it cannot be lowered. static Type lowerType(Type type) { diff --git a/lib/Dialect/FIRRTL/LowerTypes.cpp b/lib/Dialect/FIRRTL/LowerTypes.cpp new file mode 100644 index 0000000000..fdd43d06c4 --- /dev/null +++ b/lib/Dialect/FIRRTL/LowerTypes.cpp @@ -0,0 +1,370 @@ +//===- LowerTypes.cpp - Lower FIRRTL types to ground types ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +#include "PassDetails.h" +#include "circt/Dialect/FIRRTL/Ops.h" +#include "circt/Dialect/FIRRTL/Passes.h" +#include "circt/Dialect/FIRRTL/Types.h" +#include "circt/Dialect/FIRRTL/Visitors.h" +#include "circt/Support/ImplicitLocOpBuilder.h" +using namespace circt; +using namespace firrtl; + +// This represents a flattened bundle field element. +struct FlatBundleFieldEntry { + // This is the underlying ground type of the field. + FIRRTLType type; + // This is a suffix to add to the field name to make it unique. + std::string suffix; + // This indicates whether the field was flipped to be an output. + bool isOutput; + + // Helper to determine if a fully flattened type needs to be flipped. + FIRRTLType getPortType() { return isOutput ? FlipType::get(type) : type; } +}; + +// Convert a nested bundle of fields into a flat list of fields. This is used +// when working with instances and mems to flatten them. +static void flattenBundleTypes(FIRRTLType type, StringRef suffixSoFar, + bool isFlipped, + SmallVectorImpl &results) { + if (auto flip = type.dyn_cast()) + return flattenBundleTypes(flip.getElementType(), suffixSoFar, !isFlipped, + results); + + // In the base case we record this field. + auto bundle = type.dyn_cast(); + if (!bundle) { + results.push_back({type, suffixSoFar.str(), isFlipped}); + return; + } + + SmallString<16> tmpSuffix(suffixSoFar); + + // Otherwise, we have a bundle type. Break it down. + for (auto &elt : bundle.getElements()) { + // Construct the suffix to pass down. + tmpSuffix.resize(suffixSoFar.size()); + tmpSuffix.push_back('_'); + tmpSuffix.append(elt.first.strref()); + // Recursively process subelements. + flattenBundleTypes(elt.second, tmpSuffix, isFlipped, results); + } +} + +//===----------------------------------------------------------------------===// +// Pass Infrastructure +//===----------------------------------------------------------------------===// + +namespace { +struct FIRRTLTypesLowering : public LowerFIRRTLTypesBase, + public FIRRTLVisitor { + + using ValueIdentifier = std::pair; + using FIRRTLVisitor::visitDecl; + using FIRRTLVisitor::visitExpr; + using FIRRTLVisitor::visitStmt; + + void runOnOperation() override; + + // Lowering operations. + void visitDecl(InstanceOp op); + void visitExpr(SubfieldOp op); + void visitStmt(ConnectOp op); + +private: + // Lowering module block arguments. + void lowerArg(BlockArgument arg, FIRRTLType type); + + // Helpers to manage state. + Value addArg(Type type, unsigned oldArgNumber, StringRef nameSuffix = ""); + + void setBundleLowering(Value oldValue, StringRef flatField, Value newValue); + Value getBundleLowering(Value oldValue, StringRef flatField); + void getAllBundleLowerings(Value oldValue, SmallVectorImpl &results); + + // The builder is set and maintained in the main loop. + ImplicitLocOpBuilder *builder; + + // State to keep track of arguments and operations to clean up at the end. + SmallVector argsToRemove; + SmallVector opsToRemove; + + // State to keep a mapping from (Value, Identifier) pairs to flattened values. + DenseMap loweredBundleValues; +}; +} // end anonymous namespace + +/// This is the pass constructor. +std::unique_ptr circt::firrtl::createLowerFIRRTLTypesPass() { + return std::make_unique(); +} + +// This is the main entrypoint for the lowering pass. +void FIRRTLTypesLowering::runOnOperation() { + auto module = getOperation(); + auto *body = module.getBodyBlock(); + + ImplicitLocOpBuilder theBuilder(module.getLoc(), &getContext()); + builder = &theBuilder; + + // Lower the module block arguments. + SmallVector args(body->args_begin(), body->args_end()); + for (auto arg : args) + if (auto type = arg.getType().dyn_cast()) + lowerArg(arg, type); + + // Lower the operations. + for (auto &op : body->getOperations()) { + builder->setInsertionPoint(&op); + builder->setLoc(op.getLoc()); + dispatchVisitor(&op); + } + + // Remove ops that have been lowered. + while (!opsToRemove.empty()) + opsToRemove.pop_back_val()->erase(); + + // Remove args that have been lowered. + getOperation().eraseArguments(argsToRemove); + argsToRemove.clear(); + + // Reset lowered state. + loweredBundleValues.clear(); +} + +//===----------------------------------------------------------------------===// +// Lowering module block arguments. +//===----------------------------------------------------------------------===// + +// Lower arguments with bundle type by flattening them. +void FIRRTLTypesLowering::lowerArg(BlockArgument arg, FIRRTLType type) { + unsigned argNumber = arg.getArgNumber(); + + // Flatten any bundle types. + SmallVector fieldTypes; + flattenBundleTypes(type, "", false, fieldTypes); + + for (auto field : fieldTypes) { + // Create new block arguments. + auto type = field.getPortType(); + auto newValue = addArg(type, argNumber, field.suffix); + + // If this field was flattened from a bundle. + if (!field.suffix.empty()) { + // Remove field separator prefix for consitency with the rest of the pass. + auto fieldName = StringRef(field.suffix).drop_front(1); + + // Map the flattened suffix for the original bundle to the new value. + setBundleLowering(arg, fieldName, newValue); + } else { + // Lower any other arguments by copying them to keep the relative order. + arg.replaceAllUsesWith(newValue); + } + } + + // Remember to remove the original block argument. + argsToRemove.push_back(argNumber); +} + +//===----------------------------------------------------------------------===// +// Lowering operations. +//===----------------------------------------------------------------------===// + +// Lower instance operations in the same way as module block arguments. Bundles +// are flattened, and other arguments are copied to keep the relative order. By +// ensuring both lowerings are the same, we can process every module in the +// circuit in parallel, and every instance will have the correct ports. +void FIRRTLTypesLowering::visitDecl(InstanceOp op) { + // The instance's ports are represented as one value with a bundle type. + BundleType originalType = op.result().getType().cast(); + + // Create a new, flat bundle type for the new result + SmallVector bundleElements; + for (auto element : originalType.getElements()) { + // Flatten any nested bundle types the usual way. + SmallVector fieldTypes; + flattenBundleTypes(element.second, element.first, false, fieldTypes); + + for (auto field : fieldTypes) { + // Store the flat type for the new bundle type. + auto flatName = builder->getIdentifier(field.suffix); + auto flatType = field.getPortType(); + auto newElement = BundleType::BundleElement(flatName, flatType); + bundleElements.push_back(newElement); + } + } + + // Get the new bundle type and create a new instance. + auto newType = BundleType::get(bundleElements, &getContext()); + + auto newInstance = + builder->create(newType, op.moduleNameAttr(), op.nameAttr()); + + // Create new subfield ops for each field of the instance. + for (auto element : bundleElements) { + auto newSubfield = + builder->create(element.second, newInstance, element.first); + + // Map the flattened suffix for the original bundle to the new value. + setBundleLowering(op, element.first, newSubfield); + } + + // Remember to remove the original op. + opsToRemove.push_back(op); +} + +// Lowering subfield operations has to deal with three different cases: +// a) the input value is from a module block argument +// b) the input value is from another subfield operation's result +// c) the input value is from an instance +// +// This is accomplished by storing value and suffix mappings that point to the +// flattened value. If the subfield op is accessing the leaf field of a bundle, +// it replaces all uses with the flattened value. Otherwise, it flattens the +// rest of the bundle and adds the flattened values to the mapping for each +// partial suffix. +void FIRRTLTypesLowering::visitExpr(SubfieldOp op) { + Value input = op.input(); + Value result = op.result(); + StringRef fieldname = op.fieldname(); + FIRRTLType resultType = result.getType().cast(); + + // Flatten any nested bundle types the usual way. + SmallVector fieldTypes; + flattenBundleTypes(resultType, fieldname, false, fieldTypes); + + for (auto field : fieldTypes) { + // Look up the mapping for this suffix. + auto newValue = getBundleLowering(input, field.suffix); + + // The prefix is the field name and possibly field separator. + auto prefixSize = fieldname.size(); + if (field.suffix.size() > fieldname.size()) + prefixSize += 1; + + // Get the remaining field suffix by removing the prefix. + auto partialSuffix = StringRef(field.suffix).drop_front(prefixSize); + + // If we are at the leaf of a bundle. + if (partialSuffix.empty()) + // Replace the result with the flattened value. + result.replaceAllUsesWith(newValue); + else + // Map the partial suffix for the result value to the flattened value. + setBundleLowering(result, partialSuffix, newValue); + } + + // Remember to remove the original op. + opsToRemove.push_back(op); +} + +// Lowering connects only has to deal with one special case: connecting two +// bundles. This situation should only arise when both of the arguments are a +// bundle that was: +// a) originally a block argument +// b) originally an instance's port +// +// When two such bundles are connected, none of the subfield visits have a +// chance to lower them, so we must ensure they have the same number of +// flattened values and flatten out this connect into multiple connects. +void FIRRTLTypesLowering::visitStmt(ConnectOp op) { + Value dest = op.dest(); + Value src = op.src(); + + // If we aren't connecting two bundles, there is nothing to do. + if (!dest.getType().isa() || !src.getType().isa()) + return; + + // Get the lowered values for each side. + SmallVector destValues; + getAllBundleLowerings(dest, destValues); + + SmallVector srcValues; + getAllBundleLowerings(src, srcValues); + + // Check that we got out the same number of values from each bundle. + assert(destValues.size() == srcValues.size() && + "connected bundles don't match"); + + for (auto tuple : llvm::zip_first(destValues, srcValues)) { + Value newDest = std::get<0>(tuple); + Value newSrc = std::get<1>(tuple); + builder->create(newDest, newSrc); + } + + // Remember to remove the original op. + opsToRemove.push_back(op); +} + +//===----------------------------------------------------------------------===// +// Helpers to manage state. +//===----------------------------------------------------------------------===// + +static DictionaryAttr getArgAttrs(StringAttr nameAttr, StringRef suffix, + OpBuilder *builder) { + SmallString<16> newName(nameAttr.getValue()); + newName += suffix; + + StringAttr newNameAttr = builder->getStringAttr(newName); + auto attr = builder->getNamedAttr("firrtl.name", newNameAttr); + + return builder->getDictionaryAttr(attr); +} + +// Creates and returns a new block argument of the specified type to the module. +// This also maintains the name attribute for the new argument, possibly with a +// new suffix appended. +Value FIRRTLTypesLowering::addArg(Type type, unsigned oldArgNumber, + StringRef nameSuffix) { + FModuleOp module = getOperation(); + Block *body = module.getBodyBlock(); + + // Append the new argument. + auto newValue = body->addArgument(type); + + // Keep the module's type up-to-date. + module.setType(builder->getFunctionType(body->getArgumentTypes(), {})); + + // Copy over the name attribute for the new argument. + StringAttr nameAttr = getFIRRTLNameAttr(module.getArgAttrs(oldArgNumber)); + if (nameAttr) { + auto newArgAttrs = getArgAttrs(nameAttr, nameSuffix, builder); + module.setArgAttrs(newValue.getArgNumber(), newArgAttrs); + } + + return newValue; +} + +// Store the mapping from a bundle typed value to a mapping from its field names +// to flat values. +void FIRRTLTypesLowering::setBundleLowering(Value oldValue, StringRef flatField, + Value newValue) { + auto flatFieldId = builder->getIdentifier(flatField); + auto &entry = loweredBundleValues[ValueIdentifier(oldValue, flatFieldId)]; + assert(!entry && "bundle lowering has already been set"); + entry = newValue; +} + +// For a mapped bundle typed value and a flat subfield name, retrieve and return +// the flat value if it exists. +Value FIRRTLTypesLowering::getBundleLowering(Value oldValue, + StringRef flatField) { + auto flatFieldId = builder->getIdentifier(flatField); + auto &entry = loweredBundleValues[ValueIdentifier(oldValue, flatFieldId)]; + assert(entry && "bundle lowering was not set"); + return entry; +} + +// For a mapped bundle typed value, retrieve and return the flat values for each +// field. +void FIRRTLTypesLowering::getAllBundleLowerings( + Value value, SmallVectorImpl &results) { + BundleType bundleType = value.getType().cast(); + for (auto element : bundleType.getElements()) + results.push_back(getBundleLowering(value, element.first)); +} diff --git a/lib/Dialect/FIRRTL/PassDetails.h b/lib/Dialect/FIRRTL/PassDetails.h new file mode 100644 index 0000000000..7f792e49ba --- /dev/null +++ b/lib/Dialect/FIRRTL/PassDetails.h @@ -0,0 +1,24 @@ +//===- PassDetails.h - FIRRTL pass class details ----------------*- C++ -*-===// +// +//===----------------------------------------------------------------------===// + +// clang-tidy seems to expect the absolute path in the header guard on some +// systems, so just disable it. +// NOLINTNEXTLINE(llvm-header-guard) +#ifndef DIALECT_FIRRTL_PASSDETAILS_H +#define DIALECT_FIRRTL_PASSDETAILS_H + +#include "circt/Dialect/FIRRTL/Ops.h" +#include "circt/Dialect/RTL/Ops.h" +#include "mlir/Pass/Pass.h" + +namespace circt { +namespace firrtl { + +#define GEN_PASS_CLASSES +#include "circt/Dialect/FIRRTL/FIRRTLPasses.h.inc" + +} // namespace firrtl +} // namespace circt + +#endif // DIALECT_FIRRTL_PASSDETAILS_H diff --git a/test/firrtl/lower-types.mlir b/test/firrtl/lower-types.mlir new file mode 100644 index 0000000000..1c279b29db --- /dev/null +++ b/test/firrtl/lower-types.mlir @@ -0,0 +1,99 @@ +// RUN: circt-opt -pass-pipeline='firrtl.circuit(lower-firrtl-types)' %s | FileCheck %s + +firrtl.circuit "TopLevel" { + + // CHECK-LABEL: firrtl.module @Simple + // CHECK-SAME: %[[SOURCE_VALID_NAME:source_valid]]: [[SOURCE_VALID_TYPE:!firrtl.uint<1>]] + // CHECK-SAME: %[[SOURCE_READY_NAME:source_ready]]: [[SOURCE_READY_TYPE:!firrtl.flip>]] + // CHECK-SAME: %[[SOURCE_DATA_NAME:source_data]]: [[SOURCE_DATA_TYPE:!firrtl.uint<64>]] + // CHECK-SAME: %[[SINK_VALID_NAME:sink_valid]]: [[SINK_VALID_TYPE:!firrtl.flip>]] + // CHECK-SAME: %[[SINK_READY_NAME:sink_ready]]: [[SINK_READY_TYPE:!firrtl.uint<1>]] + // CHECK-SAME: %[[SINK_DATA_NAME:sink_data]]: [[SINK_DATA_TYPE:!firrtl.flip>]] + firrtl.module @Simple(%source: !firrtl.bundle, ready: flip>, data: uint<64>>, + %sink: !firrtl.bundle>, ready: uint<1>, data: flip>>) { + + // CHECK-NEXT: firrtl.when %[[SOURCE_VALID_NAME]] + // CHECK-NEXT: firrtl.connect %[[SINK_DATA_NAME]], %[[SOURCE_DATA_NAME]] : [[SINK_DATA_TYPE]], [[SOURCE_DATA_TYPE]] + // CHECK-NEXT: firrtl.connect %[[SINK_VALID_NAME]], %[[SOURCE_VALID_NAME]] : [[SINK_VALID_TYPE]], [[SOURCE_VALID_TYPE]] + // CHECK-NEXT: firrtl.connect %[[SOURCE_READY_NAME]], %[[SINK_READY_NAME]] : [[SOURCE_READY_TYPE]], [[SINK_READY_TYPE]] + + %0 = firrtl.subfield %source("valid") : (!firrtl.bundle, ready: flip>, data: uint<64>>) -> !firrtl.uint<1> + %1 = firrtl.subfield %source("ready") : (!firrtl.bundle, ready: flip>, data: uint<64>>) -> !firrtl.flip> + %2 = firrtl.subfield %source("data") : (!firrtl.bundle, ready: flip>, data: uint<64>>) -> !firrtl.uint<64> + %3 = firrtl.subfield %sink("valid") : (!firrtl.bundle>, ready: uint<1>, data: flip>>) -> !firrtl.flip> + %4 = firrtl.subfield %sink("ready") : (!firrtl.bundle>, ready: uint<1>, data: flip>>) -> !firrtl.uint<1> + %5 = firrtl.subfield %sink("data") : (!firrtl.bundle>, ready: uint<1>, data: flip>>) -> !firrtl.flip> + firrtl.when %0 { + firrtl.connect %5, %2 : !firrtl.flip>, !firrtl.uint<64> + firrtl.connect %3, %0 : !firrtl.flip>, !firrtl.uint<1> + firrtl.connect %1, %4 : !firrtl.flip>, !firrtl.uint<1> + } + } + + // CHECK-LABEL: firrtl.module @Recursive + // CHECK-SAME: %[[FLAT_ARG_1_NAME:arg_foo_bar_baz]]: [[FLAT_ARG_1_TYPE:!firrtl.uint<1>]] + // CHECK-SAME: %[[FLAT_ARG_2_NAME:arg_foo_qux]]: [[FLAT_ARG_2_TYPE:!firrtl.sint<64>]] + // CHECK-SAME: %[[OUT_1_NAME:out1]]: [[OUT_1_TYPE:!firrtl.flip>]] + // CHECK-SAME: %[[OUT_2_NAME:out2]]: [[OUT_2_TYPE:!firrtl.flip>]] + firrtl.module @Recursive(%arg: !firrtl.bundle>, qux: sint<64>>>, + %out1: !firrtl.flip>, %out2: !firrtl.flip>) { + + // CHECK-NEXT: firrtl.connect %[[OUT_1_NAME]], %[[FLAT_ARG_1_NAME]] : [[OUT_1_TYPE]], [[FLAT_ARG_1_TYPE]] + // CHECK-NEXT: firrtl.connect %[[OUT_2_NAME]], %[[FLAT_ARG_2_NAME]] : [[OUT_2_TYPE]], [[FLAT_ARG_2_TYPE]] + + %0 = firrtl.subfield %arg("foo") : (!firrtl.bundle>, qux: sint<64>>>) -> !firrtl.bundle>, qux: sint<64>> + %1 = firrtl.subfield %0("bar") : (!firrtl.bundle>, qux: sint<64>>) -> !firrtl.bundle> + %2 = firrtl.subfield %1("baz") : (!firrtl.bundle>) -> !firrtl.uint<1> + %3 = firrtl.subfield %0("qux") : (!firrtl.bundle>, qux: sint<64>>) -> !firrtl.sint<64> + firrtl.connect %out1, %2 : !firrtl.flip>, !firrtl.uint<1> + firrtl.connect %out2, %3 : !firrtl.flip>, !firrtl.sint<64> + } + + // CHECK-LABEL: firrtl.module @Uniquification + // CHECK-SAME: %[[FLATTENED_ARG:a_b]]: [[FLATTENED_TYPE:!firrtl.uint<1>]], + // CHECK-NOT: %[[FLATTENED_ARG]] + // CHECK-SAME: %[[RENAMED_ARG:a_b.+]]: [[RENAMED_TYPE:!firrtl.uint<1>]] {firrtl.name = "[[FLATTENED_ARG]]"} + firrtl.module @Uniquification(%a: !firrtl.bundle>, %a_b: !firrtl.uint<1>) { + } + + // CHECK-LABEL: firrtl.module @TopLevel + // CHECK-SAME: %[[SOURCE_VALID_NAME:source_valid]]: [[SOURCE_VALID_TYPE:!firrtl.uint<1>]] + // CHECK-SAME: %[[SOURCE_READY_NAME:source_ready]]: [[SOURCE_READY_TYPE:!firrtl.flip>]] + // CHECK-SAME: %[[SOURCE_DATA_NAME:source_data]]: [[SOURCE_DATA_TYPE:!firrtl.uint<64>]] + // CHECK-SAME: %[[SINK_VALID_NAME:sink_valid]]: [[SINK_VALID_TYPE:!firrtl.flip>]] + // CHECK-SAME: %[[SINK_READY_NAME:sink_ready]]: [[SINK_READY_TYPE:!firrtl.uint<1>]] + // CHECK-SAME: %[[SINK_DATA_NAME:sink_data]]: [[SINK_DATA_TYPE:!firrtl.flip>]] + firrtl.module @TopLevel(%source: !firrtl.bundle, ready: flip>, data: uint<64>>, + %sink: !firrtl.bundle>, ready: uint<1>, data: flip>>) { + + // CHECK-NEXT: %[[INSTANCE:.+]] = firrtl.instance @Simple + // CHECK-SAME: !firrtl.bundle + // CHECK-SAME: [[SUB_SOURCE_VALID_NAME:source_valid]]: [[SUB_SOURCE_VALID_TYPE:flip>]] + // CHECK-SAME: [[SUB_SOURCE_READY_NAME:source_ready]]: [[SUB_SOURCE_READY_TYPE:uint<1>]] + // CHECK-SAME: [[SUB_SOURCE_DATA_NAME:source_data]]: [[SUB_SOURCE_DATA_TYPE:flip>]] + // CHECK-SAME: [[SUB_SINK_VALID_NAME:sink_valid]]: [[SUB_SINK_VALID_TYPE:uint<1>]] + // CHECK-SAME: [[SUB_SINK_READY_NAME:sink_ready]]: [[SUB_SINK_READY_TYPE:flip>]] + // CHECK-SAME: [[SUB_SINK_DATA_NAME:sink_data]]: [[SUB_SINK_DATA_TYPE:uint<64>]] + %0 = firrtl.instance @Simple {name = ""} : !firrtl.bundle>, ready: uint<1>, data: flip>>, sink: bundle, ready: flip>, data: uint<64>>> + + // CHECK-NEXT: %[[FIELD1:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_VALID_NAME]]") {{.*}} [[SUB_SOURCE_VALID_TYPE]] + // CHECK-NEXT: %[[FIELD2:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_READY_NAME]]") {{.*}} [[SUB_SOURCE_READY_TYPE]] + // CHECK-NEXT: %[[FIELD3:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SOURCE_DATA_NAME]]") {{.*}} [[SUB_SOURCE_DATA_TYPE]] + // CHECK-NEXT: %[[FIELD4:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_VALID_NAME]]") {{.*}} [[SUB_SINK_VALID_TYPE]] + // CHECK-NEXT: %[[FIELD5:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_READY_NAME]]") {{.*}} [[SUB_SINK_READY_TYPE]] + // CHECK-NEXT: %[[FIELD6:.+]] = firrtl.subfield %[[INSTANCE]]("[[SUB_SINK_DATA_NAME]]") {{.*}} [[SUB_SINK_DATA_TYPE]] + + // CHECK-NEXT: firrtl.connect %[[FIELD1]], %[[SOURCE_VALID_NAME]] : !firrtl.[[SUB_SOURCE_VALID_TYPE]], [[SOURCE_VALID_TYPE]] + // CHECK-NEXT: firrtl.connect %[[FIELD2]], %[[SOURCE_READY_NAME]] : !firrtl.[[SUB_SOURCE_READY_TYPE]], [[SOURCE_READY_TYPE]] + // CHECK-NEXT: firrtl.connect %[[FIELD3]], %[[SOURCE_DATA_NAME]] : !firrtl.[[SUB_SOURCE_DATA_TYPE]], [[SOURCE_DATA_TYPE]] + // CHECK-NEXT: firrtl.connect %[[SINK_VALID_NAME]], %[[FIELD4]] : [[SINK_VALID_TYPE]], !firrtl.[[SUB_SINK_VALID_TYPE]] + // CHECK-NEXT: firrtl.connect %[[SINK_READY_NAME]], %[[FIELD5]] : [[SINK_READY_TYPE]], !firrtl.[[SUB_SINK_READY_TYPE]] + // CHECK-NEXT: firrtl.connect %[[SINK_DATA_NAME]], %[[FIELD6]] : [[SINK_DATA_TYPE]], !firrtl.[[SUB_SINK_DATA_TYPE]] + + %1 = firrtl.subfield %0("source") : (!firrtl.bundle>, ready: uint<1>, data: flip>>, sink: bundle, ready: flip>, data: uint<64>>>) -> !firrtl.bundle>, ready: uint<1>, data: flip>> + firrtl.connect %1, %source : !firrtl.bundle>, ready: uint<1>, data: flip>>, !firrtl.bundle, ready: flip>, data: uint<64>> + + %2 = firrtl.subfield %0("sink") : (!firrtl.bundle>, ready: uint<1>, data: flip>>, sink: bundle, ready: flip>, data: uint<64>>>) -> !firrtl.bundle, ready: flip>, data: uint<64>> + firrtl.connect %sink, %2 : !firrtl.bundle>, ready: uint<1>, data: flip>>, !firrtl.bundle, ready: flip>, data: uint<64>> + } +} diff --git a/tools/firtool/firtool.cpp b/tools/firtool/firtool.cpp index 3fe649c1c2..e2a5ae6687 100644 --- a/tools/firtool/firtool.cpp +++ b/tools/firtool/firtool.cpp @@ -112,6 +112,8 @@ processBuffer(std::unique_ptr ownedBuffer, // Run the lower-to-rtl pass if requested. if (lowerToRTL) { + pm.nest().nest().addPass( + firrtl::createLowerFIRRTLTypesPass()); pm.addPass(firrtl::createLowerFIRRTLToRTLModulePass()); pm.nest().addPass(firrtl::createLowerFIRRTLToRTLPass()); }