diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 589a3d52a..25e98144b 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -336,14 +336,22 @@ class Sequential extends _Always { clk.glitch.listen((event) async { // we want the first previousValue from the first glitch of this tick _preTickClkValues[i] ??= event.previousValue; - if (!_pendingExecute) { - unawaited(Simulator.clkStable.first.then((value) { - // once the clocks are stable, execute the contents of the FF - _execute(); - _pendingExecute = false; - })); + + if (Simulator.phase == SimulatorPhase.clkStable) { + // this could be an output of a flop driving the clock of this + // flop, so execute immediately (e.g. clock divider) + _execute(); + _pendingExecute = false; + } else { + if (!_pendingExecute) { + unawaited(Simulator.clkStable.first.then((value) { + // once the clocks are stable, execute the contents of the FF + _execute(); + _pendingExecute = false; + })); + } + _pendingExecute = true; } - _pendingExecute = true; }); } } diff --git a/test/clock_divider_test.dart b/test/clock_divider_test.dart new file mode 100644 index 000000000..8dac98041 --- /dev/null +++ b/test/clock_divider_test.dart @@ -0,0 +1,112 @@ +/// Copyright (C) 2022 Intel Corporation +/// SPDX-License-Identifier: BSD-3-Clause +/// +/// clock_divider_test.dart +/// Unit tests for a clock divider. +/// +/// 2022 November 1 +/// Author: Max Korbel +/// + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/utilities/simcompare.dart'; +import 'package:test/test.dart'; + +import '../example/example.dart' as sync_reset; +import 'counter_test.dart' as async_reset; + +class ClockDivider extends Module { + Logic get clkOut => output('clkOut'); + ClockDivider(Logic clkIn, Logic reset, {bool syncReset = true}) + : super(name: 'clockDivider') { + clkIn = addInput('clkIn', clkIn); + reset = addInput('reset', reset); + final clkOut = addOutput('clkOut'); + + Sequential.multi([ + clkIn, + if (!syncReset) reset + ], [ + If( + reset, + then: [clkOut < 0], + orElse: [clkOut < ~clkOut], + ), + ]); + } +} + +class TwoCounters extends Module { + TwoCounters(Logic resetClks, Logic resetCounters, {required bool syncReset}) { + resetClks = addInput('resetClks', resetClks); + resetCounters = addInput('resetCounters', resetCounters); + + final clk = SimpleClockGenerator(10).clk; + final clkDiv = ClockDivider(clk, resetClks, syncReset: syncReset).clkOut; + + addOutput('cntFast', width: 8) <= + (syncReset + ? sync_reset.Counter(Const(1), resetCounters, clk, + name: 'fastCounter') + .val + : async_reset.Counter(Const(1), resetCounters, + clkOverride: clk, name: 'fastCounter') + .val); + addOutput('cntSlow', width: 8) <= + (syncReset + ? sync_reset.Counter(Const(1), resetCounters, clkDiv, + name: 'slowCounter') + .val + : async_reset.Counter(Const(1), resetCounters, + clkOverride: clkDiv, name: 'slowCounter') + .val); + } +} + +void main() { + tearDown(Simulator.reset); + group('clock divider', () { + final vectors = [ + Vector({'resetClks': 1, 'resetCounters': 1}, {}), + Vector({'resetClks': 0, 'resetCounters': 1}, {}), + Vector({'resetClks': 0, 'resetCounters': 1}, {}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 0, 'cntFast': 0}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 1, 'cntFast': 1}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 1, 'cntFast': 2}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 2, 'cntFast': 3}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 2, 'cntFast': 4}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 3, 'cntFast': 5}), + Vector( + {'resetClks': 0, 'resetCounters': 0}, {'cntSlow': 3, 'cntFast': 6}), + ]; + final signalToWidthMap = {'cntSlow': 8, 'cntFast': 8}; + + test('sync reset', () async { + final mod = TwoCounters(Logic(), Logic(), syncReset: true); + await mod.build(); + + await SimCompare.checkFunctionalVector(mod, vectors); + final simResult = SimCompare.iverilogVector( + mod.generateSynth(), mod.runtimeType.toString(), vectors, + signalToWidthMap: signalToWidthMap); + expect(simResult, equals(true)); + }); + + test('async reset', () async { + final mod = TwoCounters(Logic(), Logic(), syncReset: false); + await mod.build(); + + await SimCompare.checkFunctionalVector(mod, vectors); + final simResult = SimCompare.iverilogVector( + mod.generateSynth(), mod.runtimeType.toString(), vectors, + signalToWidthMap: signalToWidthMap); + expect(simResult, equals(true)); + }); + }); +} diff --git a/test/counter_test.dart b/test/counter_test.dart index e9bb9f5d3..e696d7c92 100644 --- a/test/counter_test.dart +++ b/test/counter_test.dart @@ -18,7 +18,8 @@ import 'package:test/test.dart'; class Counter extends Module { final int width; Logic get val => output('val'); - Counter(Logic en, Logic reset, {this.width = 8}) : super(name: 'counter') { + Counter(Logic en, Logic reset, + {this.width = 8, Logic? clkOverride, super.name = 'counter'}) { en = addInput('en', en); reset = addInput('reset', reset); @@ -29,7 +30,10 @@ class Counter extends Module { nextVal <= val + 1; Sequential.multi([ - SimpleClockGenerator(10).clk, + if (clkOverride != null) + addInput('clk', clkOverride) + else + SimpleClockGenerator(10).clk, reset ], [ If(reset, then: [