12 #include "mlir/Pass/Pass.h"
13 #include "llvm/ADT/TypeSwitch.h"
14 #include "llvm/Support/Debug.h"
16 #define DEBUG_TYPE "arc-isolate-clocks"
20 #define GEN_PASS_DEF_ISOLATECLOCKS
21 #include "circt/Dialect/Arc/ArcPasses.h.inc"
25 using namespace circt;
36 ClockDomain(Value clock, MLIRContext *context)
37 : clock(clock), domainBlock(std::make_unique<Block>()),
38 builder(OpBuilder(context)) {
39 builder.setInsertionPointToStart(domainBlock.get());
44 bool moveToDomain(Operation *op);
47 void sinkFanIn(SmallVectorImpl<Value> &);
50 void computeCrossingValues(SmallVectorImpl<Value> &inputs,
51 SmallVectorImpl<Value> &outputs);
55 ClockDomainOp materialize(OpBuilder &materializeBuilder, Location loc);
59 std::unique_ptr<Block> domainBlock;
64 bool ClockDomain::moveToDomain(Operation *op) {
67 if (op->getBlock() == domainBlock.get())
70 builder.setInsertionPointToStart(domainBlock.get());
79 void ClockDomain::sinkFanIn(SmallVectorImpl<Value> &worklist) {
80 while (!worklist.empty()) {
81 auto *op = worklist.pop_back_val().getDefiningOp();
87 if (isa<ClockDomainOp>(op))
89 if (
auto clockedOp = dyn_cast<ClockedOpInterface>(op);
90 clockedOp && clockedOp.isClocked())
99 if (llvm::any_of(op->getUsers(), [&](
auto *user) {
100 return user->getBlock() != domainBlock.get();
104 if (moveToDomain(op))
105 worklist.append(op->getOperands().begin(), op->getOperands().end());
109 ClockDomainOp ClockDomain::materialize(OpBuilder &materializeBuilder,
111 builder.setInsertionPointToEnd(domainBlock.get());
112 SmallVector<Value> inputs, outputs;
113 computeCrossingValues(inputs, outputs);
116 auto outputOp = builder.create<arc::OutputOp>(loc, outputs);
117 auto clockDomainOp = materializeBuilder.create<ClockDomainOp>(
118 loc, ValueRange(outputs).getTypes(), inputs, clock);
119 for (
auto [domainOutput, val] :
120 llvm::zip(clockDomainOp.getOutputs(), outputOp->getOperands())) {
121 val.replaceUsesWithIf(domainOutput, [&](OpOperand &operand) {
122 return operand.getOwner()->getBlock() != domainBlock.get();
128 domainBlock->addArguments(ValueRange(inputs).getTypes(),
129 SmallVector<Location>(inputs.size(), loc));
130 for (
auto [domainArg, val] :
131 llvm::zip(domainBlock->getArguments(), clockDomainOp.getInputs())) {
132 val.replaceUsesWithIf(domainArg, [&](OpOperand &operand) {
133 return operand.getOwner()->getBlock() == domainBlock.get();
136 clockDomainOp->getRegion(0).push_back(domainBlock.release());
138 return clockDomainOp;
141 void ClockDomain::computeCrossingValues(SmallVectorImpl<Value> &inputs,
142 SmallVectorImpl<Value> &outputs) {
143 DenseSet<Value> inputSet, outputSet;
144 for (
auto &op : *domainBlock) {
145 for (
auto operand : op.getOperands()) {
146 auto *defOp = operand.getDefiningOp();
147 if (!defOp || defOp->getBlock() != domainBlock.get()) {
148 if (inputSet.insert(operand).second)
149 inputs.push_back(operand);
152 for (
auto result : op.getResults()) {
153 if (llvm::any_of(result.getUsers(), [&](
auto *user) {
154 return user->getBlock() != domainBlock.get();
156 if (outputSet.insert(result).second)
157 outputs.push_back(result);
168 struct IsolateClocksPass
169 :
public arc::impl::IsolateClocksBase<IsolateClocksPass> {
170 void runOnOperation()
override;
175 void IsolateClocksPass::runOnOperation() {
176 for (
auto module : getOperation().getOps<hw::HWModuleOp>())
177 if (failed(runOnModule(module)))
178 return signalPassFailure();
181 LogicalResult IsolateClocksPass::runOnModule(
hw::HWModuleOp module) {
182 llvm::MapVector<Value, SmallVector<ClockedOpInterface>> clocks;
185 for (
auto &op : *module.getBodyBlock()) {
191 if (op.getNumRegions() != 0 && !isa<ClockDomainOp>(&op))
192 return op.emitOpError(
"operations with regions not supported yet!");
194 if (
auto clockedOp = dyn_cast<ClockedOpInterface>(&op);
195 clockedOp && clockedOp.isClocked())
196 clocks[clockedOp.getClock()].push_back(clockedOp);
199 SmallVector<Value> worklist;
202 for (
auto [clock, clockedOps] : clocks) {
203 ClockDomain domain(clock, module.getContext());
208 for (
auto op : clockedOps) {
210 if (domain.moveToDomain(op)) {
212 worklist.append(op->getOperands().begin(), op->getOperands().end());
213 domain.sinkFanIn(worklist);
218 OpBuilder builder(module.getBodyBlock()->getTerminator());
219 domain.materialize(builder, module.getLoc());
226 return std::make_unique<IsolateClocksPass>();
assert(baseType &&"element must be base type")
std::unique_ptr< mlir::Pass > createIsolateClocksPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.