From 10541f0f88654b126a59a16631c4b62ab00df43c Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Mon, 7 Oct 2024 23:40:49 +0200 Subject: [PATCH 1/4] Implement trigger ops. --- include/circt/Dialect/Sim/SimOps.h | 2 + include/circt/Dialect/Sim/SimOps.td | 129 ++++++++++++++++++- include/circt/Dialect/Sim/SimTypes.h | 2 + include/circt/Dialect/Sim/SimTypes.td | 15 +++ lib/Dialect/Sim/SimDialect.cpp | 7 + lib/Dialect/Sim/SimOps.cpp | 177 ++++++++++++++++++++++++++ 6 files changed, 326 insertions(+), 6 deletions(-) diff --git a/include/circt/Dialect/Sim/SimOps.h b/include/circt/Dialect/Sim/SimOps.h index 2e41f6eb71d6..7d27d76caad7 100644 --- a/include/circt/Dialect/Sim/SimOps.h +++ b/include/circt/Dialect/Sim/SimOps.h @@ -18,6 +18,7 @@ #include "mlir/IR/SymbolTable.h" #include "circt/Dialect/HW/HWOpInterfaces.h" +#include "circt/Dialect/HW/HWOps.h" #include "circt/Dialect/Seq/SeqDialect.h" #include "circt/Dialect/Seq/SeqTypes.h" #include "circt/Dialect/Sim/SimDialect.h" @@ -25,6 +26,7 @@ #include "circt/Support/BuilderUtils.h" #include "mlir/Interfaces/CallInterfaces.h" #include "mlir/Interfaces/FunctionInterfaces.h" +#include "mlir/Interfaces/InferTypeOpInterface.h" #define GET_OP_CLASSES #include "circt/Dialect/Sim/Sim.h.inc" diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index c3bdb370f23b..3d0a541cc258 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -15,9 +15,13 @@ include "mlir/Interfaces/SideEffectInterfaces.td" include "mlir/Interfaces/FunctionInterfaces.td" +include "mlir/Interfaces/InferTypeOpInterface.td" +include "mlir/IR/BuiltinAttributes.td" +include "mlir/IR/RegionKindInterface.td" include "circt/Dialect/Sim/SimDialect.td" include "circt/Dialect/Sim/SimTypes.td" include "circt/Dialect/Seq/SeqTypes.td" +include "circt/Dialect/HW/HWEnums.td" include "circt/Dialect/HW/HWOpInterfaces.td" include "circt/Dialect/HW/HWTypes.td" @@ -319,12 +323,6 @@ def FormatStringConcatOp : SimOp<"fmt.concat", [Pure]> { /// and flattening continues without recursing into the cycle. LogicalResult getFlattenedInputs(llvm::SmallVectorImpl &flatOperands); }]; - - let builders = [ - OpBuilder<(ins "mlir::ValueRange":$inputs), [{ - return build($_builder, $_state, circt::sim::FormatStringType::get($_builder.getContext()), inputs); - }]> - ]; } def PrintFormattedOp : SimOp<"print"> { @@ -365,4 +363,123 @@ def PrintFormattedProcOp : SimOp<"proc.print"> { let assemblyFormat = "$input attr-dict"; } +// --- Trigger Ops --- + +def OnEdgeOp : SimOp<"on_edge", [ + Pure, + DeclareOpInterfaceMethods +]> { + let summary = "Invoke a trigger on a clock edge event."; + let arguments = (ins ClockType:$clock, EventControlAttr:$event); + let results = (outs EdgeTriggerType:$result); + let assemblyFormat = "$event $clock attr-dict"; +} + +def OnInitOp : SimOp<"on_init", [Pure]> { + let summary = "Invoke a trigger at the start of simulation."; + let results = (outs InitTriggerType:$result); + let assemblyFormat = "attr-dict"; +} + +def TriggerSequenceOp : SimOp<"trigger_sequence", [ + Pure, + DeclareOpInterfaceMethods +]> { + let summary = "Derive a sequence of triggers from a parent trigger."; + let description = [{ + Creates a series of sequenced triggers. + The first resulting trigger is invoked when the parent trigger is invoked. + The subsequent triggers are invoked after all preceeding triggers + have completed. The operation completes after all result triggers have + completed. + }]; + let arguments = (ins AnyTriggerType:$parent, UI32Attr:$length); + let results = (outs Variadic:$triggers); + let assemblyFormat = + "$parent `,` $length attr-dict `:` qualified(type($parent))"; + let hasFolder = true; + let hasCanonicalizeMethod = true; + let hasVerifier = true; +} + +def YieldSeqOp : SimOp<"yield_seq",[ + Terminator, HasParent<"circt::sim::TriggeredOp"> +]> { + let summary = [{Yield results form a triggerd region with 'seq' + (i.e. register-like) semantics."}]; + let description = [{ + Terminates a triggered region and produces the given list of values. + The results only become visible after all triggers and register updates + occuring on the same event as the parent operation have completed. + E.g., the following snippet produces a counter that increments on every + rising edge of '%clk': + ``` + %posedge = sim.on_edge posedge %clk + %counter = sim.triggered (%counter) on %posedge tieoff [0 : i8] { + ^bb0(%arg0: i8): + %cst1 = hw.constant 1 : i8 + %inc = comb.add bin %arg0, %cst1 : i8 + sim.yield_seq %inc : i8 + } : (i8) -> (i8) + ``` + }]; + let arguments = (ins Variadic:$inputs); + let assemblyFormat = "($inputs^ `:` qualified(type($inputs)))? attr-dict"; + let builders = [OpBuilder<(ins), "build($_builder, $_state, {});">]; +} + +def TriggeredOp : SimOp<"triggered", [ + AttrSizedOperandSegments, + IsolatedFromAbove, + RegionKindInterface, + RecursiveMemoryEffects, + RecursivelySpeculatable, + SingleBlockImplicitTerminator<"sim::YieldSeqOp">, + HasParent<"circt::hw::HWModuleOp"> +]> { + let summary = [{ + Defines a procedure invoked on a given trigger and condition. + }]; + let description = [{ + Creates a procedural region which is invoked on a given trigger. + The optional condition allows the execution of the body to be skipped, if + the condition evaluates to `false` at the time when the triggers's + root event occurs. + The body region must complete without 'consuming' simulation time. It + may not run indefinitely or wait for any simulation event. It is allowed to + have side-effects and produce results. + For every result a 'tieoff' constant must be provided. It specifies the + respective result's value before the body is first invoked. + For non-simulation flows the results are replaced by their tie-off values. + }]; + let arguments = (ins AnyTriggerType:$trigger, + Optional:$condition, + Variadic:$inputs, + OptionalAttr>:$tieoffs + ); + let results = (outs Variadic); + let regions = (region SizedRegion<1>:$body); + + let assemblyFormat = [{ + ` ` `(` $inputs `)` + `on` ` ` `(` $trigger `:` qualified(type($trigger)) `)` + (`if` $condition^)? + (`tieoff` $tieoffs^)? attr-dict-with-keyword + $body + `:` functional-type($inputs, results) + }]; + + let extraClassDeclaration = [{ + // Implement RegionKindInterface. + static RegionKind getRegionKind(unsigned index) { + return RegionKind::SSACFG; + } + }]; + + let hasVerifier = true; + let hasFolder = true; + let hasCanonicalizeMethod = true; +} + #endif // CIRCT_DIALECT_SIM_SIMOPS_TD diff --git a/include/circt/Dialect/Sim/SimTypes.h b/include/circt/Dialect/Sim/SimTypes.h index 669eaaf14f5d..12f10213e237 100644 --- a/include/circt/Dialect/Sim/SimTypes.h +++ b/include/circt/Dialect/Sim/SimTypes.h @@ -12,6 +12,8 @@ #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/Types.h" +#include "circt/Dialect/HW/HWEnums.h" + #define GET_TYPEDEF_CLASSES #include "circt/Dialect/Sim/SimTypes.h.inc" diff --git a/include/circt/Dialect/Sim/SimTypes.td b/include/circt/Dialect/Sim/SimTypes.td index 90c901b5236b..cb80cd871aca 100644 --- a/include/circt/Dialect/Sim/SimTypes.td +++ b/include/circt/Dialect/Sim/SimTypes.td @@ -26,4 +26,19 @@ def FormatStringType : SimTypeDef<"FormatString"> { }]; } + +def EdgeTriggerType : SimTypeDef<"EdgeTrigger"> { + let summary = "Trigger derived from an edge event."; + let parameters = (ins "::circt::hw::EventControl":$edgeEvent); + let mnemonic = "trigger.edge"; + let assemblyFormat = "`<` $edgeEvent `>`"; +} + +def InitTriggerType : SimTypeDef<"InitTrigger"> { + let summary = "Trigger derived from the simulation start event."; + let mnemonic = "trigger.init"; +} + +def AnyTriggerType : AnyTypeOf<[EdgeTriggerType, InitTriggerType]>; + #endif // CIRCT_DIALECT_SIM_SIMTYPES_TD diff --git a/lib/Dialect/Sim/SimDialect.cpp b/lib/Dialect/Sim/SimDialect.cpp index a05f160036c7..031656ba48c4 100644 --- a/lib/Dialect/Sim/SimDialect.cpp +++ b/lib/Dialect/Sim/SimDialect.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/Sim/SimDialect.h" +#include "circt/Dialect/HW/HWDialect.h" #include "circt/Dialect/HW/HWOps.h" #include "circt/Dialect/Sim/SimOps.h" #include "mlir/IR/Builders.h" @@ -40,6 +41,12 @@ Operation *SimDialect::materializeConstant(::mlir::OpBuilder &builder, ::mlir::Type type, ::mlir::Location loc) { + // Delegate non 'sim' types to the HW dialect materializer. + if (!isa(type.getDialect())) + return builder.getContext() + ->getLoadedDialect() + ->materializeConstant(builder, value, type, loc); + if (auto fmtStrType = llvm::dyn_cast(type)) return builder.create(loc, fmtStrType, llvm::cast(value)); diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index 7ae834a12931..c6aa175c8ea0 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -397,6 +397,183 @@ LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op, return failure(); } +// --- OnEdgeOp --- + +LogicalResult OnEdgeOp::inferReturnTypes( + MLIRContext *context, std::optional location, ValueRange operands, + DictionaryAttr attributes, OpaqueProperties properties, RegionRange regions, + SmallVectorImpl &inferredReturnTypes) { + auto eventAttr = properties.as()->getEvent(); + inferredReturnTypes.emplace_back( + EdgeTriggerType::get(context, eventAttr.getValue())); + return success(); +} + +// --- TriggeredOp --- + +LogicalResult TriggeredOp::verify() { + if (getNumResults() > 0 && !getTieoffs()) + return emitError("Tie-off constants must be provided for all results."); + auto numTieoffs = !getTieoffs() ? 0 : getTieoffsAttr().size(); + if (numTieoffs != getNumResults()) + return emitError( + "Number of tie-off constants does not match number of results."); + if (numTieoffs == 0) + return success(); + unsigned idx = 0; + bool failed = false; + for (const auto &[res, tieoff] : + llvm::zip(getResultTypes(), getTieoffsAttr())) { + if (res != cast(tieoff).getType()) { + emitError("Tie-off type does not match for result at index " + + Twine(idx)); + failed = true; + } + ++idx; + } + return success(!failed); +} + +LogicalResult TriggeredOp::fold(FoldAdaptor adaptor, + SmallVectorImpl &results) { + if (auto constCond = dyn_cast_or_null(adaptor.getCondition())) { + if (constCond.getValue().isAllOnes()) { + // Strip constant true condition. + getConditionMutable().clear(); + return success(); + } + // Never enabled, fold to tie-offs. + if (getNumResults() > 0) { + results.append(adaptor.getTieoffsAttr().begin(), + adaptor.getTieoffsAttr().end()); + return success(); + } + } + return failure(); +} + +LogicalResult TriggeredOp::canonicalize(TriggeredOp op, + PatternRewriter &rewriter) { + if (op.getNumResults() > 0) + return failure(); + + bool isDeadOrEmpty = false; + + auto *bodyBlock = &op.getBodyRegion().front(); + isDeadOrEmpty = bodyBlock->without_terminator().empty(); + + if (!isDeadOrEmpty && !!op.getCondition()) + if (auto cstCond = op.getCondition().getDefiningOp()) + isDeadOrEmpty = cstCond.getValue().isZero(); + + if (!isDeadOrEmpty) + return failure(); + + rewriter.eraseOp(op); + return success(); +} + +// --- TriggerSequenceOp --- + +LogicalResult TriggerSequenceOp::inferReturnTypes( + MLIRContext *context, std::optional location, ValueRange operands, + DictionaryAttr attributes, OpaqueProperties properties, RegionRange regions, + SmallVectorImpl &inferredReturnTypes) { + // Create N results matching the type of the parent trigger, where N is the + // specified length of the sequence. + auto lengthAttr = + properties.as()->getLength(); + uint32_t len = lengthAttr.getValue().getZExtValue(); + Type trigType = operands.front().getType(); + inferredReturnTypes.resize_for_overwrite(len); + for (size_t i = 0; i < len; ++i) + inferredReturnTypes[i] = trigType; + return success(); +} + +LogicalResult TriggerSequenceOp::verify() { + if (getLength() != getNumResults()) + return emitOpError("specified length does not match number of results."); + return success(); +} + +LogicalResult TriggerSequenceOp::fold(FoldAdaptor adaptor, + SmallVectorImpl &results) { + // Fold trivial sequences to the parent trigger. + if (getLength() == 1 && getResult(0) != getParent()) { + results.push_back(getParent()); + return success(); + } + return failure(); +} + +LogicalResult TriggerSequenceOp::canonicalize(TriggerSequenceOp op, + PatternRewriter &rewriter) { + // Check if there are unused results (which can be removed) or + // non-concurrent sub-sequences (which can be inlined). + auto getSingleSequenceUser = [](Value trigger) -> TriggerSequenceOp { + if (!trigger.hasOneUse()) + return {}; + return dyn_cast(trigger.use_begin()->getOwner()); + }; + + bool canBeChanged = false; + for (auto res : op.getResults()) { + auto singleSeqUser = getSingleSequenceUser(res); + if (singleSeqUser == op) { + op.emitWarning("Recursive trigger sequence."); + return failure(); + } + if (res.use_empty() || !!singleSeqUser) { + canBeChanged = true; + break; + } + } + + if (!canBeChanged) + return failure(); + + // Build a list of new result values. + SmallVector resultValues; + SmallVector locs; + SmallVector childSeqs; + locs.emplace_back(op.getLoc()); + resultValues.reserve(op.getNumResults()); + for (auto res : op.getResults()) { + if (res.use_empty()) + continue; + + if (auto seqUser = getSingleSequenceUser(res)) { + resultValues.append(seqUser.getResults().begin(), + seqUser.getResults().end()); + locs.emplace_back(seqUser.getLoc()); + childSeqs.emplace_back(seqUser); + } else { + resultValues.emplace_back(res); + } + } + + // Remove empty sequences. + if (resultValues.empty()) { + rewriter.eraseOp(op); + return success(); + } + + // Replace the current operation with a new sequence. + rewriter.setInsertionPoint(op); + auto fusedLoc = FusedLoc::get(rewriter.getContext(), locs); + auto newOp = rewriter.create(fusedLoc, op.getParent(), + resultValues.size()); + for (auto [rval, newRes] : llvm::zip(resultValues, newOp.getResults())) + rewriter.replaceAllUsesWith(rval, newRes); + // Remove sequences that have been inlined + for (auto child : childSeqs) + rewriter.eraseOp(child); + + rewriter.eraseOp(op); + return success(); +} + //===----------------------------------------------------------------------===// // TableGen generated logic. //===----------------------------------------------------------------------===// From ae5de1114bae504546fb9a9568d09d0bd022b03b Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 00:14:07 +0200 Subject: [PATCH 2/4] Add trigger tests --- include/circt/Dialect/Sim/SimOps.td | 2 +- test/Dialect/Sim/sim-errors.mlir | 45 +++++++++ test/Dialect/Sim/triggers.mlir | 139 ++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 test/Dialect/Sim/triggers.mlir diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 3d0a541cc258..2971d1079136 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -443,7 +443,7 @@ def TriggeredOp : SimOp<"triggered", [ let description = [{ Creates a procedural region which is invoked on a given trigger. The optional condition allows the execution of the body to be skipped, if - the condition evaluates to `false` at the time when the triggers's + the condition evaluates to `false` at the time when the trigger's root event occurs. The body region must complete without 'consuming' simulation time. It may not run indefinitely or wait for any simulation event. It is allowed to diff --git a/test/Dialect/Sim/sim-errors.mlir b/test/Dialect/Sim/sim-errors.mlir index a70360b1cdfe..bcd4a99ca621 100644 --- a/test/Dialect/Sim/sim-errors.mlir +++ b/test/Dialect/Sim/sim-errors.mlir @@ -51,3 +51,48 @@ hw.module @dpi_call(in %clock : !seq.clock, in %in: i1) { // expected-error @below {{callee must be 'sim.dpi.func' or 'func.func' but got 'hw.module.extern'}} %0, %1 = sim.func.dpi.call @non_func(%in) : (i1) -> (i1, i1) } + +// ----- + +hw.module @not_enough_triggers(in %in : !sim.trigger.edge) { + // expected-error @below {{operation defines 1 results but was provided 2 to bind}} + %res:2 = sim.trigger_sequence %in, 1 : !sim.trigger.edge +} + +// ----- + +hw.module @recursive_trigger() { + // expected-warning @below {{Recursive trigger sequence}} + %res = sim.trigger_sequence %res, 1 : !sim.trigger.edge +} + +// ----- + +hw.module @missing_tieoffs(in %trig : !sim.trigger.edge) { + // expected-error @below {{Tie-off constants must be provided for all results}} + %res = sim.triggered () on (%trig : !sim.trigger.edge) { + %cst = hw.constant 0 : i2 + sim.yield_seq %cst : i2 + } : () -> i2 +} + +// ----- + +hw.module @wrong_tieoff(in %trig : !sim.trigger.edge) { + // expected-error @below {{Tie-off type does not match for result at index 0}} + %res = sim.triggered () on (%trig : !sim.trigger.edge) tieoff [0 : i1] { + %cst = hw.constant 0 : i2 + sim.yield_seq %cst : i2 + } : () -> i2 +} + +// ----- + +hw.module @too_many_tieoffs(in %trig : !sim.trigger.edge) { + // expected-error @below {{Number of tie-off constants does not match number of results}} + %res = sim.triggered () on (%trig : !sim.trigger.edge) tieoff [0 : i2, 0 : i2] { + %cst = hw.constant 0 : i2 + sim.yield_seq %cst : i2 + } : () -> i2 +} + diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir new file mode 100644 index 000000000000..1e63168637e5 --- /dev/null +++ b/test/Dialect/Sim/triggers.mlir @@ -0,0 +1,139 @@ +// RUN: circt-opt %s --canonicalize | FileCheck %s + +// CHECK-LABEL: hw.module @root_triggers +// CHECK-DAG: [[PE:%.*]] = sim.on_edge posedge %clock +// CHECK-DAG: [[NE:%.*]] = sim.on_edge negedge %clock +// CHECK-DAG: [[BE:%.*]] = sim.on_edge edge %clock +// CHECK-DAG: [[IT:%.*]] = sim.on_init +// CHECK: hw.output [[PE]], [[NE]], [[BE]], [[IT]] : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.init +hw.module @root_triggers(in %clock : !seq.clock, out o0 : !sim.trigger.edge, out o1 : !sim.trigger.edge, out o2 : !sim.trigger.edge, out o3 : !sim.trigger.init) { + %0 = sim.on_edge posedge %clock + %1 = sim.on_edge negedge %clock + %2 = sim.on_edge edge %clock + %3 = sim.on_init + hw.output %0, %1, %2, %3 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.init +} + +// CHECK-LABEL: hw.module @fold_triggered +hw.module @fold_triggered(in %a : i8, in %en : i1, in %trig : !sim.trigger.edge, out o0 : i8, out o1 : i9, out o2 : i8) { + // CHECK: %[[CST12:.*]] = hw.constant 12 : i8 + %true = hw.constant true + %false = hw.constant false + + // CHECK: sim.triggered () on (%trig : !sim.trigger.edge) { + sim.triggered () on (%trig : !sim.trigger.edge) if %true { + %0 = sim.fmt.lit "Remove constant true condition" + sim.proc.print %0 + } : () -> () + + // CHECK: sim.triggered () on (%trig : !sim.trigger.edge) if %en { + // CHECK-NEXT: "Don't touch live process" + sim.triggered () on (%trig : !sim.trigger.edge) if %en { + %0 = sim.fmt.lit "Don't touch live process" + sim.proc.print %0 + } : () -> () + + // CHECK: %[[RES:.*]]:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) if %en tieoff [12 : i8, 33 : i9] { + // CHECK: "Don't touch live process with results" + // CHECK: arith.extui %{{.+}} : i8 to i9 + // CHECK: (i8) -> (i8, i9) + + %res:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) if %en tieoff [12 : i8, 33 : i9] { + ^bb0(%arg: i8): + %0 = sim.fmt.lit "Don't touch live process with results" + sim.proc.print %0 + %ext = arith.extui %arg : i8 to i9 + sim.yield_seq %arg, %ext : i8, i9 + } : (i8) -> (i8, i9) + + // CHECK-NOT: sim.triggered + + %fold = sim.triggered () on (%trig : !sim.trigger.edge) if %false tieoff [12 : i8] { + %cst0_i8 = hw.constant 0 : i8 + %0 = sim.fmt.lit "Fold dead process with result" + sim.proc.print %0 + sim.yield_seq %cst0_i8 : i8 + } : () -> (i8) + + sim.triggered () on (%trig : !sim.trigger.edge) if %false { + %0 = sim.fmt.lit "Remove dead process" + sim.proc.print %0 + } : () -> () + + sim.triggered () on (%trig : !sim.trigger.edge) if %en { + } : () -> () + + // CHECK: hw.output %[[RES]]#0, %[[RES]]#1, %[[CST12]] : i8, i9, i8 + hw.output %res#0, %res#1, %fold : i8, i9, i8 +} + +// CHECK-LABEL: hw.module @empty_sequence +// CHECK-NOT: sim.trigger_sequence +hw.module @empty_sequence(in %trig : !sim.trigger.init) { + sim.trigger_sequence %trig, 0 : !sim.trigger.init +} + +// CHECK-LABEL: hw.module @trivial_sequence +// CHECK-NOT: sim.trigger_sequence +// CHECK: hw.output %trig +hw.module @trivial_sequence(in %trig : !sim.trigger.init, out o: !sim.trigger.init) { + %out = sim.trigger_sequence %trig, 1 : !sim.trigger.init + hw.output %out : !sim.trigger.init +} + +// CHECK-LABEL: hw.module @dead_sequence +// CHECK-NOT: sim.trigger_sequence +hw.module @dead_sequence(in %trig : !sim.trigger.init) { + %dead:128 = sim.trigger_sequence %trig, 128 : !sim.trigger.init +} + +// CHECK-LABEL: hw.module @mostly_dead_sequence +// CHECK: %[[RES:.*]]:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.init +// CHECK-NEXT: hw.output %[[RES]]#1, %[[RES]]#0, %[[RES]]#1 +hw.module @mostly_dead_sequence(in %trig : !sim.trigger.init, out o0: !sim.trigger.init, out o1: !sim.trigger.init, out o2: !sim.trigger.init) { + %notdead:128 = sim.trigger_sequence %trig, 128 : !sim.trigger.init + hw.output %notdead#50, %notdead#2, %notdead#50 : !sim.trigger.init, !sim.trigger.init, !sim.trigger.init +} + +// CHECK-LABEL: hw.module @nested_sequence_0 +// CHECK: [[R:%.*]]:8 = sim.trigger_sequence %trig, 8 : !sim.trigger.edge +// CHECK-NEXT: hw.output [[R]]#0, [[R]]#1, [[R]]#2, [[R]]#3, [[R]]#4, [[R]]#5, [[R]]#6, [[R]]#7 +hw.module @nested_sequence_0(in %trig : !sim.trigger.edge, out o0: !sim.trigger.edge, out o1: !sim.trigger.edge, out o2: !sim.trigger.edge, out o3: !sim.trigger.edge, out o4: !sim.trigger.edge, out o5: !sim.trigger.edge, out o6: !sim.trigger.edge, out o7: !sim.trigger.edge) { + %a:4 = sim.trigger_sequence %trig, 4 : !sim.trigger.edge + %b:2 = sim.trigger_sequence %a#0, 2 : !sim.trigger.edge + %c:3 = sim.trigger_sequence %a#1, 3 : !sim.trigger.edge + %d:2 = sim.trigger_sequence %a#2, 2 : !sim.trigger.edge + + hw.output %b#0, %b#1, %c#0, %c#1, %c#2, %d#0, %d#1, %a#3 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge +} + +// CHECK-LABEL: hw.module @nested_sequence_1 +// CHECK: [[R:%.*]]:8 = sim.trigger_sequence %trig, 8 : !sim.trigger.edge +// CHECK-NEXT: hw.output [[R]]#0, [[R]]#1, [[R]]#2, [[R]]#3, [[R]]#4, [[R]]#5, [[R]]#6, [[R]]#7 +hw.module @nested_sequence_1(in %trig : !sim.trigger.edge, out o0: !sim.trigger.edge, out o1: !sim.trigger.edge, out o2: !sim.trigger.edge, out o3: !sim.trigger.edge, out o4: !sim.trigger.edge, out o5: !sim.trigger.edge, out o6: !sim.trigger.edge, out o7: !sim.trigger.edge) { + %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge + + %b:2 = sim.trigger_sequence %a#0, 2 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge + + %d:2 = sim.trigger_sequence %b#0, 2 : !sim.trigger.edge + %e:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge + %f:2 = sim.trigger_sequence %c#0, 2 : !sim.trigger.edge + %g:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge + + hw.output %d#0, %d#1, %e#0, %e#1, %f#0, %f#1, %g#0, %g#1 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge +} + +// CHECK-LABEL: hw.module @nested_sequence_2 +// CHECK: [[R0:%.*]]:4 = sim.trigger_sequence %trig, 4 +// CHECK: [[R1:%.*]]:2 = sim.trigger_sequence [[R0]]#3, 2 +// CHECK: [[R2:%.*]]:2 = sim.trigger_sequence [[R0]]#3, 2 +// CHECK: hw.output [[R0]]#0, [[R0]]#1, [[R0]]#2, [[R0]]#0, [[R1]]#0, [[R1]]#1, [[R2]]#0, [[R2]]#1 +hw.module @nested_sequence_2(in %trig : !sim.trigger.edge, out o0: !sim.trigger.edge, out o1: !sim.trigger.edge, out o2: !sim.trigger.edge, out o3: !sim.trigger.edge, out o4: !sim.trigger.edge, out o5: !sim.trigger.edge, out o6: !sim.trigger.edge, out o7: !sim.trigger.edge) { + %a:4 = sim.trigger_sequence %trig, 4 : !sim.trigger.edge + %b:3 = sim.trigger_sequence %a#1, 3 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %b#0, 2 : !sim.trigger.edge + %d:3 = sim.trigger_sequence %a#3, 3 : !sim.trigger.edge + %e:3 = sim.trigger_sequence %a#3, 3 : !sim.trigger.edge + hw.output %b#0, %b#1, %b#2, %c#1, %d#0, %d#2, %e#1, %e#2 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge +} \ No newline at end of file From 59d9ee2273c235a841038d594abef5a06c6fdfef Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 02:17:46 +0200 Subject: [PATCH 3/4] Don't CSE TriggerSequenceOps --- include/circt/Dialect/Sim/SimOps.td | 1 - lib/Dialect/Sim/SimOps.cpp | 5 +++++ test/Dialect/Sim/triggers.mlir | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 2971d1079136..53878122a969 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -382,7 +382,6 @@ def OnInitOp : SimOp<"on_init", [Pure]> { } def TriggerSequenceOp : SimOp<"trigger_sequence", [ - Pure, DeclareOpInterfaceMethods ]> { let summary = "Derive a sequence of triggers from a parent trigger."; diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index c6aa175c8ea0..23aa735e9fe5 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -509,6 +509,11 @@ LogicalResult TriggerSequenceOp::fold(FoldAdaptor adaptor, LogicalResult TriggerSequenceOp::canonicalize(TriggerSequenceOp op, PatternRewriter &rewriter) { + if (op.getNumResults() == 0) { + rewriter.eraseOp(op); + return success(); + } + // Check if there are unused results (which can be removed) or // non-concurrent sub-sequences (which can be inlined). auto getSingleSequenceUser = [](Value trigger) -> TriggerSequenceOp { diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir index 1e63168637e5..388911f1fb44 100644 --- a/test/Dialect/Sim/triggers.mlir +++ b/test/Dialect/Sim/triggers.mlir @@ -1,4 +1,5 @@ // RUN: circt-opt %s --canonicalize | FileCheck %s +// RUN: circt-opt %s --canonicalize --cse | FileCheck %s // CHECK-LABEL: hw.module @root_triggers // CHECK-DAG: [[PE:%.*]] = sim.on_edge posedge %clock From 361c6c49cb1e65411afd67b282281ce572cf8b08 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 02:45:34 +0200 Subject: [PATCH 4/4] EOF --- test/Dialect/Sim/triggers.mlir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir index 388911f1fb44..ebba4a4488a0 100644 --- a/test/Dialect/Sim/triggers.mlir +++ b/test/Dialect/Sim/triggers.mlir @@ -137,4 +137,4 @@ hw.module @nested_sequence_2(in %trig : !sim.trigger.edge, out o0: !sim %d:3 = sim.trigger_sequence %a#3, 3 : !sim.trigger.edge %e:3 = sim.trigger_sequence %a#3, 3 : !sim.trigger.edge hw.output %b#0, %b#1, %b#2, %c#1, %d#0, %d#2, %e#1, %e#2 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge -} \ No newline at end of file +}