From 386a8b300411ed0d149aec08fbf57846883a911f Mon Sep 17 00:00:00 2001 From: Nandor Licker Date: Tue, 2 Apr 2024 05:17:42 -0700 Subject: [PATCH] [FIRRTL] Expose clock dividers as a FIRRTL intrinsic This PR adds an intrinsic to expose `seq.clock_div` to FIRRTL. --- .../circt/Dialect/FIRRTL/FIRRTLIntrinsics.td | 16 +++++++ include/circt/Dialect/FIRRTL/FIRRTLVisitors.h | 12 ++--- lib/Conversion/FIRRTLToHW/LowerToHW.cpp | 6 +++ .../FIRRTL/Transforms/LowerIntrinsics.cpp | 44 +++++++++++++++---- test/Conversion/FIRRTLToHW/intrinsics.mlir | 15 ++++--- test/Dialect/FIRRTL/lower-intrinsics.mlir | 11 +++++ 6 files changed, 86 insertions(+), 18 deletions(-) diff --git a/include/circt/Dialect/FIRRTL/FIRRTLIntrinsics.td b/include/circt/Dialect/FIRRTL/FIRRTLIntrinsics.td index 835afd44dcd1..d5c095b51ff6 100644 --- a/include/circt/Dialect/FIRRTL/FIRRTLIntrinsics.td +++ b/include/circt/Dialect/FIRRTL/FIRRTLIntrinsics.td @@ -123,6 +123,22 @@ def ClockInverterIntrinsicOp : FIRRTLOp<"int.clock_inv", [Pure]> { let assemblyFormat = "$input attr-dict"; } +def ClockDividerIntrinsicOp : FIRRTLOp<"int.clock_div", [Pure]> { + let summary = "Produces a clock divided by a power of two"; + let description = [{ + The `firrtl.int.clock_div` takes a clock signal and divides it by a + power-of-two ratio. The output clock is phase-aligned to the input clock. + + ``` + %div_clock = seq.clock_div %clock by 1 + ``` + }]; + + let arguments = (ins NonConstClockType:$input, I8Attr:$pow2); + let results = (outs NonConstClockType:$output); + let assemblyFormat = "$input `by` $pow2 attr-dict"; +} + //===----------------------------------------------------------------------===// // Verification Intrinsics //===----------------------------------------------------------------------===// diff --git a/include/circt/Dialect/FIRRTL/FIRRTLVisitors.h b/include/circt/Dialect/FIRRTL/FIRRTLVisitors.h index c3f2a26d68f8..c15dc748410b 100644 --- a/include/circt/Dialect/FIRRTL/FIRRTLVisitors.h +++ b/include/circt/Dialect/FIRRTL/FIRRTLVisitors.h @@ -49,11 +49,12 @@ class ExprVisitor { // Intrinsic Expressions. IsXIntrinsicOp, PlusArgsValueIntrinsicOp, PlusArgsTestIntrinsicOp, SizeOfIntrinsicOp, ClockGateIntrinsicOp, ClockInverterIntrinsicOp, - LTLAndIntrinsicOp, LTLOrIntrinsicOp, LTLDelayIntrinsicOp, - LTLConcatIntrinsicOp, LTLNotIntrinsicOp, LTLImplicationIntrinsicOp, - LTLEventuallyIntrinsicOp, LTLClockIntrinsicOp, - LTLDisableIntrinsicOp, Mux2CellIntrinsicOp, Mux4CellIntrinsicOp, - HasBeenResetIntrinsicOp, FPGAProbeIntrinsicOp, GenericIntrinsicOp, + ClockDividerIntrinsicOp, LTLAndIntrinsicOp, LTLOrIntrinsicOp, + LTLDelayIntrinsicOp, LTLConcatIntrinsicOp, LTLNotIntrinsicOp, + LTLImplicationIntrinsicOp, LTLEventuallyIntrinsicOp, + LTLClockIntrinsicOp, LTLDisableIntrinsicOp, Mux2CellIntrinsicOp, + Mux4CellIntrinsicOp, HasBeenResetIntrinsicOp, FPGAProbeIntrinsicOp, + GenericIntrinsicOp, // Miscellaneous. BitsPrimOp, HeadPrimOp, MuxPrimOp, PadPrimOp, ShlPrimOp, ShrPrimOp, TailPrimOp, VerbatimExprOp, HWStructCastOp, BitCastOp, RefSendOp, @@ -167,6 +168,7 @@ class ExprVisitor { HANDLE(SizeOfIntrinsicOp, Unhandled); HANDLE(ClockGateIntrinsicOp, Unhandled); HANDLE(ClockInverterIntrinsicOp, Unhandled); + HANDLE(ClockDividerIntrinsicOp, Unhandled); HANDLE(LTLAndIntrinsicOp, Unhandled); HANDLE(LTLOrIntrinsicOp, Unhandled); HANDLE(LTLDelayIntrinsicOp, Unhandled); diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index 5deaface0225..73658317b707 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -1606,6 +1606,7 @@ struct FIRRTLLowering : public FIRRTLVisitor { LogicalResult visitExpr(PlusArgsValueIntrinsicOp op); LogicalResult visitExpr(FPGAProbeIntrinsicOp op); LogicalResult visitExpr(ClockInverterIntrinsicOp op); + LogicalResult visitExpr(ClockDividerIntrinsicOp op); LogicalResult visitExpr(SizeOfIntrinsicOp op); LogicalResult visitExpr(ClockGateIntrinsicOp op); LogicalResult visitExpr(LTLAndIntrinsicOp op); @@ -3669,6 +3670,11 @@ LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) { return setLoweringTo(op, operand); } +LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) { + auto operand = getLoweredValue(op.getInput()); + return setLoweringTo(op, operand, op.getPow2()); +} + LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) { return setLoweringToLTL( op, diff --git a/lib/Dialect/FIRRTL/Transforms/LowerIntrinsics.cpp b/lib/Dialect/FIRRTL/Transforms/LowerIntrinsics.cpp index c4687f51b75a..3e18614165dd 100644 --- a/lib/Dialect/FIRRTL/Transforms/LowerIntrinsics.cpp +++ b/lib/Dialect/FIRRTL/Transforms/LowerIntrinsics.cpp @@ -30,6 +30,14 @@ using namespace circt; using namespace firrtl; +// Get parameter by the given name. Null if not found. +static ParamDeclAttr getNamedParam(ArrayAttr params, StringRef name) { + for (auto param : params.getAsRange()) + if (param.getName().getValue().equals(name)) + return param; + return {}; +} + namespace { class CirctSizeofConverter : public IntrinsicConverter { @@ -161,6 +169,32 @@ class CirctClockInverterConverter : public IntrinsicConverter { } }; +class CirctClockDividerConverter : public IntrinsicConverter { +public: + using IntrinsicConverter::IntrinsicConverter; + + bool check() override { + return hasNPorts(2) || namedPort(0, "in") || namedPort(1, "out") || + hasNParam(1) || namedIntParam("POW_2"); + } + + LogicalResult convert(InstanceOp inst) override { + auto pow2 = cast( + getNamedParam(mod.getParameters(), "POW_2").getValue()); + + ImplicitLocOpBuilder builder(inst.getLoc(), inst); + auto in = builder.create(inst.getResult(0).getType()).getResult(); + inst.getResult(0).replaceAllUsesWith(in); + auto out = builder.create(in, pow2); + auto name = inst.getInstanceName(); + Value outWire = builder.create(out.getType(), name).getResult(); + builder.create(outWire, out); + inst.getResult(1).replaceAllUsesWith(outWire); + inst.erase(); + return success(); + } +}; + class EICGWrapperToClockGateConverter : public IntrinsicConverter { public: using IntrinsicConverter::IntrinsicConverter; @@ -594,14 +628,6 @@ static ParseResult allInputs(ArrayRef ports) { return success(); } -// Get parameter by the given name. Null if not found. -static ParamDeclAttr getNamedParam(ArrayAttr params, StringRef name) { - for (auto param : params.getAsRange()) - if (param.getName().getValue().equals(name)) - return param; - return {}; -} - namespace { template @@ -774,6 +800,8 @@ void LowerIntrinsicsPass::runOnOperation() { lowering.add("circt.clock_gate", "circt_clock_gate"); lowering.add("circt.clock_inv", "circt_clock_inv"); + lowering.add("circt.clock_div", + "circt_clock_div"); lowering.add("circt.ltl.and", "circt_ltl_and"); lowering.add("circt.ltl.or", "circt_ltl_or"); lowering.add("circt.ltl.delay", "circt_ltl_delay"); diff --git a/test/Conversion/FIRRTLToHW/intrinsics.mlir b/test/Conversion/FIRRTLToHW/intrinsics.mlir index d64f41e71a1a..6521be1e8858 100644 --- a/test/Conversion/FIRRTLToHW/intrinsics.mlir +++ b/test/Conversion/FIRRTLToHW/intrinsics.mlir @@ -162,13 +162,18 @@ firrtl.circuit "Intrinsics" { firrtl.int.fpga_probe %clock, %in : !firrtl.uint<8> } - // CHECK-LABEL: hw.module @ClockInverter - firrtl.module @ClockInverter( + // CHECK-LABEL: hw.module @ClockOps + firrtl.module @ClockOps( in %clock_in: !firrtl.clock, - out %clock_out: !firrtl.clock + out %clock_inv: !firrtl.clock, + out %clock_div: !firrtl.clock ) { // CHECK: seq.clock_inv %clock_in - %0 = firrtl.int.clock_inv %clock_in - firrtl.strictconnect %clock_out, %0 : !firrtl.clock + %clock_inv_out = firrtl.int.clock_inv %clock_in + firrtl.strictconnect %clock_inv, %clock_inv_out : !firrtl.clock + + // CHECK: seq.clock_div %clock_in + %clock_div_out = firrtl.int.clock_div %clock_in by 4 + firrtl.strictconnect %clock_div, %clock_div_out : !firrtl.clock } } diff --git a/test/Dialect/FIRRTL/lower-intrinsics.mlir b/test/Dialect/FIRRTL/lower-intrinsics.mlir index fd20df776da5..33569904508b 100644 --- a/test/Dialect/FIRRTL/lower-intrinsics.mlir +++ b/test/Dialect/FIRRTL/lower-intrinsics.mlir @@ -65,6 +65,17 @@ firrtl.circuit "Foo" { firrtl.strictconnect %in2, %clk : !firrtl.clock } + // CHECK-NOT: ClockDivider1 + firrtl.intmodule @ClockDivider1(in in: !firrtl.clock, out out: !firrtl.clock) attributes {intrinsic = "circt.clock_div"} + + // CHECK: ClockDivider + firrtl.module @ClockDivider(in %clk: !firrtl.clock) { + // CHECK-NOT: ClockDivider1 + // CHECK: firrtl.int.clock_div + %in2, %out2 = firrtl.instance "" @ClockDivider1(in in: !firrtl.clock, out out: !firrtl.clock) + firrtl.strictconnect %in2, %clk : !firrtl.clock + } + // CHECK-NOT: LTLAnd // CHECK-NOT: LTLOr // CHECK-NOT: LTLDelay1