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;
51 hw::HWModuleOp getTarget(hw::InstanceOp instance);
52 bool isRewritable(hw::HWModuleOp module);
53 bool canPromote(hw::HWModuleOp module,
size_t portId);
54 bool canDriveClock(hw::HWModuleOp module,
unsigned output);
55 void markClocks(SmallVectorImpl<Value> &worklist);
56 void rewriteModule(hw::HWModuleOp module);
58 hw::InstanceGraph *instanceGraph =
nullptr;
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());
98bool InferClockPortsPass::isRewritable(hw::HWModuleOp module) {
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.type.isInteger(1) && !port.getSym() && isRewritable(module);
125bool InferClockPortsPass::canDriveClock(hw::HWModuleOp module,
127 SmallVector<std::pair<Operation *, unsigned>> chain;
130 auto key = std::make_pair(module.getOperation(), output);
131 auto [it, inserted] = drivesClock.try_emplace(key,
false);
136 chain.push_back(key);
137 if (!canPromote(module, module.getPortIdForOutputId(output)))
140 auto driver =
getOutputOp(module).getOperand(output);
141 if (driver.getDefiningOp<seq::FromClockOp>()) {
145 if (
auto arg = dyn_cast<BlockArgument>(driver)) {
147 canPromote(module, module.getPortIdForInputId(arg.getArgNumber()));
150 auto instance = driver.getDefiningOp<hw::InstanceOp>();
153 module = getTarget(instance);
156 output = cast<OpResult>(driver).getResultNumber();
159 for (
auto key : chain)
160 drivesClock[key] = result;
164void InferClockPortsPass::markClocks(SmallVectorImpl<Value> &worklist) {
165 while (!worklist.empty()) {
166 auto value = worklist.pop_back_val();
167 if (!value.getType().isInteger(1))
171 if (
auto arg = dyn_cast<BlockArgument>(value)) {
172 auto module = dyn_cast<hw::HWModuleOp>(arg.getOwner()->getParentOp());
173 unsigned input = arg.getArgNumber();
174 if (!module || !canPromote(module, module.getPortIdForInputId(input)) ||
175 !
markPort(clockInputs, module, input, module.getNumInputPorts()))
177 LLVM_DEBUG(llvm::dbgs() <<
"Clock input " << module.getModuleName() <<
"."
178 << module.getInputName(input) <<
"\n");
179 ++numInputPortsPromoted;
180 for (
auto *use : instanceGraph->lookup(module)->uses())
181 if (
auto instance = use->getInstance<hw::InstanceOp>())
182 worklist.push_back(instance.getOperand(input));
186 if (
auto instance = value.getDefiningOp<hw::InstanceOp>()) {
187 auto module = getTarget(instance);
188 unsigned output = cast<OpResult>(value).getResultNumber();
189 if (!module || !canDriveClock(module, output) ||
190 !
markPort(clockOutputs, module, output, module.getNumOutputPorts()))
192 LLVM_DEBUG(llvm::dbgs() <<
"Clock output " << module.getModuleName()
193 <<
"." << module.getOutputName(output) <<
"\n");
194 ++numOutputPortsPromoted;
195 worklist.push_back(
getOutputOp(module).getOperand(output));
203void InferClockPortsPass::rewriteModule(hw::HWModuleOp module) {
204 auto clockType = seq::ClockType::get(&getContext());
205 auto *body =
module.getBodyBlock();
206 auto inputs = clockInputs.lookup(module);
207 auto outputs = clockOutputs.lookup(module);
208 SmallVector<std::pair<hw::InstanceOp, hw::HWModuleOp>> instances;
209 module.walk([&](hw::InstanceOp instance) {
210 if (auto target = getTarget(instance))
211 if (clockInputs.count(target) || clockOutputs.count(target))
212 instances.push_back({instance, target});
214 if (inputs.none() && outputs.none() && instances.empty())
218 SmallSetVector<seq::FromClockOp, 8> casts;
220 auto retype = [&](Value value, OpBuilder &builder) {
221 value.setType(clockType);
223 auto cast = seq::FromClockOp::create(builder, value.getLoc(), value);
224 value.replaceAllUsesExcept(cast, cast);
229 auto toClock = [&](Value value, OpBuilder &builder) -> Value {
230 if (
auto cast = value.getDefiningOp<seq::FromClockOp>()) {
232 return cast.getInput();
234 return seq::ToClockOp::create(builder, value.getLoc(), value);
238 auto builder = OpBuilder::atBlockBegin(body);
239 for (
unsigned input : inputs.set_bits())
240 retype(body->getArgument(input), builder);
241 for (
auto [instance, target] : instances) {
242 builder.setInsertionPointAfter(instance);
244 BitVector targetOutputs = clockOutputs.lookup(target);
245 for (
unsigned output : targetOutputs.set_bits())
246 retype(instance.getResult(output), builder);
250 builder.setInsertionPoint(outputOp);
251 for (
unsigned output : outputs.set_bits())
252 outputOp.setOperand(output, toClock(outputOp.getOperand(output), builder));
253 for (
auto [instance, target] : instances) {
254 builder.setInsertionPoint(instance);
255 BitVector targetInputs = clockInputs.lookup(target);
256 for (
unsigned input : targetInputs.set_bits())
257 instance.setOperand(input, toClock(instance.getOperand(input), builder));
261 for (
auto cast : casts) {
262 for (
auto *user : llvm::make_early_inc_range(cast->getUsers())) {
263 if (
auto toClockOp = dyn_cast<seq::ToClockOp>(user)) {
264 toClockOp.replaceAllUsesWith(cast.getInput());
269 if (cast.use_empty())
273 if (inputs.none() && outputs.none())
275 auto type =
module.getHWModuleType();
276 SmallVector<hw::ModulePort> ports(type.getPorts());
277 for (
unsigned input : inputs.set_bits())
278 ports[type.getPortIdForInputId(input)].type = clockType;
279 for (
unsigned output : outputs.set_bits())
280 ports[type.getPortIdForOutputId(output)].type = clockType;
281 module.setHWModuleType(hw::ModuleType::get(&getContext(), ports));
284void InferClockPortsPass::runOnOperation() {
285 instanceGraph = &getAnalysis<hw::InstanceGraph>();
287 clockOutputs.clear();
291 SmallVector<Value> worklist;
293 [&](seq::ToClockOp op) { worklist.push_back(op.getInput()); });
294 markClocks(worklist);
296 if (clockInputs.empty() && clockOutputs.empty())
297 return markAllAnalysesPreserved();
299 for (
auto module : getOperation().getOps<hw::HWModuleOp>())
300 rewriteModule(module);
302 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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.