Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 122 additions & 78 deletions lib/Conversion/SimToSV/SimToSV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,40 @@ static LogicalResult convert(PauseOp op, PatternRewriter &rewriter) {
return success();
}

class TriggeredLowering : public SimConversionPattern<TriggeredOp> {
public:
using SimConversionPattern<TriggeredOp>::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>{sv::EventControl::AtPosEdge},
ArrayRef<Value>{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<DPICallOp> {
public:
using SimConversionPattern<DPICallOp>::SimConversionPattern;
Expand Down Expand Up @@ -434,89 +468,98 @@ addFragments(hw::HWModuleOp module,
static bool moveOpsIntoIfdefGuardsAndProcesses(Operation *rootOp) {
bool usedSynthesisMacro = false;

rootOp->walk([&](Operation *op) {
auto loc = op->getLoc();

// Move the op into an ifdef guard if needed.
if (needsIfdefGuard(op)) {
// Try to reuse an ifdef guard immediately before the op.
Block *block = nullptr;
if (op->getPrevNode())
block = TypeSwitch<Operation *, Block *>(op->getPrevNode())
.Case<sv::IfDefOp, sv::IfDefProceduralOp>(
[&](auto guardOp) -> Block * {
if (guardOp.getCond().getIdent().getAttr() ==
"SYNTHESIS" &&
guardOp.hasElse())
return guardOp.getElseBlock();
return nullptr;
})
.Default([](auto) { return nullptr; });

// If there was no pre-existing guard, create one.
if (!block) {
OpBuilder builder(op);
if (op->getParentOp()->hasTrait<ProceduralRegion>())
block = sv::IfDefProceduralOp::create(
builder, loc, "SYNTHESIS", [] {}, [] {})
.getElseBlock();
else
block = sv::IfDefOp::create(
builder, loc, "SYNTHESIS", [] {}, [] {})
.getElseBlock();
usedSynthesisMacro = true;
}
rootOp->walk<mlir::WalkOrder::PreOrder>(
[&](Operation *op) -> mlir::WalkResult {
// `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<TriggeredOp>(op))
return mlir::WalkResult::skip();
Comment thread
nanjo712 marked this conversation as resolved.
Outdated

auto loc = op->getLoc();

// Move the op into an ifdef guard if needed.
if (needsIfdefGuard(op)) {
// Try to reuse an ifdef guard immediately before the op.
Block *block = nullptr;
if (op->getPrevNode())
block = TypeSwitch<Operation *, Block *>(op->getPrevNode())
.Case<sv::IfDefOp, sv::IfDefProceduralOp>(
[&](auto guardOp) -> Block * {
if (guardOp.getCond().getIdent().getAttr() ==
"SYNTHESIS" &&
guardOp.hasElse())
return guardOp.getElseBlock();
return nullptr;
})
.Default([](auto) { return nullptr; });

// If there was no pre-existing guard, create one.
if (!block) {
OpBuilder builder(op);
if (op->getParentOp()->hasTrait<ProceduralRegion>())
block = sv::IfDefProceduralOp::create(
builder, loc, "SYNTHESIS", [] {}, [] {})
.getElseBlock();
else
block = sv::IfDefOp::create(
builder, loc, "SYNTHESIS", [] {}, [] {})
.getElseBlock();
usedSynthesisMacro = true;
}

// Move the op into the guard block.
op->moveBefore(block, block->end());
}
// Move the op into the guard block.
op->moveBefore(block, block->end());
}

// Check if the op requires an clock and condition wrapper.
auto [clock, condition] = needsClockAndConditionWrapper(op);

// Create an enclosing always process.
if (clock) {
// Try to reuse an always process immediately before the op.
Block *block = nullptr;
if (auto alwaysOp = dyn_cast_or_null<sv::AlwaysOp>(op->getPrevNode()))
if (alwaysOp.getNumConditions() == 1 &&
alwaysOp.getCondition(0).event == sv::EventControl::AtPosEdge)
if (auto clockOp = alwaysOp.getCondition(0)
.value.getDefiningOp<seq::FromClockOp>())
if (clockOp.getInput() == clock)
block = alwaysOp.getBodyBlock();

// If there was no pre-existing always process, create one.
if (!block) {
OpBuilder builder(op);
clock = seq::FromClockOp::create(builder, loc, clock);
block = sv::AlwaysOp::create(builder, loc, sv::EventControl::AtPosEdge,
clock, [] {})
.getBodyBlock();
}
// Check if the op requires an clock and condition wrapper.
auto [clock, condition] = needsClockAndConditionWrapper(op);

// Create an enclosing always process.
if (clock) {
// Try to reuse an always process immediately before the op.
Block *block = nullptr;
if (auto alwaysOp = dyn_cast_or_null<sv::AlwaysOp>(op->getPrevNode()))
if (alwaysOp.getNumConditions() == 1 &&
alwaysOp.getCondition(0).event == sv::EventControl::AtPosEdge)
if (auto clockOp = alwaysOp.getCondition(0)
.value.getDefiningOp<seq::FromClockOp>())
if (clockOp.getInput() == clock)
block = alwaysOp.getBodyBlock();

// If there was no pre-existing always process, create one.
if (!block) {
OpBuilder builder(op);
clock = seq::FromClockOp::create(builder, loc, clock);
block = sv::AlwaysOp::create(
builder, loc, sv::EventControl::AtPosEdge, clock, [] {})
.getBodyBlock();
}

// Move the op into the process.
op->moveBefore(block, block->end());
}
// Move the op into the process.
op->moveBefore(block, block->end());
}

// Create an enclosing if condition.
if (condition) {
// Try to reuse an if statement immediately before the op.
Block *block = nullptr;
if (auto ifOp = dyn_cast_or_null<sv::IfOp>(op->getPrevNode()))
if (ifOp.getCond() == condition)
block = ifOp.getThenBlock();

// If there was no pre-existing if statement, create one.
if (!block) {
OpBuilder builder(op);
block = sv::IfOp::create(builder, loc, condition, [] {}).getThenBlock();
}
// Create an enclosing if condition.
if (condition) {
// Try to reuse an if statement immediately before the op.
Block *block = nullptr;
if (auto ifOp = dyn_cast_or_null<sv::IfOp>(op->getPrevNode()))
if (ifOp.getCond() == condition)
block = ifOp.getThenBlock();

// If there was no pre-existing if statement, create one.
if (!block) {
OpBuilder builder(op);
block =
sv::IfOp::create(builder, loc, condition, [] {}).getThenBlock();
}

// Move the op into the if body.
op->moveBefore(block, block->end());
}
});
// Move the op into the if body.
op->moveBefore(block, block->end());
}
return mlir::WalkResult::advance();
});

return usedSynthesisMacro;
}
Expand Down Expand Up @@ -843,6 +886,7 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
patterns.add<ClockedPauseOp>(convert);
patterns.add<TerminateOp>(convert);
patterns.add<PauseOp>(convert);
patterns.add<TriggeredLowering>(context, state);
patterns.add<DPICallLowering>(context, state);
auto result = applyPartialConversion(module, target, std::move(patterns));

Expand Down
80 changes: 80 additions & 0 deletions test/Conversion/SimToSV/triggered.mlir
Comment thread
nanjo712 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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: }
}
Loading