diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 407c3f9341c1..d7b6ceed8402 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -38,24 +38,6 @@ def PlusArgsValueOp : SimOp<"plusargs.value", [Pure]> { let assemblyFormat = "$formatString attr-dict `:` type($result)"; } -def FinishOp : SimOp<"finish"> { - let summary = "Simulation finish condition"; - - let arguments = (ins ClockType:$clk, I1:$cond); - let results = (outs); - - let assemblyFormat = "$clk `,` $cond attr-dict"; -} - -def FatalOp : SimOp<"fatal"> { - let summary = "Simulation failure condition"; - - let arguments = (ins ClockType:$clk, I1:$cond); - let results = (outs); - - let assemblyFormat = "$clk `,` $cond attr-dict"; -} - def DPIFuncOp : SimOp<"func.dpi", [IsolatedFromAbove, Symbol, OpAsmOpInterface, FunctionOpInterface]> { @@ -379,4 +361,96 @@ def PrintFormattedProcOp : SimOp<"proc.print"> { let assemblyFormat = "$input attr-dict"; } +//===----------------------------------------------------------------------===// +// Simulation Control +//===----------------------------------------------------------------------===// + +def ClockedTerminateOp : SimOp<"clocked_terminate"> { + let summary = "Terminate the simulation"; + let description = [{ + Implements the semantics of `sim.terminate` if the given condition is true + on the rising edge of the clock operand. + }]; + let arguments = (ins + ClockType:$clock, + I1:$condition, + BoolAttr:$success, + BoolAttr:$verbose + ); + let assemblyFormat = [{ + $clock `,` $condition `,` + custom($success, "\"success\"", "\"failure\"") `,` + custom($verbose, "\"verbose\"" , "\"quiet\"") attr-dict + }]; +} + +def ClockedPauseOp : SimOp<"clocked_pause"> { + let summary = "Pause the simulation"; + let description = [{ + Implements the semantics of `sim.pause` if the given condition is true on + the rising edge of the clock operand. + }]; + let arguments = (ins + ClockType:$clock, + I1:$condition, + BoolAttr:$verbose + ); + let assemblyFormat = [{ + $clock `,` $condition `,` + custom($verbose, "\"verbose\"" , "\"quiet\"") attr-dict + }]; +} + +def TerminateOp : SimOp<"terminate"> { + let summary = "Terminate the simulation"; + let description = [{ + Terminate the simulation with a success or failure exit code. Depending on + the `verbose` operand, simulators may print additional information about the + current simulation time and hierarchical location of the op. + + This op correpsonds to the following SystemVerilog constructs: + + | Operation | SystemVerilog | + |----------------------------------|---------------------------| + | `sim.terminate success, quiet` | `$finish(0)` | + | `sim.terminate success, verbose` | `$finish` or `$finish(1)` | + | `sim.terminate failure, quiet` | `$fatal(0)` | + | `sim.terminate failure, verbose` | `$fatal` or `$fatal(1)` | + + Note that this op does not match the behavior of the `$exit` system task in + SystemVerilog, which blocks execution of the calling process until all + `program` instances have terminated, and then calls `$finish`. + }]; + let arguments = (ins + BoolAttr:$success, + BoolAttr:$verbose + ); + let assemblyFormat = [{ + custom($success, "\"success\"", "\"failure\"") `,` + custom($verbose, "\"verbose\"" , "\"quiet\"") attr-dict + }]; +} + +def PauseOp : SimOp<"pause"> { + let summary = "Pause the simulation"; + let description = [{ + Interrupt the simulation and give control back to the user in case of an + interactive simulation. Non-interactive simulations may choose to terminate + instead. Depending on the `verbose` operand, simulators may print additional + information about the current simulation time and hierarchical location of + the op. + + This op corresponds to the following SystemVerilog constructs: + + | Operation | SystemVerilog | + |---------------------|------------------------| + | `sim.pause quiet` | `$stop(0)` | + | `sim.pause verbose` | `$stop` or `$stop(1)` | + }]; + let arguments = (ins BoolAttr:$verbose); + let assemblyFormat = [{ + custom($verbose, "\"verbose\"" , "\"quiet\"") attr-dict + }]; +} + #endif // CIRCT_DIALECT_SIM_SIMOPS_TD diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index d6f0bd5c7a7c..7d2fc14b610c 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -851,7 +851,7 @@ void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op, // Helper function to emit #ifndef guard. auto emitGuard = [&](const char *guard, llvm::function_ref body) { sv::IfDefOp::create( - b, guard, []() {}, body); + b, guard, [] {}, body); }; if (state.usedFileDescriptorLib) { @@ -2895,7 +2895,7 @@ void FIRRTLLowering::addToAlwaysBlock( // It is weird but intended. Here we want to create an empty sv.if // with an else block. insideIfOp = sv::IfOp::create( - builder, reset, []() {}, []() {}); + builder, reset, [] {}, [] {}); }; if (resetStyle == sv::ResetType::AsyncReset) { sv::EventControl events[] = {clockEdge, resetEdge}; @@ -4973,10 +4973,9 @@ LogicalResult FIRRTLLowering::visitStmt(StopOp op) { sv::MacroRefExprOp::create(builder, cond.getType(), "STOP_COND_"); Value exitCond = builder.createOrFold(stopCond, cond, true); - if (op.getExitCode()) - sim::FatalOp::create(builder, clock, exitCond); - else - sim::FinishOp::create(builder, clock, exitCond); + sim::ClockedTerminateOp::create(builder, clock, exitCond, + /*success=*/op.getExitCode() == 0, + /*verbose=*/true); return success(); } diff --git a/lib/Conversion/SimToSV/SimToSV.cpp b/lib/Conversion/SimToSV/SimToSV.cpp index 4bcae887acf6..66af5fc66dd5 100644 --- a/lib/Conversion/SimToSV/SimToSV.cpp +++ b/lib/Conversion/SimToSV/SimToSV.cpp @@ -36,6 +36,23 @@ namespace circt { using namespace circt; using namespace sim; +/// Check whether an op should be placed inside an ifdef guard that prevents it +/// from affecting synthesis runs. +static bool needsIfdefGuard(Operation *op) { + return isa(op); +} + +/// Check whether an op should be placed inside an always process triggered on a +/// clock, and an if statement checking for a condition. +static std::pair needsClockAndConditionWrapper(Operation *op) { + return TypeSwitch>(op) + .Case( + [](auto op) -> std::pair { + return {op.getClock(), op.getCondition()}; + }) + .Default({}); +} + namespace { struct SimConversionState { @@ -146,34 +163,31 @@ class PlusArgsValueLowering : public SimConversionPattern { } }; -template -class SimulatorStopLowering : public SimConversionPattern { -public: - using SimConversionPattern::SimConversionPattern; - - LogicalResult - matchAndRewrite(FromOp op, typename FromOp::Adaptor adaptor, - ConversionPatternRewriter &rewriter) const final { - auto loc = op.getLoc(); - - Value clockCast = seq::FromClockOp::create(rewriter, loc, adaptor.getClk()); +static LogicalResult convert(ClockedTerminateOp op, PatternRewriter &rewriter) { + if (op.getSuccess()) + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + else + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + return success(); +} - this->state.usedSynthesisMacro = true; - sv::IfDefOp::create( - rewriter, loc, "SYNTHESIS", [&] {}, - [&] { - sv::AlwaysOp::create( - rewriter, loc, sv::EventControl::AtPosEdge, clockCast, [&] { - sv::IfOp::create(rewriter, loc, adaptor.getCond(), - [&] { ToOp::create(rewriter, loc); }); - }); - }); +static LogicalResult convert(ClockedPauseOp op, PatternRewriter &rewriter) { + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + return success(); +} - rewriter.eraseOp(op); +static LogicalResult convert(TerminateOp op, PatternRewriter &rewriter) { + if (op.getSuccess()) + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + else + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + return success(); +} - return success(); - } -}; +static LogicalResult convert(PauseOp op, PatternRewriter &rewriter) { + rewriter.replaceOpWithNewOp(op, op.getVerbose()); + return success(); +} class DPICallLowering : public SimConversionPattern { public: @@ -315,6 +329,96 @@ void LowerDPIFunc::addFragments(hw::HWModuleOp module, ArrayAttr::get(module.getContext(), fragments.takeVector())); } +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(op->getPrevNode()) + .Case( + [&](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()) + 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()); + } + + // 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(op->getPrevNode())) + if (alwaysOp.getNumConditions() == 1 && + alwaysOp.getCondition(0).event == sv::EventControl::AtPosEdge) + if (auto clockOp = alwaysOp.getCondition(0) + .value.getDefiningOp()) + 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()); + } + + // 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(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()); + } + }); + + return usedSynthesisMacro; +} + namespace { struct SimToSVPass : public circt::impl::LowerSimToSVBase { void runOnOperation() override { @@ -329,6 +433,9 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase { std::atomic usedSynthesisMacro = false; auto lowerModule = [&](hw::HWModuleOp module) { + if (moveOpsIntoIfdefGuardsAndProcesses(module)) + usedSynthesisMacro = true; + SimConversionState state; ConversionTarget target(*context); target.addIllegalDialect(); @@ -340,10 +447,10 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase { RewritePatternSet patterns(context); patterns.add(context, state); patterns.add(context, state); - patterns.add>(context, - state); - patterns.add>(context, - state); + patterns.add(convert); + patterns.add(convert); + patterns.add(convert); + patterns.add(convert); patterns.add(context, state); auto result = applyPartialConversion(module, target, std::move(patterns)); diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index 3e3c3d09816e..4ee60617484b 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -13,6 +13,7 @@ #include "circt/Dialect/Sim/SimOps.h" #include "circt/Dialect/HW/ModuleImplementation.h" #include "circt/Dialect/SV/SVOps.h" +#include "circt/Support/CustomDirectiveImpl.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Interfaces/FunctionImplementation.h" diff --git a/test/Conversion/FIRRTLToHW/lower-to-hw.mlir b/test/Conversion/FIRRTLToHW/lower-to-hw.mlir index 9a1861a92b3c..3eac43627412 100644 --- a/test/Conversion/FIRRTLToHW/lower-to-hw.mlir +++ b/test/Conversion/FIRRTLToHW/lower-to-hw.mlir @@ -461,12 +461,12 @@ firrtl.circuit "Simple" attributes {annotations = [{class = firrtl.module private @Stop(in %clock1: !firrtl.clock, in %clock2: !firrtl.clock, in %reset: !firrtl.uint<1>) { // CHECK-NEXT: [[STOP_COND_1:%.+]] = sv.macro.ref.expr @STOP_COND_ // CHECK-NEXT: [[COND:%.+]] = comb.and bin [[STOP_COND_1]], %reset : i1 - // CHECK-NEXT: sim.fatal %clock1, [[COND]] + // CHECK-NEXT: sim.clocked_terminate %clock1, [[COND]], failure firrtl.stop %clock1, %reset, 42 : !firrtl.clock, !firrtl.uint<1> // CHECK-NEXT: [[STOP_COND_2:%.+]] = sv.macro.ref.expr @STOP_COND_ // CHECK-NEXT: [[COND:%.+]] = comb.and bin [[STOP_COND_2:%.+]], %reset : i1 - // CHECK-NEXT: sim.finish %clock2, [[COND]] + // CHECK-NEXT: sim.clocked_terminate %clock2, [[COND]], success firrtl.stop %clock2, %reset, 0 : !firrtl.clock, !firrtl.uint<1> } diff --git a/test/Conversion/SimToSV/simulation-control.mlir b/test/Conversion/SimToSV/simulation-control.mlir new file mode 100644 index 000000000000..2ca1ba948fc2 --- /dev/null +++ b/test/Conversion/SimToSV/simulation-control.mlir @@ -0,0 +1,119 @@ +// RUN: circt-opt --lower-sim-to-sv %s | FileCheck %s + +// CHECK-LABEL: hw.module @NonProcedural +hw.module @NonProcedural(in %clock: !seq.clock, in %cond: i1) { + // CHECK-NEXT: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: [[CLOCK:%.+]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge [[CLOCK]] { + // CHECK-NEXT: sv.if %cond { + + // CHECK-NEXT: sv.finish 1 + sim.clocked_terminate %clock, %cond, success, verbose + // CHECK-NEXT: sv.fatal 1 + sim.clocked_terminate %clock, %cond, failure, verbose + // CHECK-NEXT: sv.finish 0 + sim.clocked_terminate %clock, %cond, success, quiet + // CHECK-NEXT: sv.fatal 0 + sim.clocked_terminate %clock, %cond, failure, quiet + // CHECK-NEXT: sv.stop 1 + sim.clocked_pause %clock, %cond, verbose + // CHECK-NEXT: sv.stop 0 + sim.clocked_pause %clock, %cond, quiet + + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } +} + +// CHECK-LABEL: hw.module @Procedural +hw.module @Procedural() { + // CHECK-NEXT: sv.initial { + sv.initial { + // CHECK-NEXT: sv.ifdef.procedural @SYNTHESIS { + // CHECK-NEXT: } else { + + // CHECK-NEXT: sv.finish 1 + sim.terminate success, verbose + // CHECK-NEXT: sv.fatal 1 + sim.terminate failure, verbose + // CHECK-NEXT: sv.finish 0 + sim.terminate success, quiet + // CHECK-NEXT: sv.fatal 0 + sim.terminate failure, quiet + // CHECK-NEXT: sv.stop 1 + sim.pause verbose + // CHECK-NEXT: sv.stop 0 + sim.pause quiet + + // CHECK-NEXT: } + } + // CHECK-NEXT: } +} + +sv.macro.decl @SCHMINTHESIS + +// CHECK-LABEL: hw.module @DontMergeIntoIfdefWithDifferentMacro +hw.module @DontMergeIntoIfdefWithDifferentMacro(in %clock: !seq.clock, in %cond: i1) { + // CHECK: sv.ifdef @SCHMINTHESIS + sv.ifdef @SCHMINTHESIS {} else {} + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: [[CLOCK:%.+]] = seq.from_clock %clock + // CHECK-NEXT: sv.always posedge [[CLOCK]] { + // CHECK-NEXT: sv.if %cond { + // CHECK-NEXT: sv.stop 0 + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } + sim.clocked_pause %clock, %cond, quiet + + // CHECK: sv.initial + sv.initial { + // CHECK: sv.ifdef.procedural @SCHMINTHESIS + sv.ifdef.procedural @SCHMINTHESIS {} else {} + // CHECK: sv.ifdef.procedural @SYNTHESIS { + // CHECK-NEXT: } else { + // CHECK-NEXT: sv.stop 0 + // CHECK-NEXT: } + sim.pause quiet + } +} + +// CHECK-LABEL: hw.module @DontMergeDifferentClocksOrConditions +hw.module @DontMergeDifferentClocksOrConditions( + in %clockA: !seq.clock, + in %clockB: !seq.clock, + in %condA: i1, + in %condB: i1 +) { + // CHECK: sv.ifdef @SYNTHESIS { + // CHECK-NEXT: } else { + + // CHECK-NEXT: [[TMP:%.+]] = seq.from_clock %clockA + // CHECK-NEXT: sv.always posedge [[TMP]] { + // CHECK-NEXT: sv.if %condA { + // CHECK-NEXT: sv.stop 0 + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: [[TMP:%.+]] = seq.from_clock %clockB + // CHECK-NEXT: sv.always posedge [[TMP]] { + // CHECK-NEXT: sv.if %condA { + // CHECK-NEXT: sv.stop 1 + // CHECK-NEXT: } + // CHECK-NEXT: } + sim.clocked_pause %clockA, %condA, quiet + sim.clocked_pause %clockB, %condA, verbose + + // CHECK-NEXT: [[TMP:%.+]] = seq.from_clock %clockA + // CHECK-NEXT: sv.always posedge [[TMP]] { + // CHECK-NEXT: sv.if %condA { + // CHECK-NEXT: sv.stop 0 + // CHECK-NEXT: } + // CHECK-NEXT: sv.if %condB { + // CHECK-NEXT: sv.stop 1 + // CHECK-NEXT: } + // CHECK-NEXT: } + sim.clocked_pause %clockA, %condA, quiet + sim.clocked_pause %clockA, %condB, verbose +} diff --git a/test/Conversion/SimToSV/stop.mlir b/test/Conversion/SimToSV/stop.mlir deleted file mode 100644 index 99bfa66f3f8a..000000000000 --- a/test/Conversion/SimToSV/stop.mlir +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: circt-opt --lower-sim-to-sv %s | FileCheck %s - -// CHECK-LABEL: hw.module @finish -hw.module @finish(in %clock : !seq.clock, in %cond : i1) { - // CHECK: [[CLK_SV:%.+]] = seq.from_clock %clock - // CHECK-NEXT: sv.ifdef @SYNTHESIS { - // CHECK-NEXT: } else { - // CHECK-NEXT: sv.always posedge [[CLK_SV]] { - // CHECK-NEXT: sv.if %cond { - // CHECK-NEXT: sv.finish 1 - // CHECK-NEXT: } - // CHECK-NEXT: } - // CHECK-NEXT: } - sim.finish %clock, %cond -} - -// CHECK-LABEL: hw.module @fatal -hw.module @fatal(in %clock : !seq.clock, in %cond : i1) { - // CHECK: [[CLK_SV:%.+]] = seq.from_clock %clock - // CHECK-NEXT: sv.ifdef @SYNTHESIS { - // CHECK-NEXT: } else { - // CHECK-NEXT: sv.always posedge [[CLK_SV]] { - // CHECK-NEXT: sv.if %cond { - // CHECK-NEXT: sv.fatal 1 - // CHECK-NEXT: } - // CHECK-NEXT: } - // CHECK-NEXT: } - sim.fatal %clock, %cond -} diff --git a/test/Dialect/Sim/round-trip.mlir b/test/Dialect/Sim/round-trip.mlir index bbbe07d4d931..3028a74d51db 100644 --- a/test/Dialect/Sim/round-trip.mlir +++ b/test/Dialect/Sim/round-trip.mlir @@ -1,5 +1,4 @@ -// RUN: circt-opt %s -verify-diagnostics | circt-opt -verify-diagnostics | FileCheck %s - +// RUN: circt-opt --verify-roundtrip --verify-diagnostics %s | FileCheck %s // CHECK-LABEL: hw.module @plusargs_value hw.module @plusargs_value() { @@ -9,18 +8,11 @@ hw.module @plusargs_value() { %1, %2 = sim.plusargs.value "bar" : i5 } -// CHECK-LABEL: hw.module @stop_finish -hw.module @stop_finish(in %clock : !seq.clock, in %cond : i1) { - // CHECK: sim.finish %clock, %cond - sim.finish %clock, %cond - // CHECK: sim.fatal %clock, %cond - sim.fatal %clock, %cond -} - // CHECK-LABEL: sim.func.dpi @dpi(out arg0 : i1, in %arg1 : i1, out arg2 : i1) sim.func.dpi @dpi(out arg0: i1, in %arg1: i1, out arg2: i1) func.func private @func(%arg1: i1) -> (i1, i1) +// CHECK-LABEL: hw.module @dpi_call hw.module @dpi_call(in %clock : !seq.clock, in %enable : i1, in %in: i1) { // CHECK: sim.func.dpi.call @dpi(%in) clock %clock enable %enable : (i1) -> (i1, i1) %0, %1 = sim.func.dpi.call @dpi(%in) clock %clock enable %enable: (i1) -> (i1, i1) @@ -31,3 +23,38 @@ hw.module @dpi_call(in %clock : !seq.clock, in %enable : i1, in %in: i1) { // CHECK: sim.func.dpi.call @func(%in) : (i1) -> (i1, i1) %6, %7 = sim.func.dpi.call @func(%in) : (i1) -> (i1, i1) } + +// CHECK-LABEL: hw.module @GraphSimulationControl +hw.module @GraphSimulationControl(in %clock: !seq.clock, in %en: i1) { + // CHECK: sim.clocked_terminate %clock, %en, success, verbose + sim.clocked_terminate %clock, %en, success, verbose + // CHECK: sim.clocked_terminate %clock, %en, success, quiet + sim.clocked_terminate %clock, %en, success, quiet + // CHECK: sim.clocked_terminate %clock, %en, failure, verbose + sim.clocked_terminate %clock, %en, failure, verbose + // CHECK: sim.clocked_terminate %clock, %en, failure, quiet + sim.clocked_terminate %clock, %en, failure, quiet + + // CHECK: sim.clocked_pause %clock, %en, verbose + sim.clocked_pause %clock, %en, verbose + // CHECK: sim.clocked_pause %clock, %en, quiet + sim.clocked_pause %clock, %en, quiet +} + +// CHECK-LABEL: func.func @SimulationControl +func.func @SimulationControl() { + // CHECK: sim.terminate success, verbose + sim.terminate success, verbose + // CHECK: sim.terminate success, quiet + sim.terminate success, quiet + // CHECK: sim.terminate failure, verbose + sim.terminate failure, verbose + // CHECK: sim.terminate failure, quiet + sim.terminate failure, quiet + + // CHECK: sim.pause verbose + sim.pause verbose + // CHECK: sim.pause quiet + sim.pause quiet + return +}