20#include "mlir/IR/Builders.h"
21#include "mlir/Pass/Pass.h"
22#include "llvm/ADT/BitVector.h"
23#include "llvm/ADT/DenseMap.h"
24#include "llvm/ADT/SetVector.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/Support/Debug.h"
28#define DEBUG_TYPE "seq-infer-clock-ports"
36#define GEN_PASS_DEF_INFERCLOCKPORTS
37#include "circt/Dialect/Seq/SeqPasses.h.inc"
43class InferClockPortsPass
44 :
public circt::seq::impl::InferClockPortsBase<InferClockPortsPass> {
46 using InferClockPortsBase::InferClockPortsBase;
48 void runOnOperation()
override;
55 void markClocks(SmallVectorImpl<Value> &worklist);
61 DenseMap<Operation *, BitVector> clockInputs;
62 DenseMap<Operation *, BitVector> clockOutputs;
65 DenseMap<Operation *, bool> rewritable;
66 DenseMap<std::pair<Operation *, unsigned>,
bool> drivesClock;
72static bool markPort(DenseMap<Operation *, BitVector> &ports, Operation *op,
73 unsigned idx,
unsigned numPorts) {
74 auto &bits = ports[op];
76 bits.resize(numPorts);
84 return cast<hw::OutputOp>(module.getBodyBlock()->getTerminator());
89hw::HWModuleOp InferClockPortsPass::getTarget(hw::InstanceOp instance) {
91 instanceGraph->lookupOrNull(instance.getReferencedModuleNameAttr());
94 return dyn_cast<hw::HWModuleOp>(node->getModule().getOperation());
99 auto [it, inserted] = rewritable.try_emplace(module,
false);
102 if (!promotePublicPorts && module.isPublic())
104 for (
auto *use : instanceGraph->lookup(module)->uses()) {
106 if (!use->getInstance())
108 if (!use->getInstance<hw::InstanceOp>())
110 if (!isa<hw::HWModuleOp>(use->getParent()->getModule().getOperation()))
113 return rewritable[module] =
true;
117bool InferClockPortsPass::canPromote(
hw::HWModuleOp module,
size_t portId) {
118 auto port =
module.getPort(portId);
119 return !port.isInOut() && port.type.isInteger(1) && !port.getSym() &&
120 isRewritable(module);
128 SmallVector<std::pair<Operation *, unsigned>> chain;
131 auto key = std::make_pair(module.getOperation(), output);
132 auto [it, inserted] = drivesClock.try_emplace(key,
false);
137 chain.push_back(key);
138 if (!canPromote(module, module.getPortIdForOutputId(output)))
141 auto driver =
getOutputOp(module).getOperand(output);
142 if (driver.getDefiningOp<seq::FromClockOp>()) {
146 if (
auto arg = dyn_cast<BlockArgument>(driver)) {
148 canPromote(module, module.getPortIdForInputId(arg.getArgNumber()));
151 auto instance = driver.getDefiningOp<hw::InstanceOp>();
154 module = getTarget(instance);
157 output = cast<OpResult>(driver).getResultNumber();
160 for (
auto key : chain)
161 drivesClock[key] = result;
165void InferClockPortsPass::markClocks(SmallVectorImpl<Value> &worklist) {
166 while (!worklist.empty()) {
167 auto value = worklist.pop_back_val();
168 if (!value.getType().isInteger(1))
172 if (
auto arg = dyn_cast<BlockArgument>(value)) {
173 auto module = dyn_cast<hw::HWModuleOp>(arg.getOwner()->getParentOp());
174 unsigned input = arg.getArgNumber();
175 if (!module || !canPromote(module, module.getPortIdForInputId(input)) ||
176 !
markPort(clockInputs, module, input, module.getNumInputPorts()))
178 LLVM_DEBUG(llvm::dbgs() <<
"Clock input " << module.getModuleName() <<
"."
179 << module.getInputName(input) <<
"\n");
180 ++numInputPortsPromoted;
181 for (
auto *use : instanceGraph->lookup(module)->uses())
182 if (auto instance = use->getInstance<
hw::InstanceOp>())
183 worklist.push_back(instance.getOperand(input));
187 if (
auto instance = value.getDefiningOp<hw::InstanceOp>()) {
188 auto module = getTarget(instance);
189 unsigned output = cast<OpResult>(value).getResultNumber();
190 if (!module || !canDriveClock(module, output) ||
191 !
markPort(clockOutputs, module, output, module.getNumOutputPorts()))
193 LLVM_DEBUG(llvm::dbgs() <<
"Clock output " << module.getModuleName()
194 <<
"." << module.getOutputName(output) <<
"\n");
195 ++numOutputPortsPromoted;
196 worklist.push_back(
getOutputOp(module).getOperand(output));
205 auto clockType = seq::ClockType::get(&getContext());
206 auto *body =
module.getBodyBlock();
207 auto inputs = clockInputs.lookup(module);
208 auto outputs = clockOutputs.lookup(module);
209 SmallVector<std::pair<hw::InstanceOp, hw::HWModuleOp>> instances;
210 module.walk([&](hw::InstanceOp instance) {
211 if (auto target = getTarget(instance))
212 if (clockInputs.count(target) || clockOutputs.count(target))
213 instances.push_back({instance, target});
215 if (inputs.none() && outputs.none() && instances.empty())
221 auto retype = [&](Value value, OpBuilder &builder) {
222 value.setType(clockType);
224 auto cast = seq::FromClockOp::create(builder, value.getLoc(), value);
225 value.replaceAllUsesExcept(cast, cast);
230 auto toClock = [&](Value value, OpBuilder &builder) -> Value {
231 if (
auto cast = value.getDefiningOp<seq::FromClockOp>()) {
233 return cast.getInput();
235 return seq::ToClockOp::create(builder, value.getLoc(), value);
239 auto builder = OpBuilder::atBlockBegin(body);
240 for (
unsigned input : inputs.set_bits())
241 retype(body->getArgument(input), builder);
242 for (
auto [instance, target] : instances) {
243 builder.setInsertionPointAfter(instance);
244 for (
unsigned output : clockOutputs.lookup(target).set_bits())
245 retype(instance.getResult(output), builder);
249 builder.setInsertionPoint(outputOp);
250 for (
unsigned output : outputs.set_bits())
251 outputOp.setOperand(output, toClock(outputOp.getOperand(output), builder));
252 for (
auto [instance, target] : instances) {
253 builder.setInsertionPoint(instance);
254 for (
unsigned input : clockInputs.lookup(target).set_bits())
255 instance.setOperand(input, toClock(instance.getOperand(input), builder));
259 for (
auto cast : casts) {
260 for (
auto *user :
llvm::make_early_inc_range(cast->getUsers())) {
261 if (
auto toClockOp = dyn_cast<seq::ToClockOp>(user)) {
262 toClockOp.replaceAllUsesWith(cast.getInput());
267 if (cast.use_empty())
271 if (inputs.none() && outputs.none())
273 auto type =
module.getHWModuleType();
274 SmallVector<hw::ModulePort> ports(type.getPorts());
275 for (
unsigned input : inputs.set_bits())
276 ports[type.getPortIdForInputId(input)].type = clockType;
277 for (
unsigned output : outputs.set_bits())
278 ports[type.getPortIdForOutputId(output)].type = clockType;
279 module.setHWModuleType(hw::ModuleType::get(&getContext(), ports));
282void InferClockPortsPass::runOnOperation() {
283 instanceGraph = &getAnalysis<hw::InstanceGraph>();
285 clockOutputs.clear();
289 SmallVector<Value> worklist;
291 [&](seq::ToClockOp op) { worklist.push_back(op.getInput()); });
292 markClocks(worklist);
294 if (clockInputs.empty() && clockOutputs.empty())
295 return markAllAnalysesPreserved();
297 for (
auto module : getOperation().getOps<
hw::HWModuleOp>())
298 rewriteModule(module);
300 markAnalysesPreserved<hw::InstanceGraph>();
static hw::OutputOp getOutputOp(hw::HWModuleOp module)
static bool markPort(DenseMap< Operation *, BitVector > &ports, Operation *op, unsigned idx, unsigned numPorts)
Returns false if already marked, so that each port is visited only once.
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.