14 #include "../PassDetail.h"
24 #include "mlir/IR/ImplicitLocOpBuilder.h"
25 #include "mlir/Transforms/DialectConversion.h"
26 #include "llvm/ADT/TypeSwitch.h"
29 using namespace circt;
39 using OpConversionPattern::OpConversionPattern;
43 ConversionPatternRewriter &rewriter)
const override {
44 SmallVector<hw::PortInfo> hwInputInfo;
45 auto portInfo = component.getPortInfo();
46 for (
auto [name, type, direction, _] : portInfo)
47 hwInputInfo.push_back({{name, type, hwDirection(direction)}});
50 SmallVector<Value> argValues;
51 auto hwMod = rewriter.create<HWModuleOp>(
52 component.getLoc(), component.getNameAttr(), hwPortInfo,
54 for (
auto [name, type, direction, _] : portInfo) {
57 assert(ports.getInput(name).getType() == type);
58 argValues.push_back(ports.getInput(name));
61 auto wire = b.create<sv::WireOp>(component.getLoc(), type, name);
63 b.create<sv::ReadInOutOp>(component.getLoc(), wire);
64 argValues.push_back(wireRead);
65 ports.setOutput(name, wireRead);
71 auto *outputOp = hwMod.getBodyBlock()->getTerminator();
72 rewriter.mergeBlocks(component.getBodyBlock(), hwMod.getBodyBlock(),
74 outputOp->moveAfter(&hwMod.getBodyBlock()->back());
75 rewriter.eraseOp(component);
87 llvm_unreachable(
"unknown direction");
92 using OpConversionPattern::OpConversionPattern;
96 ConversionPatternRewriter &rewriter)
const override {
97 HWModuleOp hwMod = wires->getParentOfType<HWModuleOp>();
98 rewriter.inlineRegionBefore(wires.getBody(), hwMod.getBodyRegion(),
99 hwMod.getBodyRegion().end());
100 rewriter.eraseOp(wires);
101 rewriter.inlineBlockBefore(&hwMod.getBodyRegion().getBlocks().back(),
102 &hwMod.getBodyBlock()->back());
108 using OpConversionPattern::OpConversionPattern;
112 ConversionPatternRewriter &rewriter)
const override {
113 if (!control.getBodyBlock()->empty())
114 return control.emitOpError(
"calyx control must be structural");
115 rewriter.eraseOp(control);
121 using OpConversionPattern::OpConversionPattern;
125 ConversionPatternRewriter &rewriter)
const override {
126 Value src = adaptor.getSrc();
127 if (
auto guard = adaptor.getGuard()) {
129 rewriter.create<hw::ConstantOp>(assign.getLoc(), src.getType(), 0);
130 src = rewriter.create<MuxOp>(assign.getLoc(), guard, src, zero);
131 for (Operation *destUser :
132 llvm::make_early_inc_range(assign.getDest().getUsers())) {
133 if (destUser == assign)
135 if (
auto otherAssign = dyn_cast<calyx::AssignOp>(destUser)) {
136 src = rewriter.create<MuxOp>(assign.getLoc(), otherAssign.getGuard(),
137 otherAssign.getSrc(), src);
138 rewriter.eraseOp(destUser);
147 Value dest = adaptor.getDest();
148 if (
auto readInOut = dyn_cast<ReadInOutOp>(dest.getDefiningOp()))
149 dest = readInOut.getInput();
151 rewriter.replaceOpWithNewOp<sv::AssignOp>(assign, dest, src);
158 using OpInterfaceConversionPattern::OpInterfaceConversionPattern;
162 ConversionPatternRewriter &rewriter)
const override {
163 assert(operands.empty() &&
"calyx cells do not have operands");
165 SmallVector<Value> wires;
166 ImplicitLocOpBuilder
builder(cell.getLoc(), rewriter);
167 convertPrimitiveOp(cell, wires,
builder);
168 if (wires.size() != cell.getPortInfo().size()) {
169 auto diag = cell.emitOpError(
"couldn't convert to core primitive");
170 for (Value wire : wires)
171 diag.attachNote() <<
"with wire: " << wire;
175 rewriter.replaceOp(cell, wires);
182 ImplicitLocOpBuilder &b)
const {
183 TypeSwitch<Operation *>(op)
185 .Case([&](EqLibOp op) {
186 convertCompareBinaryOp(op, ICmpPredicate::eq, wires, b);
188 .Case([&](NeqLibOp op) {
189 convertCompareBinaryOp(op, ICmpPredicate::ne, wires, b);
191 .Case([&](LtLibOp op) {
192 convertCompareBinaryOp(op, ICmpPredicate::ult, wires, b);
194 .Case([&](LeLibOp op) {
195 convertCompareBinaryOp(op, ICmpPredicate::ule, wires, b);
197 .Case([&](GtLibOp op) {
198 convertCompareBinaryOp(op, ICmpPredicate::ugt, wires, b);
200 .Case([&](GeLibOp op) {
201 convertCompareBinaryOp(op, ICmpPredicate::uge, wires, b);
203 .Case([&](SltLibOp op) {
204 convertCompareBinaryOp(op, ICmpPredicate::slt, wires, b);
206 .Case([&](SleLibOp op) {
207 convertCompareBinaryOp(op, ICmpPredicate::sle, wires, b);
209 .Case([&](SgtLibOp op) {
210 convertCompareBinaryOp(op, ICmpPredicate::sgt, wires, b);
212 .Case([&](SgeLibOp op) {
213 convertCompareBinaryOp(op, ICmpPredicate::sge, wires, b);
216 .Case([&](AddLibOp op) {
217 convertArithBinaryOp<AddLibOp, AddOp>(op, wires, b);
219 .Case([&](SubLibOp op) {
220 convertArithBinaryOp<SubLibOp, SubOp>(op, wires, b);
222 .Case([&](RshLibOp op) {
223 convertArithBinaryOp<RshLibOp, ShrUOp>(op, wires, b);
225 .Case([&](SrshLibOp op) {
226 convertArithBinaryOp<SrshLibOp, ShrSOp>(op, wires, b);
228 .Case([&](LshLibOp op) {
229 convertArithBinaryOp<LshLibOp, ShlOp>(op, wires, b);
231 .Case([&](AndLibOp op) {
232 convertArithBinaryOp<AndLibOp, AndOp>(op, wires, b);
234 .Case([&](OrLibOp op) {
235 convertArithBinaryOp<OrLibOp, OrOp>(op, wires, b);
237 .Case([&](XorLibOp op) {
238 convertArithBinaryOp<XorLibOp, XorOp>(op, wires, b);
240 .Case([&](MuxLibOp op) {
241 auto sel = wireIn(op.getSel(), op.instanceName(),
242 op.portName(op.getSel()), b);
243 auto tru = wireIn(op.getTru(), op.instanceName(),
244 op.portName(op.getTru()), b);
245 auto fal = wireIn(op.getFal(), op.instanceName(),
246 op.portName(op.getFal()), b);
248 auto mux = b.create<MuxOp>(sel, tru, fal);
251 wireOut(mux, op.instanceName(), op.portName(op.getOut()), b);
252 wires.append({sel.getInput(), tru.getInput(), fal.getInput(), out});
255 .Case([&](MultPipeLibOp op) {
256 convertPipelineOp<MultPipeLibOp, comb::MulOp>(op, wires, b);
258 .Case([&](DivUPipeLibOp op) {
259 convertPipelineOp<DivUPipeLibOp, comb::DivUOp>(op, wires, b);
261 .Case([&](DivSPipeLibOp op) {
262 convertPipelineOp<DivSPipeLibOp, comb::DivSOp>(op, wires, b);
264 .Case([&](RemSPipeLibOp op) {
265 convertPipelineOp<RemSPipeLibOp, comb::ModSOp>(op, wires, b);
267 .Case([&](RemUPipeLibOp op) {
268 convertPipelineOp<RemUPipeLibOp, comb::ModUOp>(op, wires, b);
271 .Case([&](RegisterOp op) {
273 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
274 auto writeEn = wireIn(op.getWriteEn(), op.instanceName(),
275 op.portName(op.getWriteEn()), b);
276 auto clk = wireIn(op.getClk(), op.instanceName(),
277 op.portName(op.getClk()), b);
278 auto reset = wireIn(op.getReset(), op.instanceName(),
279 op.portName(op.getReset()), b);
280 auto seqClk = b.create<seq::ToClockOp>(clk);
282 reg(writeEn, seqClk, reset, op.instanceName() +
"_done_reg", b);
284 wireOut(doneReg, op.instanceName(), op.portName(op.getDone()), b);
287 regCe(in, seqClk, clockEn, reset, op.instanceName() +
"_reg", b);
288 auto out = wireOut(outReg, op.instanceName(),
"", b);
289 wires.append({in.getInput(), writeEn.getInput(), clk.getInput(),
290 reset.getInput(), out, done});
293 .Case([&](SliceLibOp op) {
295 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
296 auto outWidth = op.getOut().getType().getIntOrFloatBitWidth();
298 auto extract = b.create<ExtractOp>(in, 0, outWidth);
301 wireOut(extract, op.instanceName(), op.portName(op.getOut()), b);
302 wires.append({in.getInput(), out});
304 .Case([&](NotLibOp op) {
306 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
311 wireOut(notOp, op.instanceName(), op.portName(op.getOut()), b);
312 wires.append({in.getInput(), out});
314 .Case([&](WireLibOp op) {
315 auto wire = wireIn(op.getIn(), op.instanceName(),
"", b);
316 wires.append({wire.getInput(), wire});
318 .Case([&](PadLibOp op) {
320 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
321 auto srcWidth = in.getType().getIntOrFloatBitWidth();
322 auto destWidth = op.getOut().getType().getIntOrFloatBitWidth();
323 auto zero = b.create<hw::ConstantOp>(op.getLoc(),
324 APInt(destWidth - srcWidth, 0));
325 auto padded = wireOut(b.createOrFold<comb::ConcatOp>(zero, in),
326 op.instanceName(), op.portName(op.getOut()), b);
327 wires.append({in.getInput(), padded});
329 .Case([&](ExtSILibOp op) {
331 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
332 auto extsi = wireOut(
334 op.instanceName(), op.portName(op.getOut()), b);
335 wires.append({in.getInput(), extsi});
337 .Default([](Operation *) {
return SmallVector<Value>(); });
340 template <
typename OpTy,
typename ResultTy>
342 ImplicitLocOpBuilder &b)
const {
344 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
346 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
348 auto add = b.create<ResultTy>(left, right,
false);
350 auto out = wireOut(add, op.instanceName(), op.portName(op.getOut()), b);
351 wires.append({left.getInput(), right.getInput(), out});
354 template <
typename OpTy>
356 SmallVectorImpl<Value> &wires,
357 ImplicitLocOpBuilder &b)
const {
359 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
361 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
363 auto add = b.create<ICmpOp>(pred, left, right,
false);
365 auto out = wireOut(add, op.instanceName(), op.portName(op.getOut()), b);
366 wires.append({left.getInput(), right.getInput(), out});
369 template <
typename SrcOpTy,
typename TargetOpTy>
371 ImplicitLocOpBuilder &b)
const {
373 wireIn(op.getClk(), op.instanceName(), op.portName(op.getClk()), b);
375 wireIn(op.getReset(), op.instanceName(), op.portName(op.getReset()), b);
376 auto go = wireIn(op.getGo(), op.instanceName(), op.portName(op.getGo()), b);
378 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
380 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
381 wires.append({clk.getInput(), reset.getInput(), go.getInput(),
382 left.getInput(), right.getInput()});
384 auto doneReg =
reg(go, clk, reset,
385 op.instanceName() +
"_" + op.portName(op.getDone()), b);
387 wireOut(doneReg, op.instanceName(), op.portName(op.getDone()), b);
389 auto targetOp = b.create<TargetOpTy>(left, right,
false);
390 for (
auto &&[targetRes, sourceRes] :
391 llvm::zip(targetOp->getResults(), op.getOutputPorts())) {
392 auto portName = op.portName(sourceRes);
394 auto resReg = regCe(targetRes, clk, clockEn, reset,
395 createName(op.instanceName(), portName), b);
396 wires.push_back(wireOut(resReg, op.instanceName(), portName, b));
399 wires.push_back(done);
402 ReadInOutOp
wireIn(Value source, StringRef instanceName, StringRef portName,
403 ImplicitLocOpBuilder &b)
const {
404 auto wire = b.create<sv::WireOp>(source.getType(),
405 createName(instanceName, portName));
406 return b.create<ReadInOutOp>(wire);
409 ReadInOutOp
wireOut(Value source, StringRef instanceName, StringRef portName,
410 ImplicitLocOpBuilder &b)
const {
411 auto wire = b.create<sv::WireOp>(source.getType(),
412 createName(instanceName, portName));
413 b.create<sv::AssignOp>(wire, source);
414 return b.create<ReadInOutOp>(wire);
417 CompRegOp
reg(Value source, Value clock, Value reset,
const Twine &name,
418 ImplicitLocOpBuilder &b)
const {
419 auto resetValue = b.create<hw::ConstantOp>(source.getType(), 0);
420 return b.create<CompRegOp>(source, clock, reset, resetValue, name.str());
423 CompRegClockEnabledOp
regCe(Value source, Value clock, Value ce, Value reset,
425 ImplicitLocOpBuilder &b)
const {
426 auto resetValue = b.create<hw::ConstantOp>(source.getType(), 0);
427 return b.create<CompRegClockEnabledOp>(source, clock, ce, reset, resetValue,
431 std::string
createName(StringRef instanceName, StringRef portName)
const {
432 std::string name = instanceName.str();
433 if (!portName.empty())
434 name += (
"_" + portName).str();
442 class CalyxToHWPass :
public CalyxToHWBase<CalyxToHWPass> {
444 void runOnOperation()
override;
447 LogicalResult runOnModule(ModuleOp module);
451 void CalyxToHWPass::runOnOperation() {
452 ModuleOp mod = getOperation();
453 if (failed(runOnModule(mod)))
454 return signalPassFailure();
457 LogicalResult CalyxToHWPass::runOnModule(ModuleOp module) {
458 MLIRContext &context = getContext();
460 ConversionTarget target(context);
461 target.addIllegalDialect<CalyxDialect>();
462 target.addLegalDialect<HWDialect>();
463 target.addLegalDialect<CombDialect>();
464 target.addLegalDialect<SeqDialect>();
465 target.addLegalDialect<SVDialect>();
467 RewritePatternSet
patterns(&context);
474 return applyPartialConversion(module, target, std::move(
patterns));
478 return std::make_unique<CalyxToHWPass>();
assert(baseType &&"element must be base type")
Direction
The direction of a Component or Cell port.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createCalyxToHWPass()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
LogicalResult matchAndRewrite(calyx::AssignOp assign, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
ReadInOutOp wireOut(Value source, StringRef instanceName, StringRef portName, ImplicitLocOpBuilder &b) const
LogicalResult matchAndRewrite(CellInterface cell, ArrayRef< Value > operands, ConversionPatternRewriter &rewriter) const override
std::string createName(StringRef instanceName, StringRef portName) const
void convertCompareBinaryOp(OpTy op, ICmpPredicate pred, SmallVectorImpl< Value > &wires, ImplicitLocOpBuilder &b) const
void convertArithBinaryOp(OpTy op, SmallVectorImpl< Value > &wires, ImplicitLocOpBuilder &b) const
CompRegOp reg(Value source, Value clock, Value reset, const Twine &name, ImplicitLocOpBuilder &b) const
CompRegClockEnabledOp regCe(Value source, Value clock, Value ce, Value reset, const Twine &name, ImplicitLocOpBuilder &b) const
void convertPrimitiveOp(Operation *op, SmallVectorImpl< Value > &wires, ImplicitLocOpBuilder &b) const
void convertPipelineOp(SrcOpTy op, SmallVectorImpl< Value > &wires, ImplicitLocOpBuilder &b) const
ReadInOutOp wireIn(Value source, StringRef instanceName, StringRef portName, ImplicitLocOpBuilder &b) const
LogicalResult matchAndRewrite(ComponentOp component, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
hw::ModulePort::Direction hwDirection(calyx::Direction dir) const
LogicalResult matchAndRewrite(ControlOp control, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(WiresOp wires, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
This holds a decoded list of input/inout and output ports for a module or instance.