23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 #include "mlir/Pass/Pass.h"
25 #include "mlir/Transforms/DialectConversion.h"
26 #include "llvm/ADT/TypeSwitch.h"
29 #define GEN_PASS_DEF_CALYXTOHW
30 #include "circt/Conversion/Passes.h.inc"
34 using namespace circt;
44 using OpConversionPattern::OpConversionPattern;
48 ConversionPatternRewriter &rewriter)
const override {
49 SmallVector<hw::PortInfo> hwInputInfo;
50 auto portInfo = component.getPortInfo();
51 for (
auto [name, type, direction, _] : portInfo)
52 hwInputInfo.push_back({{name, type, hwDirection(direction)}});
55 SmallVector<Value> argValues;
56 auto hwMod = rewriter.create<HWModuleOp>(
57 component.getLoc(), component.getNameAttr(), hwPortInfo,
59 for (
auto [name, type, direction, _] : portInfo) {
62 assert(ports.getInput(name).getType() == type);
63 argValues.push_back(ports.getInput(name));
66 auto wire = b.create<
sv::WireOp>(component.getLoc(), type, name);
69 argValues.push_back(wireRead);
70 ports.setOutput(name, wireRead);
76 auto *outputOp = hwMod.getBodyBlock()->getTerminator();
77 rewriter.mergeBlocks(component.getBodyBlock(), hwMod.getBodyBlock(),
79 outputOp->moveAfter(&hwMod.getBodyBlock()->back());
80 rewriter.eraseOp(component);
92 llvm_unreachable(
"unknown direction");
97 using OpConversionPattern::OpConversionPattern;
101 ConversionPatternRewriter &rewriter)
const override {
102 HWModuleOp hwMod = wires->getParentOfType<HWModuleOp>();
103 rewriter.inlineRegionBefore(wires.getBody(), hwMod.getBodyRegion(),
104 hwMod.getBodyRegion().end());
105 rewriter.eraseOp(wires);
106 rewriter.inlineBlockBefore(&hwMod.getBodyRegion().getBlocks().back(),
107 &hwMod.getBodyBlock()->back());
113 using OpConversionPattern::OpConversionPattern;
117 ConversionPatternRewriter &rewriter)
const override {
118 if (!control.getBodyBlock()->empty())
119 return control.emitOpError(
"calyx control must be structural");
120 rewriter.eraseOp(control);
126 using OpConversionPattern::OpConversionPattern;
130 ConversionPatternRewriter &rewriter)
const override {
131 Value src = adaptor.getSrc();
132 if (
auto guard = adaptor.getGuard()) {
134 rewriter.create<
hw::ConstantOp>(assign.getLoc(), src.getType(), 0);
135 src = rewriter.
create<MuxOp>(assign.getLoc(), guard, src, zero);
136 for (Operation *destUser :
137 llvm::make_early_inc_range(assign.getDest().getUsers())) {
138 if (destUser == assign)
140 if (
auto otherAssign = dyn_cast<calyx::AssignOp>(destUser)) {
141 src = rewriter.
create<MuxOp>(assign.getLoc(), otherAssign.getGuard(),
142 otherAssign.getSrc(), src);
143 rewriter.eraseOp(destUser);
152 Value dest = adaptor.getDest();
153 if (
auto readInOut = dyn_cast<ReadInOutOp>(dest.getDefiningOp()))
154 dest = readInOut.getInput();
156 rewriter.replaceOpWithNewOp<
sv::AssignOp>(assign, dest, src);
163 using OpInterfaceConversionPattern::OpInterfaceConversionPattern;
167 ConversionPatternRewriter &rewriter)
const override {
168 assert(operands.empty() &&
"calyx cells do not have operands");
170 SmallVector<Value> wires;
171 ImplicitLocOpBuilder builder(cell.getLoc(), rewriter);
172 convertPrimitiveOp(cell, wires, builder);
173 if (wires.size() != cell.getPortInfo().size()) {
174 auto diag = cell.emitOpError(
"couldn't convert to core primitive");
175 for (Value wire : wires)
176 diag.attachNote() <<
"with wire: " << wire;
180 rewriter.replaceOp(cell, wires);
187 ImplicitLocOpBuilder &b)
const {
188 TypeSwitch<Operation *>(op)
190 .Case([&](EqLibOp op) {
191 convertCompareBinaryOp(op, ICmpPredicate::eq, wires, b);
193 .Case([&](NeqLibOp op) {
194 convertCompareBinaryOp(op, ICmpPredicate::ne, wires, b);
196 .Case([&](LtLibOp op) {
197 convertCompareBinaryOp(op, ICmpPredicate::ult, wires, b);
199 .Case([&](LeLibOp op) {
200 convertCompareBinaryOp(op, ICmpPredicate::ule, wires, b);
202 .Case([&](GtLibOp op) {
203 convertCompareBinaryOp(op, ICmpPredicate::ugt, wires, b);
205 .Case([&](GeLibOp op) {
206 convertCompareBinaryOp(op, ICmpPredicate::uge, wires, b);
208 .Case([&](SltLibOp op) {
209 convertCompareBinaryOp(op, ICmpPredicate::slt, wires, b);
211 .Case([&](SleLibOp op) {
212 convertCompareBinaryOp(op, ICmpPredicate::sle, wires, b);
214 .Case([&](SgtLibOp op) {
215 convertCompareBinaryOp(op, ICmpPredicate::sgt, wires, b);
217 .Case([&](SgeLibOp op) {
218 convertCompareBinaryOp(op, ICmpPredicate::sge, wires, b);
221 .Case([&](AddLibOp op) {
222 convertArithBinaryOp<AddLibOp, AddOp>(op, wires, b);
224 .Case([&](SubLibOp op) {
225 convertArithBinaryOp<SubLibOp, SubOp>(op, wires, b);
227 .Case([&](RshLibOp op) {
228 convertArithBinaryOp<RshLibOp, ShrUOp>(op, wires, b);
230 .Case([&](SrshLibOp op) {
231 convertArithBinaryOp<SrshLibOp, ShrSOp>(op, wires, b);
233 .Case([&](LshLibOp op) {
234 convertArithBinaryOp<LshLibOp, ShlOp>(op, wires, b);
236 .Case([&](AndLibOp op) {
237 convertArithBinaryOp<AndLibOp, AndOp>(op, wires, b);
239 .Case([&](OrLibOp op) {
240 convertArithBinaryOp<OrLibOp, OrOp>(op, wires, b);
242 .Case([&](XorLibOp op) {
243 convertArithBinaryOp<XorLibOp, XorOp>(op, wires, b);
245 .Case([&](MuxLibOp op) {
246 auto sel = wireIn(op.getCond(), op.instanceName(),
247 op.portName(op.getCond()), b);
248 auto tru = wireIn(op.getTru(), op.instanceName(),
249 op.portName(op.getTru()), b);
250 auto fal = wireIn(op.getFal(), op.instanceName(),
251 op.portName(op.getFal()), b);
253 auto mux = b.create<MuxOp>(sel, tru, fal);
256 wireOut(mux, op.instanceName(), op.portName(op.getOut()), b);
257 wires.append({sel.getInput(), tru.getInput(), fal.getInput(), out});
260 .Case([&](MultPipeLibOp op) {
261 convertPipelineOp<MultPipeLibOp, comb::MulOp>(op, wires, b);
263 .Case([&](DivUPipeLibOp op) {
264 convertPipelineOp<DivUPipeLibOp, comb::DivUOp>(op, wires, b);
266 .Case([&](DivSPipeLibOp op) {
267 convertPipelineOp<DivSPipeLibOp, comb::DivSOp>(op, wires, b);
269 .Case([&](RemSPipeLibOp op) {
270 convertPipelineOp<RemSPipeLibOp, comb::ModSOp>(op, wires, b);
272 .Case([&](RemUPipeLibOp op) {
273 convertPipelineOp<RemUPipeLibOp, comb::ModUOp>(op, wires, b);
276 .Case([&](RegisterOp op) {
278 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
279 auto writeEn = wireIn(op.getWriteEn(), op.instanceName(),
280 op.portName(op.getWriteEn()), b);
281 auto clk = wireIn(op.getClk(), op.instanceName(),
282 op.portName(op.getClk()), b);
283 auto reset = wireIn(op.getReset(), op.instanceName(),
284 op.portName(op.getReset()), b);
285 auto seqClk = b.create<seq::ToClockOp>(clk);
287 reg(writeEn, seqClk, reset, op.instanceName() +
"_done_reg", b);
289 wireOut(doneReg, op.instanceName(), op.portName(op.getDone()), b);
292 regCe(in, seqClk, clockEn, reset, op.instanceName() +
"_reg", b);
293 auto out = wireOut(outReg, op.instanceName(),
"", b);
294 wires.append({in.getInput(), writeEn.getInput(), clk.getInput(),
295 reset.getInput(), out, done});
298 .Case([&](SliceLibOp op) {
300 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
301 auto outWidth = op.getOut().getType().getIntOrFloatBitWidth();
303 auto extract = b.create<ExtractOp>(in, 0, outWidth);
306 wireOut(extract, op.instanceName(), op.portName(op.getOut()), b);
307 wires.append({in.getInput(), out});
309 .Case([&](NotLibOp op) {
311 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
316 wireOut(notOp, op.instanceName(), op.portName(op.getOut()), b);
317 wires.append({in.getInput(), out});
319 .Case([&](WireLibOp op) {
320 auto wire = wireIn(op.getIn(), op.instanceName(),
"", b);
321 wires.append({wire.getInput(), wire});
323 .Case([&](UndefLibOp op) {
324 auto undef = b.create<sv::ConstantXOp>(op.getType());
325 wires.append({undef});
327 .Case([&](PadLibOp op) {
329 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
330 auto srcWidth = in.getType().getIntOrFloatBitWidth();
331 auto destWidth = op.getOut().getType().getIntOrFloatBitWidth();
333 APInt(destWidth - srcWidth, 0));
335 op.instanceName(), op.portName(op.getOut()), b);
336 wires.append({in.getInput(), padded});
338 .Case([&](ExtSILibOp op) {
340 wireIn(op.getIn(), op.instanceName(), op.portName(op.getIn()), b);
341 auto extsi = wireOut(
343 op.instanceName(), op.portName(op.getOut()), b);
344 wires.append({in.getInput(), extsi});
346 .Default([](Operation *) {
return SmallVector<Value>(); });
349 template <
typename OpTy,
typename ResultTy>
351 ImplicitLocOpBuilder &b)
const {
353 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
355 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
357 auto add = b.create<ResultTy>(left, right,
false);
359 auto out = wireOut(add, op.instanceName(), op.portName(op.getOut()), b);
360 wires.append({left.getInput(), right.getInput(), out});
363 template <
typename OpTy>
365 SmallVectorImpl<Value> &wires,
366 ImplicitLocOpBuilder &b)
const {
368 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
370 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
372 auto add = b.create<ICmpOp>(pred, left, right,
false);
374 auto out = wireOut(add, op.instanceName(), op.portName(op.getOut()), b);
375 wires.append({left.getInput(), right.getInput(), out});
378 template <
typename SrcOpTy,
typename TargetOpTy>
380 ImplicitLocOpBuilder &b)
const {
382 wireIn(op.getClk(), op.instanceName(), op.portName(op.getClk()), b);
384 wireIn(op.getReset(), op.instanceName(), op.portName(op.getReset()), b);
385 auto go = wireIn(op.getGo(), op.instanceName(), op.portName(op.getGo()), b);
387 wireIn(op.getLeft(), op.instanceName(), op.portName(op.getLeft()), b);
389 wireIn(op.getRight(), op.instanceName(), op.portName(op.getRight()), b);
390 wires.append({clk.getInput(), reset.getInput(), go.getInput(),
391 left.getInput(), right.getInput()});
393 auto doneReg =
reg(go, clk, reset,
394 op.instanceName() +
"_" + op.portName(op.getDone()), b);
396 wireOut(doneReg, op.instanceName(), op.portName(op.getDone()), b);
398 auto targetOp = b.create<TargetOpTy>(left, right,
false);
399 for (
auto &&[targetRes, sourceRes] :
400 llvm::zip(targetOp->getResults(), op.getOutputPorts())) {
401 auto portName = op.portName(sourceRes);
403 auto resReg = regCe(targetRes, clk, clockEn, reset,
404 createName(op.instanceName(), portName), b);
405 wires.push_back(wireOut(resReg, op.instanceName(), portName, b));
408 wires.push_back(done);
411 ReadInOutOp
wireIn(Value source, StringRef instanceName, StringRef portName,
412 ImplicitLocOpBuilder &b)
const {
413 auto wire = b.create<
sv::WireOp>(source.getType(),
414 createName(instanceName, portName));
415 return b.
create<ReadInOutOp>(wire);
418 ReadInOutOp
wireOut(Value source, StringRef instanceName, StringRef portName,
419 ImplicitLocOpBuilder &b)
const {
420 auto wire = b.create<
sv::WireOp>(source.getType(),
421 createName(instanceName, portName));
423 return b.
create<ReadInOutOp>(wire);
426 CompRegOp
reg(Value source, Value clock, Value reset,
const Twine &name,
427 ImplicitLocOpBuilder &b)
const {
429 return b.
create<CompRegOp>(source, clock, reset, resetValue, name.str());
432 CompRegClockEnabledOp
regCe(Value source, Value clock, Value ce, Value reset,
434 ImplicitLocOpBuilder &b)
const {
436 return b.
create<CompRegClockEnabledOp>(source, clock, ce, reset, resetValue,
440 std::string
createName(StringRef instanceName, StringRef portName)
const {
441 std::string name = instanceName.str();
442 if (!portName.empty())
443 name += (
"_" + portName).str();
451 class CalyxToHWPass :
public circt::impl::CalyxToHWBase<CalyxToHWPass> {
453 void runOnOperation()
override;
456 LogicalResult runOnModule(ModuleOp module);
460 void CalyxToHWPass::runOnOperation() {
461 ModuleOp mod = getOperation();
462 if (failed(runOnModule(mod)))
463 return signalPassFailure();
466 LogicalResult CalyxToHWPass::runOnModule(ModuleOp module) {
467 MLIRContext &context = getContext();
469 ConversionTarget target(context);
470 target.addIllegalDialect<CalyxDialect>();
471 target.addLegalDialect<HWDialect>();
472 target.addLegalDialect<CombDialect>();
473 target.addLegalDialect<SeqDialect>();
474 target.addLegalDialect<SVDialect>();
476 RewritePatternSet
patterns(&context);
483 return applyPartialConversion(module, target, std::move(
patterns));
487 return std::make_unique<CalyxToHWPass>();
assert(baseType &&"element must be base type")
def create(data_type, value)
def create(data_type, name=None, sym_name=None)
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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.