From 92041a13ebd53dc6aa10888768bfc1cc71507f3d Mon Sep 17 00:00:00 2001 From: woshiren Date: Tue, 26 May 2026 19:35:43 +0800 Subject: [PATCH 1/6] [FIRRTLToHW] Add lower-to-core for firrtl.fflush --- lib/Conversion/FIRRTLToHW/LowerToHW.cpp | 25 +++++++++++++++++-- .../FIRRTLToHW/lower-to-core-errors.mlir | 14 ----------- test/Conversion/FIRRTLToHW/lower-to-core.mlir | 18 +++++++++++++ test/firtool/lower-to-core.fir | 10 ++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index e0245a2cfe54..2a1c57d99506 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -5499,8 +5499,29 @@ LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) { // FFlush lowers into $fflush statement. LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) { - if (circuitState.lowerToCore) - return op.emitOpError("lower-to-core does not support firrtl.fflush yet"); + if (circuitState.lowerToCore) { + auto clock = getLoweredValue(op.getClock()); + auto cond = getLoweredValue(op.getCond()); + if (!clock || !cond) + return failure(); + + if (!op.getOutputFileAttr()) { + auto stderrOp = sim::StderrStreamOp::create(builder); + sim::TriggeredOp::create(builder, clock, cond, [&] { + sim::FlushOp::create(builder, stderrOp); + }); + } else { + auto fileFormatString = lowerSimFormatString( + op.getOutputFileAttr(), op.getOutputFileSubstitutions()); + if (failed(fileFormatString)) + return failure(); + sim::TriggeredOp::create(builder, clock, cond, [&] { + auto fileOp = sim::GetFileOp::create(builder, *fileFormatString); + sim::FlushOp::create(builder, fileOp); + }); + } + return success(); + } auto clock = getLoweredNonClockValue(op.getClock()); auto cond = getLoweredValue(op.getCond()); diff --git a/test/Conversion/FIRRTLToHW/lower-to-core-errors.mlir b/test/Conversion/FIRRTLToHW/lower-to-core-errors.mlir index f323ac6b0f54..060636bec8bb 100644 --- a/test/Conversion/FIRRTLToHW/lower-to-core-errors.mlir +++ b/test/Conversion/FIRRTLToHW/lower-to-core-errors.mlir @@ -15,20 +15,6 @@ firrtl.circuit "time_printf" { // ----- -firrtl.circuit "fflush_unsupported" { - firrtl.module @fflush_unsupported( - in %clock: !firrtl.clock, - in %enable: !firrtl.uint<1>) { - // expected-error @+2 {{'firrtl.fflush' op lower-to-core does not support firrtl.fflush yet}} - // expected-error @below {{'firrtl.fflush' op LowerToHW couldn't handle this operation}} - firrtl.fflush %clock, %enable, "out.txt"() - : !firrtl.clock, !firrtl.uint<1> - firrtl.skip - } -} - -// ----- - firrtl.circuit "force_unsupported" { firrtl.module @force_unsupported(in %in: !firrtl.uint<42>) { %foo = firrtl.verbatim.wire "foo" : () -> !firrtl.uint<42> diff --git a/test/Conversion/FIRRTLToHW/lower-to-core.mlir b/test/Conversion/FIRRTLToHW/lower-to-core.mlir index b71dc1186778..5085fa8c553f 100644 --- a/test/Conversion/FIRRTLToHW/lower-to-core.mlir +++ b/test/Conversion/FIRRTLToHW/lower-to-core.mlir @@ -52,6 +52,24 @@ firrtl.circuit "LowerToCore" { firrtl.fprintf %clock, %enable, "out%d.txt"(%x), "value=%d @ {{}}\0A"(%x, %hier) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.sint<4>, !firrtl.fstring + // CHECK: [[LIT4:%.+]] = sim.fmt.literal "out" + // CHECK: [[FILEVAL:%.+]] = sim.fmt.dec %x signed : i4 + // CHECK: [[LIT5:%.+]] = sim.fmt.literal ".txt + // CHECK: [[FMTFILE3:%.+]] = sim.fmt.concat ([[LIT4]], [[FILEVAL]], [[LIT5]]) + // CHECK: sim.triggered %clock if %enable { + // CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE3]] + // CHECK-NEXT: sim.flush [[FILE3]] + // CHECK-NEXT: } + firrtl.fflush %clock, %enable, "out%d.txt"(%x) + : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4> + + // CHECK: [[STDERR:%.+]] = sim.stderr_stream + // CHECK: sim.triggered %clock if %enable { + // CHECK-NEXT: sim.flush [[STDERR]] + // CHECK-NEXT: } + firrtl.fflush %clock, %enable + : !firrtl.clock, !firrtl.uint<1> + firrtl.skip } diff --git a/test/firtool/lower-to-core.fir b/test/firtool/lower-to-core.fir index 3928f51dcc87..9a742ad132de 100644 --- a/test/firtool/lower-to-core.fir +++ b/test/firtool/lower-to-core.fir @@ -12,6 +12,8 @@ circuit LowerToCore: printf(clock, enable, "value=%d\n", x) fprintf(clock, enable, "out.txt", "value=%d\n", x) fprintf(clock, enable, "out%d.txt", x, "value=%d\n", x) + fflush(clock, enable, "out%d.txt", x) + fflush(clock, enable) ; CHECK-LABEL: hw.module @LowerToCore ; CHECK-DAG: [[LITFILE1:%.+]] = sim.fmt.literal "out" @@ -36,5 +38,13 @@ circuit LowerToCore: ; CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]] ; CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE2]] ; CHECK-NEXT: } +; CHECK: sim.triggered %clock if %enable { +; CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE2]] +; CHECK-NEXT: sim.flush [[FILE3]] +; CHECK-NEXT: } +; CHECK: [[STDERR:%.+]] = sim.stderr_stream +; CHECK: sim.triggered %clock if %enable { +; CHECK-NEXT: sim.flush [[STDERR]] +; CHECK-NEXT: } ; CHECK-NOT: sv.assert ; CHECK-NOT: sv.fwrite From d9db72b44afbc46fc6fe93e234ee7b13f65a3ca1 Mon Sep 17 00:00:00 2001 From: woshiren Date: Wed, 27 May 2026 17:21:10 +0800 Subject: [PATCH 2/6] resolve conflict --- lib/Firtool/CMakeLists.txt | 1 + lib/Firtool/Firtool.cpp | 4 ++++ test/firtool/lower-to-core.fir | 11 +---------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/Firtool/CMakeLists.txt b/lib/Firtool/CMakeLists.txt index 25687341b5bb..0d568284952e 100644 --- a/lib/Firtool/CMakeLists.txt +++ b/lib/Firtool/CMakeLists.txt @@ -11,6 +11,7 @@ add_circt_library(CIRCTFirtool CIRCTOMTransforms CIRCTSeqToSV CIRCTSimToSV + CIRCTSimTransforms CIRCTSeqTransforms CIRCTSVTransforms CIRCTTransforms diff --git a/lib/Firtool/Firtool.cpp b/lib/Firtool/Firtool.cpp index 8b4d814cc5a3..569c80a9bd90 100644 --- a/lib/Firtool/Firtool.cpp +++ b/lib/Firtool/Firtool.cpp @@ -14,6 +14,7 @@ #include "circt/Dialect/OM/OMPasses.h" #include "circt/Dialect/SV/SVPasses.h" #include "circt/Dialect/Seq/SeqPasses.h" +#include "circt/Dialect/Sim/SimPasses.h" #include "circt/Dialect/Verif/VerifPasses.h" #include "circt/Support/Passes.h" #include "circt/Transforms/Passes.h" @@ -307,6 +308,9 @@ LogicalResult firtool::populateLowFIRRTLToHW(mlir::PassManager &pm, opt.getVerificationFlavor(), opt.shouldLowerToCore())); + if (opt.shouldLowerToCore()) + pm.addNestedPass(sim::createSquashSimTriggered()); + if (!opt.shouldDisableOptimization()) { auto &modulePM = pm.nest(); modulePM.addPass(mlir::createCSEPass()); diff --git a/test/firtool/lower-to-core.fir b/test/firtool/lower-to-core.fir index 9a742ad132de..55f5bf7751ed 100644 --- a/test/firtool/lower-to-core.fir +++ b/test/firtool/lower-to-core.fir @@ -25,25 +25,16 @@ circuit LowerToCore: ; CHECK-DAG: verif.clocked_assert %pred if %enable, posedge [[CLK]] : i1 ; CHECK-DAG: [[FMT:%.+]] = sim.fmt.dec %x signed : i4 ; CHECK: [[MSG:%.+]] = sim.fmt.concat ([[LIT]], [[FMT]], [[NL]]) +; CHECK: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LITFILE1]], [[FMT]], [[LITFILE2]]) ; CHECK: [[STDERR:%.+]] = sim.stderr_stream ; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: sim.proc.print [[MSG]] to [[STDERR]] -; CHECK-NEXT: } -; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]] ; CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE1]] -; CHECK-NEXT: } -; CHECK: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LITFILE1]], [[FMT]], [[LITFILE2]]) -; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]] ; CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE2]] -; CHECK-NEXT: } -; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE2]] ; CHECK-NEXT: sim.flush [[FILE3]] -; CHECK-NEXT: } -; CHECK: [[STDERR:%.+]] = sim.stderr_stream -; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: sim.flush [[STDERR]] ; CHECK-NEXT: } ; CHECK-NOT: sv.assert From 6f5a640828ffdd8dafee38ad2d1327ab7a1f3bba Mon Sep 17 00:00:00 2001 From: woshiren Date: Wed, 27 May 2026 18:05:23 +0800 Subject: [PATCH 3/6] update test --- test/firtool/lower-to-core.fir | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/firtool/lower-to-core.fir b/test/firtool/lower-to-core.fir index 55f5bf7751ed..3864a965a7fe 100644 --- a/test/firtool/lower-to-core.fir +++ b/test/firtool/lower-to-core.fir @@ -24,9 +24,9 @@ circuit LowerToCore: ; CHECK-DAG: [[CLK:%.+]] = seq.from_clock %clock ; CHECK-DAG: verif.clocked_assert %pred if %enable, posedge [[CLK]] : i1 ; CHECK-DAG: [[FMT:%.+]] = sim.fmt.dec %x signed : i4 -; CHECK: [[MSG:%.+]] = sim.fmt.concat ([[LIT]], [[FMT]], [[NL]]) -; CHECK: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LITFILE1]], [[FMT]], [[LITFILE2]]) -; CHECK: [[STDERR:%.+]] = sim.stderr_stream +; CHECK-DAG: [[MSG:%.+]] = sim.fmt.concat ([[LIT]], [[FMT]], [[NL]]) +; CHECK-DAG: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LITFILE1]], [[FMT]], [[LITFILE2]]) +; CHECK-DAG: [[STDERR:%.+]] = sim.stderr_stream ; CHECK: sim.triggered %clock if %enable { ; CHECK-NEXT: sim.proc.print [[MSG]] to [[STDERR]] ; CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]] From 6af51cecc56e38e2df166d2c13ae716894878516 Mon Sep 17 00:00:00 2001 From: woshiren Date: Thu, 28 May 2026 14:33:49 +0800 Subject: [PATCH 4/6] add addToSimTriggeredBlock and canonicalization --- include/circt/Conversion/Passes.td | 1 + include/circt/Dialect/Sim/SimOps.td | 1 + lib/Conversion/FIRRTLToHW/CMakeLists.txt | 1 + lib/Conversion/FIRRTLToHW/LowerToHW.cpp | 60 ++++++++++++++-- lib/Dialect/Sim/CMakeLists.txt | 1 + lib/Dialect/Sim/SimOps.cpp | 25 +++++++ lib/Firtool/CMakeLists.txt | 1 - lib/Firtool/Firtool.cpp | 4 -- test/Conversion/FIRRTLToHW/lower-to-core.mlir | 69 +++++++++++++------ test/Dialect/Sim/canonicalization.mlir | 52 ++++++++++++++ 10 files changed, 184 insertions(+), 31 deletions(-) diff --git a/include/circt/Conversion/Passes.td b/include/circt/Conversion/Passes.td index 297282cfabe9..32814164313e 100644 --- a/include/circt/Conversion/Passes.td +++ b/include/circt/Conversion/Passes.td @@ -429,6 +429,7 @@ def LowerFIRRTLToHW : Pass<"lower-firrtl-to-hw", "mlir::ModuleOp"> { "emit::EmitDialect", "hw::HWDialect", "ltl::LTLDialect", + "mlir::scf::SCFDialect", "seq::SeqDialect", "sim::SimDialect", "sv::SVDialect", diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 265ee5732c8a..459ec9a33797 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -1187,6 +1187,7 @@ def TriggeredOp : SimOp<"triggered", [ let arguments = (ins ClockType:$clock, Optional:$condition); let regions = (region SizedRegion<1>:$body); + let hasCanonicalizeMethod = true; let assemblyFormat = [{ $clock (`if` $condition^)? $body attr-dict }]; diff --git a/lib/Conversion/FIRRTLToHW/CMakeLists.txt b/lib/Conversion/FIRRTLToHW/CMakeLists.txt index c35080f70cd5..1b6f2319decb 100644 --- a/lib/Conversion/FIRRTLToHW/CMakeLists.txt +++ b/lib/Conversion/FIRRTLToHW/CMakeLists.txt @@ -18,5 +18,6 @@ add_circt_conversion_library(CIRCTFIRRTLToHW CIRCTSV CIRCTSVLoweringUtils CIRCTVerif + MLIRSCFDialect MLIRTransforms ) diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index 2a1c57d99506..e94d86f7ae11 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -34,6 +34,7 @@ #include "circt/Dialect/Verif/VerifOps.h" #include "circt/Support/BackedgeBuilder.h" #include "circt/Support/Namespace.h" +#include "mlir/Dialect/SCF/IR/SCF.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/ImplicitLocOpBuilder.h" @@ -1808,6 +1809,8 @@ struct FIRRTLLowering : public FIRRTLVisitor { sv::EventControl(), Value(), body, std::function()); } + void addToSimTriggeredBlock(Value clock, Value condition, + const std::function &body = {}); LogicalResult emitGuards(Location loc, ArrayRef guards, std::function emit); @@ -2107,6 +2110,10 @@ struct FIRRTLLowering : public FIRRTLVisitor { sv::ResetType, sv::EventControl, Value>; llvm::SmallDenseMap> alwaysBlocks; + llvm::SmallDenseMap, sim::TriggeredOp> + simTriggeredBlocks; + llvm::SmallDenseMap> + simTriggeredLastConditionBlocks; llvm::SmallDenseMap, sv::IfDefOp> ifdefBlocks; llvm::SmallDenseMap initialBlocks; @@ -3271,6 +3278,51 @@ void FIRRTLLowering::addToAlwaysBlock( builder.getInsertionPoint()); } +void FIRRTLLowering::addToSimTriggeredBlock( + Value clock, Value condition, const std::function &body) { + auto key = std::make_pair(builder.getBlock(), clock); + auto triggered = simTriggeredBlocks.lookup(key); + if (!triggered) { + triggered = sim::TriggeredOp::create(builder, clock); + simTriggeredBlocks[key] = triggered; + } + + if (condition) { + auto &lastConditionBlock = simTriggeredLastConditionBlocks[triggered]; + auto ifOp = lastConditionBlock.first == condition + ? lastConditionBlock.second + : mlir::scf::IfOp(); + if (!ifOp) { + auto oldIP = builder.saveInsertionPoint(); + builder.setInsertionPointToEnd(triggered.getBodyBlock()); + ifOp = mlir::scf::IfOp::create(builder, builder.getLoc(), TypeRange{}, + condition, true, false); + builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); + mlir::scf::YieldOp::create(builder, builder.getLoc()); + builder.restoreInsertionPoint(oldIP); + lastConditionBlock = {condition, ifOp}; + } + + auto oldIP = builder.saveInsertionPoint(); + auto &thenBlock = ifOp.getThenRegion().front(); + builder.setInsertionPoint(thenBlock.getTerminator()); + body(); + builder.restoreInsertionPoint(oldIP); + } else { + auto oldIP = builder.saveInsertionPoint(); + builder.setInsertionPointToEnd(triggered.getBodyBlock()); + body(); + builder.restoreInsertionPoint(oldIP); + simTriggeredLastConditionBlocks[triggered] = {}; + } + + // Move the earlier triggered block(s) down to where the last would have been + // inserted. This ensures that any values used by the triggered blocks are + // defined ahead of the uses. + triggered->moveBefore(builder.getInsertionBlock(), + builder.getInsertionPoint()); +} + LogicalResult FIRRTLLowering::emitGuards(Location loc, ArrayRef guards, std::function emit) { @@ -5456,7 +5508,7 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) { return failure(); auto stderrOp = sim::StderrStreamOp::create(builder); - sim::TriggeredOp::create(builder, clock, cond, [&] { + addToSimTriggeredBlock(clock, cond, [&] { sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp); }); return success(); @@ -5479,7 +5531,7 @@ LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) { if (failed(formatString)) return failure(); - sim::TriggeredOp::create(builder, clock, cond, [&] { + addToSimTriggeredBlock(clock, cond, [&] { auto fileOp = sim::GetFileOp::create(builder, *fileFormatString); sim::PrintFormattedProcOp::create(builder, *formatString, fileOp); }); @@ -5507,7 +5559,7 @@ LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) { if (!op.getOutputFileAttr()) { auto stderrOp = sim::StderrStreamOp::create(builder); - sim::TriggeredOp::create(builder, clock, cond, [&] { + addToSimTriggeredBlock(clock, cond, [&] { sim::FlushOp::create(builder, stderrOp); }); } else { @@ -5515,7 +5567,7 @@ LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) { op.getOutputFileAttr(), op.getOutputFileSubstitutions()); if (failed(fileFormatString)) return failure(); - sim::TriggeredOp::create(builder, clock, cond, [&] { + addToSimTriggeredBlock(clock, cond, [&] { auto fileOp = sim::GetFileOp::create(builder, *fileFormatString); sim::FlushOp::create(builder, fileOp); }); diff --git a/lib/Dialect/Sim/CMakeLists.txt b/lib/Dialect/Sim/CMakeLists.txt index 08ec677c82ee..84d239e7fcf3 100644 --- a/lib/Dialect/Sim/CMakeLists.txt +++ b/lib/Dialect/Sim/CMakeLists.txt @@ -34,6 +34,7 @@ add_circt_dialect_library(CIRCTSim MLIRLLVMDialect MLIRIR MLIRPass + MLIRSCFDialect MLIRTransforms ) diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index d1e17bbfccc3..2acf7d8675e6 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -18,6 +18,7 @@ #include "circt/Support/ProceduralRegionTrait.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMTypes.h" +#include "mlir/Dialect/SCF/IR/SCF.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Interfaces/FunctionImplementation.h" #include "llvm/ADT/MapVector.h" @@ -805,6 +806,30 @@ void TriggeredOp::build(OpBuilder &builder, OperationState &odsState, bodyCtor(); } +LogicalResult TriggeredOp::canonicalize(TriggeredOp op, + PatternRewriter &rewriter) { + if (op.getCondition()) + return failure(); + + Block *body = op.getBodyBlock(); + if (!llvm::hasSingleElement(body->getOperations())) + return failure(); + + auto ifOp = dyn_cast(body->front()); + if (!ifOp || ifOp->getNumResults() != 0 || ifOp.elseBlock()) + return failure(); + + op.getConditionMutable().append(ifOp.getCondition()); + + Block *thenBlock = ifOp.thenBlock(); + auto insertionPoint = ifOp->getIterator(); + body->getOperations().splice(insertionPoint, thenBlock->getOperations(), + thenBlock->begin(), + Block::iterator(thenBlock->getTerminator())); + rewriter.eraseOp(ifOp); + return success(); +} + //===----------------------------------------------------------------------===// // TableGen generated logic. //===----------------------------------------------------------------------===// diff --git a/lib/Firtool/CMakeLists.txt b/lib/Firtool/CMakeLists.txt index 0d568284952e..25687341b5bb 100644 --- a/lib/Firtool/CMakeLists.txt +++ b/lib/Firtool/CMakeLists.txt @@ -11,7 +11,6 @@ add_circt_library(CIRCTFirtool CIRCTOMTransforms CIRCTSeqToSV CIRCTSimToSV - CIRCTSimTransforms CIRCTSeqTransforms CIRCTSVTransforms CIRCTTransforms diff --git a/lib/Firtool/Firtool.cpp b/lib/Firtool/Firtool.cpp index 569c80a9bd90..8b4d814cc5a3 100644 --- a/lib/Firtool/Firtool.cpp +++ b/lib/Firtool/Firtool.cpp @@ -14,7 +14,6 @@ #include "circt/Dialect/OM/OMPasses.h" #include "circt/Dialect/SV/SVPasses.h" #include "circt/Dialect/Seq/SeqPasses.h" -#include "circt/Dialect/Sim/SimPasses.h" #include "circt/Dialect/Verif/VerifPasses.h" #include "circt/Support/Passes.h" #include "circt/Transforms/Passes.h" @@ -308,9 +307,6 @@ LogicalResult firtool::populateLowFIRRTLToHW(mlir::PassManager &pm, opt.getVerificationFlavor(), opt.shouldLowerToCore())); - if (opt.shouldLowerToCore()) - pm.addNestedPass(sim::createSquashSimTriggered()); - if (!opt.shouldDisableOptimization()) { auto &modulePM = pm.nest(); modulePM.addPass(mlir::createCSEPass()); diff --git a/test/Conversion/FIRRTLToHW/lower-to-core.mlir b/test/Conversion/FIRRTLToHW/lower-to-core.mlir index 5085fa8c553f..551eb3bbb594 100644 --- a/test/Conversion/FIRRTLToHW/lower-to-core.mlir +++ b/test/Conversion/FIRRTLToHW/lower-to-core.mlir @@ -21,22 +21,15 @@ firrtl.circuit "LowerToCore" { // CHECK: [[LIT1:%.+]] = sim.fmt.literal " @ " // CHECK: [[HIER:%.+]] = sim.fmt.hier_path // CHECK: [[NL:%.+]] = sim.fmt.literal "\0A" - // CHECK: [[MSG:%.+]] = sim.fmt.concat ([[LIT0]], [[FMTVAL]], [[LIT1]], [[HIER]], [[NL]]) - // CHECK: [[STDERR:%.+]] = sim.stderr_stream - // CHECK: sim.triggered %clock if %enable { - // CHECK-NEXT: sim.proc.print [[MSG]] to [[STDERR]] - // CHECK-NEXT: } + // CHECK: [[MSG0:%.+]] = sim.fmt.concat ([[LIT0]], [[FMTVAL]], [[LIT1]], [[HIER]], [[NL]]) + // CHECK: [[STDERR0:%.+]] = sim.stderr_stream // CHECK-NOT: sv.assert // CHECK-NOT: sv.fwrite firrtl.printf %clock, %enable, "value=%d @ {{}}\0A"(%x, %hier) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.fstring // CHECK: [[FMTFILE1:%.+]] = sim.fmt.literal "out.txt" - // CHECK: [[MSG:%.+]] = sim.fmt.concat - // CHECK: sim.triggered %clock if %enable { - // CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]] - // CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE1]] - // CHECK-NEXT: } + // CHECK: [[MSG1:%.+]] = sim.fmt.concat firrtl.fprintf %clock, %enable, "out.txt"(), "value=%d @ {{}}\0A"(%x, %hier) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.fstring @@ -44,11 +37,7 @@ firrtl.circuit "LowerToCore" { // CHECK: [[FILEVAL:%.+]] = sim.fmt.dec %x signed : i4 // CHECK: [[LIT3:%.+]] = sim.fmt.literal ".txt" // CHECK: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LIT2]], [[FILEVAL]], [[LIT3]]) - // CHECK: [[MSG:%.+]] = sim.fmt.concat - // CHECK: sim.triggered %clock if %enable { - // CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]] - // CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE2]] - // CHECK-NEXT: } + // CHECK: [[MSG2:%.+]] = sim.fmt.concat firrtl.fprintf %clock, %enable, "out%d.txt"(%x), "value=%d @ {{}}\0A"(%x, %hier) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.sint<4>, !firrtl.fstring @@ -56,16 +45,21 @@ firrtl.circuit "LowerToCore" { // CHECK: [[FILEVAL:%.+]] = sim.fmt.dec %x signed : i4 // CHECK: [[LIT5:%.+]] = sim.fmt.literal ".txt // CHECK: [[FMTFILE3:%.+]] = sim.fmt.concat ([[LIT4]], [[FILEVAL]], [[LIT5]]) - // CHECK: sim.triggered %clock if %enable { - // CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE3]] - // CHECK-NEXT: sim.flush [[FILE3]] - // CHECK-NEXT: } firrtl.fflush %clock, %enable, "out%d.txt"(%x) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4> - // CHECK: [[STDERR:%.+]] = sim.stderr_stream - // CHECK: sim.triggered %clock if %enable { - // CHECK-NEXT: sim.flush [[STDERR]] + // CHECK: [[STDERR1:%.+]] = sim.stderr_stream + // CHECK: sim.triggered %clock { + // CHECK-NEXT: scf.if %enable { + // CHECK-NEXT: sim.proc.print [[MSG0]] to [[STDERR0]] + // CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]] + // CHECK-NEXT: sim.proc.print [[MSG1]] to [[FILE1]] + // CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]] + // CHECK-NEXT: sim.proc.print [[MSG2]] to [[FILE2]] + // CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE3]] + // CHECK-NEXT: sim.flush [[FILE3]] + // CHECK-NEXT: sim.flush [[STDERR1]] + // CHECK-NEXT: } // CHECK-NEXT: } firrtl.fflush %clock, %enable : !firrtl.clock, !firrtl.uint<1> @@ -81,4 +75,35 @@ firrtl.circuit "LowerToCore" { %sink = firrtl.instance sink @AnalogSink(in a: !firrtl.analog<8>) firrtl.attach %a, %sink : !firrtl.analog<8>, !firrtl.analog<8> } + + // CHECK-LABEL: hw.module @InterleavedConditions( + firrtl.module @InterleavedConditions( + in %clock: !firrtl.clock, + in %enable_a: !firrtl.uint<1>, + in %enable_b: !firrtl.uint<1>) { + // CHECK: [[A0:%.+]] = sim.fmt.literal "a0\0A" + // CHECK: [[STDERR0:%.+]] = sim.stderr_stream + // CHECK: [[B0:%.+]] = sim.fmt.literal "b0\0A" + // CHECK: [[STDERR1:%.+]] = sim.stderr_stream + // CHECK: [[A1:%.+]] = sim.fmt.literal "a1\0A" + // CHECK: [[STDERR2:%.+]] = sim.stderr_stream + // CHECK: sim.triggered %clock { + // CHECK-NEXT: scf.if %enable_a { + // CHECK-NEXT: sim.proc.print [[A0]] to [[STDERR0]] + // CHECK-NEXT: } + // CHECK-NEXT: scf.if %enable_b { + // CHECK-NEXT: sim.proc.print [[B0]] to [[STDERR1]] + // CHECK-NEXT: } + // CHECK-NEXT: scf.if %enable_a { + // CHECK-NEXT: sim.proc.print [[A1]] to [[STDERR2]] + // CHECK-NEXT: } + // CHECK-NEXT: } + firrtl.printf %clock, %enable_a, "a0\0A"() + : !firrtl.clock, !firrtl.uint<1> + firrtl.printf %clock, %enable_b, "b0\0A"() + : !firrtl.clock, !firrtl.uint<1> + firrtl.printf %clock, %enable_a, "a1\0A"() + : !firrtl.clock, !firrtl.uint<1> + firrtl.skip + } } diff --git a/test/Dialect/Sim/canonicalization.mlir b/test/Dialect/Sim/canonicalization.mlir index 886246e7ea2d..06566315d223 100644 --- a/test/Dialect/Sim/canonicalization.mlir +++ b/test/Dialect/Sim/canonicalization.mlir @@ -73,3 +73,55 @@ func.func @string_get_special_chars() -> i8 { // CHECK: return [[TMP]] return %char : i8 } + +//===----------------------------------------------------------------------===// +// TriggeredOp +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: hw.module @triggered_hoist_single_if( +hw.module @triggered_hoist_single_if(in %clock: !seq.clock, in %enable: i1) { + %msg = sim.fmt.literal "hello" + // CHECK: [[MSG:%.+]] = sim.fmt.literal "hello" + // CHECK: sim.triggered %clock if %enable { + // CHECK-NEXT: sim.proc.print [[MSG]] + // CHECK-NEXT: } + sim.triggered %clock { + scf.if %enable { + sim.proc.print %msg + } + } + hw.output +} + +// CHECK-LABEL: hw.module @triggered_keep_interleaved_ifs( +hw.module @triggered_keep_interleaved_ifs(in %clock: !seq.clock, in %a: i1, in %b: i1) { + %a0 = sim.fmt.literal "a0" + %b0 = sim.fmt.literal "b0" + %a1 = sim.fmt.literal "a1" + // CHECK: [[A0:%.+]] = sim.fmt.literal "a0" + // CHECK: [[B0:%.+]] = sim.fmt.literal "b0" + // CHECK: [[A1:%.+]] = sim.fmt.literal "a1" + // CHECK: sim.triggered %clock { + // CHECK-NEXT: scf.if %a { + // CHECK-NEXT: sim.proc.print [[A0]] + // CHECK-NEXT: } + // CHECK-NEXT: scf.if %b { + // CHECK-NEXT: sim.proc.print [[B0]] + // CHECK-NEXT: } + // CHECK-NEXT: scf.if %a { + // CHECK-NEXT: sim.proc.print [[A1]] + // CHECK-NEXT: } + // CHECK-NEXT: } + sim.triggered %clock { + scf.if %a { + sim.proc.print %a0 + } + scf.if %b { + sim.proc.print %b0 + } + scf.if %a { + sim.proc.print %a1 + } + } + hw.output +} From fc379a2b35b95fdae3e7f331092482ae56ba23b7 Mon Sep 17 00:00:00 2001 From: woshiren Date: Thu, 28 May 2026 14:38:16 +0800 Subject: [PATCH 5/6] use InsertionGuard --- lib/Conversion/FIRRTLToHW/LowerToHW.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index e94d86f7ae11..be3c285713c8 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -3293,26 +3293,23 @@ void FIRRTLLowering::addToSimTriggeredBlock( ? lastConditionBlock.second : mlir::scf::IfOp(); if (!ifOp) { - auto oldIP = builder.saveInsertionPoint(); + OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointToEnd(triggered.getBodyBlock()); ifOp = mlir::scf::IfOp::create(builder, builder.getLoc(), TypeRange{}, condition, true, false); builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); mlir::scf::YieldOp::create(builder, builder.getLoc()); - builder.restoreInsertionPoint(oldIP); lastConditionBlock = {condition, ifOp}; } - auto oldIP = builder.saveInsertionPoint(); + OpBuilder::InsertionGuard guard(builder); auto &thenBlock = ifOp.getThenRegion().front(); builder.setInsertionPoint(thenBlock.getTerminator()); body(); - builder.restoreInsertionPoint(oldIP); } else { - auto oldIP = builder.saveInsertionPoint(); + OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointToEnd(triggered.getBodyBlock()); body(); - builder.restoreInsertionPoint(oldIP); simTriggeredLastConditionBlocks[triggered] = {}; } From fd6498131b183677d78da6d8af702c1b5dbaeb01 Mon Sep 17 00:00:00 2001 From: woshiren Date: Thu, 28 May 2026 14:54:38 +0800 Subject: [PATCH 6/6] fix format --- lib/Conversion/FIRRTLToHW/LowerToHW.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp index be3c285713c8..8321356e0b70 100644 --- a/lib/Conversion/FIRRTLToHW/LowerToHW.cpp +++ b/lib/Conversion/FIRRTLToHW/LowerToHW.cpp @@ -5556,9 +5556,8 @@ LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) { if (!op.getOutputFileAttr()) { auto stderrOp = sim::StderrStreamOp::create(builder); - addToSimTriggeredBlock(clock, cond, [&] { - sim::FlushOp::create(builder, stderrOp); - }); + addToSimTriggeredBlock(clock, cond, + [&] { sim::FlushOp::create(builder, stderrOp); }); } else { auto fileFormatString = lowerSimFormatString( op.getOutputFileAttr(), op.getOutputFileSubstitutions());