diff --git a/lib/Conversion/SimToSV/SimToSV.cpp b/lib/Conversion/SimToSV/SimToSV.cpp index 838e5b12af10..607e3df9e45f 100644 --- a/lib/Conversion/SimToSV/SimToSV.cpp +++ b/lib/Conversion/SimToSV/SimToSV.cpp @@ -26,6 +26,7 @@ #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/ImplicitLocOpBuilder.h" #include "mlir/IR/Threading.h" +#include "mlir/IR/Visitors.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/RegionUtils.h" @@ -40,6 +41,7 @@ namespace circt { using namespace circt; using namespace sim; +using namespace mlir; /// Check whether an op should be placed inside an ifdef guard that prevents it /// from affecting synthesis runs. @@ -267,6 +269,40 @@ static LogicalResult convert(PauseOp op, PatternRewriter &rewriter) { return success(); } +class TriggeredLowering : public SimConversionPattern { +public: + using SimConversionPattern::SimConversionPattern; + + LogicalResult + matchAndRewrite(TriggeredOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const final { + auto loc = op.getLoc(); + state.usedSynthesisMacro = true; + + sv::IfDefOp::create( + rewriter, loc, "SYNTHESIS", [] {}, + [&] { + auto trigger = + seq::FromClockOp::create(rewriter, loc, adaptor.getClock()); + auto alwaysOp = sv::AlwaysOp::create( + rewriter, loc, + ArrayRef{sv::EventControl::AtPosEdge}, + ArrayRef{trigger}); + + Block *destination = alwaysOp.getBodyBlock(); + if (auto condition = adaptor.getCondition()) { + rewriter.setInsertionPointToStart(destination); + destination = sv::IfOp::create(rewriter, loc, condition, [] { + }).getThenBlock(); + } + + rewriter.mergeBlocks(op.getBodyBlock(), destination); + }); + rewriter.eraseOp(op); + return success(); + } +}; + class DPICallLowering : public SimConversionPattern { public: using SimConversionPattern::SimConversionPattern; @@ -434,7 +470,13 @@ addFragments(hw::HWModuleOp module, static bool moveOpsIntoIfdefGuardsAndProcesses(Operation *rootOp) { bool usedSynthesisMacro = false; - rootOp->walk([&](Operation *op) { + rootOp->walk([&](Operation *op) { + // `sim.triggered` is lowered as a whole later on. Do not pre-wrap the + // ops nested inside it here, or we may create redundant/invalid + // structure. + if (isa(op)) + return WalkResult::skip(); + auto loc = op->getLoc(); // Move the op into an ifdef guard if needed. @@ -516,6 +558,7 @@ static bool moveOpsIntoIfdefGuardsAndProcesses(Operation *rootOp) { // Move the op into the if body. op->moveBefore(block, block->end()); } + return WalkResult::advance(); }); return usedSynthesisMacro; @@ -843,6 +886,7 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase { patterns.add(convert); patterns.add(convert); patterns.add(convert); + patterns.add(context, state); patterns.add(context, state); auto result = applyPartialConversion(module, target, std::move(patterns)); diff --git a/test/Conversion/SimToSV/triggered.mlir b/test/Conversion/SimToSV/triggered.mlir new file mode 100644 index 000000000000..8b09292702f4 --- /dev/null +++ b/test/Conversion/SimToSV/triggered.mlir @@ -0,0 +1,80 @@ +// RUN: circt-opt --lower-sim-to-sv --allow-unregistered-dialect %s | FileCheck %s + +// CHECK-LABEL: hw.module @simple_triggered +hw.module @simple_triggered(in %clock : !seq.clock) { + sim.triggered %clock { + "some.user"() : () -> () + } + + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: %[[CLOCK:.*]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge %[[CLOCK]] { + // CHECK-NEXT: "some.user"() : () -> () + // CHECK-NEXT: } + // CHECK-NEXT: } +} + +// CHECK-LABEL: hw.module @conditional_triggered +hw.module @conditional_triggered( + in %clock : !seq.clock, in %en : i1) { + sim.triggered %clock if %en { + "some.user"() : () -> () + } + + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: %[[CLOCK:.*]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge %[[CLOCK]] { + // CHECK-NEXT: sv.if %en { + // CHECK-NEXT: "some.user"() : () -> () + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } +} + +// CHECK-LABEL: hw.module @multiple_triggered +hw.module @multiple_triggered( + in %clock : !seq.clock, in %en : i1) { + + sim.triggered %clock { + "some.user"() : () -> () + } + + sim.triggered %clock if %en { + "some.user"() : () -> () + } + + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: %[[CLOCK0:.*]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge %[[CLOCK0]] { + // CHECK-NEXT: "some.user"() : () -> () + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: %[[CLOCK1:.*]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge %[[CLOCK1]] { + // CHECK-NEXT: sv.if %en { + // CHECK-NEXT: "some.user"() : () -> () + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } +} + +// CHECK-LABEL: hw.module @triggered_with_pause +hw.module @triggered_with_pause(in %clock : !seq.clock) { + sim.triggered %clock { + sim.pause quiet + } + + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NOT: sv.ifdef.procedural @SYNTHESIS + // CHECK-NEXT: } else { + // CHECK-NEXT: %[[CLOCK:.*]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge %[[CLOCK]] { + // CHECK-NEXT: sv.stop 0 + // CHECK-NEXT: } + // CHECK-NEXT: } +}