Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion lib/src/modules/clkgen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class SimpleClockGenerator extends Module with SystemVerilog {
///
/// For example, if the [clockPeriod] is 10, then the frequency is 1/10,
/// and the time between positive edges of the generated clock is 10.
///
/// The clock toggles once every half period, so [clockPeriod] must be an
/// even number of time units greater than or equal to 2.
final int clockPeriod;

/// The generated clock.
Expand All @@ -24,8 +27,20 @@ class SimpleClockGenerator extends Module with SystemVerilog {
/// Constructs a very simple clock generator. Generates a non-synthesizable
/// SystemVerilog representation.
///
/// Set the frequency via [clockPeriod].
/// Set the frequency via [clockPeriod], which must be an even number of
/// time units greater than or equal to 2. Other values throw an
/// [IllegalConfigurationException].
SimpleClockGenerator(this.clockPeriod, {super.name = 'clkgen'}) {
if (clockPeriod < 2 || clockPeriod.isOdd) {
throw IllegalConfigurationException(
'The clockPeriod must be an even number of time units greater than'
' or equal to 2, but got $clockPeriod. The clock toggles once every'
' half period, so a period below 2 would schedule both edges within'
' the same time unit and prevent the simulation from advancing, and'
' an odd period would silently generate a clock whose period is'
' ${clockPeriod - 1} instead.');
}

addOutput('clk');

clk.makeUnassignable(
Expand Down
2 changes: 1 addition & 1 deletion test/changed_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ void main() {
final clk = SimpleClockGenerator(200).clk;

// faster clk just to add more events to the Simulator
SimpleClockGenerator(17).clk;
SimpleClockGenerator(16).clk;

final posedgeChangingSignal = Logic()..put(0);
final negedgeChangingSignal = Logic()..put(0);
Expand Down
74 changes: 74 additions & 0 deletions test/clkgen_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) 2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// clkgen_test.dart
// Tests for the simple clock generator
//
// 2026 September 21
// Author: Shubham Padkonde <shubhampadkonde12@gmail.com>

import 'dart:async';

import 'package:rohd/rohd.dart';
import 'package:test/test.dart';

/// Collects the times of the first [count] positive edges of a clock with
/// [clockPeriod].
Future<List<int>> posedgeTimes(int clockPeriod, {int count = 4}) async {
await Simulator.reset();

final clk = SimpleClockGenerator(clockPeriod).clk;
final times = <int>[];

clk.posedge.listen((_) {
times.add(Simulator.time);
if (times.length == count) {
unawaited(Simulator.endSimulation());
}
});

Simulator.setMaxSimTime(clockPeriod * (count + 2));
await Simulator.run();

return times;
}

void main() {
tearDown(() async {
await Simulator.reset();
});

group('generates the requested period', () {
for (final clockPeriod in [2, 4, 10]) {
test('of $clockPeriod', () async {
final times = await posedgeTimes(clockPeriod);

expect(times.length, 4);
for (var i = 1; i < times.length; i++) {
expect(times[i] - times[i - 1], clockPeriod,
reason: 'positive edges should be $clockPeriod apart');
}
});
}
});

group('rejects a period it cannot generate', () {
// A period below 2 has a half period of 0, which schedules both edges in
// the same time unit and hangs the simulation.
for (final clockPeriod in [-2, -1, 0, 1]) {
test('of $clockPeriod', () {
expect(() => SimpleClockGenerator(clockPeriod),
throwsA(isA<IllegalConfigurationException>()));
});
}

// An odd period is rounded down by the half period, so the generated
// clock would silently run at a different frequency than requested.
for (final clockPeriod in [3, 5, 11]) {
test('of $clockPeriod', () {
expect(() => SimpleClockGenerator(clockPeriod),
throwsA(isA<IllegalConfigurationException>()));
});
}
});
}
2 changes: 1 addition & 1 deletion test/unassignable_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void main() {

test('SimpleClockGenerator outputs cannot be assigned', () {
try {
SimpleClockGenerator(1).clk <= Logic();
SimpleClockGenerator(2).clk <= Logic();
fail('Should have thrown an exception');
} on UnassignableException catch (e) {
expect(e.toString(), contains('SimpleClockGenerator'));
Expand Down