mirror of https://github.com/llvm/circt.git
[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:
parent
a358f9bef5
commit
5cffc8537b
|
@ -236,11 +236,11 @@ MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulatePreprocessTransforms(
|
||||||
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
||||||
|
|
||||||
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateCHIRRTLToLowFIRRTL(
|
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateCHIRRTLToLowFIRRTL(
|
||||||
MlirPassManager pm, CirctFirtoolFirtoolOptions options,
|
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
||||||
MlirStringRef inputFilename);
|
|
||||||
|
|
||||||
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateLowFIRRTLToHW(
|
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateLowFIRRTLToHW(
|
||||||
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
MlirPassManager pm, CirctFirtoolFirtoolOptions options,
|
||||||
|
MlirStringRef inputFilename);
|
||||||
|
|
||||||
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateHWToSV(
|
MLIR_CAPI_EXPORTED MlirLogicalResult circtFirtoolPopulateHWToSV(
|
||||||
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
MlirPassManager pm, CirctFirtoolFirtoolOptions options);
|
||||||
|
|
|
@ -444,11 +444,11 @@ LogicalResult populatePreprocessTransforms(mlir::PassManager &pm,
|
||||||
const FirtoolOptions &opt);
|
const FirtoolOptions &opt);
|
||||||
|
|
||||||
LogicalResult populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
LogicalResult populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
||||||
const FirtoolOptions &opt,
|
const FirtoolOptions &opt);
|
||||||
StringRef inputFilename);
|
|
||||||
|
|
||||||
LogicalResult populateLowFIRRTLToHW(mlir::PassManager &pm,
|
LogicalResult populateLowFIRRTLToHW(mlir::PassManager &pm,
|
||||||
const FirtoolOptions &opt);
|
const FirtoolOptions &opt,
|
||||||
|
StringRef inputFilename);
|
||||||
|
|
||||||
LogicalResult populateHWToSV(mlir::PassManager &pm, const FirtoolOptions &opt);
|
LogicalResult populateHWToSV(mlir::PassManager &pm, const FirtoolOptions &opt);
|
||||||
|
|
||||||
|
|
|
@ -340,16 +340,17 @@ circtFirtoolPopulatePreprocessTransforms(MlirPassManager pm,
|
||||||
|
|
||||||
MlirLogicalResult
|
MlirLogicalResult
|
||||||
circtFirtoolPopulateCHIRRTLToLowFIRRTL(MlirPassManager pm,
|
circtFirtoolPopulateCHIRRTLToLowFIRRTL(MlirPassManager pm,
|
||||||
CirctFirtoolFirtoolOptions options,
|
CirctFirtoolFirtoolOptions options) {
|
||||||
MlirStringRef inputFilename) {
|
return wrap(
|
||||||
return wrap(firtool::populateCHIRRTLToLowFIRRTL(*unwrap(pm), *unwrap(options),
|
firtool::populateCHIRRTLToLowFIRRTL(*unwrap(pm), *unwrap(options)));
|
||||||
unwrap(inputFilename)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MlirLogicalResult
|
MlirLogicalResult
|
||||||
circtFirtoolPopulateLowFIRRTLToHW(MlirPassManager pm,
|
circtFirtoolPopulateLowFIRRTLToHW(MlirPassManager pm,
|
||||||
CirctFirtoolFirtoolOptions options) {
|
CirctFirtoolFirtoolOptions options,
|
||||||
return wrap(firtool::populateLowFIRRTLToHW(*unwrap(pm), *unwrap(options)));
|
MlirStringRef inputFilename) {
|
||||||
|
return wrap(firtool::populateLowFIRRTLToHW(*unwrap(pm), *unwrap(options),
|
||||||
|
unwrap(inputFilename)));
|
||||||
}
|
}
|
||||||
|
|
||||||
MlirLogicalResult
|
MlirLogicalResult
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "circt/Dialect/FIRRTL/FIRRTLUtils.h"
|
#include "circt/Dialect/FIRRTL/FIRRTLUtils.h"
|
||||||
#include "circt/Dialect/FIRRTL/Namespace.h"
|
#include "circt/Dialect/FIRRTL/Namespace.h"
|
||||||
#include "circt/Dialect/FIRRTL/Passes.h"
|
#include "circt/Dialect/FIRRTL/Passes.h"
|
||||||
|
#include "circt/Dialect/HW/HierPathCache.h"
|
||||||
#include "circt/Dialect/HW/InnerSymbolNamespace.h"
|
#include "circt/Dialect/HW/InnerSymbolNamespace.h"
|
||||||
#include "circt/Dialect/SV/SVOps.h"
|
#include "circt/Dialect/SV/SVOps.h"
|
||||||
#include "circt/Support/Utils.h"
|
#include "circt/Support/Utils.h"
|
||||||
|
@ -52,6 +53,18 @@ struct ConnectInfo {
|
||||||
/// for modules and files, as well as by convention.
|
/// for modules and files, as well as by convention.
|
||||||
enum class Delimiter { BindModule = '_', BindFile = '-', InlineMacro = '$' };
|
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
|
} // namespace
|
||||||
|
|
||||||
// A mapping of an old InnerRefAttr to the new inner symbol and module name that
|
// 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;
|
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`,
|
/// For a layerblock `@A::@B::@C`,
|
||||||
/// the generated instance is called `a_b_c`.
|
/// the generated instance is called `a_b_c`.
|
||||||
static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
|
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
|
/// Safely build a new module with a given namehint. This handles geting a
|
||||||
/// lock to modify the top-level circuit.
|
/// lock to modify the top-level circuit.
|
||||||
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock,
|
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock);
|
||||||
SmallVectorImpl<PortInfo> &ports);
|
|
||||||
|
|
||||||
/// Strip layer colors from the module's interface.
|
/// Strip layer colors from the module's interface.
|
||||||
FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
|
FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
|
||||||
|
@ -174,10 +196,6 @@ class LowerLayersPass
|
||||||
/// Update the value's type to remove any layers from any probe types.
|
/// Update the value's type to remove any layers from any probe types.
|
||||||
void removeLayersFromValue(Value value);
|
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.
|
/// Lower an inline layerblock to an ifdef block.
|
||||||
void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
|
void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
|
||||||
|
|
||||||
|
@ -195,14 +213,18 @@ class LowerLayersPass
|
||||||
/// Indicates exclusive access to modify the circuitNamespace and the circuit.
|
/// Indicates exclusive access to modify the circuitNamespace and the circuit.
|
||||||
llvm::sys::SmartMutex<true> *circuitMutex;
|
llvm::sys::SmartMutex<true> *circuitMutex;
|
||||||
|
|
||||||
/// A map of layer blocks to module name that should be created for it.
|
/// A map of layer blocks to "safe" global names which are fine to create in
|
||||||
DenseMap<LayerBlockOp, StringRef> moduleNames;
|
/// the circuit namespace.
|
||||||
|
DenseMap<LayerBlockOp, LayerBlockGlobals> layerBlockGlobals;
|
||||||
|
|
||||||
/// A map from inline layers to their macro names.
|
/// A map from inline layers to their macro names.
|
||||||
DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
|
DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
|
||||||
|
|
||||||
/// A mapping of symbol name to layer operation.
|
/// A mapping of symbol name to layer operation.
|
||||||
DenseMap<SymbolRefAttr, LayerOp> symbolToLayer;
|
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.
|
/// 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
|
/// generated if there are conflicts with the namehint in the circuit-level
|
||||||
/// namespace.
|
/// namespace.
|
||||||
FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
|
FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
|
||||||
LayerBlockOp layerBlock,
|
LayerBlockOp layerBlock) {
|
||||||
SmallVectorImpl<PortInfo> &ports) {
|
|
||||||
auto location = layerBlock.getLoc();
|
auto location = layerBlock.getLoc();
|
||||||
auto namehint = moduleNames.lookup(layerBlock);
|
auto namehint = layerBlockGlobals.lookup(layerBlock).moduleName;
|
||||||
llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
|
llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
|
||||||
FModuleOp newModule = builder.create<FModuleOp>(
|
FModuleOp newModule = builder.create<FModuleOp>(
|
||||||
location, builder.getStringAttr(namehint),
|
location, builder.getStringAttr(namehint),
|
||||||
ConventionAttr::get(builder.getContext(), Convention::Internal), ports,
|
ConventionAttr::get(builder.getContext(), Convention::Internal),
|
||||||
ArrayAttr{});
|
ArrayRef<PortInfo>{}, ArrayAttr{});
|
||||||
if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
|
if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
|
||||||
assert(dir.isDirectory());
|
assert(dir.isDirectory());
|
||||||
newModule->setAttr("output_file", dir);
|
newModule->setAttr("output_file", dir);
|
||||||
|
@ -234,22 +255,6 @@ void LowerLayersPass::removeLayersFromValue(Value value) {
|
||||||
value.setType(type.removeLayer());
|
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) {
|
void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
|
||||||
auto oldTypeAttrs = moduleLike.getPortTypesAttr();
|
auto oldTypeAttrs = moduleLike.getPortTypesAttr();
|
||||||
SmallVector<Attribute> newTypeAttrs;
|
SmallVector<Attribute> newTypeAttrs;
|
||||||
|
@ -324,47 +329,180 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
StringRef circuitName = circuitOp.getName();
|
StringRef circuitName = circuitOp.getName();
|
||||||
hw::InnerSymbolNamespace ns(moduleOp);
|
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
|
// 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
|
// check if this was an instance that we created and to do fast module
|
||||||
// dereferencing (avoiding a symbol table).
|
// dereferencing (avoiding a symbol table).
|
||||||
DenseMap<InstanceOp, FModuleOp> createdInstances;
|
DenseMap<InstanceOp, FModuleOp> createdInstances;
|
||||||
|
|
||||||
// Post-order traversal that expands a layer block into its parent. For each
|
// Check that the preconditions for this pass are met. Reject any ops which
|
||||||
// layer block found do the following:
|
// 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
|
// 1. Any layer coloring is stripped.
|
||||||
// this layer block that drives an instance marked lowerToBind and move
|
// 2. Layers with Inline convention are converted to SV ifdefs.
|
||||||
// this instance outside the layer block.
|
// 3. Layers with Bind convention are converted to new modules and then
|
||||||
// 2. Create one input port for each value captured by this layer block.
|
// instantiated at their original location. Any captured values are either
|
||||||
// 3. Create a new module for this layer block and move the (mutated) body of
|
// moved, cloned, or converted to XMR deref ops.
|
||||||
// this layer block to the new module.
|
// 4. Move instances created from earlier (3) conversions out of later (3)
|
||||||
// 4. Instantiate the new module outside the layer block and hook it up.
|
// conversions. This is necessary to avoid a SystemVerilog-illegal
|
||||||
// 5. Erase the layer block.
|
// 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) {
|
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.
|
// Strip layer requirements from any op that might represent a probe.
|
||||||
if (auto wire = dyn_cast<WireOp>(op)) {
|
if (auto wire = dyn_cast<WireOp>(op)) {
|
||||||
removeLayersFromValue(wire.getResult());
|
removeLayersFromValue(wire.getResult());
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
}
|
}
|
||||||
if (auto sub = dyn_cast<RefSubOp>(op)) {
|
|
||||||
removeLayersFromValue(sub.getResult());
|
|
||||||
return WalkResult::advance();
|
|
||||||
}
|
|
||||||
if (auto instance = dyn_cast<InstanceOp>(op)) {
|
if (auto instance = dyn_cast<InstanceOp>(op)) {
|
||||||
instance.setLayers({});
|
instance.setLayers({});
|
||||||
for (auto result : instance.getResults())
|
for (auto result : instance.getResults())
|
||||||
removeLayersFromValue(result);
|
removeLayersFromValue(result);
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
}
|
}
|
||||||
if (auto cast = dyn_cast<RefCastOp>(op)) {
|
|
||||||
removeLayersFromRefCast(cast);
|
|
||||||
return WalkResult::advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto layerBlock = dyn_cast<LayerBlockOp>(op);
|
auto layerBlock = dyn_cast<LayerBlockOp>(op);
|
||||||
if (!layerBlock)
|
if (!layerBlock)
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
|
|
||||||
|
// After this point, we are dealing with a layer block.
|
||||||
auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
|
auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
|
||||||
|
|
||||||
if (layer.getConvention() == LayerConvention::Inline) {
|
if (layer.getConvention() == LayerConvention::Inline) {
|
||||||
|
@ -372,116 +510,73 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After this point, we are dealing with a bind convention layer block.
|
||||||
assert(layer.getConvention() == LayerConvention::Bind);
|
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);
|
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<hw::InnerSymAttr> innerSyms;
|
||||||
SmallVector<RWProbeOp> rwprobes;
|
|
||||||
SmallVector<sv::VerbatimOp> verbatims;
|
SmallVector<sv::VerbatimOp> verbatims;
|
||||||
|
DenseSet<Operation *> spilledSubOps;
|
||||||
auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
|
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.
|
// Record any operations inside the layer block which have inner symbols.
|
||||||
// Theses may have symbol users which need to be updated.
|
// 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 symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
|
||||||
if (auto innerSym = symOp.getInnerSymAttr())
|
if (auto innerSym = symOp.getInnerSymAttr())
|
||||||
innerSyms.push_back(innerSym);
|
innerSyms.push_back(innerSym);
|
||||||
|
@ -511,119 +606,19 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle subfields, subindexes, and subaccesses which are indexing into
|
// Handle captures. For any captured operands, convert them to a suitable
|
||||||
// non-passive values. If these are kept in the module, then the module
|
// replacement value. The `getReplacement` function will automatically
|
||||||
// must create bi-directional ports. This doesn't make sense as the
|
// reuse values whenever possible.
|
||||||
// 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.
|
|
||||||
for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
|
for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
|
||||||
auto operand = op->getOperand(i);
|
auto operand = op->getOperand(i);
|
||||||
|
|
||||||
// If the operand is in this layer block, do nothing.
|
// If the operand is in this layer block, do nothing.
|
||||||
|
//
|
||||||
|
// Note: This check is what avoids handling ConnectOp destinations.
|
||||||
if (isAncestorOfValueOwner(layerBlock, operand))
|
if (isAncestorOfValueOwner(layerBlock, operand))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If the operand is "special", e.g., it has no port representation,
|
op->setOperand(i, getReplacement(op, operand));
|
||||||
// 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
|
if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
|
||||||
|
@ -636,33 +631,22 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
return WalkResult::interrupt();
|
return WalkResult::interrupt();
|
||||||
|
|
||||||
// Create the new module. This grabs a lock to modify the circuit.
|
// 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::setSymbolVisibility(newModule,
|
||||||
SymbolTable::Visibility::Private);
|
SymbolTable::Visibility::Private);
|
||||||
newModule.getBody().takeBody(layerBlock.getRegion());
|
newModule.getBody().takeBody(layerBlock.getRegion());
|
||||||
|
|
||||||
LLVM_DEBUG({
|
LLVM_DEBUG({
|
||||||
llvm::dbgs() << " New Module: " << moduleNames.lookup(layerBlock)
|
llvm::dbgs() << " New Module: "
|
||||||
<< "\n";
|
<< layerBlockGlobals.lookup(layerBlock).moduleName << "\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";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace the original layer block with an instance. Hook up the instance.
|
// Replace the original layer block with an instance. Hook up the
|
||||||
// Intentionally create instance with probe ports which do not have an
|
// instance. Intentionally create instance with probe ports which do
|
||||||
// associated layer. This is illegal IR that will be made legal by the end
|
// not have an associated layer. This is illegal IR that will be
|
||||||
// of the pass. This is done to avoid having to revisit and rewrite each
|
// made legal by the end of the pass. This is done to avoid having
|
||||||
// instance everytime it is moved into a parent layer.
|
// to revisit and rewrite each instance everytime it is moved into a
|
||||||
|
// parent layer.
|
||||||
builder.setInsertionPointAfter(layerBlock);
|
builder.setInsertionPointAfter(layerBlock);
|
||||||
auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
|
auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
|
||||||
auto instanceOp = builder.create<InstanceOp>(
|
auto instanceOp = builder.create<InstanceOp>(
|
||||||
|
@ -695,34 +679,6 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
<< splice.second << "\n";);
|
<< 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.
|
// Update verbatims that target operations extracted alongside.
|
||||||
if (!verbatims.empty()) {
|
if (!verbatims.empty()) {
|
||||||
mlir::AttrTypeReplacer replacer;
|
mlir::AttrTypeReplacer replacer;
|
||||||
|
@ -737,34 +693,6 @@ LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
|
||||||
replacer.replaceElementsIn(verbatim);
|
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();
|
layerBlock.erase();
|
||||||
|
|
||||||
return WalkResult::advance();
|
return WalkResult::advance();
|
||||||
|
@ -820,14 +748,22 @@ void LowerLayersPass::runOnOperation() {
|
||||||
circuitMutex = &mutex;
|
circuitMutex = &mutex;
|
||||||
|
|
||||||
CircuitNamespace ns(circuitOp);
|
CircuitNamespace ns(circuitOp);
|
||||||
|
hw::HierPathCache hpc(
|
||||||
|
&ns, OpBuilder::InsertPoint(getOperation().getBodyBlock(),
|
||||||
|
getOperation().getBodyBlock()->begin()));
|
||||||
|
hierPathCache = &hpc;
|
||||||
|
|
||||||
for (auto &op : *circuitOp.getBodyBlock()) {
|
for (auto &op : *circuitOp.getBodyBlock()) {
|
||||||
// Determine names for all modules that will be created. Do this serially
|
// Determine names for all modules that will be created. Do this serially
|
||||||
// to avoid non-determinism from creating these in the parallel region.
|
// to avoid non-determinism from creating these in the parallel region.
|
||||||
if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
|
if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
|
||||||
moduleOp->walk([&](LayerBlockOp layerBlockOp) {
|
moduleOp->walk([&](LayerBlockOp layerBlockOp) {
|
||||||
auto name = moduleNameForLayer(moduleOp.getModuleName(),
|
auto moduleName = moduleNameForLayer(moduleOp.getModuleName(),
|
||||||
layerBlockOp.getLayerName());
|
layerBlockOp.getLayerName());
|
||||||
moduleNames.insert({layerBlockOp, ns.newName(name)});
|
auto hierPathName = hierPathNameForLayer(moduleOp.getModuleName(),
|
||||||
|
layerBlockOp.getLayerName());
|
||||||
|
layerBlockGlobals.insert(
|
||||||
|
{layerBlockOp, {ns.newName(moduleName), ns.newName(hierPathName)}});
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -980,6 +916,9 @@ void LowerLayersPass::runOnOperation() {
|
||||||
for (auto layerOp :
|
for (auto layerOp :
|
||||||
llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
|
llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
|
||||||
layerOp.erase();
|
layerOp.erase();
|
||||||
|
|
||||||
|
// Cleanup state.
|
||||||
|
hierPathCache = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {
|
std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {
|
||||||
|
|
|
@ -53,8 +53,7 @@ LogicalResult firtool::populatePreprocessTransforms(mlir::PassManager &pm,
|
||||||
}
|
}
|
||||||
|
|
||||||
LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
||||||
const FirtoolOptions &opt,
|
const FirtoolOptions &opt) {
|
||||||
StringRef inputFilename) {
|
|
||||||
// TODO: Ensure instance graph and other passes can handle instance choice
|
// TODO: Ensure instance graph and other passes can handle instance choice
|
||||||
// then run this pass after all diagnostic passes have run.
|
// then run this pass after all diagnostic passes have run.
|
||||||
pm.addNestedPass<firrtl::CircuitOp>(firrtl::createSpecializeOptionPass(
|
pm.addNestedPass<firrtl::CircuitOp>(firrtl::createSpecializeOptionPass(
|
||||||
|
@ -217,6 +216,17 @@ LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
||||||
pm.nest<firrtl::CircuitOp>().nest<firrtl::FModuleOp>().addPass(
|
pm.nest<firrtl::CircuitOp>().nest<firrtl::FModuleOp>().addPass(
|
||||||
firrtl::createVectorizationPass());
|
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
|
// Layer lowering passes. Move operations into layers when possible and
|
||||||
// remove layers by converting them to other constructs. This lowering
|
// remove layers by converting them to other constructs. This lowering
|
||||||
// process can create a few optimization opportunities.
|
// process can create a few optimization opportunities.
|
||||||
|
@ -258,21 +268,12 @@ LogicalResult firtool::populateCHIRRTLToLowFIRRTL(mlir::PassManager &pm,
|
||||||
: opt.getBlackBoxRootPath();
|
: opt.getBlackBoxRootPath();
|
||||||
pm.nest<firrtl::CircuitOp>().addPass(
|
pm.nest<firrtl::CircuitOp>().addPass(
|
||||||
firrtl::createBlackBoxReaderPass(blackBoxRoot));
|
firrtl::createBlackBoxReaderPass(blackBoxRoot));
|
||||||
return success();
|
|
||||||
}
|
|
||||||
|
|
||||||
LogicalResult firtool::populateLowFIRRTLToHW(mlir::PassManager &pm,
|
|
||||||
const FirtoolOptions &opt) {
|
|
||||||
// Remove TraceAnnotations and write their updated paths to an output
|
// Remove TraceAnnotations and write their updated paths to an output
|
||||||
// annotation file.
|
// annotation file.
|
||||||
pm.nest<firrtl::CircuitOp>().addPass(
|
pm.nest<firrtl::CircuitOp>().addPass(
|
||||||
firrtl::createResolveTracesPass(opt.getOutputAnnotationFilename()));
|
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::createLowerDPIPass());
|
||||||
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerClassesPass());
|
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createLowerClassesPass());
|
||||||
pm.nest<firrtl::CircuitOp>().addPass(om::createVerifyObjectFieldsPass());
|
pm.nest<firrtl::CircuitOp>().addPass(om::createVerifyObjectFieldsPass());
|
||||||
|
|
|
@ -50,11 +50,11 @@ void exportVerilog(MlirContext ctx, bool disableOptimization) {
|
||||||
circtFirtoolPopulatePreprocessTransforms(pm, options);
|
circtFirtoolPopulatePreprocessTransforms(pm, options);
|
||||||
assert(mlirLogicalResultIsSuccess(result));
|
assert(mlirLogicalResultIsSuccess(result));
|
||||||
|
|
||||||
result = circtFirtoolPopulateCHIRRTLToLowFIRRTL(
|
result = circtFirtoolPopulateCHIRRTLToLowFIRRTL(pm, options);
|
||||||
pm, options, mlirStringRefCreateFromCString("-"));
|
|
||||||
assert(mlirLogicalResultIsSuccess(result));
|
assert(mlirLogicalResultIsSuccess(result));
|
||||||
|
|
||||||
result = circtFirtoolPopulateLowFIRRTLToHW(pm, options);
|
result = circtFirtoolPopulateLowFIRRTLToHW(
|
||||||
|
pm, options, mlirStringRefCreateFromCString("-"));
|
||||||
assert(mlirLogicalResultIsSuccess(result));
|
assert(mlirLogicalResultIsSuccess(result));
|
||||||
|
|
||||||
result = circtFirtoolPopulateHWToSV(pm, options);
|
result = circtFirtoolPopulateHWToSV(pm, options);
|
||||||
|
|
|
@ -10,23 +10,7 @@ firrtl.circuit "NonPassiveSubaccess" {
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
%n = firrtl.node %b : !firrtl.uint<1>
|
%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}}
|
// 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>>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// RUN: circt-opt -firrtl-lower-layers -split-input-file %s | FileCheck %s
|
// RUN: circt-opt -firrtl-lower-layers -split-input-file %s | FileCheck %s
|
||||||
|
|
||||||
|
// CHECK-LABEL: firrtl.circuit "Test"
|
||||||
firrtl.circuit "Test" {
|
firrtl.circuit "Test" {
|
||||||
firrtl.module @Test() {}
|
firrtl.module @Test() {}
|
||||||
|
|
||||||
|
@ -32,10 +33,6 @@ firrtl.circuit "Test" {
|
||||||
firrtl.module @ColoredThings() {
|
firrtl.module @ColoredThings() {
|
||||||
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
|
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
|
||||||
%0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>, @A>
|
%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
|
// CHECK-LABEL: @ColoredThingUnderWhen
|
||||||
|
@ -44,10 +41,6 @@ firrtl.circuit "Test" {
|
||||||
firrtl.when %b : !firrtl.uint<1> {
|
firrtl.when %b : !firrtl.uint<1> {
|
||||||
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
|
// CHECK: %0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>>
|
||||||
%0 = firrtl.wire : !firrtl.probe<bundle<f: uint<1>>, @A>
|
%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
|
// CHECK-NOT: firrtl.layer @GoodbyeCruelWorld
|
||||||
firrtl.layer @GoodbyeCruelWorld bind {}
|
firrtl.layer @GoodbyeCruelWorld bind {}
|
||||||
|
|
||||||
// CHECK-LABEL @WithLayerBlock
|
// CHECK-LABEL firrtl.module @WithLayerBlock
|
||||||
firrtl.module @WithLayerBlock() {
|
firrtl.module @WithLayerBlock() {
|
||||||
// CHECK-NOT firrtl.layerblock @GoodbyeCruelWorld
|
// CHECK-NOT firrtl.layerblock @GoodbyeCruelWorld
|
||||||
firrtl.layerblock @GoodbyeCruelWorld {
|
firrtl.layerblock @GoodbyeCruelWorld {
|
||||||
|
@ -82,16 +75,20 @@ firrtl.circuit "Test" {
|
||||||
// Capture
|
// Capture
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
|
|
||||||
// CHECK: firrtl.module private @[[A:.+]](in %[[x:.+]]: !firrtl.uint<1>, in %[[y:.+]]: !firrtl.uint<1>)
|
// CHECK: hw.hierpath private @[[CaptureHardware_c0_ui1_path:.+]] [@CaptureHardware::@[[CaptureHardware_c0_ui1_sym:.+]]]
|
||||||
// CHECK: %0 = firrtl.add %[[x]], %[[y]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
// CHECK-NEXT: hw.hierpath private @[[CaptureHardware_c1_ui1_path:.+]] [@CaptureHardware::@[[CaptureHardware_c1_ui1_sym:.+]]]
|
||||||
// CHECK: }
|
// CHECK-NEXT: firrtl.module private @CaptureHardware_A() {
|
||||||
// CHECK: firrtl.module @CaptureHardware() {
|
// CHECK-NEXT: %[[c1_ui1:.+]] = firrtl.xmr.deref @[[CaptureHardware_c1_ui1_path]] : !firrtl.uint<1>
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: %[[c0_ui1:.+]] = firrtl.xmr.deref @[[CaptureHardware_c0_ui1_path]] : !firrtl.uint<1>
|
||||||
// CHECK: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.add %[[c0_ui1]], %[[c1_ui1]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
||||||
// CHECK: %[[p:.+]], %[[q:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
|
// CHECK-NEXT: }
|
||||||
// CHECK: firrtl.matchingconnect %[[q]], %c1_ui1 : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module @CaptureHardware() {
|
||||||
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
|
// CHECK-NEXT: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
||||||
// CHECK: }
|
// 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() {
|
firrtl.module @CaptureHardware() {
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
||||||
%c1_ui1 = firrtl.constant 1 : !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: hw.hierpath private @[[CapturePort_port_path:.+]] [@CapturePort::@[[CapturePort_port_sym:.+]]]
|
||||||
// CHECK: %x = firrtl.node %[[p]] : !firrtl.uint<1>
|
// 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: }
|
||||||
// CHECK: firrtl.module @CapturePort(in %in: !firrtl.uint<1>) {
|
// CHECK: firrtl.module @CapturePort(in %in: !firrtl.uint<1> sym @[[CapturePort_port_sym]]) {
|
||||||
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
|
// CHECK: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CapturePort_A
|
||||||
// CHECK: firrtl.matchingconnect %[[p]], %in : !firrtl.uint<1>
|
|
||||||
// CHECK: }
|
// CHECK: }
|
||||||
firrtl.module @CapturePort(in %in: !firrtl.uint<1>){
|
firrtl.module @CapturePort(in %in: !firrtl.uint<1>){
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
|
@ -113,90 +111,82 @@ firrtl.circuit "Test" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
|
// CHECK: hw.hierpath private @[[CaptureConnect_a_path:.+]] [@CaptureConnect::@[[CaptureConnect_a_sym:.+]]]
|
||||||
// CHECK: %w = firrtl.wire : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module private @CaptureConnect_A()
|
||||||
// CHECK: firrtl.connect %w, %[[p]] : !firrtl.uint<1>
|
// CHECK-NEXT: %[[a:.+]] = firrtl.xmr.deref @[[CaptureConnect_a_path]] : !firrtl.uint<1>
|
||||||
// CHECK: }
|
// CHECK-NEXT: %b = firrtl.wire : !firrtl.uint<1>
|
||||||
// CHECK: firrtl.module @CaptureHardwareViaConnect() {
|
// CHECK-NEXT: firrtl.connect %b, %[[a]] : !firrtl.uint<1>
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
|
// CHECK-NEXT: firrtl.module @CaptureConnect() {
|
||||||
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
|
// CHECK-NEXT: %a = firrtl.wire : !firrtl.uint<1>
|
||||||
// CHECK: }
|
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[CaptureConnect_a_sym]] %a
|
||||||
firrtl.module @CaptureHardwareViaConnect() {
|
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CaptureConnect_A()
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
|
firrtl.module @CaptureConnect() {
|
||||||
|
%a = firrtl.wire : !firrtl.uint<1>
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
%w = firrtl.wire : !firrtl.uint<1>
|
%b = firrtl.wire : !firrtl.uint<1>
|
||||||
firrtl.connect %w, %c0_ui1 : !firrtl.uint<1>, !firrtl.uint<1>
|
firrtl.connect %b, %a : !firrtl.uint<1>, !firrtl.uint<1>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
|
// CHECK: firrtl.module private @CaptureProbeSrc_A()
|
||||||
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @xmrPath : !firrtl.uint<1>
|
||||||
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
|
// CHECK-NEXT: }
|
||||||
// CHECK: }
|
// CHECK-NEXT: firrtl.module @CaptureProbeSrc() {
|
||||||
// CHECK: firrtl.module @CaptureProbeSrc() {
|
// CHECK-NEXT: %w = firrtl.wire : !firrtl.uint<1>
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: %w_probe = firrtl.node sym @sym interesting_name %w : !firrtl.uint<1>
|
||||||
// CHECK: %w = firrtl.wire : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @CaptureProbeSrc_A
|
||||||
// CHECK: %0 = firrtl.ref.send %w : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
// 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 @CaptureProbeSrc() {
|
firrtl.module @CaptureProbeSrc() {
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
|
||||||
%w = firrtl.wire : !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.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: hw.hierpath private @[[NestedCapture_x_path:.+]] [@NestedCapture::@[[inst:.+]], @NestedCapture_A::@[[NestedCapture_a_sym:.+]]]
|
||||||
// CHECK: %0 = firrtl.add %[[p]], %[[q]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
// CHECK-NEXT: firrtl.module private @NestedCapture_A_B()
|
||||||
// CHECK: }
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[NestedCapture_x_path]]
|
||||||
// CHECK: firrtl.module private @[[A:.+]](out %[[p:.+]]: !firrtl.probe<uint<1>>, out %[[q:.+]]: !firrtl.probe<uint<1>>) attributes {
|
// CHECK-NEXT: %1 = firrtl.node %0 : !firrtl.uint<1>
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
// CHECK: %0 = firrtl.ref.send %c0_ui1 : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module private @NestedCapture_A() {
|
||||||
// CHECK: firrtl.ref.define %[[q]], %0 : !firrtl.probe<uint<1>>
|
// CHECK-NEXT: %x = firrtl.wire : !firrtl.uint<1>
|
||||||
// CHECK: %c0_ui1_1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[NestedCapture_a_sym]] %x
|
||||||
// CHECK: %1 = firrtl.ref.send %c0_ui1_1 : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
// CHECK: firrtl.ref.define %[[p]], %1 : !firrtl.probe<uint<1>>
|
// CHECK-NEXT: firrtl.module @NestedCapture() {
|
||||||
// CHECK: }
|
// CHECK-NEXT: firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A-B.sv", excludeFromFileList>} @NestedCapture_A_B()
|
||||||
// CHECK: firrtl.module @NestedCaptureHardware() {
|
// CHECK-NEXT: firrtl.instance {{.+}} sym @[[inst]] {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @NestedCapture_A()
|
||||||
// CHECK: %[[b1:.+]], %[[b2:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A-B.sv", excludeFromFileList>} @[[B]]
|
// CHECK-NEXT: }
|
||||||
// CHECK: %[[a1:.+]], %[[a2:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
|
firrtl.module @NestedCapture() {
|
||||||
// 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() {
|
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
%x = firrtl.wire : !firrtl.uint<1>
|
||||||
%c1_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
|
||||||
firrtl.layerblock @A::@B {
|
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: hw.hierpath private @[[WhenUnderLayer_x_path:.+]] [@WhenUnderLayer::@[[WhenUnderLayer_x_sym:.+]]]
|
||||||
// CHECK: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module private @WhenUnderLayer_A()
|
||||||
// CHECK: firrtl.when %[[p]] : !firrtl.uint<1> {
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[WhenUnderLayer_x_path]] : !firrtl.uint<1>
|
||||||
// CHECK: %0 = firrtl.add %[[p]], %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
// CHECK-NEXT: %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
||||||
// CHECK: }
|
// CHECK-NEXT: firrtl.when %0 : !firrtl.uint<1> {
|
||||||
// CHECK: }
|
// CHECK-NEXT: %1 = firrtl.add %0, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
||||||
// CHECK: firrtl.module @WhenUnderLayer() {
|
// CHECK-NEXT: }
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK-NEXT: }
|
||||||
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} {lowerToBind, output_file = #hw.output_file<"layers-Test-A.sv", excludeFromFileList>} @[[A]]
|
// CHECK-NEXT: firrtl.module @WhenUnderLayer() {
|
||||||
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
|
// CHECK-NEXT: %x = firrtl.wire : !firrtl.uint<1>
|
||||||
// CHECK: }
|
// 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() {
|
firrtl.module @WhenUnderLayer() {
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
%x = firrtl.wire : !firrtl.uint<1>
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
|
||||||
firrtl.when %c0_ui1 : !firrtl.uint<1> {
|
firrtl.when %x : !firrtl.uint<1> {
|
||||||
%0 = firrtl.add %c0_ui1, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
%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
|
// Test that subfield, subindex, and subaccess are moved out of layerblocks to
|
||||||
// avoid capturing non-passive types.
|
// avoid capturing non-passive types.
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module private @[[SubOpsInLayerBlock_A:[A-Za-z0-9_]+]]
|
// CHECK: hw.hierpath private @[[SubOpsInLayerBlock_sub_path:.+]] [@SubOpsInLayerBlock::@[[SubOpsInLayerBlock_sub_sym:.+]]]
|
||||||
// CHECK-SAME: in %[[port:[A-Za-z0-9_]+]]: !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module private @SubOpsInLayerBlock_A() {
|
||||||
// CHECK-NEXT: firrtl.node %[[port]]
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[SubOpsInLayerBlock_sub_path]]
|
||||||
|
// CHECK-NEXT: firrtl.node %0
|
||||||
// CHECK-NEXT: }
|
// CHECK-NEXT: }
|
||||||
// CHECK: firrtl.module @SubOpsInLayerBlock
|
// CHECK: firrtl.module @SubOpsInLayerBlock(
|
||||||
// CHECK-NEXT: firrtl.subaccess
|
// CHECK-NEXT: %0 = firrtl.subaccess %a[%b]
|
||||||
// CHECK-NEXT: firrtl.subindex
|
// CHECK-NEXT: %1 = firrtl.subindex %0[0]
|
||||||
// CHECK-NEXT: firrtl.subfield
|
// 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(
|
firrtl.module @SubOpsInLayerBlock(
|
||||||
in %a: !firrtl.vector<vector<bundle<a: uint<1>, b flip: uint<2>>, 2>, 2>,
|
in %a: !firrtl.vector<vector<bundle<a: uint<1>, b flip: uint<2>>, 2>, 2>,
|
||||||
in %b: !firrtl.uint<1>
|
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>
|
%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>>
|
%2 = firrtl.subfield %1[a] : !firrtl.bundle<a: uint<1>, b flip: uint<2>>
|
||||||
%3 = firrtl.node %2 : !firrtl.uint<1>
|
%3 = firrtl.node %2 : !firrtl.uint<1>
|
||||||
|
%4 = firrtl.node %3 : !firrtl.uint<1>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: firrtl.module private @CaptureInWhen_A(
|
// CHECK: hw.hierpath private @[[CaptureWhen2_a_path:.+]] [@CaptureWhen2::@[[CaptureWhen2_a_sym:.+]]]
|
||||||
// CHECK-SAME: in %a: !firrtl.uint<1>
|
// CHECK-NEXT: hw.hierpath private @[[CaptureWhen2_cond_path:.+]] [@CaptureWhen2::@[[CaptureWhen2_cond_sym:.+]]]
|
||||||
// CHECK-SAME: in %cond: !firrtl.uint<1>
|
// CHECK-NEXT: firrtl.module private @CaptureWhen2_A() {
|
||||||
// CHECK-SAME: )
|
// 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: firrtl.module @CaptureWhen2(
|
||||||
// CHECK: %a_a, %a_cond = firrtl.instance a
|
// CHECK-NEXT: %a = firrtl.wire
|
||||||
// CHECK-NEXT: firrtl.matchingconnect %a_cond, %cond :
|
// CHECK-NEXT: %_layer_probe = firrtl.node {{.*}} %a
|
||||||
// CHECK-NEXT: firrtl.matchingconnect %a_a, %a :
|
// CHECK-NEXT: firrtl.instance a
|
||||||
firrtl.module @CaptureInWhen(in %cond: !firrtl.uint<1>) {
|
firrtl.module @CaptureWhen2(in %cond: !firrtl.uint<1>) {
|
||||||
%a = firrtl.wire : !firrtl.uint<1>
|
%a = firrtl.wire : !firrtl.uint<1>
|
||||||
firrtl.layerblock @A {
|
firrtl.layerblock @A {
|
||||||
firrtl.when %cond : !firrtl.uint<1> {
|
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.
|
// An FString operation is outside the layer block. This needs to be cloned.
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module private @[[A:.+]]() {
|
// CHECK: firrtl.module private @[[A:.+]]() {
|
||||||
|
@ -338,76 +307,23 @@ firrtl.circuit "Test" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
// XMR Ref ops used by force_initial are cloned.
|
||||||
// Resolving Colored Probes
|
//
|
||||||
//===--------------------------------------------------------------------===//
|
// CHECK: firrtl.module private @XmrRef_A()
|
||||||
|
// CHECK-NEXT: %0 = firrtl.xmr.ref @RefXmrRef_path : !firrtl.rwprobe<uint<1>, @A>
|
||||||
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>) {
|
// CHECK-NEXT: %a = firrtl.wire
|
||||||
// CHECK: %0 = firrtl.ref.send %[[p]] : !firrtl.uint<1>
|
// CHECK-NEXT: %c1_ui1 = firrtl.constant 1
|
||||||
// CHECK: %1 = firrtl.ref.resolve %0 : !firrtl.probe<uint<1>>
|
// CHECK-NEXT: firrtl.ref.force_initial %c1_ui1, %0, %c1_ui1
|
||||||
// CHECK: }
|
hw.hierpath private @XmrRef_path [@XmrRef::@a]
|
||||||
// CHECK: firrtl.module @ResolveColoredRefUnderLayerBlock() {
|
firrtl.module @XmrRef() {
|
||||||
// CHECK: %w = firrtl.wire : !firrtl.probe<uint<1>>
|
%0 = firrtl.xmr.ref @RefXmrRef_path : !firrtl.rwprobe<uint<1>, @A>
|
||||||
// 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>
|
|
||||||
firrtl.layerblock @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
|
// Inline Layers
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
|
@ -497,55 +413,42 @@ firrtl.circuit "Simple" {
|
||||||
// CHECK-SAME: define layers_Simple_A"
|
// CHECK-SAME: define layers_Simple_A"
|
||||||
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
|
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module private @Simple_A_B_C(
|
// CHECK: hw.hierpath private @[[Simple_A_B_cc_path:.+]] [@Simple::@a_b, @Simple_A_B::@[[Simple_A_B_cc_sym:.+]]]
|
||||||
// CHECK-NOT: firrtl.module
|
//
|
||||||
// CHECK-SAME: in %[[cc_port:[_a-zA-Z0-9]+]]: !firrtl.uint<3>
|
// CHECK: firrtl.module private @Simple_A_B_C() {
|
||||||
// CHECK-NEXT: %ccc = firrtl.node %[[cc_port]]
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_A_B_cc_path]]
|
||||||
|
// CHECK-NEXT: %ccc = firrtl.node %0
|
||||||
// CHECK-NEXT: }
|
// CHECK-NEXT: }
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module private @Simple_A_B(
|
// CHECK: hw.hierpath private @[[Simple_b_path:.+]] [@Simple::@[[Simple_b_sym:.+]]]
|
||||||
// CHECK-NOT: firrtl.module
|
// CHECK-NEXT: hw.hierpath private @[[Simple_A_c_path:.+]] [@Simple::@a, @Simple_A::@[[Simple_A_c_sym:.+]]]
|
||||||
// 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: firrtl.module private @Simple_A_B() {
|
||||||
// CHECK-SAME: out %[[cc_port:[_a-zA-Z0-9_]+]]: !firrtl.probe<uint<3>>
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_A_c_path]]
|
||||||
// CHECK-NEXT: %bb = firrtl.node %[[b_port]]
|
// CHECK-NEXT: %1 = firrtl.xmr.deref @[[Simple_b_path]]
|
||||||
// CHECK-NEXT: %cc = firrtl.node %[[c_port]]
|
// CHECK-NEXT: %bb = firrtl.node %1
|
||||||
// CHECK-NEXT: %0 = firrtl.ref.send %cc
|
// CHECK-NEXT: %cc = firrtl.node %0
|
||||||
// CHECK-NEXT: firrtl.ref.define %[[cc_port]], %0
|
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_A_B_cc_sym]] %cc
|
||||||
// CHECK-NEXT: }
|
// CHECK-NEXT: }
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module private @Simple_A(
|
// CHECK: hw.hierpath private @[[Simple_a_path:.+]] [@Simple::@[[Simple_a_sym:.+]]]
|
||||||
// CHECK-NOT: firrtl.module
|
//
|
||||||
// CHECK-SAME: in %[[a_port:[_a-zA-Z0-9]+]]: !firrtl.uint<1>
|
// CHECK: firrtl.module private @Simple_A() {
|
||||||
// CHECK-SAME: out %[[c_port:[_a-zA-Z0-9_]+]]: !firrtl.probe<uint<3>>
|
// CHECK-NEXT: %0 = firrtl.xmr.deref @[[Simple_a_path]]
|
||||||
// CHECK-NEXT: %aa = firrtl.node %[[a_port]]
|
// CHECK-NEXT: %aa = firrtl.node %0
|
||||||
// CHECK: %[[c_ref:[_a-zA-Z0-9]+]] = firrtl.ref.send %c
|
// CHECK-NEXT: %c = firrtl.wire
|
||||||
// CHECK-NEXT: firrtl.ref.define %[[c_port]], %[[c_ref]]
|
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_A_c_sym]] %c
|
||||||
// CHECK-NEXT: }
|
// CHECK-NEXT: }
|
||||||
//
|
//
|
||||||
// CHECK: firrtl.module @Simple() {
|
// CHECK: firrtl.module @Simple() {
|
||||||
// CHECK-NOT: firrtl.module
|
// CHECK-NEXT: %a = firrtl.wire
|
||||||
// CHECK-NOT: firrtl.layerblock
|
// CHECK-NEXT: %_layer_probe = firrtl.node sym @[[Simple_a_sym]] %a
|
||||||
// CHECK: %[[A_B_C_cc:[_a-zA-Z0-9_]+]] = firrtl.instance a_b_c {
|
// CHECK-NEXT: %b = firrtl.wire
|
||||||
// CHECK-SAME: lowerToBind
|
// CHECK-NEXT: %_layer_probe_0 = firrtl.node sym @[[Simple_b_sym]] %b
|
||||||
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A-B-C.sv"
|
// CHECK-NEXT: firrtl.instance a_b_c {{.*}}
|
||||||
// CHECK-SAME: excludeFromFileList
|
// CHECK-NEXT: firrtl.instance a_b {{.*}}
|
||||||
// CHECK-SAME: @Simple_A_B_C(
|
// CHECK-NEXT: firrtl.instance a {{.*}}
|
||||||
// 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-NEXT: }
|
||||||
// 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-DAG: sv.verbatim "`endif // layers_Simple_A"
|
// CHECK-DAG: sv.verbatim "`endif // layers_Simple_A"
|
||||||
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
|
// CHECK-SAME: output_file = #hw.output_file<"layers-Simple-A.sv", excludeFromFileList>
|
||||||
|
@ -556,7 +459,7 @@ firrtl.circuit "Simple" {
|
||||||
|
|
||||||
firrtl.circuit "ModuleNameConflict" {
|
firrtl.circuit "ModuleNameConflict" {
|
||||||
firrtl.layer @A bind {}
|
firrtl.layer @A bind {}
|
||||||
firrtl.module private @ModuleNameConflict_A() {}
|
firrtl.module @ModuleNameConflict_A() {}
|
||||||
firrtl.module @ModuleNameConflict() {
|
firrtl.module @ModuleNameConflict() {
|
||||||
%a = firrtl.wire : !firrtl.uint<1>
|
%a = firrtl.wire : !firrtl.uint<1>
|
||||||
firrtl.instance foo @ModuleNameConflict_A()
|
firrtl.instance foo @ModuleNameConflict_A()
|
||||||
|
@ -568,7 +471,8 @@ firrtl.circuit "ModuleNameConflict" {
|
||||||
|
|
||||||
// CHECK-LABEL: 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: firrtl.module @ModuleNameConflict()
|
||||||
// CHECK-NOT: firrtl.module
|
// CHECK-NOT: firrtl.module
|
||||||
|
@ -587,13 +491,16 @@ firrtl.circuit "CaptureHardwareMultipleTimes" {
|
||||||
|
|
||||||
firrtl.extmodule @CaptureHardwareMultipleTimes ()
|
firrtl.extmodule @CaptureHardwareMultipleTimes ()
|
||||||
|
|
||||||
// CHECK: firrtl.module private @[[A:.+]](in %[[p:.+]]: !firrtl.uint<1>)
|
// CHECK: hw.hierpath private @[[path:.+]] [@CaptureSrcTwice::@[[sym:.+]]]
|
||||||
// CHECK: %0 = firrtl.add %[[p]], %[[p]] : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<2>
|
//
|
||||||
|
// 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: }
|
||||||
// CHECK: firrtl.module @CaptureSrcTwice() {
|
// CHECK: firrtl.module @CaptureSrcTwice() {
|
||||||
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
// CHECK: %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
||||||
// CHECK: %[[p:.+]] = firrtl.instance {{.+}} @[[A]]
|
// CHECK: %_layer_probe = firrtl.node sym @[[sym]] %c0_ui1 : !firrtl.uint<1>
|
||||||
// CHECK: firrtl.matchingconnect %[[p]], %c0_ui1 : !firrtl.uint<1>
|
// CHECK: firrtl.instance {{.+}} @[[A]]
|
||||||
// CHECK: }
|
// CHECK: }
|
||||||
firrtl.module @CaptureSrcTwice() {
|
firrtl.module @CaptureSrcTwice() {
|
||||||
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
|
%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 sv.verbatim inner refs are updated, as occurs with views under layers.
|
||||||
// CHECK-LABEL: circuit "Verbatim"
|
// CHECK-LABEL: circuit "Verbatim"
|
||||||
firrtl.circuit "Verbatim" {
|
firrtl.circuit "Verbatim" {
|
||||||
|
|
|
@ -9,9 +9,9 @@ FIRRTL version 4.0.0
|
||||||
; CHECK-NOT: module Child
|
; CHECK-NOT: module Child
|
||||||
|
|
||||||
; CHECK: module Top_Verification_Assert
|
; CHECK: module Top_Verification_Assert
|
||||||
; CHECK: assert(p) else $error("before child");
|
; CHECK: assert(Top.p) else $error("before child");
|
||||||
; CHECK: assert(p) else $error("in child");
|
; CHECK: assert(Top.p) else $error("in child");
|
||||||
; CHECK: assert(p) else $error("after child");
|
; CHECK: assert(Top.p) else $error("after child");
|
||||||
; CHECK: endmodule
|
; CHECK: endmodule
|
||||||
|
|
||||||
circuit Top: %[[
|
circuit Top: %[[
|
||||||
|
@ -29,7 +29,7 @@ circuit Top: %[[
|
||||||
layerblock Verification:
|
layerblock Verification:
|
||||||
layerblock Assert:
|
layerblock Assert:
|
||||||
assert(c, p, e, "in child")
|
assert(c, p, e, "in child")
|
||||||
|
|
||||||
public module Top:
|
public module Top:
|
||||||
input p : UInt<1>
|
input p : UInt<1>
|
||||||
input e : UInt<1>
|
input e : UInt<1>
|
||||||
|
@ -38,7 +38,7 @@ circuit Top: %[[
|
||||||
layerblock Verification:
|
layerblock Verification:
|
||||||
layerblock Assert:
|
layerblock Assert:
|
||||||
assert(c, p, e, "before child")
|
assert(c, p, e, "before child")
|
||||||
|
|
||||||
inst child of Child
|
inst child of Child
|
||||||
connect child.p, p
|
connect child.p, p
|
||||||
connect child.e, e
|
connect child.e, e
|
||||||
|
|
|
@ -37,42 +37,29 @@ circuit Foo: %[[
|
||||||
node z = x
|
node z = x
|
||||||
assert(clock, cond, enable, "Test")
|
assert(clock, cond, enable, "Test")
|
||||||
|
|
||||||
; CHECK-LABEL: module Foo_A_B(
|
; CHECK-LABEL: module Foo_A_B();
|
||||||
; CHECK-NEXT: input x,
|
; CHECK: wire y = Foo.a._layer_probe;
|
||||||
; CHECK-NEXT: cond,
|
; CHECK: wire z = Foo.a._layer_probe;
|
||||||
; CHECK-NEXT: enable,
|
; CHECK: always @(posedge Foo.clock) begin
|
||||||
; CHECK-NEXT: clock
|
; CHECK-NEXT: if (Foo.cond & Foo.a._layer_probe & Foo.enable)
|
||||||
; CHECK-NEXT: );
|
; CHECK-NEXT: assert(Foo.cond) else $error("Test");
|
||||||
; 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-NEXT: end // always @(posedge)
|
; CHECK-NEXT: end // always @(posedge)
|
||||||
; CHECK-NEXT: endmodule
|
; CHECK-NEXT: endmodule
|
||||||
|
|
||||||
; CHECK-LABEL: module Foo_A(
|
; CHECK-LABEL: module Foo_A();
|
||||||
; CHECK-NEXT: input in
|
; CHECK: wire x = Foo.in;
|
||||||
; CHECK: wire x = in;
|
; CHECK-NEXT: wire _layer_probe = x;
|
||||||
; CHECK-NEXT: wire x_probe = x;
|
|
||||||
; CHECK-NEXT: endmodule
|
; CHECK-NEXT: endmodule
|
||||||
|
|
||||||
; CHECK-LABEL: FILE "layers-Foo-A-B.sv"
|
; CHECK-LABEL: FILE "layers-Foo-A-B.sv"
|
||||||
; CHECK: `include "layers-Foo-A.sv"
|
; CHECK: `include "layers-Foo-A.sv"
|
||||||
; CHECK-NEXT: `ifndef layers_Foo_A_B
|
; CHECK-NEXT: `ifndef layers_Foo_A_B
|
||||||
; CHECK-NEXT: `define layers_Foo_A_B
|
; CHECK-NEXT: `define layers_Foo_A_B
|
||||||
; CHECK-NEXT: bind Foo Foo_A_B 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: `endif // layers_Foo_A_B
|
; CHECK-NEXT: `endif // layers_Foo_A_B
|
||||||
|
|
||||||
; CHECK-LABEL: FILE "layers-Foo-A.sv"
|
; CHECK-LABEL: FILE "layers-Foo-A.sv"
|
||||||
; CHECK: `ifndef layers_Foo_A
|
; CHECK: `ifndef layers_Foo_A
|
||||||
; CHECK-NEXT: `define layers_Foo_A
|
; CHECK-NEXT: `define layers_Foo_A
|
||||||
; CHECK-NEXT: bind Foo Foo_A a (
|
; CHECK-NEXT: bind Foo Foo_A a ();
|
||||||
; CHECK-NEXT: .in (in)
|
|
||||||
; CHECK-NEXT: );
|
|
||||||
; CHECK-NEXT: `endif // layers_Foo_A
|
; CHECK-NEXT: `endif // layers_Foo_A
|
||||||
|
|
|
@ -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,
|
; 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
|
; probing into a device-under-test (Bar), and reading from hardware which is
|
||||||
|
@ -83,14 +83,11 @@ circuit TestHarness:
|
||||||
|
|
||||||
layer Verification, bind:
|
layer Verification, bind:
|
||||||
|
|
||||||
; CHECK: module DUT_Verification(
|
; CHECK: module DUT_Verification();
|
||||||
; CHECK: input clock,
|
|
||||||
; CHECK: input [31:0] a
|
|
||||||
; CHECK: );
|
|
||||||
; CHECK: reg [31:0] pc_d;
|
; CHECK: reg [31:0] pc_d;
|
||||||
; CHECK: wire [31:0] pc_d_probe = pc_d;
|
; CHECK: wire [31:0] pc_d_probe = pc_d;
|
||||||
; CHECK: always @(posedge clock)
|
; CHECK: always @(posedge DUT.clock)
|
||||||
; CHECK: pc_d <= a;
|
; CHECK: pc_d <= DUT.a;
|
||||||
; CHECK: endmodule
|
; CHECK: endmodule
|
||||||
|
|
||||||
; CHECK: module DUT(
|
; CHECK: module DUT(
|
||||||
|
@ -124,15 +121,11 @@ circuit TestHarness:
|
||||||
layerblock Verification:
|
layerblock Verification:
|
||||||
define trace = x
|
define trace = x
|
||||||
|
|
||||||
; CHECK: module TestHarness_Verification(
|
; CHECK: module TestHarness_Verification()
|
||||||
; CHECK: input [31:0] dut_trace,
|
|
||||||
; CHECK: input clock,
|
|
||||||
; CHECK: reset
|
|
||||||
; CHECK: );
|
|
||||||
; CHECK: `ifndef SYNTHESIS
|
; CHECK: `ifndef SYNTHESIS
|
||||||
; CHECK: always @(posedge clock) begin
|
; CHECK: always @(posedge TestHarness.clock) begin
|
||||||
; CHECK: if ((`PRINTF_COND_) & reset)
|
; CHECK: if ((`PRINTF_COND_) & TestHarness.reset)
|
||||||
; CHECK: $fwrite(`PRINTF_FD_, "The last PC was: %x", dut_trace);
|
; CHECK: $fwrite(`PRINTF_FD_, "The last PC was: %x", TestHarness.dut.verification.pc_d_probe);
|
||||||
; CHECK: end // always @(posedge)
|
; CHECK: end // always @(posedge)
|
||||||
; CHECK: `endif // not def SYNTHESIS
|
; CHECK: `endif // not def SYNTHESIS
|
||||||
; CHECK: endmodule
|
; CHECK: endmodule
|
||||||
|
@ -167,13 +160,95 @@ circuit TestHarness:
|
||||||
; CHECK: FILE "layers-TestHarness-Verification.sv"
|
; CHECK: FILE "layers-TestHarness-Verification.sv"
|
||||||
; CHECK: `ifndef layers_TestHarness_Verification
|
; CHECK: `ifndef layers_TestHarness_Verification
|
||||||
; CHECK: `define layers_TestHarness_Verification
|
; CHECK: `define layers_TestHarness_Verification
|
||||||
; CHECK: bind DUT DUT_Verification verification (
|
; CHECK: bind DUT DUT_Verification verification ();
|
||||||
; CHECK: .clock (clock),
|
; CHECK: bind TestHarness TestHarness_Verification verification ();
|
||||||
; 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: `endif // layers_TestHarness_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
|
||||||
|
|
|
@ -473,8 +473,7 @@ static LogicalResult processBuffer(
|
||||||
if (failed(parsePassPipeline(StringRef(highFIRRTLPassPlugin), pm)))
|
if (failed(parsePassPipeline(StringRef(highFIRRTLPassPlugin), pm)))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
if (failed(firtool::populateCHIRRTLToLowFIRRTL(pm, firtoolOptions,
|
if (failed(firtool::populateCHIRRTLToLowFIRRTL(pm, firtoolOptions)))
|
||||||
inputFilename)))
|
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
if (!lowFIRRTLPassPlugin.empty())
|
if (!lowFIRRTLPassPlugin.empty())
|
||||||
|
@ -484,7 +483,8 @@ static LogicalResult processBuffer(
|
||||||
// Lower if we are going to verilog or if lowering was specifically
|
// Lower if we are going to verilog or if lowering was specifically
|
||||||
// requested.
|
// requested.
|
||||||
if (outputFormat != OutputIRFir) {
|
if (outputFormat != OutputIRFir) {
|
||||||
if (failed(firtool::populateLowFIRRTLToHW(pm, firtoolOptions)))
|
if (failed(
|
||||||
|
firtool::populateLowFIRRTLToHW(pm, firtoolOptions, inputFilename)))
|
||||||
return failure();
|
return failure();
|
||||||
if (!hwPassPlugin.empty())
|
if (!hwPassPlugin.empty())
|
||||||
if (failed(parsePassPipeline(StringRef(hwPassPlugin), pm)))
|
if (failed(parsePassPipeline(StringRef(hwPassPlugin), pm)))
|
||||||
|
|
Loading…
Reference in New Issue