18#include "mlir/Dialect/Func/IR/FuncOps.h"
19#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
20#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
21#include "mlir/Pass/Pass.h"
22#include "mlir/Transforms/DialectConversion.h"
23#include "llvm/ADT/PostOrderIterator.h"
24#include "llvm/ADT/Twine.h"
30using namespace igraph;
33#define GEN_PASS_DEF_EXTERNALIZEREGISTERS
34#include "circt/Tools/circt-bmc/Passes.h.inc"
42struct ExternalizeRegistersPass
43 :
public circt::impl::ExternalizeRegistersBase<ExternalizeRegistersPass> {
44 using ExternalizeRegistersBase::ExternalizeRegistersBase;
45 void runOnOperation()
override;
48 DenseMap<StringAttr, SmallVector<Type>> addedInputs;
49 DenseMap<StringAttr, SmallVector<StringAttr>> addedInputNames;
50 DenseMap<StringAttr, SmallVector<Type>> addedOutputs;
51 DenseMap<StringAttr, SmallVector<StringAttr>> addedOutputNames;
52 DenseMap<StringAttr, SmallVector<Attribute>> initialValues;
54 LogicalResult externalizeReg(
HWModuleOp module, Operation *op, Twine regName,
55 Value clock, Attribute initState, Value reset,
56 bool isAsync, Value resetValue, Value next);
60void ExternalizeRegistersPass::runOnOperation() {
61 auto &instanceGraph = getAnalysis<hw::InstanceGraph>();
62 DenseSet<Operation *> handled;
66 for (
auto *startNode : instanceGraph) {
67 if (handled.count(startNode->getModule().getOperation()))
74 if (!handled.insert(node->getModule().getOperation()).second)
78 dyn_cast_or_null<HWModuleOp>(node->getModule().getOperation());
83 bool foundClk =
false;
84 for (
auto ty : module.getInputTypes()) {
85 if (isa<seq::ClockType>(ty)) {
87 module.emitError("modules with multiple clocks not yet supported");
88 return signalPassFailure();
93 module->walk([&](Operation *op) {
94 if (auto regOp = dyn_cast<seq::CompRegOp>(op)) {
95 mlir::Attribute initState = {};
96 if (
auto initVal = regOp.getInitialValue()) {
99 if (!initVal.getDefiningOp<seq::InitialOp>()) {
100 regOp.emitError(
"registers with initial values not directly "
101 "defined by a seq.initial op not yet supported");
102 return signalPassFailure();
105 .getDefiningOp<hw::ConstantOp>()) {
108 initState = constantOp.getValueAttr();
110 regOp.emitError(
"registers with initial values not directly "
111 "defined by a hw.constant op in a seq.initial op "
112 "not yet supported");
113 return signalPassFailure();
116 Twine regName = regOp.getName() && !(regOp.getName().value().empty())
117 ? regOp.getName().value()
118 :
"reg_" + Twine(numRegs);
120 if (failed(externalizeReg(module, op, regName, regOp.getClk(),
121 initState, regOp.getReset(),
false,
122 regOp.getResetValue(), regOp.getInput()))) {
123 return signalPassFailure();
129 if (
auto regOp = dyn_cast<seq::FirRegOp>(op)) {
130 mlir::Attribute initState = {};
131 if (
auto preset = regOp.getPreset()) {
133 initState = mlir::IntegerAttr::get(
134 mlir::IntegerType::get(&getContext(), preset->getBitWidth()),
137 Twine regName = regOp.getName().empty() ?
"reg_" + Twine(numRegs)
140 if (failed(externalizeReg(module, op, regName, regOp.getClk(),
141 initState, regOp.getReset(),
142 regOp.getIsAsync(), regOp.getResetValue(),
144 return signalPassFailure();
150 if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
151 OpBuilder builder(instanceOp);
153 addedInputs[instanceOp.getModuleNameAttr().getAttr()];
155 addedInputNames[instanceOp.getModuleNameAttr().getAttr()];
157 addedOutputs[instanceOp.getModuleNameAttr().getAttr()];
158 auto newOutputNames =
159 addedOutputNames[instanceOp.getModuleNameAttr().getAttr()];
160 addedInputs[
module.getSymNameAttr()].append(newInputs);
161 addedInputNames[
module.getSymNameAttr()].append(newInputNames);
162 addedOutputs[
module.getSymNameAttr()].append(newOutputs);
163 addedOutputNames[
module.getSymNameAttr()].append(newOutputNames);
164 initialValues[
module.getSymNameAttr()].append(
165 initialValues[instanceOp.getModuleNameAttr().getAttr()]);
166 SmallVector<Attribute> argNames(
167 instanceOp.getInputNames().getValue());
168 SmallVector<Attribute> resultNames(
169 instanceOp.getOutputNames().getValue());
171 for (
auto [input, name] : zip_equal(newInputs, newInputNames)) {
172 instanceOp.getInputsMutable().append(
173 module.appendInput(name, input).second);
174 argNames.push_back(name);
176 for (
auto outputName : newOutputNames) {
177 resultNames.push_back(outputName);
179 SmallVector<Type> resTypes(instanceOp->getResultTypes());
180 resTypes.append(newOutputs);
181 auto newInst = InstanceOp::create(
182 builder, instanceOp.getLoc(), resTypes,
183 instanceOp.getInstanceNameAttr(), instanceOp.getModuleNameAttr(),
184 instanceOp.getInputs(), builder.getArrayAttr(argNames),
185 builder.getArrayAttr(resultNames), instanceOp.getParametersAttr(),
186 instanceOp.getInnerSymAttr(), instanceOp.getDoNotPrintAttr());
187 for (
auto [output, name] :
188 zip(newInst->getResults().take_back(newOutputs.size()),
190 module.appendOutput(name, output);
191 numRegs += newInputs.size();
192 instanceOp.replaceAllUsesWith(
193 newInst.getResults().take_front(instanceOp->getNumResults()));
194 instanceGraph.replaceInstance(instanceOp, newInst);
202 IntegerAttr::get(IntegerType::get(&getContext(), 32), numRegs));
204 module->setAttr("initial_values",
205 ArrayAttr::get(&getContext(),
206 initialValues[module.getSymNameAttr()]));
211LogicalResult ExternalizeRegistersPass::externalizeReg(
212 HWModuleOp module, Operation *op, Twine regName, Value clock,
213 Attribute initState, Value reset,
bool isAsync, Value resetValue,
215 if (!isa<BlockArgument>(clock)) {
216 op->emitError(
"only clocks directly given as block arguments "
221 OpBuilder builder(op);
222 auto result = op->getResult(0);
223 auto regType = result.getType();
227 initialValues[
module.getSymNameAttr()].push_back(
228 initState ? initState : mlir::UnitAttr::get(&getContext()));
230 StringAttr newInputName(builder.getStringAttr(regName +
"_state")),
231 newOutputName(builder.getStringAttr(regName +
"_next"));
232 addedInputs[
module.getSymNameAttr()].push_back(regType);
233 addedInputNames[
module.getSymNameAttr()].push_back(newInputName);
234 addedOutputs[
module.getSymNameAttr()].push_back(next.getType());
235 addedOutputNames[
module.getSymNameAttr()].push_back(newOutputName);
238 auto newInput =
module.appendInput(newInputName, regType).second;
239 result.replaceAllUsesWith(newInput);
243 op->emitError(
"registers with an async reset are not yet supported");
247 auto mux = comb::MuxOp::create(builder, op->getLoc(), regType, reset,
249 module.appendOutput(newOutputName, mux);
252 module.appendOutput(newOutputName, next);
This is a Node in the InstanceGraph.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.