diff --git a/lib/src/finite_state_machine.dart b/lib/src/finite_state_machine.dart index 1582cbe04..32ead8261 100644 --- a/lib/src/finite_state_machine.dart +++ b/lib/src/finite_state_machine.dart @@ -52,8 +52,15 @@ class FiniteStateMachine { return _stateValueLookup[_stateLookup[id]]; } - /// The clock signal to the FSM. - final Logic clk; + /// The clock signal to the FSM (when only single-triggered). Otherwise, the + /// first clock. + /// + /// Deprecated: do not reference the clock from [FiniteStateMachine]. + @Deprecated('Do not reference the clock from the `FiniteStateMachine`.') + Logic get clk => _clks.first; + + /// The clock signals to the FSM. + final List _clks; /// The reset signal to the FSM. final Logic reset; @@ -79,12 +86,21 @@ class FiniteStateMachine { /// Width of the state. final int _stateWidth; - //TODO: async reset (#406) - /// Creates an finite state machine for the specified list of [_states], with /// an initial state of [resetState] (when synchronous [reset] is high) and /// transitions on positive [clk] edges. - FiniteStateMachine(this.clk, this.reset, this.resetState, this._states) + FiniteStateMachine( + Logic clk, + Logic reset, + StateIdentifier resetState, + List> states, + ) : this.multi([clk], reset, resetState, states); + + /// Creates an finite state machine for the specified list of [_states], with + /// an initial state of [resetState] (when synchronous [reset] is high) and + /// transitions on positive edges of any of [_clks]. + FiniteStateMachine.multi( + this._clks, this.reset, this.resetState, this._states) : _stateWidth = _logBase(_states.length, 2), currentState = Logic(name: 'currentState', width: _logBase(_states.length, 2)), @@ -137,7 +153,7 @@ class FiniteStateMachine { ]) ]); - Sequential(clk, reset: reset, resetValues: { + Sequential.multi(_clks, reset: reset, resetValues: { currentState: _stateValueLookup[_stateLookup[resetState]] }, [ currentState < nextState, diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 3d8f15710..b11b3487a 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -405,6 +405,10 @@ class Sequential extends _Always { /// specified reset value. Sequential.multi(List clks, super._conditionals, {super.reset, super.resetValues, super.name = 'sequential'}) { + if (clks.isEmpty) { + throw IllegalConfigurationException('Must provide at least one clock.'); + } + for (var i = 0; i < clks.length; i++) { final clk = clks[i]; if (clk.width > 1) { diff --git a/lib/src/modules/pipeline.dart b/lib/src/modules/pipeline.dart index 8fff663aa..b6a81c427 100644 --- a/lib/src/modules/pipeline.dart +++ b/lib/src/modules/pipeline.dart @@ -74,8 +74,13 @@ class _PipeStage { /// A simple pipeline, separating arbitrary combinational logic by flop stages. class Pipeline { - /// The clock whose positive edge triggers the flops in this pipeline. - final Logic clk; + /// The clock whose positive edge triggers the flops in this pipeline when + /// single-triggered. Otherwise, the first clock. + @Deprecated('Do not reference the clock from the `Pipeline`.') + Logic get clk => _clks.first; + + /// The clocks whose positive edges trigger the flops in this pipeline. + final List _clks; /// An optional reset signal for all pipelined signals. final Logic? reset; @@ -112,7 +117,21 @@ class Pipeline { /// Each stage can be stalled independently using [stalls], where every index /// of [stalls] corresponds to the index of the stage to be stalled. When /// a stage's stall is asserted, the output of that stage will not change. - Pipeline(this.clk, + Pipeline(Logic clk, + {List Function(PipelineStageInfo p)> stages = const [], + List? stalls, + List signals = const [], + Map resetValues = const {}, + Logic? reset}) + : this.multi([clk], + stages: stages, + stalls: stalls, + signals: signals, + resetValues: resetValues, + reset: reset); + + /// Constructs a [Pipeline] with multiple triggers on any of [_clks]. + Pipeline.multi(this._clks, {List Function(PipelineStageInfo p)> stages = const [], List? stalls, List signals = const [], @@ -225,7 +244,7 @@ class Pipeline { ]) ]; } - Sequential(clk, ffAssignsWithStall, name: 'ff_${newLogic.name}'); + Sequential.multi(_clks, ffAssignsWithStall, name: 'ff_${newLogic.name}'); } /// The stage input for a signal associated with [logic] to [stageIndex]. @@ -304,14 +323,33 @@ class ReadyValidPipeline extends Pipeline { /// If contents are pushed in when the pipeline is not ready, they /// will be dropped. ReadyValidPipeline( - super.clk, + Logic clk, + Logic validPipeIn, + Logic readyPipeOut, { + List Function(PipelineStageInfo p)> stages = const [], + Map resetValues = const {}, + List signals = const [], + Logic? reset, + }) : this.multi( + [clk], + validPipeIn, + readyPipeOut, + stages: stages, + resetValues: resetValues, + signals: signals, + reset: reset, + ); + + /// Creates a [ReadyValidPipeline] with multiple triggers. + ReadyValidPipeline.multi( + super._clks, this.validPipeIn, this.readyPipeOut, { List Function(PipelineStageInfo p)> stages = const [], super.resetValues, List signals = const [], super.reset, - }) : super( + }) : super.multi( stages: stages, signals: [validPipeIn, ...signals], stalls: List.generate(