From fd15723c1422135bbdcacd7d71b45d554d3b4bce Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Mon, 7 Oct 2024 23:40:49 +0200 Subject: [PATCH 01/10] Implement trigger ops. --- include/circt/Dialect/Sim/SimOps.h | 1 + include/circt/Dialect/Sim/SimOps.td | 127 ++++++++++++++++++ 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, 329 insertions(+) diff --git a/include/circt/Dialect/Sim/SimOps.h b/include/circt/Dialect/Sim/SimOps.h index 85061a8389c9..55492bae4b2f 100644 --- a/include/circt/Dialect/Sim/SimOps.h +++ b/include/circt/Dialect/Sim/SimOps.h @@ -14,6 +14,7 @@ #define CIRCT_DIALECT_SIM_SIMOPS_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" diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index b13c6d0588ec..bd30efb2c775 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -13,6 +13,12 @@ #ifndef CIRCT_DIALECT_SIM_SIMOPS_TD #define CIRCT_DIALECT_SIM_SIMOPS_TD +include "mlir/Interfaces/SideEffectInterfaces.td" +include "mlir/Interfaces/FunctionInterfaces.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" include "circt/Dialect/Seq/SeqTypes.td" @@ -21,7 +27,9 @@ include "circt/Dialect/Sim/SimTypes.td" include "mlir/Interfaces/FunctionInterfaces.td" include "mlir/Interfaces/InferTypeOpInterface.td" include "mlir/Interfaces/SideEffectInterfaces.td" +include "mlir/IR/BuiltinAttributes.td" include "mlir/IR/OpAsmInterface.td" +include "mlir/IR/RegionKindInterface.td" class SimOp traits = []> : Op; @@ -379,4 +387,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 6c9c3736751a..84a10716ce65 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 adbd35e5b51a..e79b14261ed7 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 428fc3ddf3699842dff0552d7b16f0747a5376e3 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 00:14:07 +0200 Subject: [PATCH 02/10] 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 bd30efb2c775..b19d0d710feb 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -467,7 +467,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 91d16a3fbee3e96f01169c465806b2a3842944c4 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 02:17:46 +0200 Subject: [PATCH 03/10] 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 b19d0d710feb..33096891547f 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -406,7 +406,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 e79b14261ed7..4fbcc557e894 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 e2bdb8140e5169e0bebb546d029bd2985e5e0d38 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 8 Oct 2024 02:45:34 +0200 Subject: [PATCH 04/10] 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 +} From bc517684e57ff638fe835daffc72c06ec57b3919 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Fri, 22 Nov 2024 14:30:38 +0100 Subject: [PATCH 05/10] Improve TriggerSequenceOp canonicalizer Flatten nested sequences in a single rewrite rather than iteratively. --- lib/Dialect/Sim/SimOps.cpp | 97 +++++++++++++++++++++++----------- test/Dialect/Sim/triggers.mlir | 24 +++++++++ 2 files changed, 90 insertions(+), 31 deletions(-) diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index 4fbcc557e894..cfe8bd846517 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -514,21 +514,29 @@ LogicalResult TriggerSequenceOp::canonicalize(TriggerSequenceOp op, return success(); } - // Check if there are unused results (which can be removed) or - // non-concurrent sub-sequences (which can be inlined). + // If the current op can be inlined into the parent, + // leave it to the parent's canonicalization. + if (auto parentSeq = op.getParent().getDefiningOp()) { + if (parentSeq == op) { + op.emitWarning("Recursive trigger sequence."); + return failure(); + } + if (op.getParent().hasOneUse()) + return failure(); + } + auto getSingleSequenceUser = [](Value trigger) -> TriggerSequenceOp { if (!trigger.hasOneUse()) return {}; return dyn_cast(trigger.use_begin()->getOwner()); }; + // Check if there are unused results (which can be removed) or + // non-concurrent sub-sequences (which can be inlined). + 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; @@ -538,43 +546,70 @@ LogicalResult TriggerSequenceOp::canonicalize(TriggerSequenceOp op, 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); + // DFS for inlinable values. + SmallVector newResultValues; + SmallVector inlinedSequences; + llvm::SmallVector> sequenceOpStack; + + sequenceOpStack.push_back({op, 0}); + while (!sequenceOpStack.empty()) { + auto &top = sequenceOpStack.back(); + auto currentSequence = top.first; + unsigned resultIndex = top.second; + + while (resultIndex < currentSequence.getNumResults()) { + auto currentResult = currentSequence.getResult(resultIndex); + // Check we do not walk in a cycle. + if (currentResult == op.getParent()) { + op.emitWarning("Recursive trigger sequence."); + return failure(); + } + + if (auto inlinableChildSequence = getSingleSequenceUser(currentResult)) { + // Save the next result index to visit on the + // stack and put the new sequence on top. + top.second = resultIndex + 1; + sequenceOpStack.push_back({inlinableChildSequence, 0}); + inlinedSequences.push_back(inlinableChildSequence); + inlinableChildSequence->dropAllReferences(); + break; + } + + if (!currentResult.use_empty()) + newResultValues.push_back(currentResult); + resultIndex++; } + // Pop the sequence off of the stack if we have visited all results. + if (resultIndex >= currentSequence.getNumResults()) + sequenceOpStack.pop_back(); } - // Remove empty sequences. - if (resultValues.empty()) { + // Remove dead sequences. + if (newResultValues.empty()) { + for (auto deadSubSequence : inlinedSequences) + rewriter.eraseOp(deadSubSequence); rewriter.eraseOp(op); return success(); } // Replace the current operation with a new sequence. rewriter.setInsertionPoint(op); - auto fusedLoc = FusedLoc::get(rewriter.getContext(), locs); + + SmallVector inlinedLocs; + inlinedLocs.reserve(inlinedSequences.size() + 1); + inlinedLocs.push_back(op.getLoc()); + for (auto subSequence : inlinedSequences) + inlinedLocs.push_back(subSequence.getLoc()); + auto fusedLoc = FusedLoc::get(op.getContext(), inlinedLocs); + inlinedLocs.clear(); + auto newOp = rewriter.create(fusedLoc, op.getParent(), - resultValues.size()); - for (auto [rval, newRes] : llvm::zip(resultValues, newOp.getResults())) + newResultValues.size()); + for (auto [rval, newRes] : llvm::zip(newResultValues, newOp.getResults())) rewriter.replaceAllUsesWith(rval, newRes); - // Remove sequences that have been inlined - for (auto child : childSeqs) - rewriter.eraseOp(child); + for (auto deadSubSequence : inlinedSequences) + rewriter.eraseOp(deadSubSequence); rewriter.eraseOp(op); return success(); } diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir index ebba4a4488a0..ebda1e3f26de 100644 --- a/test/Dialect/Sim/triggers.mlir +++ b/test/Dialect/Sim/triggers.mlir @@ -138,3 +138,27 @@ hw.module @nested_sequence_2(in %trig : !sim.trigger.edge, out o0: !sim %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 } + +// CHECK-LABEL: hw.module @skewed_binary_tree +// CHECK: [[R0:%.*]]:7 = sim.trigger_sequence %trig, 7 +// CHECK-NEXT: hw.output [[R0]]#0, [[R0]]#1, [[R0]]#2, [[R0]]#3, [[R0]]#4, [[R0]]#5, [[R0]]#6 +hw.module @skewed_binary_tree(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) { + %h:2 = sim.trigger_sequence %g#1, 2 : !sim.trigger.edge + %g:2 = sim.trigger_sequence %f#1, 2 : !sim.trigger.edge + %f:2 = sim.trigger_sequence %e#1, 2 : !sim.trigger.edge + %e:2 = sim.trigger_sequence %d#1, 2 : !sim.trigger.edge + %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge + %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge + %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge + hw.output %a#0, %b#0, %c#0, %d#0, %f#0, %g#0, %h#0 : !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 @dead_skewed_binary_tree +// CHECK-NOT: sim.trigger_sequence %trig, +hw.module @dead_skewed_binary_tree(in %trig : !sim.trigger.edge) { + %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge + %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge + %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge +} From 7aba24aa4bea2dc9f06591fc85d412134aad1c23 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Thu, 12 Dec 2024 16:04:10 +0100 Subject: [PATCH 06/10] Remove TriggeredOp `condition` operand --- include/circt/Dialect/Sim/SimOps.td | 9 +------- lib/Dialect/Sim/SimOps.cpp | 35 ++++------------------------ test/Dialect/Sim/sim-errors.mlir | 1 - test/Dialect/Sim/triggers.mlir | 36 ++++++----------------------- 4 files changed, 13 insertions(+), 68 deletions(-) diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 33096891547f..df301507c3cf 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -452,7 +452,6 @@ def YieldSeqOp : SimOp<"yield_seq",[ } def TriggeredOp : SimOp<"triggered", [ - AttrSizedOperandSegments, IsolatedFromAbove, RegionKindInterface, RecursiveMemoryEffects, @@ -465,9 +464,6 @@ 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 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 have side-effects and produce results. @@ -476,10 +472,9 @@ def TriggeredOp : SimOp<"triggered", [ For non-simulation flows the results are replaced by their tie-off values. }]; let arguments = (ins AnyTriggerType:$trigger, - Optional:$condition, Variadic:$inputs, OptionalAttr>:$tieoffs + TypedAttrInterface, "Tie-off constants">>:$tieoffs ); let results = (outs Variadic); let regions = (region SizedRegion<1>:$body); @@ -487,7 +482,6 @@ def TriggeredOp : SimOp<"triggered", [ let assemblyFormat = [{ ` ` `(` $inputs `)` `on` ` ` `(` $trigger `:` qualified(type($trigger)) `)` - (`if` $condition^)? (`tieoff` $tieoffs^)? attr-dict-with-keyword $body `:` functional-type($inputs, results) @@ -501,7 +495,6 @@ def TriggeredOp : SimOp<"triggered", [ }]; let hasVerifier = true; - let hasFolder = true; let hasCanonicalizeMethod = true; } diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index cfe8bd846517..08afa52135ff 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -434,43 +434,18 @@ LogicalResult TriggeredOp::verify() { 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(); + if (bodyBlock->without_terminator().empty()) { + rewriter.eraseOp(op); + return success(); + } - rewriter.eraseOp(op); - return success(); + return failure(); } // --- TriggerSequenceOp --- diff --git a/test/Dialect/Sim/sim-errors.mlir b/test/Dialect/Sim/sim-errors.mlir index bcd4a99ca621..6483ae2a44bd 100644 --- a/test/Dialect/Sim/sim-errors.mlir +++ b/test/Dialect/Sim/sim-errors.mlir @@ -95,4 +95,3 @@ hw.module @too_many_tieoffs(in %trig : !sim.trigger.edge) { sim.yield_seq %cst : i2 } : () -> i2 } - diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir index ebda1e3f26de..b705a9deacdd 100644 --- a/test/Dialect/Sim/triggers.mlir +++ b/test/Dialect/Sim/triggers.mlir @@ -16,30 +16,20 @@ hw.module @root_triggers(in %clock : !seq.clock, out o0 : !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 - +hw.module @fold_triggered(in %a : i8, in %en : i1, in %trig : !sim.trigger.edge, out o0 : i8, out o1 : i9) { // 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 { + sim.triggered () on (%trig : !sim.trigger.edge) { %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: %[[RES:.*]]:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) 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] { + %res:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) tieoff [12 : i8, 33 : i9] { ^bb0(%arg: i8): %0 = sim.fmt.lit "Don't touch live process with results" sim.proc.print %0 @@ -49,23 +39,11 @@ hw.module @fold_triggered(in %a : i8, in %en : i1, in %trig : !sim.trigger.edge< // 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 { + sim.triggered () on (%trig : !sim.trigger.edge) { } : () -> () - // CHECK: hw.output %[[RES]]#0, %[[RES]]#1, %[[CST12]] : i8, i9, i8 - hw.output %res#0, %res#1, %fold : i8, i9, i8 + // CHECK: hw.output %[[RES]]#0, %[[RES]]#1 : i8, i9 + hw.output %res#0, %res#1 : i8, i9 } // CHECK-LABEL: hw.module @empty_sequence From 1601d40ffaa20ce04c36fae74441bff7b4b588f1 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Thu, 12 Dec 2024 16:11:57 +0100 Subject: [PATCH 07/10] Doc review --- include/circt/Dialect/Sim/SimOps.td | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index df301507c3cf..dde26c5150c1 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -393,14 +393,15 @@ def OnEdgeOp : SimOp<"on_edge", [ Pure, DeclareOpInterfaceMethods ]> { - let summary = "Invoke a trigger on a clock edge event."; + let summary = "Create a trigger that gets invoked 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 summary = + "Create a trigger that gets invoked at the start of simulation."; let results = (outs InitTriggerType:$result); let assemblyFormat = "attr-dict"; } @@ -428,7 +429,7 @@ def TriggerSequenceOp : SimOp<"trigger_sequence", [ def YieldSeqOp : SimOp<"yield_seq",[ Terminator, HasParent<"circt::sim::TriggeredOp"> ]> { - let summary = [{Yield results form a triggerd region with 'seq' + let summary = [{Yield results from a triggerd region with 'seq' (i.e. register-like) semantics."}]; let description = [{ Terminates a triggered region and produces the given list of values. From 0a27e1b69182c8748ccf22429ea40cd1464a0faf Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Wed, 8 Jan 2025 15:40:44 +0100 Subject: [PATCH 08/10] Tablegen comments --- include/circt/Dialect/Sim/SimOps.td | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index dde26c5150c1..fd0fe2b4bddf 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -13,11 +13,6 @@ #ifndef CIRCT_DIALECT_SIM_SIMOPS_TD #define CIRCT_DIALECT_SIM_SIMOPS_TD -include "mlir/Interfaces/SideEffectInterfaces.td" -include "mlir/Interfaces/FunctionInterfaces.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" @@ -429,8 +424,8 @@ def TriggerSequenceOp : SimOp<"trigger_sequence", [ def YieldSeqOp : SimOp<"yield_seq",[ Terminator, HasParent<"circt::sim::TriggeredOp"> ]> { - let summary = [{Yield results from a triggerd region with 'seq' - (i.e. register-like) semantics."}]; + let summary = + "Yield results from a triggered region with sequential 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 @@ -460,9 +455,8 @@ def TriggeredOp : SimOp<"triggered", [ SingleBlockImplicitTerminator<"sim::YieldSeqOp">, HasParent<"circt::hw::HWModuleOp"> ]> { - let summary = [{ - Defines a procedure invoked on a given trigger and condition. - }]; + 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 body region must complete without 'consuming' simulation time. It From 080017a4425a965d64fac1c3defbf3b12072d210 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 3 Jun 2025 00:18:57 +0200 Subject: [PATCH 09/10] Remove EdgeEventAttr from sim.trigger.edge type --- include/circt/Dialect/Sim/SimOps.td | 5 +- include/circt/Dialect/Sim/SimTypes.td | 2 - lib/Dialect/Sim/SimOps.cpp | 12 ---- test/Dialect/Sim/triggers.mlir | 96 +++++++++++++-------------- 4 files changed, 49 insertions(+), 66 deletions(-) diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index fd0fe2b4bddf..d96b38615769 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -384,10 +384,7 @@ def PrintFormattedProcOp : SimOp<"proc.print"> { // --- Trigger Ops --- -def OnEdgeOp : SimOp<"on_edge", [ - Pure, - DeclareOpInterfaceMethods -]> { +def OnEdgeOp : SimOp<"on_edge", [Pure]> { let summary = "Create a trigger that gets invoked on a clock edge event."; let arguments = (ins ClockType:$clock, EventControlAttr:$event); let results = (outs EdgeTriggerType:$result); diff --git a/include/circt/Dialect/Sim/SimTypes.td b/include/circt/Dialect/Sim/SimTypes.td index 84a10716ce65..9d9f6af38fad 100644 --- a/include/circt/Dialect/Sim/SimTypes.td +++ b/include/circt/Dialect/Sim/SimTypes.td @@ -29,9 +29,7 @@ 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"> { diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index 08afa52135ff..8047cf5371ea 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -397,18 +397,6 @@ 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() { diff --git a/test/Dialect/Sim/triggers.mlir b/test/Dialect/Sim/triggers.mlir index b705a9deacdd..5edb284b1604 100644 --- a/test/Dialect/Sim/triggers.mlir +++ b/test/Dialect/Sim/triggers.mlir @@ -6,30 +6,30 @@ // 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) { +// 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 + 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) { - // CHECK: sim.triggered () on (%trig : !sim.trigger.edge) { +hw.module @fold_triggered(in %a : i8, in %en : i1, in %trig : !sim.trigger.edge, out o0 : i8, out o1 : i9) { + // CHECK: sim.triggered () on (%trig : !sim.trigger.edge) { // CHECK-NEXT: "Don't touch live process" - sim.triggered () on (%trig : !sim.trigger.edge) { + sim.triggered () on (%trig : !sim.trigger.edge) { %0 = sim.fmt.lit "Don't touch live process" sim.proc.print %0 } : () -> () - // CHECK: %[[RES:.*]]:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) tieoff [12 : i8, 33 : i9] { + // CHECK: %[[RES:.*]]:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) 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) tieoff [12 : i8, 33 : i9] { + %res:2 = sim.triggered (%a) on (%trig : !sim.trigger.edge) tieoff [12 : i8, 33 : i9] { ^bb0(%arg: i8): %0 = sim.fmt.lit "Don't touch live process with results" sim.proc.print %0 @@ -39,7 +39,7 @@ hw.module @fold_triggered(in %a : i8, in %en : i1, in %trig : !sim.trigger.edge< // CHECK-NOT: sim.triggered - sim.triggered () on (%trig : !sim.trigger.edge) { + sim.triggered () on (%trig : !sim.trigger.edge) { } : () -> () // CHECK: hw.output %[[RES]]#0, %[[RES]]#1 : i8, i9 @@ -75,32 +75,32 @@ hw.module @mostly_dead_sequence(in %trig : !sim.trigger.init, out o0: !sim.trigg } // CHECK-LABEL: hw.module @nested_sequence_0 -// CHECK: [[R:%.*]]:8 = sim.trigger_sequence %trig, 8 : !sim.trigger.edge +// 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.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 + 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: [[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 +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 + %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 + %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 + 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 @@ -108,35 +108,35 @@ hw.module @nested_sequence_1(in %trig : !sim.trigger.edge, out o0: !sim // 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 +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 } // CHECK-LABEL: hw.module @skewed_binary_tree // CHECK: [[R0:%.*]]:7 = sim.trigger_sequence %trig, 7 // CHECK-NEXT: hw.output [[R0]]#0, [[R0]]#1, [[R0]]#2, [[R0]]#3, [[R0]]#4, [[R0]]#5, [[R0]]#6 -hw.module @skewed_binary_tree(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) { - %h:2 = sim.trigger_sequence %g#1, 2 : !sim.trigger.edge - %g:2 = sim.trigger_sequence %f#1, 2 : !sim.trigger.edge - %f:2 = sim.trigger_sequence %e#1, 2 : !sim.trigger.edge - %e:2 = sim.trigger_sequence %d#1, 2 : !sim.trigger.edge - %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge - %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge - %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge - %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge - hw.output %a#0, %b#0, %c#0, %d#0, %f#0, %g#0, %h#0 : !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge, !sim.trigger.edge +hw.module @skewed_binary_tree(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) { + %h:2 = sim.trigger_sequence %g#1, 2 : !sim.trigger.edge + %g:2 = sim.trigger_sequence %f#1, 2 : !sim.trigger.edge + %f:2 = sim.trigger_sequence %e#1, 2 : !sim.trigger.edge + %e:2 = sim.trigger_sequence %d#1, 2 : !sim.trigger.edge + %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge + %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge + %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge + hw.output %a#0, %b#0, %c#0, %d#0, %f#0, %g#0, %h#0 : !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 @dead_skewed_binary_tree // CHECK-NOT: sim.trigger_sequence %trig, -hw.module @dead_skewed_binary_tree(in %trig : !sim.trigger.edge) { - %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge - %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge - %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge - %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge +hw.module @dead_skewed_binary_tree(in %trig : !sim.trigger.edge) { + %a:2 = sim.trigger_sequence %trig, 2 : !sim.trigger.edge + %b:2 = sim.trigger_sequence %a#1, 2 : !sim.trigger.edge + %c:2 = sim.trigger_sequence %b#1, 2 : !sim.trigger.edge + %d:2 = sim.trigger_sequence %c#1, 2 : !sim.trigger.edge } From 4fcf6519a81f6fb9ebc45141604e1393684f93e2 Mon Sep 17 00:00:00 2001 From: Leon Hielscher Date: Tue, 3 Jun 2025 00:34:13 +0200 Subject: [PATCH 10/10] Update error test --- test/Dialect/Sim/sim-errors.mlir | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Dialect/Sim/sim-errors.mlir b/test/Dialect/Sim/sim-errors.mlir index 6483ae2a44bd..9da65d7089b2 100644 --- a/test/Dialect/Sim/sim-errors.mlir +++ b/test/Dialect/Sim/sim-errors.mlir @@ -54,23 +54,23 @@ hw.module @dpi_call(in %clock : !seq.clock, in %in: i1) { // ----- -hw.module @not_enough_triggers(in %in : !sim.trigger.edge) { +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 + %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 + %res = sim.trigger_sequence %res, 1 : !sim.trigger.edge } // ----- -hw.module @missing_tieoffs(in %trig : !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) { + %res = sim.triggered () on (%trig : !sim.trigger.edge) { %cst = hw.constant 0 : i2 sim.yield_seq %cst : i2 } : () -> i2 @@ -78,9 +78,9 @@ hw.module @missing_tieoffs(in %trig : !sim.trigger.edge) { // ----- -hw.module @wrong_tieoff(in %trig : !sim.trigger.edge) { +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] { + %res = sim.triggered () on (%trig : !sim.trigger.edge) tieoff [0 : i1] { %cst = hw.constant 0 : i2 sim.yield_seq %cst : i2 } : () -> i2 @@ -88,9 +88,9 @@ hw.module @wrong_tieoff(in %trig : !sim.trigger.edge) { // ----- -hw.module @too_many_tieoffs(in %trig : !sim.trigger.edge) { +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] { + %res = sim.triggered () on (%trig : !sim.trigger.edge) tieoff [0 : i2, 0 : i2] { %cst = hw.constant 0 : i2 sim.yield_seq %cst : i2 } : () -> i2