[firrtl] Move LowerLayers after LowerXMR (#8405)

Move the `LowerLayers` pass after the `LowerXMR` pass.  To do this, all
passes at the end of the CHIRRTL to Low FIRRTL pipeline are moved after
`LowerXMR`.  This is necessary because the `LowerLayers` pass cannot, at
present, be moved after the passes at the end of the pipeline.

This is done to enable forcing out of layers.  By lowering probes to XMRs,
the layers can be lowered trivially to modules/instances and their XMRs
will now (seemingly) magically just work.  By doing the loweirng in this
way, it avoids ever having to represent an input probe in the FIRRTL
dialect.

A consequence of this is that there are now simplifying
assumptions (preconditions) that can be made about the `LowerLayers` pass:

1. It will never see a number of probe ops because the `LowerXMR` pass has
   a postcondition that all of these are removed.

2. Input and output ports can never be created on modules created from
   layer blocks.

While I am generally always in favor of passes being relocatable anywhere
in the pipeline, this is one pass that really does _not_ make sense to be
relocatable.  In effect, this pass is part of the lowering from FIRRTL to
HW.  There is no point in being able to use it earlier in the pipeline.
That said, it still is tested to work with things which we may one-day
preserve, like `WhenOp`s.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
This commit is contained in:
Schuyler Eldridge 2025-04-24 11:02:55 -04:00 committed by GitHub
parent a358f9bef5
commit 5cffc8537b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 624 additions and 808 deletions

View File

@ -236,11 +236,11 @@ MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulatePreprocessTransforms(
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateCHIRRTLToLowFIRRTL(
MlirPassManager pm, CirctFirtoolFirtoolOptions options,
MlirStringRef inputFilename);
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateLowFIRRTLToHW(
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
MlirPassManager pm, CirctFirtoolFirtoolOptions options,
MlirStringRef inputFilename);
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateHWToSV(
MlirPassManager pm, CirctFirtoolFirtoolOptions options);

View File

@ -444,11 +444,11 @@ LogicalResult populatePreprocessTransforms(mlir::PassManager &pm,
const FirtoolOptions &opt);
LogicalResult populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
const FirtoolOptions &opt,
StringRef inputFilename);
const FirtoolOptions &opt);
LogicalResult populateLowFIRRTLToHW(mlir::PassManager &pm,
const FirtoolOptions &opt);
const FirtoolOptions &opt,
StringRef inputFilename);
LogicalResult populateHWToSV(mlir::PassManager &pm, const FirtoolOptions &opt);

View File

@ -340,16 +340,17 @@ circtFirtoolPopulatePreprocessTransforms(MlirPassManager pm,
MlirLogicalResult
circtFirtoolPopulateCHIRRTLToLowFIRRTL(MlirPassManager pm,
CirctFirtoolFirtoolOptions options,
MlirStringRef inputFilename) {
return wrap(firtool::populateCHIRRTLToLowFIRRTL(*unwrap(pm), *unwrap(options),
unwrap(inputFilename)));
CirctFirtoolFirtoolOptions options) {
return wrap(
firtool::populateCHIRRTLToLowFIRRTL(*unwrap(pm), *unwrap(options)));
}
MlirLogicalResult
circtFirtoolPopulateLowFIRRTLToHW(MlirPassManager pm,
CirctFirtoolFirtoolOptions options) {
return wrap(firtool::populateLowFIRRTLToHW(*unwrap(pm), *unwrap(options)));
CirctFirtoolFirtoolOptions options,
MlirStringRef inputFilename) {
return wrap(firtool::populateLowFIRRTLToHW(*unwrap(pm), *unwrap(options),
unwrap(inputFilename)));
}
MlirLogicalResult

View File

@ -13,6 +13,7 @@
#include "circt/Dialect/FIRRTL/FIRRTLUtils.h"
#include "circt/Dialect/FIRRTL/Namespace.h"
#include "circt/Dialect/FIRRTL/Passes.h"
#include "circt/Dialect/HW/HierPathCache.h"
#include "circt/Dialect/HW/InnerSymbolNamespace.h"
#include "circt/Dialect/SV/SVOps.h"
#include "circt/Support/Utils.h"
@ -52,6 +53,18 @@ struct ConnectInfo {
/// for modules and files, as well as by convention.
enum class Delimiter { BindModule = '_', BindFile = '-', InlineMacro = '$' };
/// This struct contains pre-allocated "safe" names that parallel regions can
/// use to create names in the global namespace. This is allocated per-layer
/// block.
struct LayerBlockGlobals {
/// If the layer needs to create a module, use this name.
StringRef moduleName;
/// If the layer needs to create a hw::HierPathOp, use this name.
StringRef hierPathName;
};
} // namespace
// A mapping of an old InnerRefAttr to the new inner symbol and module name that
@ -98,6 +111,16 @@ static SmallString<32> moduleNameForLayer(StringRef moduleName,
return result;
}
static SmallString<32> hierPathNameForLayer(StringRef moduleName,
SymbolRefAttr layerName) {
SmallString<32> result("__lowerLayers_path");
appendName(moduleName, result, /*toLower=*/false,
/*delimiter=*/Delimiter::BindModule);
appendName(layerName, result, /*toLower=*/false,
/*delimiter=*/Delimiter::BindModule);
return result;
}
/// For a layerblock `@A::@B::@C`,
/// the generated instance is called `a_b_c`.
static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
@ -158,8 +181,7 @@ class LowerLayersPass
/// Safely build a new module with a given namehint. This handles geting a
/// lock to modify the top-level circuit.
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock,
SmallVectorImpl<PortInfo> &ports);
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock);
/// Strip layer colors from the module's interface.
FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
@ -174,10 +196,6 @@ class LowerLayersPass
/// Update the value's type to remove any layers from any probe types.
void removeLayersFromValue(Value value);
/// Remove any layers from the result of the cast. If the cast becomes a nop,
/// remove the cast itself from the IR.
void removeLayersFromRefCast(RefCastOp cast);
/// Lower an inline layerblock to an ifdef block.
void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
@ -195,14 +213,18 @@ class LowerLayersPass
/// Indicates exclusive access to modify the circuitNamespace and the circuit.
llvm::sys::SmartMutex<true> *circuitMutex;
/// A map of layer blocks to module name that should be created for it.
DenseMap<LayerBlockOp, StringRef> moduleNames;
/// A map of layer blocks to "safe" global names which are fine to create in
/// the circuit namespace.
DenseMap<LayerBlockOp, LayerBlockGlobals> layerBlockGlobals;
/// A map from inline layers to their macro names.
DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
/// A mapping of symbol name to layer operation.
DenseMap<SymbolRefAttr, LayerOp> symbolToLayer;
/// Utility for creating hw::HierPathOp.
hw::HierPathCache *hierPathCache;
};
/// Multi-process safe function to build a module in the circuit and return it.
@ -210,15 +232,14 @@ class LowerLayersPass
/// generated if there are conflicts with the namehint in the circuit-level
/// namespace.
FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
LayerBlockOp layerBlock,
SmallVectorImpl<PortInfo> &ports) {
LayerBlockOp layerBlock) {
auto location = layerBlock.getLoc();
auto namehint = moduleNames.lookup(layerBlock);
auto namehint = layerBlockGlobals.lookup(layerBlock).moduleName;
llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
FModuleOp newModule = builder.create<FModuleOp>(
location, builder.getStringAttr(namehint),
ConventionAttr::get(builder.getContext(), Convention::Internal), ports,
ArrayAttr{});
ConventionAttr::get(builder.getContext(), Convention::Internal),
ArrayRef<PortInfo>{}, ArrayAttr{});
if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
assert(dir.isDirectory());
newModule->setAttr("output_file", dir);
@ -234,22 +255,6 @@ void LowerLayersPass::removeLayersFromValue(Value value) {
value.setType(type.removeLayer());
}
void LowerLayersPass::removeLayersFromRefCast(RefCastOp cast) {
auto result = cast.getResult();
auto oldType = result.getType();
if (oldType.getLayer()) {
auto input = cast.getInput();
auto srcType = input.getType();
auto newType = oldType.removeLayer();
if (newType == srcType) {
result.replaceAllUsesWith(input);
cast->erase();
return;
}
result.setType(newType);
}
}
void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
auto oldTypeAttrs = moduleLike.getPortTypesAttr();
SmallVector<Attribute> newTypeAttrs;
@ -324,47 +329,180 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
StringRef circuitName = circuitOp.getName();
hw::InnerSymbolNamespace ns(moduleOp);
// A cache of nodes we've created for values which are captured, but cannot
// have a symbol attached to them. This is done to avoid creating the same
// node multiple times.
DenseMap<Value, NodeOp> nodeCache;
// Get or create a node within this module. This is used when we need to
// create an XMRDerefOp to an expression.
auto getOrCreateNodeOp = [&](Value operand, ImplicitLocOpBuilder &builder,
StringRef nameHint) -> NodeOp {
auto it = nodeCache.find(operand);
if (it != nodeCache.end())
return it->getSecond();
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointAfter(operand.getDefiningOp());
return nodeCache
.insert({operand,
builder.create<NodeOp>(operand.getLoc(), operand, nameHint)})
.first->getSecond();
};
// Determine the replacement for an operand within the current region. Keep a
// densemap of replacements around to avoid creating the same hardware
// multiple times.
DenseMap<Value, Value> replacements;
auto getReplacement = [&](Operation *user, Value value) -> Value {
auto it = replacements.find(value);
if (it != replacements.end())
return it->getSecond();
ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
Value replacement;
auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
// If the operand is "special", e.g., it has no XMR representation, then we
// need to clone it.
//
// TODO: Change this to recursively clone. This will matter once FString
// operations have operands.
if (type_isa<FStringType>(value.getType())) {
localBuilder.setInsertionPoint(user);
replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
replacements.insert({value, replacement});
return replacement;
}
// If the operand is an XMR ref, then we _have_ to clone it.
auto *definingOp = value.getDefiningOp();
if (isa_and_present<XMRRefOp>(definingOp)) {
replacement = localBuilder.clone(*definingOp)->getResult(0);
replacements.insert({value, replacement});
return replacement;
}
// Determine the replacement value for the captured operand. There are
// three cases that can occur:
//
// 1. Capturing something zero-width. Create a zero-width constant zero.
// 2. Capture an expression or instance port. Drop a node and XMR deref
// that.
// 3. Capture something that can handle an inner sym. XMR deref that.
//
// Note: (3) can be either an operation or a _module_ port.
auto baseType = type_cast<FIRRTLBaseType>(value.getType());
if (baseType && baseType.getBitWidthOrSentinel() == 0) {
OpBuilder::InsertionGuard guard(localBuilder);
auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
replacement = localBuilder.createOrFold<BitCastOp>(
value.getType(), localBuilder.create<ConstantOp>(
zeroUIntType, getIntZerosAttr(zeroUIntType)));
} else {
auto *definingOp = value.getDefiningOp();
hw::InnerRefAttr innerRef;
if (definingOp) {
// TODO: Always create a node. This is a trade-off between
// optimizations and dead code. By adding the node, this allows the
// original path to be better optimized, but will leave dead code in the
// design. If the node is not created, then the output is less
// optimized. Err on the side of dead code. This _should_ be able to
// be removed eventually [1]. A node should only be necessary for
// operations which cannot have a symbol attached to them, i.e.,
// expressions or instance ports.
//
// [1]: https://github.com/llvm/circt/issues/8426
definingOp = getOrCreateNodeOp(value, localBuilder, "_layer_probe");
innerRef = getInnerRefTo(
definingOp, [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
} else {
auto portIdx = cast<BlockArgument>(value).getArgNumber();
innerRef = getInnerRefTo(
cast<FModuleLike>(*moduleOp), portIdx,
[&](auto) -> hw::InnerSymbolNamespace & { return ns; });
}
hw::HierPathOp hierPathOp;
{
// TODO: Move to before parallel region to avoid the lock.
auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
Block::iterator(moduleOp));
llvm::sys::SmartScopedLock<true> circuitLock(*circuitMutex);
hierPathOp = hierPathCache->getOrCreatePath(
localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
insertPoint, layerBlockGlobals.lookup(layerBlockOp).hierPathName);
hierPathOp.setVisibility(SymbolTable::Visibility::Private);
}
replacement = localBuilder.create<XMRDerefOp>(
value.getType(), hierPathOp.getSymNameAttr());
}
replacements.insert({value, replacement});
return replacement;
};
// A map of instance ops to modules that this pass creates. This is used to
// check if this was an instance that we created and to do fast module
// dereferencing (avoiding a symbol table).
DenseMap<InstanceOp, FModuleOp> createdInstances;
// Post-order traversal that expands a layer block into its parent. For each
// layer block found do the following:
// Check that the preconditions for this pass are met. Reject any ops which
// must have been removed before this runs.
auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
// LowerXMR op removal postconditions.
if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
RWProbeOp>(op))
return op->emitOpError()
<< "cannot be handled by the lower-layers pass. This should have "
"already been removed by the lower-xmr pass.";
return success();
};
// Post-order traversal that expands a layer block into its parent. Because of
// the pass precondition that this runs _after_ `LowerXMR`, not much has to
// happen here. All of the following do happen, though:
//
// 1. Create and connect one ref-type output port for each value defined in
// this layer block that drives an instance marked lowerToBind and move
// this instance outside the layer block.
// 2. Create one input port for each value captured by this layer block.
// 3. Create a new module for this layer block and move the (mutated) body of
// this layer block to the new module.
// 4. Instantiate the new module outside the layer block and hook it up.
// 5. Erase the layer block.
// 1. Any layer coloring is stripped.
// 2. Layers with Inline convention are converted to SV ifdefs.
// 3. Layers with Bind convention are converted to new modules and then
// instantiated at their original location. Any captured values are either
// moved, cloned, or converted to XMR deref ops.
// 4. Move instances created from earlier (3) conversions out of later (3)
// conversions. This is necessary to avoid a SystemVerilog-illegal
// bind-under-bind. (See Section 23.11 of 1800-2023.)
// 5. Keep track of special ops (ops with inner symbols or verbatims) which
// need to have something updated because of the new instance hierarchy
// being created.
//
// Remember, this is post-order, in-order. Child layer blocks are visited
// before parents. Any nested regions _within_ the layer block are also
// visited before the outer layer block.
auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
if (failed(opPreconditionCheck(op)))
return WalkResult::interrupt();
// Strip layer requirements from any op that might represent a probe.
if (auto wire = dyn_cast<WireOp>(op)) {
removeLayersFromValue(wire.getResult());
return WalkResult::advance();
}
if (auto sub = dyn_cast<RefSubOp>(op)) {
removeLayersFromValue(sub.getResult());
return WalkResult::advance();
}
if (auto instance = dyn_cast<InstanceOp>(op)) {
instance.setLayers({});
for (auto result : instance.getResults())
removeLayersFromValue(result);
return WalkResult::advance();
}
if (auto cast = dyn_cast<RefCastOp>(op)) {
removeLayersFromRefCast(cast);
return WalkResult::advance();
}
auto layerBlock = dyn_cast<LayerBlockOp>(op);
if (!layerBlock)
return WalkResult::advance();
// After this point, we are dealing with a layer block.
auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
if (layer.getConvention() == LayerConvention::Inline) {
@ -372,116 +510,73 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
return WalkResult::advance();
}
// After this point, we are dealing with a bind convention layer block.
assert(layer.getConvention() == LayerConvention::Bind);
Block *body = layerBlock.getBody(0);
// Clear the replacements so that none are re-used across layer blocks.
replacements.clear();
OpBuilder builder(moduleOp);
// Ports that need to be created for the module derived from this layer
// block.
SmallVector<PortInfo> ports;
// Connection that need to be made to the instance of the derived module.
SmallVector<ConnectInfo> connectValues;
// Create an input port for an operand that is captured from outside.
auto createInputPort = [&](Value operand, Location loc) {
const auto &[portName, _] = getFieldName(FieldRef(operand, 0), true);
// If the value is a ref, we must resolve the ref inside the parent,
// passing the input as a value instead of a ref. Inside the layer, we
// convert (ref.send) the value back into a ref.
auto type = operand.getType();
if (auto refType = dyn_cast<RefType>(type))
type = refType.getType();
ports.push_back({builder.getStringAttr(portName), type, Direction::In,
/*symName=*/{},
/*location=*/loc});
// Update the layer block's body with arguments as we will swap this body
// into the module when we create it. If this is a ref type, then add a
// refsend to convert from the non-ref type input port.
Value replacement = body->addArgument(type, loc);
if (isa<RefType>(operand.getType())) {
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToStart(body);
replacement = builder.create<RefSendOp>(loc, replacement);
}
operand.replaceUsesWithIf(replacement, [&](OpOperand &use) {
auto *user = use.getOwner();
if (!layerBlock->isAncestor(user))
return false;
if (auto connectLike = dyn_cast<FConnectLike>(user)) {
if (use.getOperandNumber() == 0)
return false;
}
return true;
});
connectValues.push_back({operand, ConnectKind::NonRef});
};
// Set the location intelligently. Use the location of the capture if this
// is a port created for forwarding from a parent layer block to a nested
// layer block. Otherwise, use unknown.
auto getPortLoc = [&](Value port) -> Location {
Location loc = UnknownLoc::get(port.getContext());
if (auto *destOp = port.getDefiningOp())
if (auto instOp = dyn_cast<InstanceOp>(destOp)) {
auto modOpIt = createdInstances.find(instOp);
if (modOpIt != createdInstances.end()) {
auto portNum = cast<OpResult>(port).getResultNumber();
loc = modOpIt->getSecond().getPortLocation(portNum);
}
}
return loc;
};
// Create an output probe port port and adds a ref.define/ref.send to
// drive the port if this was not already capturing a ref type.
auto createOutputPort = [&](Value dest, Value src) {
auto loc = getPortLoc(dest);
auto portNum = ports.size();
const auto &[portName, _] = getFieldName(FieldRef(dest, 0), true);
RefType refType;
if (auto oldRef = dyn_cast<RefType>(dest.getType()))
refType = oldRef;
else
refType = RefType::get(
type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
/*forceable=*/false);
ports.push_back({builder.getStringAttr(portName), refType, Direction::Out,
/*symName=*/{}, /*location=*/loc});
Value replacement = body->addArgument(refType, loc);
if (isa<RefType>(dest.getType())) {
dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
auto *user = use.getOwner();
if (!layerBlock->isAncestor(user))
return false;
if (auto connectLike = dyn_cast<FConnectLike>(user)) {
if (use.getOperandNumber() == 0)
return true;
}
return false;
});
connectValues.push_back({dest, ConnectKind::Ref});
return;
}
connectValues.push_back({dest, ConnectKind::NonRef});
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointAfterValue(src);
builder.create<RefDefineOp>(
loc, body->getArgument(portNum),
builder.create<RefSendOp>(loc, src)->getResult(0));
};
SmallVector<hw::InnerSymAttr> innerSyms;
SmallVector<RWProbeOp> rwprobes;
SmallVector<sv::VerbatimOp> verbatims;
DenseSet<Operation *> spilledSubOps;
auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
// Error if pass preconditions are not met.
if (failed(opPreconditionCheck(op)))
return WalkResult::interrupt();
// Specialized handling of subfields, subindexes, and subaccesses which
// need to be spilled and nodes that referred to spilled nodes. If these
// are kept in the module, then the XMR is going to be bidirectional. Fix
// this for subfield and subindex by moving these ops outside the
// layerblock. Try to fix this for subaccess and error if the move can't
// be made because the index is defined inside the layerblock. (This case
// is exceedingly rare given that subaccesses are almost always unexepcted
// when this pass runs.) Additionally, if any nodes are seen that are
// transparently referencing a spilled op, spill the node, too. The node
// provides an anchor for an inner symbol (which subfield, subindex, and
// subaccess do not).
if (isa<SubfieldOp, SubindexOp>(op)) {
auto input = op->getOperand(0);
if (!firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive() &&
!isAncestorOfValueOwner(layerBlock, input)) {
op->moveBefore(layerBlock);
spilledSubOps.insert(op);
}
return WalkResult::advance();
}
if (auto subOp = dyn_cast<SubaccessOp>(op)) {
auto input = subOp.getInput();
if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive())
return WalkResult::advance();
if (!isAncestorOfValueOwner(layerBlock, input) &&
!isAncestorOfValueOwner(layerBlock, subOp.getIndex())) {
subOp->moveBefore(layerBlock);
spilledSubOps.insert(op);
return WalkResult::advance();
}
auto diag = op->emitOpError()
<< "has a non-passive operand and captures a value defined "
"outside its enclosing bind-convention layerblock. The "
"'LowerLayers' pass cannot lower this as it would "
"create an output port on the resulting module.";
diag.attachNote(layerBlock.getLoc())
<< "the layerblock is defined here";
return WalkResult::interrupt();
}
if (auto nodeOp = dyn_cast<NodeOp>(op)) {
auto *definingOp = nodeOp.getInput().getDefiningOp();
if (definingOp &&
spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
op->moveBefore(layerBlock);
return WalkResult::advance();
}
}
// Record any operations inside the layer block which have inner symbols.
// Theses may have symbol users which need to be updated.
//
// Note: this needs to _not_ index spilled NodeOps above.
if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
if (auto innerSym = symOp.getInnerSymAttr())
innerSyms.push_back(innerSym);
@ -511,119 +606,19 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
return WalkResult::advance();
}
// Handle subfields, subindexes, and subaccesses which are indexing into
// non-passive values. If these are kept in the module, then the module
// must create bi-directional ports. This doesn't make sense as the
// FIRRTL spec states that no layerblock may write to values outside it.
// Fix this for subfield and subindex by moving these ops outside the
// layerblock. Try to fix this for subaccess and error if the move can't
// be made because the index is defined inside the layerblock. (This case
// is exceedingly rare given that subaccesses are almost always unexepcted
// when this pass runs.)
if (isa<SubfieldOp, SubindexOp>(op)) {
auto input = op->getOperand(0);
if (!firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive() &&
!isAncestorOfValueOwner(layerBlock, input))
op->moveBefore(layerBlock);
return WalkResult::advance();
}
if (auto subOp = dyn_cast<SubaccessOp>(op)) {
auto input = subOp.getInput();
if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive())
return WalkResult::advance();
if (!isAncestorOfValueOwner(layerBlock, input) &&
!isAncestorOfValueOwner(layerBlock, subOp.getIndex())) {
subOp->moveBefore(layerBlock);
return WalkResult::advance();
}
auto diag = op->emitOpError()
<< "has a non-passive operand and captures a value defined "
"outside its enclosing bind-convention layerblock. The "
"'LowerLayers' pass cannot lower this as it would "
"create an output port on the resulting module.";
diag.attachNote(layerBlock.getLoc())
<< "the layerblock is defined here";
return WalkResult::interrupt();
}
if (auto rwprobe = dyn_cast<RWProbeOp>(op)) {
rwprobes.push_back(rwprobe);
return WalkResult::advance();
}
if (auto connect = dyn_cast<FConnectLike>(op)) {
auto src = connect.getSrc();
auto dst = connect.getDest();
auto srcInLayerBlock = isAncestorOfValueOwner(layerBlock, src);
auto dstInLayerBlock = isAncestorOfValueOwner(layerBlock, dst);
if (!srcInLayerBlock && !dstInLayerBlock) {
connect->moveBefore(layerBlock);
return WalkResult::advance();
}
// Create an input port.
if (!srcInLayerBlock) {
createInputPort(src, op->getLoc());
return WalkResult::advance();
}
// Create an output port.
if (!dstInLayerBlock) {
createOutputPort(dst, src);
if (!isa<RefType>(dst.getType()))
connect.erase();
return WalkResult::advance();
}
// Source and destination in layer block. Nothing to do.
return WalkResult::advance();
}
// Pre-emptively de-squiggle connections that we are creating. This will
// later be cleaned up by the de-squiggling pass. However, there is no
// point in creating deeply squiggled connections if we don't have to.
//
// This pattern matches the following structure. Move the ref.resolve
// outside the layer block. The matchingconnect will be moved outside in
// the next loop iteration:
// %0 = ...
// %1 = ...
// firrtl.layerblock {
// %2 = ref.resolve %0
// firrtl.matchingconnect %1, %2
// }
if (auto refResolve = dyn_cast<RefResolveOp>(op))
if (refResolve.getResult().hasOneUse() &&
refResolve.getRef().getParentBlock() != body)
if (auto connect = dyn_cast<MatchingConnectOp>(
*refResolve.getResult().getUsers().begin()))
if (connect.getDest().getParentBlock() != body) {
refResolve->moveBefore(layerBlock);
return WalkResult::advance();
}
// For any other ops, create input ports for any captured operands.
// Handle captures. For any captured operands, convert them to a suitable
// replacement value. The `getReplacement` function will automatically
// reuse values whenever possible.
for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
auto operand = op->getOperand(i);
// If the operand is in this layer block, do nothing.
//
// Note: This check is what avoids handling ConnectOp destinations.
if (isAncestorOfValueOwner(layerBlock, operand))
continue;
// If the operand is "special", e.g., it has no port representation,
// then we need to clone it.
//
// TODO: Change this to recursively clone. This will matter once
// FString operations have operands.
if (type_isa<FStringType>(operand.getType())) {
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPoint(op);
op->setOperand(i,
builder.clone(*operand.getDefiningOp())->getResult(0));
continue;
}
// Create a port to capture the operand.
createInputPort(operand, op->getLoc());
op->setOperand(i, getReplacement(op, operand));
}
if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
@ -636,33 +631,22 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
return WalkResult::interrupt();
// Create the new module. This grabs a lock to modify the circuit.
FModuleOp newModule = buildNewModule(builder, layerBlock, ports);
FModuleOp newModule = buildNewModule(builder, layerBlock);
SymbolTable::setSymbolVisibility(newModule,
SymbolTable::Visibility::Private);
newModule.getBody().takeBody(layerBlock.getRegion());
LLVM_DEBUG({
llvm::dbgs() << " New Module: " << moduleNames.lookup(layerBlock)
<< "\n";
llvm::dbgs() << " ports:\n";
for (size_t i = 0, e = ports.size(); i != e; ++i) {
auto port = ports[i];
auto value = connectValues[i];
llvm::dbgs() << " - name: " << port.getName() << "\n"
<< " type: " << port.type << "\n"
<< " direction: " << port.direction << "\n"
<< " value: " << value.value << "\n"
<< " kind: "
<< (value.kind == ConnectKind::NonRef ? "NonRef" : "Ref")
<< "\n";
}
llvm::dbgs() << " New Module: "
<< layerBlockGlobals.lookup(layerBlock).moduleName << "\n";
});
// Replace the original layer block with an instance. Hook up the instance.
// Intentionally create instance with probe ports which do not have an
// associated layer. This is illegal IR that will be made legal by the end
// of the pass. This is done to avoid having to revisit and rewrite each
// instance everytime it is moved into a parent layer.
// Replace the original layer block with an instance. Hook up the
// instance. Intentionally create instance with probe ports which do
// not have an associated layer. This is illegal IR that will be
// made legal by the end of the pass. This is done to avoid having
// to revisit and rewrite each instance everytime it is moved into a
// parent layer.
builder.setInsertionPointAfter(layerBlock);
auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
auto instanceOp = builder.create<InstanceOp>(
@ -695,34 +679,6 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
<< splice.second << "\n";);
}
// Update RWProbe operations.
for (auto rwprobe : rwprobes) {
auto targetRef = rwprobe.getTarget();
auto mapped = innerRefMap.find(targetRef);
if (mapped == innerRefMap.end()) {
assert(targetRef.getModule() == moduleOp.getNameAttr());
auto ist = hw::InnerSymbolTable::get(moduleOp);
if (failed(ist))
return WalkResult::interrupt();
auto target = ist->lookup(targetRef.getName());
assert(target);
auto fieldref = getFieldRefForTarget(target);
rwprobe
.emitError(
"rwprobe capture not supported with bind convention layer")
.attachNote(fieldref.getLoc())
.append("rwprobe target outside of bind layer");
return WalkResult::interrupt();
}
if (mapped->second.second != newModule.getModuleNameAttr())
return rwprobe.emitError("rwprobe target refers to different module"),
WalkResult::interrupt();
rwprobe.setTargetAttr(
hw::InnerRefAttr::get(mapped->second.second, targetRef.getName()));
}
// Update verbatims that target operations extracted alongside.
if (!verbatims.empty()) {
mlir::AttrTypeReplacer replacer;
@ -737,34 +693,6 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
replacer.replaceElementsIn(verbatim);
}
// Connect instance ports to values.
assert(ports.size() == connectValues.size() &&
"the number of instance ports and values to connect to them must be "
"equal");
for (unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
++portNum) {
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
if (instanceOp.getPortDirection(portNum) == Direction::In) {
auto src = connectValues[portNum].value;
if (isa<RefType>(src.getType()))
src = builder.create<RefResolveOp>(
newModule.getPortLocationAttr(portNum), src);
builder.create<MatchingConnectOp>(
newModule.getPortLocationAttr(portNum),
instanceOp.getResult(portNum), src);
} else if (isa<RefType>(instanceOp.getResult(portNum).getType()) &&
connectValues[portNum].kind == ConnectKind::Ref)
builder.create<RefDefineOp>(getPortLoc(connectValues[portNum].value),
connectValues[portNum].value,
instanceOp.getResult(portNum));
else
builder.create<MatchingConnectOp>(
getPortLoc(connectValues[portNum].value),
connectValues[portNum].value,
builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
instanceOp.getResult(portNum)));
}
layerBlock.erase();
return WalkResult::advance();
@ -820,14 +748,22 @@ void LowerLayersPass::runOnOperation() {
circuitMutex = &mutex;
CircuitNamespace ns(circuitOp);
hw::HierPathCache hpc(
&ns, OpBuilder::InsertPoint(getOperation().getBodyBlock(),
getOperation().getBodyBlock()->begin()));
hierPathCache = &hpc;
for (auto &op : *circuitOp.getBodyBlock()) {
// Determine names for all modules that will be created. Do this serially
// to avoid non-determinism from creating these in the parallel region.
if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
moduleOp->walk([&](LayerBlockOp layerBlockOp) {
auto name = moduleNameForLayer(moduleOp.getModuleName(),
layerBlockOp.getLayerName());
moduleNames.insert({layerBlockOp, ns.newName(name)});
auto moduleName = moduleNameForLayer(moduleOp.getModuleName(),
layerBlockOp.getLayerName());
auto hierPathName = hierPathNameForLayer(moduleOp.getModuleName(),
layerBlockOp.getLayerName());
layerBlockGlobals.insert(
{layerBlockOp, {ns.newName(moduleName), ns.newName(hierPathName)}});
});
continue;
}
@ -980,6 +916,9 @@ void LowerLayersPass::runOnOperation() {
for (auto layerOp :
llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
layerOp.erase();
// Cleanup state.
hierPathCache = nullptr;
}
std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {

View File

@ -53,8 +53,7 @@ LogicalResult firtool::populatePreprocessTransforms(mlir::PassManager &pm,
}
LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
const FirtoolOptions &opt,
StringRef inputFilename) {
const FirtoolOptions &opt) {
// TODO: Ensure instance graph and other passes can handle instance choice
// then run this pass after all diagnostic passes have run.
pm.addNestedPass<firrtl::CircuitOp>(firrtl::createSpecializeOptionPass(
@ -217,6 +216,17 @@ LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
pm.nest<firrtl::CircuitOp>().nest<firrtl::FModuleOp>().addPass(
firrtl::createVectorizationPass());
return success();
}
LogicalResult firtool::populateLowFIRRTLToHW(mlir::PassManager &pm,
const FirtoolOptions &opt,
StringRef inputFilename) {
// Lower the ref.resolve and ref.send ops and remove the RefType ports.
// LowerToHW cannot handle RefType so, this pass must be run to remove all
// RefType ports and ops.
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerXMRPass());
// Layer lowering passes. Move operations into layers when possible and
// remove layers by converting them to other constructs. This lowering
// process can create a few optimization opportunities.
@ -258,21 +268,12 @@ LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
: opt.getBlackBoxRootPath();
pm.nest<firrtl::CircuitOp>().addPass(
firrtl::createBlackBoxReaderPass(blackBoxRoot));
return success();
}
LogicalResult firtool::populateLowFIRRTLToHW(mlir::PassManager &pm,
const FirtoolOptions &opt) {
// Remove TraceAnnotations and write their updated paths to an output
// annotation file.
pm.nest<firrtl::CircuitOp>().addPass(
firrtl::createResolveTracesPass(opt.getOutputAnnotationFilename()));
// Lower the ref.resolve and ref.send ops and remove the RefType ports.
// LowerToHW cannot handle RefType so, this pass must be run to remove all
// RefType ports and ops.
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerXMRPass());
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerDPIPass());
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerClassesPass());
pm.nest<firrtl::CircuitOp>().addPass(om::createVerifyObjectFieldsPass());

View File

@ -50,11 +50,11 @@ void exportVerilog(MlirContext ctx, bool disableOptimization) {
circtFirtoolPopulatePreprocessTransforms(pm, options);
assert(mlirLogicalResultIsSuccess(result));
result = circtFirtoolPopulateCHIRRTLToLowFIRRTL(
pm, options, mlirStringRefCreateFromCString("-"));
result = circtFirtoolPopulateCHIRRTLToLowFIRRTL(pm, options);
assert(mlirLogicalResultIsSuccess(result));
result = circtFirtoolPopulateLowFIRRTLToHW(pm, options);
result = circtFirtoolPopulateLowFIRRTLToHW(
pm, options, mlirStringRefCreateFromCString("-"));
assert(mlirLogicalResultIsSuccess(result));
result = circtFirtoolPopulateHWToSV(pm, options);

View File

@ -10,23 +10,7 @@ firrtl.circuit "NonPassiveSubaccess" {
firrtl.layerblock @A {
%n = firrtl.node %b : !firrtl.uint<1>
// expected-error @below {{'firrtl.subaccess' op has a non-passive operand and captures a value defined outside its enclosing bind-convention layerblock}}
%0 = firrtl.subaccess %a[%b] : !firrtl.vector<bundle<a: uint<1>, b flip: uint<1>>, 2>, !firrtl.uint<1>
%0 = firrtl.subaccess %a[%n] : !firrtl.vector<bundle<a: uint<1>, b flip: uint<1>>, 2>, !firrtl.uint<1>
}
}
}
// -----
firrtl.circuit "RWProbeCantMove" {
firrtl.layer @A bind { }
firrtl.module @RWProbeCantMove() attributes {layers = [@A]} {
%z = firrtl.constant 0 : !firrtl.uint<5>
// expected-note @below {{rwprobe target outside of bind layer}}
%w = firrtl.node sym @sym %z : !firrtl.uint<5>
firrtl.layerblock @A {
// expected-error @below {{rwprobe capture not supported with bind convention layer}}
%rw = firrtl.ref.rwprobe <@RWProbeCantMove::@sym> : !firrtl.rwprobe<uint<5>>
}
}
}

View File

@ -1,5 +1,6 @@
// RUN: circt-opt -firrtl-lower-layers -split-input-file %s | FileCheck %s
// CHECK-LABEL: firrtl.circuit "Test"
firrtl.circuit "Test" {
firrtl.module @Test() {}
@ -32,10 +33,6 @@ firrtl.circuit "Test" {
firrtl.module @ColoredThings() {
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
%0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>, @A>
// CHECK: %1 = firrtl.ref.sub %0[0] : !firrtl.probe<bundle<f: uint<1>>>
%1 = firrtl.ref.sub %0[0] : !firrtl.probe<bundle<f: uint<1>>, @A>
// CHECK-NOT: firrtl.ref.cast
%2 = firrtl.ref.cast %1 : (!firrtl.probe<uint<1>, @A>) -> !firrtl.probe<uint<1>, @A::@B>
}
// CHECK-LABEL: @ColoredThingUnderWhen
@ -44,10 +41,6 @@ firrtl.circuit "Test" {
firrtl.when %b : !firrtl.uint<1> {
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
%0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>, @A>
// CHECK: %1 = firrtl.ref.sub %0[0] : !firrtl.probe<bundle<f: uint<1>>>
%1 = firrtl.ref.sub %0[0] : !firrtl.probe<bundle<f: uint<1>>, @A>
// CHECK-NOT: firrtl.ref.cast
%2 = firrtl.ref.cast %1 : (!firrtl.probe<uint<1>, @A>) -> !firrtl.probe<uint<1>, @A::@B>
}
}
@ -71,7 +64,7 @@ firrtl.circuit "Test" {
// CHECK-NOT: firrtl.layer @GoodbyeCruelWorld
firrtl.layer @GoodbyeCruelWorld bind {}
// CHECK-LABEL @WithLayerBlock
// CHECK-LABEL firrtl.module @WithLayerBlock
firrtl.module @WithLayerBlock() {
// CHECK-NOT firrtl.layerblock @GoodbyeCruelWorld
firrtl.layerblock @GoodbyeCruelWorld {
@ -82,16 +75,20 @@ firrtl.circuit "Test" {
// Capture
//===--------------------------------------------------------------------===//
// CHECK: firrtl.module private @[[A:.+]](in %[[x:.+]]: !firrtl.uint<1>, in %[[y:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.add %[[x]], %[[y]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK: }
// CHECK: firrtl.module @CaptureHardware() {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: %[[p:.+]], %[[q:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: firrtl.matchingconnect %[[q]], %c1_ui1 : !firrtl.uint<1>
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
// CHECK: }
// CHECK: hw.hierpath private @[[CaptureHardware_c0_ui1_path:.+]] [@CaptureHardware::@[[CaptureHardware_c0_ui1_sym:.+]]]
// CHECK-NEXT: hw.hierpath private @[[CaptureHardware_c1_ui1_path:.+]] [@CaptureHardware::@[[CaptureHardware_c1_ui1_sym:.+]]]
// CHECK-NEXT: firrtl.module private @CaptureHardware_A() {
// CHECK-NEXT: %[[c1_ui1:.+]] = firrtl.xmr.deref @[[CaptureHardware_c1_ui1_path]] : !firrtl.uint<1>
// CHECK-NEXT: %[[c0_ui1:.+]] = firrtl.xmr.deref @[[CaptureHardware_c0_ui1_path]] : !firrtl.uint<1>
// CHECK-NEXT: firrtl.add %[[c0_ui1]], %[[c1_ui1]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @CaptureHardware() {
// CHECK-NEXT: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK-NEXT: %[[c0_ui1_node:.+]] = firrtl.node sym @[[CaptureHardware_c0_ui1_sym]] %c0_ui1
// CHECK-NEXT: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
// CHECK-NEXT: %[[c1_ui1_node:.+]] = firrtl.node sym @[[CaptureHardware_c1_ui1_sym]] %c1_ui1
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CaptureHardware_A()
// CHECK-NEXT: }
firrtl.module @CaptureHardware() {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
@ -100,12 +97,13 @@ firrtl.circuit "Test" {
}
}
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>) {
// CHECK: %x = firrtl.node %[[p]] : !firrtl.uint<1>
// CHECK: hw.hierpath private @[[CapturePort_port_path:.+]] [@CapturePort::@[[CapturePort_port_sym:.+]]]
// CHECK: firrtl.module private @CapturePort_A() {
// CHECK: %[[port:.+]] = firrtl.xmr.deref @[[CapturePort_port_path]] : !firrtl.uint<1>
// CHECK: %x = firrtl.node %[[port]] : !firrtl.uint<1>
// CHECK: }
// CHECK: firrtl.module @CapturePort(in %in: !firrtl.uint<1>) {
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: firrtl.matchingconnect %[[p]], %in : !firrtl.uint<1>
// CHECK: firrtl.module @CapturePort(in %in: !firrtl.uint<1> sym @[[CapturePort_port_sym]]) {
// CHECK: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CapturePort_A
// CHECK: }
firrtl.module @CapturePort(in %in: !firrtl.uint<1>){
firrtl.layerblock @A {
@ -113,90 +111,82 @@ firrtl.circuit "Test" {
}
}
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
// CHECK: %w = firrtl.wire : !firrtl.uint<1>
// CHECK: firrtl.connect %w, %[[p]] : !firrtl.uint<1>
// CHECK: }
// CHECK: firrtl.module @CaptureHardwareViaConnect() {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
// CHECK: }
firrtl.module @CaptureHardwareViaConnect() {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: hw.hierpath private @[[CaptureConnect_a_path:.+]] [@CaptureConnect::@[[CaptureConnect_a_sym:.+]]]
// CHECK-NEXT: firrtl.module private @CaptureConnect_A()
// CHECK-NEXT: %[[a:.+]] = firrtl.xmr.deref @[[CaptureConnect_a_path]] : !firrtl.uint<1>
// CHECK-NEXT: %b = firrtl.wire : !firrtl.uint<1>
// CHECK-NEXT: firrtl.connect %b, %[[a]] : !firrtl.uint<1>
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @CaptureConnect() {
// CHECK-NEXT: %a = firrtl.wire : !firrtl.uint<1>
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[CaptureConnect_a_sym]] %a
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CaptureConnect_A()
// CHECK-NEXT: }
firrtl.module @CaptureConnect() {
%a = firrtl.wire : !firrtl.uint<1>
firrtl.layerblock @A {
%w = firrtl.wire : !firrtl.uint<1>
firrtl.connect %w, %c0_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
%b = firrtl.wire : !firrtl.uint<1>
firrtl.connect %b, %a : !firrtl.uint<1>, !firrtl.uint<1>
}
}
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @CaptureProbeSrc() {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %w = firrtl.wire : !firrtl.uint<1>
// CHECK: %0 = firrtl.ref.send %w : !firrtl.uint<1>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[p]], %1 : !firrtl.uint<1>
// CHECK: }
// CHECK: firrtl.module private @CaptureProbeSrc_A()
// CHECK-NEXT: %0 = firrtl.xmr.deref @xmrPath : !firrtl.uint<1>
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @CaptureProbeSrc() {
// CHECK-NEXT: %w = firrtl.wire : !firrtl.uint<1>
// CHECK-NEXT: %w_probe = firrtl.node sym @sym interesting_name %w : !firrtl.uint<1>
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CaptureProbeSrc_A
// CHECK-NEXT: }
firrtl.module @CaptureProbeSrc() {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%w = firrtl.wire : !firrtl.uint<1>
%r = firrtl.ref.send %w : !firrtl.uint<1>
%w_probe = firrtl.node sym @sym interesting_name %w : !firrtl.uint<1>
firrtl.layerblock @A {
firrtl.ref.resolve %r : !firrtl.probe<uint<1>>
%0 = firrtl.xmr.deref @xmrPath : !firrtl.uint<1>
}
}
// CHECK: firrtl.module private @[[B:.+]](in %[[p:.+]]: !firrtl.uint<1>, in %[[q:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.add %[[p]], %[[q]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK: }
// CHECK: firrtl.module private @[[A:.+]](out %[[p:.+]]: !firrtl.probe<uint<1>>, out %[[q:.+]]: !firrtl.probe<uint<1>>) attributes {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %0 = firrtl.ref.send %c0_ui1 : !firrtl.uint<1>
// CHECK: firrtl.ref.define %[[q]], %0 : !firrtl.probe<uint<1>>
// CHECK: %c0_ui1_1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.send %c0_ui1_1 : !firrtl.uint<1>
// CHECK: firrtl.ref.define %[[p]], %1 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @NestedCaptureHardware() {
// CHECK: %[[b1:.+]], %[[b2:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A-B.sv", excludeFromFileList>} @[[B]]
// CHECK: %[[a1:.+]], %[[a2:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: %0 = firrtl.ref.resolve %[[a2]] : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[b1]], %0 : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.resolve %[[a1]] : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[b2]], %1 : !firrtl.uint<1>
// CHECK: }
firrtl.module @NestedCaptureHardware() {
// CHECK: hw.hierpath private @[[NestedCapture_x_path:.+]] [@NestedCapture::@[[inst:.+]], @NestedCapture_A::@[[NestedCapture_a_sym:.+]]]
// CHECK-NEXT: firrtl.module private @NestedCapture_A_B()
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[NestedCapture_x_path]]
// CHECK-NEXT: %1 = firrtl.node %0 : !firrtl.uint<1>
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module private @NestedCapture_A() {
// CHECK-NEXT: %x = firrtl.wire : !firrtl.uint<1>
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[NestedCapture_a_sym]] %x
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @NestedCapture() {
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A-B.sv", excludeFromFileList>} @NestedCapture_A_B()
// CHECK-NEXT: firrtl.instance {{.+}} sym @[[inst]] {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @NestedCapture_A()
// CHECK-NEXT: }
firrtl.module @NestedCapture() {
firrtl.layerblock @A {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%c1_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%x = firrtl.wire : !firrtl.uint<1>
firrtl.layerblock @A::@B {
%0 = firrtl.add %c0_ui1, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
%0 = firrtl.node %x : !firrtl.uint<1>
}
}
}
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
// CHECK: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
// CHECK: firrtl.when %[[p]] : !firrtl.uint<1> {
// CHECK: %0 = firrtl.add %[[p]], %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK: }
// CHECK: }
// CHECK: firrtl.module @WhenUnderLayer() {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
// CHECK: }
// CHECK: hw.hierpath private @[[WhenUnderLayer_x_path:.+]] [@WhenUnderLayer::@[[WhenUnderLayer_x_sym:.+]]]
// CHECK-NEXT: firrtl.module private @WhenUnderLayer_A()
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[WhenUnderLayer_x_path]] : !firrtl.uint<1>
// CHECK-NEXT: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
// CHECK-NEXT: firrtl.when %0 : !firrtl.uint<1> {
// CHECK-NEXT: %1 = firrtl.add %0, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @WhenUnderLayer() {
// CHECK-NEXT: %x = firrtl.wire : !firrtl.uint<1>
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[WhenUnderLayer_x_sym]] %x
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @WhenUnderLayer_A
// CHECK-NEXT: }
firrtl.module @WhenUnderLayer() {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%x = firrtl.wire : !firrtl.uint<1>
firrtl.layerblock @A {
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
firrtl.when %c0_ui1 : !firrtl.uint<1> {
%0 = firrtl.add %c0_ui1, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
firrtl.when %x : !firrtl.uint<1> {
%0 = firrtl.add %x, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
}
}
}
@ -204,14 +194,17 @@ firrtl.circuit "Test" {
// Test that subfield, subindex, and subaccess are moved out of layerblocks to
// avoid capturing non-passive types.
//
// CHECK: firrtl.module private @[[SubOpsInLayerBlock_A:[A-Za-z0-9_]+]]
// CHECK-SAME: in %[[port:[A-Za-z0-9_]+]]: !firrtl.uint<1>
// CHECK-NEXT: firrtl.node %[[port]]
// CHECK: hw.hierpath private @[[SubOpsInLayerBlock_sub_path:.+]] [@SubOpsInLayerBlock::@[[SubOpsInLayerBlock_sub_sym:.+]]]
// CHECK-NEXT: firrtl.module private @SubOpsInLayerBlock_A() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[SubOpsInLayerBlock_sub_path]]
// CHECK-NEXT: firrtl.node %0
// CHECK-NEXT: }
// CHECK: firrtl.module @SubOpsInLayerBlock
// CHECK-NEXT: firrtl.subaccess
// CHECK-NEXT: firrtl.subindex
// CHECK-NEXT: firrtl.subfield
// CHECK: firrtl.module @SubOpsInLayerBlock(
// CHECK-NEXT: %0 = firrtl.subaccess %a[%b]
// CHECK-NEXT: %1 = firrtl.subindex %0[0]
// CHECK-NEXT: %2 = firrtl.subfield %1[a]
// CHECK-NEXT: %3 = firrtl.node %2 :
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[SubOpsInLayerBlock_sub_sym]] %3
firrtl.module @SubOpsInLayerBlock(
in %a: !firrtl.vector<vector<bundle<a: uint<1>, b flip: uint<2>>, 2>, 2>,
in %b: !firrtl.uint<1>
@ -221,19 +214,23 @@ firrtl.circuit "Test" {
%1 = firrtl.subindex %0[0] : !firrtl.vector<bundle<a: uint<1>, b flip: uint<2>>, 2>
%2 = firrtl.subfield %1[a] : !firrtl.bundle<a: uint<1>, b flip: uint<2>>
%3 = firrtl.node %2 : !firrtl.uint<1>
%4 = firrtl.node %3 : !firrtl.uint<1>
}
}
// CHECK: firrtl.module private @CaptureInWhen_A(
// CHECK-SAME: in %a: !firrtl.uint<1>
// CHECK-SAME: in %cond: !firrtl.uint<1>
// CHECK-SAME: )
// CHECK: hw.hierpath private @[[CaptureWhen2_a_path:.+]] [@CaptureWhen2::@[[CaptureWhen2_a_sym:.+]]]
// CHECK-NEXT: hw.hierpath private @[[CaptureWhen2_cond_path:.+]] [@CaptureWhen2::@[[CaptureWhen2_cond_sym:.+]]]
// CHECK-NEXT: firrtl.module private @CaptureWhen2_A() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[CaptureWhen2_cond_path]]
// CHECK-NEXT: %1 = firrtl.xmr.deref @[[CaptureWhen2_a_path]]
// CHECK-NEXT: firrtl.when %0 {{.*}} {
// CHECK-NEXT: %b = firrtl.node %1
// CHECK: firrtl.module @CaptureInWhen(
// CHECK: %a_a, %a_cond = firrtl.instance a
// CHECK-NEXT: firrtl.matchingconnect %a_cond, %cond :
// CHECK-NEXT: firrtl.matchingconnect %a_a, %a :
firrtl.module @CaptureInWhen(in %cond: !firrtl.uint<1>) {
// CHECK: firrtl.module @CaptureWhen2(
// CHECK-NEXT: %a = firrtl.wire
// CHECK-NEXT: %_layer_probe = firrtl.node {{.*}} %a
// CHECK-NEXT: firrtl.instance a
firrtl.module @CaptureWhen2(in %cond: !firrtl.uint<1>) {
%a = firrtl.wire : !firrtl.uint<1>
firrtl.layerblock @A {
firrtl.when %cond : !firrtl.uint<1> {
@ -242,86 +239,58 @@ firrtl.circuit "Test" {
}
}
// Capture of a zero-width value creates a local zero-width constant zero.
//
// CHECK: firrtl.module private @ZeroWidthCapture_A() {
// CHECK-NEXT: %c0_ui0 = firrtl.constant 0 : !firrtl.uint<0>
// CHECK-NEXT: %b = firrtl.node %c0_ui0 : !firrtl.uint<0>
// CHECK-NEXT: }
// CHECK: firrtl.module @ZeroWidthCapture() {
// CHECK-NEXT: %a = firrtl.wire : !firrtl.uint<0>
// CHECK-NEXT: firrtl.instance a
firrtl.module @ZeroWidthCapture() {
%a = firrtl.wire : !firrtl.uint<0>
firrtl.layerblock @A {
%b = firrtl.node %a : !firrtl.uint<0>
}
}
// Port capture needs to create a node.
//
// CHECK: hw.hierpath private @[[InstancePortCapture_ext_a_path:.+]] [@InstancePortCapture::@[[InstancePortCapture_ext_a_sym:.+]]]
// CHECK-NEXT: hw.hierpath private @[[InstancePortCapture_ext_b_path:.+]] [@InstancePortCapture::@[[InstancePortCapture_ext_b_sym:.+]]]
// CHECK-NEXT: firrtl.module private @InstancePortCapture_A() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[InstancePortCapture_ext_b_path]]
// CHECK-NEXT: %1 = firrtl.xmr.deref @[[InstancePortCapture_ext_a_path]]
// CHECK-NEXT: %a = firrtl.node %1
// CHECK-NEXT: %b = firrtl.node %0
// CHECK-NEXT: }
//
// CHECK: firrtl.module @InstancePortCapture() {
// CHECK-NEXT: %ext_a, %ext_b = firrtl.instance ext @InstancePortCapture_ext
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[InstancePortCapture_ext_b_sym]] %ext_b
// CHECK-NEXT: %_layer_probe_0 = firrtl.node sym @[[InstancePortCapture_ext_a_sym]] %ext_a
// CHECK-NEXT: firrtl.instance {{.*}}
// CHECK-NEXT: }
firrtl.extmodule @InstancePortCapture_ext(
in a: !firrtl.uint<1>,
out b: !firrtl.uint<1>
)
firrtl.module @InstancePortCapture() {
%ext_a, %ext_b = firrtl.instance ext @InstancePortCapture_ext(
in a: !firrtl.uint<1>,
out b: !firrtl.uint<1>
)
firrtl.layerblock @A {
%a = firrtl.node %ext_a : !firrtl.uint<1>
%b = firrtl.node %ext_b : !firrtl.uint<1>
}
}
//===--------------------------------------------------------------------===//
// Connecting/Defining Refs
// Cloning of special operations
//===--------------------------------------------------------------------===//
// Src and Dst Outside Layerblock.
//
// CHECK: firrtl.module private @[[A:.+]]() {
// CHECK: }
// CHECK: firrtl.module @SrcDstOutside() {
// CHECK: %0 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %1 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>>
// CHECK: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A:.+]]()
// CHECK: }
firrtl.module @SrcDstOutside() {
%0 = firrtl.wire : !firrtl.probe<uint<1>, @A>
%1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.layerblock @A {
firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>, @A>
}
}
// Src Outside Layerblock.
//
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @SrcOutside() {
// CHECK: %0 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[p]], %1 : !firrtl.uint<1>
// CHECK: }
firrtl.module @SrcOutside() {
%0 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.layerblock @A {
%1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>, @A>
}
}
// Dst Outside Layerblock.
//
// CHECK: firrtl.module private @[[A:.+]](out %[[p:.+]]: !firrtl.probe<uint<1>>)
// CHECK: %0 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %[[p]], %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @DestOutside() {
// CHECK: %0 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: firrtl.ref.define %0, %[[p]] : !firrtl.probe<uint<1>>
// CHECK: }
firrtl.module @DestOutside() {
%0 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.layerblock @A {
%1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.ref.define %0, %1 : !firrtl.probe<uint<1>, @A>
}
}
// Src and Dst Inside Layerblock.
//
// CHECK: firrtl.module private @[[A:.+]]() {
// CHECK: %0 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %1 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @SrcDstInside() {
// CHECK: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]()
// CHECK: }
firrtl.module @SrcDstInside() {
firrtl.layerblock @A {
%0 = firrtl.wire : !firrtl.probe<uint<1>, @A>
%1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.ref.define %1, %0 : !firrtl.probe<uint<1>, @A>
}
}
// An FString operation is outside the layer block. This needs to be cloned.
//
// CHECK: firrtl.module private @[[A:.+]]() {
@ -338,76 +307,23 @@ firrtl.circuit "Test" {
}
}
//===--------------------------------------------------------------------===//
// Resolving Colored Probes
//===--------------------------------------------------------------------===//
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>) {
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @ResolveColoredRefUnderLayerBlock() {
// CHECK: %w = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: %0 = firrtl.ref.resolve %w : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[p]], %0 : !firrtl.uint<1>
// CHECK: }
firrtl.module @ResolveColoredRefUnderLayerBlock() {
%w = firrtl.wire : !firrtl.probe<uint<1>, @A>
// XMR Ref ops used by force_initial are cloned.
//
// CHECK: firrtl.module private @XmrRef_A()
// CHECK-NEXT: %0 = firrtl.xmr.ref @RefXmrRef_path : !firrtl.rwprobe<uint<1>, @A>
// CHECK-NEXT: %a = firrtl.wire
// CHECK-NEXT: %c1_ui1 = firrtl.constant 1
// CHECK-NEXT: firrtl.ref.force_initial %c1_ui1, %0, %c1_ui1
hw.hierpath private @XmrRef_path [@XmrRef::@a]
firrtl.module @XmrRef() {
%0 = firrtl.xmr.ref @RefXmrRef_path : !firrtl.rwprobe<uint<1>, @A>
firrtl.layerblock @A {
%0 = firrtl.ref.resolve %w : !firrtl.probe<uint<1>, @A>
%a = firrtl.wire sym @a : !firrtl.uint<1>
%c1_ui1 = firrtl.constant 1 : !firrtl.const.uint<1>
firrtl.ref.force_initial %c1_ui1, %0, %c1_ui1 : !firrtl.const.uint<1>, !firrtl.rwprobe<uint<1>, @A>, !firrtl.const.uint<1>
}
}
// CHECK: firrtl.module @ResolveColoredRefUnderEnabledLayer() {
// CHECK: %w = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %0 = firrtl.ref.resolve %w : !firrtl.probe<uint<1>>
// CHECK: }
firrtl.module @ResolveColoredRefUnderEnabledLayer() attributes {layers=[@A]} {
%w = firrtl.wire : !firrtl.probe<uint<1>, @A>
%0 = firrtl.ref.resolve %w : !firrtl.probe<uint<1>, @A>
}
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>) {
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @ResolveColoredRefPortUnderLayerBlock1() {
// CHECK: %foo_o = firrtl.instance foo @Foo(out o: !firrtl.probe<uint<1>>)
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
// CHECK: %0 = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[p]], %0 : !firrtl.uint<1>
// CHECK: }
firrtl.module @ResolveColoredRefPortUnderLayerBlock1() {
%foo_o = firrtl.instance foo @Foo(out o : !firrtl.probe<uint<1>, @A>)
firrtl.layerblock @A {
%x = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>, @A>
}
}
// CHECK: firrtl.module private @[[A:.+]]() {
// CHECK: %foo_o = firrtl.instance foo @Foo(out o: !firrtl.probe<uint<1>>)
// CHECK: %0 = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @ResolveColoredRefPortUnderLayerBlock2() {
// CHECK: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]()
// CHECK: }
firrtl.module @ResolveColoredRefPortUnderLayerBlock2() {
firrtl.layerblock @A {
%foo_o = firrtl.instance foo @Foo(out o : !firrtl.probe<uint<1>, @A>)
%x = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>, @A>
}
}
// CHECK: firrtl.module @ResolveColoredRefPortUnderEnabledLayer() {
// CHECK: %foo_o = firrtl.instance foo @Foo(out o: !firrtl.probe<uint<1>>)
// CHECK: %0 = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>>
// CHECK: }
firrtl.module @ResolveColoredRefPortUnderEnabledLayer() attributes {layers=[@A]} {
%foo_o = firrtl.instance foo @Foo(out o : !firrtl.probe<uint<1>, @A>)
%x = firrtl.ref.resolve %foo_o : !firrtl.probe<uint<1>, @A>
}
//===--------------------------------------------------------------------===//
// Inline Layers
//===--------------------------------------------------------------------===//
@ -497,55 +413,42 @@ firrtl.circuit "Simple" {
// CHECK-SAME: define layers_Simple_A"
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
//
// CHECK: firrtl.module private @Simple_A_B_C(
// CHECK-NOT: firrtl.module
// CHECK-SAME: in %[[cc_port:[_a-zA-Z0-9]+]]: !firrtl.uint<3>
// CHECK-NEXT: %ccc = firrtl.node %[[cc_port]]
// CHECK: hw.hierpath private @[[Simple_A_B_cc_path:.+]] [@Simple::@a_b, @Simple_A_B::@[[Simple_A_B_cc_sym:.+]]]
//
// CHECK: firrtl.module private @Simple_A_B_C() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_A_B_cc_path]]
// CHECK-NEXT: %ccc = firrtl.node %0
// CHECK-NEXT: }
//
// CHECK: firrtl.module private @Simple_A_B(
// CHECK-NOT: firrtl.module
// CHECK-SAME: in %[[b_port:[_a-zA-Z0-9]+]]: !firrtl.uint<2>
// CHECK-SAME: in %[[c_port:[_a-zA-Z0-9]+]]: !firrtl.uint<3>
// CHECK-SAME: out %[[cc_port:[_a-zA-Z0-9_]+]]: !firrtl.probe<uint<3>>
// CHECK-NEXT: %bb = firrtl.node %[[b_port]]
// CHECK-NEXT: %cc = firrtl.node %[[c_port]]
// CHECK-NEXT: %0 = firrtl.ref.send %cc
// CHECK-NEXT: firrtl.ref.define %[[cc_port]], %0
// CHECK: hw.hierpath private @[[Simple_b_path:.+]] [@Simple::@[[Simple_b_sym:.+]]]
// CHECK-NEXT: hw.hierpath private @[[Simple_A_c_path:.+]] [@Simple::@a, @Simple_A::@[[Simple_A_c_sym:.+]]]
//
// CHECK: firrtl.module private @Simple_A_B() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_A_c_path]]
// CHECK-NEXT: %1 = firrtl.xmr.deref @[[Simple_b_path]]
// CHECK-NEXT: %bb = firrtl.node %1
// CHECK-NEXT: %cc = firrtl.node %0
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_A_B_cc_sym]] %cc
// CHECK-NEXT: }
//
// CHECK: firrtl.module private @Simple_A(
// CHECK-NOT: firrtl.module
// CHECK-SAME: in %[[a_port:[_a-zA-Z0-9]+]]: !firrtl.uint<1>
// CHECK-SAME: out %[[c_port:[_a-zA-Z0-9_]+]]: !firrtl.probe<uint<3>>
// CHECK-NEXT: %aa = firrtl.node %[[a_port]]
// CHECK: %[[c_ref:[_a-zA-Z0-9]+]] = firrtl.ref.send %c
// CHECK-NEXT: firrtl.ref.define %[[c_port]], %[[c_ref]]
// CHECK: hw.hierpath private @[[Simple_a_path:.+]] [@Simple::@[[Simple_a_sym:.+]]]
//
// CHECK: firrtl.module private @Simple_A() {
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_a_path]]
// CHECK-NEXT: %aa = firrtl.node %0
// CHECK-NEXT: %c = firrtl.wire
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_A_c_sym]] %c
// CHECK-NEXT: }
//
// CHECK: firrtl.module @Simple() {
// CHECK-NOT: firrtl.module
// CHECK-NOT: firrtl.layerblock
// CHECK: %[[A_B_C_cc:[_a-zA-Z0-9_]+]] = firrtl.instance a_b_c {
// CHECK-SAME: lowerToBind
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A-B-C.sv"
// CHECK-SAME: excludeFromFileList
// CHECK-SAME: @Simple_A_B_C(
// CHECK-NEXT: %[[A_B_b:[_a-zA-Z0-9_]+]], %[[A_B_c:[_a-zA-Z0-9_]+]], %[[A_B_cc:[_a-zA-Z0-9_]+]] = firrtl.instance a_b {
// CHECK-SAME: lowerToBind
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A-B.sv", excludeFromFileList>
// CHECK-SAME: @Simple_A_B(
// CHECK-NEXT: %[[A_B_cc_resolve:[_a-zA-Z0-9]+]] = firrtl.ref.resolve %[[A_B_cc]]
// CHECK-NEXT: firrtl.matchingconnect %[[A_B_C_cc]], %[[A_B_cc_resolve]]
// CHECK-NEXT: firrtl.matchingconnect %[[A_B_b]], %b
// CHECK-NEXT: %[[A_a:[_a-zA-Z0-9_]+]], %[[A_c:[_a-zA-Z0-9_]+]] = firrtl.instance a {
// CHECK-SAME: lowerToBind
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
// CHECK-SAME: @Simple_A(
// CHECK-NEXT: %[[A_c_resolve:[_a-zA-Z0-9]+]] = firrtl.ref.resolve %[[A_c]]
// CHECK-NEXT: firrtl.matchingconnect %[[A_B_c]], %[[A_c_resolve]]
// CHECK-NEXT: firrtl.matchingconnect %[[A_a]], %a
// CHECK: }
// CHECK-NEXT: %a = firrtl.wire
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_a_sym]] %a
// CHECK-NEXT: %b = firrtl.wire
// CHECK-NEXT: %_layer_probe_0 = firrtl.node sym @[[Simple_b_sym]] %b
// CHECK-NEXT: firrtl.instance a_b_c {{.*}}
// CHECK-NEXT: firrtl.instance a_b {{.*}}
// CHECK-NEXT: firrtl.instance a {{.*}}
// CHECK-NEXT: }
//
// CHECK-DAG: sv.verbatim "`endif // layers_Simple_A"
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
@ -556,7 +459,7 @@ firrtl.circuit "Simple" {
firrtl.circuit "ModuleNameConflict" {
firrtl.layer @A bind {}
firrtl.module private @ModuleNameConflict_A() {}
firrtl.module @ModuleNameConflict_A() {}
firrtl.module @ModuleNameConflict() {
%a = firrtl.wire : !firrtl.uint<1>
firrtl.instance foo @ModuleNameConflict_A()
@ -568,7 +471,8 @@ firrtl.circuit "ModuleNameConflict" {
// CHECK-LABEL: firrtl.circuit "ModuleNameConflict"
//
// CHECK: firrtl.module private @[[groupModule:[_a-zA-Z0-9_]+]](in
// CHECK: firrtl.module @ModuleNameConflict_A()
// CHECK: firrtl.module private @[[groupModule:[_a-zA-Z0-9_]+]]()
//
// CHECK: firrtl.module @ModuleNameConflict()
// CHECK-NOT: firrtl.module
@ -587,13 +491,16 @@ firrtl.circuit "CaptureHardwareMultipleTimes" {
firrtl.extmodule @CaptureHardwareMultipleTimes ()
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.add %[[p]], %[[p]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK: hw.hierpath private @[[path:.+]] [@CaptureSrcTwice::@[[sym:.+]]]
//
// CHECK: firrtl.module private @[[A:.+]]()
// CHECK: %0 = firrtl.xmr.deref @[[path]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.add %0, %0 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
// CHECK: }
// CHECK: firrtl.module @CaptureSrcTwice() {
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} @[[A]]
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
// CHECK: %_layer_probe = firrtl.node sym @[[sym]] %c0_ui1 : !firrtl.uint<1>
// CHECK: firrtl.instance {{.+}} @[[A]]
// CHECK: }
firrtl.module @CaptureSrcTwice() {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
@ -602,53 +509,6 @@ firrtl.circuit "CaptureHardwareMultipleTimes" {
}
}
// CHECK: firrtl.module private @[[A:.+]](out %[[dst:.+]]: !firrtl.probe<uint<1>>, in %[[src:.+]]: !firrtl.uint<1>)
// CHECK: %0 = firrtl.ref.send %[[src]] : !firrtl.uint<1>
// CHECK: %w2 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %[[dst]], %w2 : !firrtl.probe<uint<1>>
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @CaptureAsDstThenSrc() {
// CHECK: %w1 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %[[out:.+]], %[[in:.+]] = firrtl.instance {{.+}} @[[A]](out {{.+}}: !firrtl.probe<uint<1>>, in {{.+}}: !firrtl.uint<1>)
// CHECK: %0 = firrtl.ref.resolve %w1 : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[in]], %0 : !firrtl.uint<1>
// CHECK: firrtl.ref.define %w1, %[[out]] : !firrtl.probe<uint<1>>
// CHECK: }
firrtl.module @CaptureAsDstThenSrc() {
%w1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.layerblock @A {
// capture first as a sink.
%w2 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.ref.define %w1, %w2 : !firrtl.probe<uint<1>, @A>
// Capture again, as a source.
%2 = firrtl.ref.resolve %w1 : !firrtl.probe<uint<1>, @A>
}
}
// CHECK: firrtl.module private @[[A:.+]](in %[[src:.+]]: !firrtl.uint<1>, out %[[dst:.+]]: !firrtl.probe<uint<1>>)
// CHECK: %0 = firrtl.ref.send %[[src]] : !firrtl.uint<1>
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
// CHECK: %w2 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: firrtl.ref.define %[[dst]], %w2 : !firrtl.probe<uint<1>>
// CHECK: }
// CHECK: firrtl.module @CaptureAsSrcThenDst() {
// CHECK: %w1 = firrtl.wire : !firrtl.probe<uint<1>>
// CHECK: %[[in:.+]], %[[out:.+]] = firrtl.instance {{.+}} @[[A]]
// CHECK: firrtl.ref.define %w1, %[[out]] : !firrtl.probe<uint<1>>
// CHECK: %0 = firrtl.ref.resolve %w1 : !firrtl.probe<uint<1>>
// CHECK: firrtl.matchingconnect %[[in]], %0 : !firrtl.uint<1>
// CHECK: }
firrtl.module @CaptureAsSrcThenDst() {
%w1 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.layerblock @A {
// capture first as a source.
%2 = firrtl.ref.resolve %w1 : !firrtl.probe<uint<1>, @A>
// capture again, as a sink.
%w2 = firrtl.wire : !firrtl.probe<uint<1>, @A>
firrtl.ref.define %w1, %w2 : !firrtl.probe<uint<1>, @A>
}
}
}
// -----
@ -751,37 +611,6 @@ firrtl.circuit "Foo" {
// -----
// Check rwprobe ops are updated.
// CHECK-LABEL: circuit "RWTH"
firrtl.circuit "RWTH" {
firrtl.layer @T bind { }
firrtl.module @RWTH() attributes {convention = #firrtl<convention scalarized>, layers = [@T]} {
%d_p = firrtl.instance d @DUT(out p: !firrtl.rwprobe<uint<1>, @T>)
%one = firrtl.constant 1 : !firrtl.uint<1>
firrtl.ref.force_initial %one, %d_p, %one: !firrtl.uint<1>, !firrtl.rwprobe<uint<1>, @T>, !firrtl.uint<1>
}
// CHECK: firrtl.module private @DUT_T(out %p: !firrtl.rwprobe<uint<1>>) {
// CHECK-NEXT: %w = firrtl.wire sym @[[SYM:.+]] : !firrtl.uint<1>
// CHECK-NEXT: %0 = firrtl.ref.rwprobe <@DUT_T::@[[SYM]]> : !firrtl.rwprobe<uint<1>>
// CHECK-NEXT: firrtl.ref.define %p, %0 : !firrtl.rwprobe<uint<1>>
// CHECK-NEXT: }
// CHECK-NEXT: firrtl.module @DUT(out %p: !firrtl.rwprobe<uint<1>>) attributes {convention = #firrtl<convention scalarized>} {
// CHECK-NEXT: %t_p = firrtl.instance t sym @t {lowerToBind, output_file = #hw.output_file<"layers-RWTH-T.sv", excludeFromFileList>} @DUT_T(out p: !firrtl.rwprobe<uint<1>>)
// CHECK-NEXT: firrtl.ref.define %p, %t_p : !firrtl.rwprobe<uint<1>>
// CHECK-NEXT: }
firrtl.module @DUT(out %p: !firrtl.rwprobe<uint<1>, @T>) attributes {convention = #firrtl<convention scalarized>} {
firrtl.layerblock @T {
%w = firrtl.wire sym @sym : !firrtl.uint<1>
%0 = firrtl.ref.rwprobe <@DUT::@sym> : !firrtl.rwprobe<uint<1>>
%1 = firrtl.ref.cast %0 : (!firrtl.rwprobe<uint<1>>) -> !firrtl.rwprobe<uint<1>, @T>
firrtl.ref.define %p, %1 : !firrtl.rwprobe<uint<1>, @T>
}
}
}
// -----
// Check sv.verbatim inner refs are updated, as occurs with views under layers.
// CHECK-LABEL: circuit "Verbatim"
firrtl.circuit "Verbatim" {

View File

@ -9,9 +9,9 @@ FIRRTL version 4.0.0
; CHECK-NOT: module Child
; CHECK: module Top_Verification_Assert
; CHECK: assert(p) else $error("before child");
; CHECK: assert(p) else $error("in child");
; CHECK: assert(p) else $error("after child");
; CHECK: assert(Top.p) else $error("before child");
; CHECK: assert(Top.p) else $error("in child");
; CHECK: assert(Top.p) else $error("after child");
; CHECK: endmodule
circuit Top: %[[

View File

@ -37,42 +37,29 @@ circuit Foo: %[[
node z = x
assert(clock, cond, enable, "Test")
; CHECK-LABEL: module Foo_A_B(
; CHECK-NEXT: input x,
; CHECK-NEXT: cond,
; CHECK-NEXT: enable,
; CHECK-NEXT: clock
; CHECK-NEXT: );
; CHECK: wire y = x;
; CHECK: wire z = x;
; CHECK: always @(posedge clock) begin
; CHECK-NEXT: if (cond & x & enable)
; CHECK-NEXT: assert(cond) else $error("Test");
; CHECK-LABEL: module Foo_A_B();
; CHECK: wire y = Foo.a._layer_probe;
; CHECK: wire z = Foo.a._layer_probe;
; CHECK: always @(posedge Foo.clock) begin
; CHECK-NEXT: if (Foo.cond & Foo.a._layer_probe & Foo.enable)
; CHECK-NEXT: assert(Foo.cond) else $error("Test");
; CHECK-NEXT: end // always @(posedge)
; CHECK-NEXT: endmodule
; CHECK-LABEL: module Foo_A(
; CHECK-NEXT: input in
; CHECK: wire x = in;
; CHECK-NEXT: wire x_probe = x;
; CHECK-LABEL: module Foo_A();
; CHECK: wire x = Foo.in;
; CHECK-NEXT: wire _layer_probe = x;
; CHECK-NEXT: endmodule
; CHECK-LABEL: FILE "layers-Foo-A-B.sv"
; CHECK: `include "layers-Foo-A.sv"
; CHECK-NEXT: `ifndef layers_Foo_A_B
; CHECK-NEXT: `define layers_Foo_A_B
; CHECK-NEXT: bind Foo Foo_A_B a_b (
; CHECK-NEXT: .x (Foo.a.x_probe),
; CHECK-NEXT: .cond (cond),
; CHECK-NEXT: .enable (enable),
; CHECK-NEXT: .clock (clock)
; CHECK-NEXT: );
; CHECK-NEXT: bind Foo Foo_A_B a_b ();
; CHECK-NEXT: `endif // layers_Foo_A_B
; CHECK-LABEL: FILE "layers-Foo-A.sv"
; CHECK: `ifndef layers_Foo_A
; CHECK-NEXT: `define layers_Foo_A
; CHECK-NEXT: bind Foo Foo_A a (
; CHECK-NEXT: .in (in)
; CHECK-NEXT: );
; CHECK-NEXT: bind Foo Foo_A a ();
; CHECK-NEXT: `endif // layers_Foo_A

View File

@ -1,4 +1,4 @@
; RUN: firtool %s -disable-all-randomization -split-input-file | FileCheck %s
; RUN: firtool %s -disable-all-randomization -split-input-file -advanced-layer-sink -lowering-options=emittedLineLength=1024 | FileCheck %s
; This is an end-to-end example of a test-bench (Foo) enabling verification,
; probing into a device-under-test (Bar), and reading from hardware which is
@ -83,14 +83,11 @@ circuit TestHarness:
layer Verification, bind:
; CHECK: module DUT_Verification(
; CHECK: input clock,
; CHECK: input [31:0] a
; CHECK: );
; CHECK: module DUT_Verification();
; CHECK: reg [31:0] pc_d;
; CHECK: wire [31:0] pc_d_probe = pc_d;
; CHECK: always @(posedge clock)
; CHECK: pc_d <= a;
; CHECK: always @(posedge DUT.clock)
; CHECK: pc_d <= DUT.a;
; CHECK: endmodule
; CHECK: module DUT(
@ -124,15 +121,11 @@ circuit TestHarness:
layerblock Verification:
define trace = x
; CHECK: module TestHarness_Verification(
; CHECK: input [31:0] dut_trace,
; CHECK: input clock,
; CHECK: reset
; CHECK: );
; CHECK: module TestHarness_Verification()
; CHECK: `ifndef SYNTHESIS
; CHECK: always @(posedge clock) begin
; CHECK: if ((`PRINTF_COND_) & reset)
; CHECK: $fwrite(`PRINTF_FD_, "The last PC was: %x", dut_trace);
; CHECK: always @(posedge TestHarness.clock) begin
; CHECK: if ((`PRINTF_COND_) & TestHarness.reset)
; CHECK: $fwrite(`PRINTF_FD_, "The last PC was: %x", TestHarness.dut.verification.pc_d_probe);
; CHECK: end // always @(posedge)
; CHECK: `endif // not def SYNTHESIS
; CHECK: endmodule
@ -167,13 +160,95 @@ circuit TestHarness:
; CHECK: FILE "layers-TestHarness-Verification.sv"
; CHECK: `ifndef layers_TestHarness_Verification
; CHECK: `define layers_TestHarness_Verification
; CHECK: bind DUT DUT_Verification verification (
; CHECK: .clock (clock),
; CHECK: .a (a)
; CHECK: );
; CHECK: bind TestHarness TestHarness_Verification verification (
; CHECK: .dut_trace (TestHarness.dut.verification.pc_d_probe),
; CHECK: .clock (clock),
; CHECK: .reset (reset)
; CHECK: );
; CHECK: bind DUT DUT_Verification verification ();
; CHECK: bind TestHarness TestHarness_Verification verification ();
; CHECK: `endif // layers_TestHarness_Verification
; // -----
; This example demonstrates forcing _out_ of a layer into the outer module, a
; parent layer, or into another module.
FIRRTL version 5.1.0
circuit Foo: %[[
{"class": "firrtl.transforms.DontTouchAnnotation", "target": "~|ForceOutOfLayer>a"},
{"class": "firrtl.transforms.DontTouchAnnotation", "target": "~|Submodule>root"},
{"class": "firrtl.transforms.DontTouchAnnotation", "target": "~|Submodule>a"},
{"class": "firrtl.transforms.DontTouchAnnotation", "target": "~|Submodule>b"}
]]
layer A, bind:
layer B, inline:
; Test that forcing out of a layer into the root module works.
;
; CHECK: module ForceOutOfLayer_A();
; CHECK: initial
; CHECK-NEXT: force ForceOutOfLayer.a = 2'h1;
; CHECK: endmodule
;
; CHECK: module ForceOutOfLayer();
; CHECK: initial
; CHECK-NEXT: force ForceOutOfLayer.a = 2'h2;
; CHECK: endmodule
module ForceOutOfLayer:
wire a: UInt<2>
connect a, UInt<2>(0)
wire a_probe: RWProbe<UInt<2>>
define a_probe = rwprobe(a)
layerblock A:
force_initial(a_probe, UInt<2>(1))
layerblock B:
force_initial(a_probe, UInt<2>(2))
; Test that forcing out of a layer into another works. Test both forcing into
; the other module and forcing into a layer in the other module.
;
; CHECK: module ForceIntoSubmodule_A();
; CHECK: initial
; CHECK-NEXT: force ForceIntoSubmodule.submodule.root = 2'h1;
; CHECK-NEXT: force ForceIntoSubmodule.submodule.a.a = 2'h1;
; CHECK: endmodule
;
; CHECK: module ForceIntoSubmodule();
; CHECK: initial
; CHECK-NEXT: force ForceIntoSubmodule.submodule.root = 2'h2;
; CHECK-NEXT: force ForceIntoSubmodule.submodule.b = 2'h2;
; CHECK: endmodule
module Submodule:
output root_probe: RWProbe<UInt<2>>
output a_probe: RWProbe<UInt<2>, A>
output b_probe: RWProbe<UInt<2>, B>
wire root: UInt<2>
connect root, UInt<2>(0)
define root_probe = rwprobe(root)
layerblock A:
wire a: UInt<2>
connect a, UInt<2>(0)
define a_probe = rwprobe(a)
layerblock B:
wire b: UInt<2>
connect b, UInt<2>(0)
define b_probe = rwprobe(b)
module ForceIntoSubmodule:
inst submodule of Submodule
layerblock A:
force_initial(submodule.root_probe, UInt<2>(1))
force_initial(submodule.a_probe, UInt<2>(1))
layerblock B:
force_initial(submodule.root_probe, UInt<2>(2))
force_initial(submodule.b_probe, UInt<2>(2))
public module Foo:
inst forceOutOfLayer of ForceOutOfLayer
inst forceIntoSubmodule of ForceIntoSubmodule

View File

@ -473,8 +473,7 @@ static LogicalResult processBuffer(
if (failed(parsePassPipeline(StringRef(highFIRRTLPassPlugin), pm)))
return failure();
if (failed(firtool::populateCHIRRTLToLowFIRRTL(pm, firtoolOptions,
inputFilename)))
if (failed(firtool::populateCHIRRTLToLowFIRRTL(pm, firtoolOptions)))
return failure();
if (!lowFIRRTLPassPlugin.empty())
@ -484,7 +483,8 @@ static LogicalResult processBuffer(
// Lower if we are going to verilog or if lowering was specifically
// requested.
if (outputFormat != OutputIRFir) {
if (failed(firtool::populateLowFIRRTLToHW(pm, firtoolOptions)))
if (failed(
firtool::populateLowFIRRTLToHW(pm, firtoolOptions, inputFilename)))
return failure();
if (!hwPassPlugin.empty())
if (failed(parsePassPipeline(StringRef(hwPassPlugin), pm)))