Skip to content

Simulator upgrades for rohme compatibility (registering now and cancelling) #468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 29, 2024
Merged
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
34 changes: 30 additions & 4 deletions lib/src/simulator.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (C) 2021-2024 Intel Corporation
// Copyright (C) 2024 Adam Rose
// SPDX-License-Identifier: BSD-3-Clause
//
// simulator.dart
// The ROHD event-based static simulator
//
// 2021 May 7
// Author: Max Korbel <[email protected]>

//
// 2024 Feb 28th
// Amended by Adam Rose <[email protected]> for Rohme compatibility
//
import 'dart:async';
import 'dart:collection';

Expand Down Expand Up @@ -183,7 +187,7 @@ abstract class Simulator {
///
/// The [action], if it returns a [Future], will be `await`ed.
static void registerAction(int timestamp, dynamic Function() action) {
if (timestamp <= _currentTimestamp) {
if (timestamp < _currentTimestamp) {
throw Exception('Cannot add timestamp "$timestamp" in the past.'
' Current time is ${Simulator.time}');
}
Expand All @@ -193,6 +197,25 @@ abstract class Simulator {
_pendingTimestamps[timestamp]!.add(action);
}

/// Cancels an [action] previously scheduled for [timestamp].
///
/// Returns true iff a [action] was previously registered at [timestamp].
static bool cancelAction(int timestamp, dynamic Function() action) {
if (!_pendingTimestamps.containsKey(timestamp)) {
return false;
}

if (!_pendingTimestamps[timestamp]!.remove(action)) {
return false;
}

if (_pendingTimestamps[timestamp]!.isEmpty) {
_pendingTimestamps.remove(timestamp);
}

return true;
}

/// Registers an arbitrary [action] to be executed at the end of the
/// simulation.
///
Expand Down Expand Up @@ -231,18 +254,21 @@ abstract class Simulator {
}

final nextTimeStamp = _pendingTimestamps.firstKey();

if (nextTimeStamp == null) {
return;
}

_currentTimestamp = nextTimeStamp;

final pendingList = _pendingTimestamps[nextTimeStamp]!;
_pendingTimestamps.remove(_currentTimestamp);

await tickExecute(() async {
for (final func in _pendingTimestamps[nextTimeStamp]!) {
for (final func in pendingList) {
await func();
}
});
_pendingTimestamps.remove(_currentTimestamp);
}

/// Executes all pending injected actions.
Expand Down
93 changes: 92 additions & 1 deletion test/simulator_test.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (C) 2021-2024 Intel Corporation
// Copyright (C) 2024 Adam Rose
// SPDX-License-Identifier: BSD-3-Clause
//
// simulator_test.dart
// Unit tests for the ROHD simulator
//
// 2021 May 7
// Author: Max Korbel <[email protected]>

//
// 2024 Feb 28th
// Amended by Adam Rose <[email protected]> for Rohme compatibility
//
import 'dart:async';

import 'package:rohd/rohd.dart';
Expand All @@ -24,6 +28,23 @@ void main() {
expect(actionTaken, equals(true));
});

test('simulator supports cancelation of previously scheduled actions',
() async {
var actionCount = 0;

void incrementCount() {
actionCount++;
}

Simulator.registerAction(
50, () => Simulator.cancelAction(100, incrementCount));
Simulator.registerAction(100, incrementCount);
Simulator.registerAction(200, incrementCount);

await Simulator.run();
expect(actionCount, equals(1));
});

test('simulator stops at maximum time', () async {
const timeLimit = 1000;
Simulator.setMaxSimTime(timeLimit);
Expand Down Expand Up @@ -115,4 +136,74 @@ void main() {
await Simulator.run();
expect(injectedActionExecuted, isTrue);
});

group('Rohme compatibility tests', () {
test('simulator supports delta cycles', () async {
// ignore: omit_local_variable_types
final List<String> testLog = [];

void deltaFunc(int t, int i) {
testLog.add('wake up $i');
Simulator.registerAction(100, () => testLog.add('delta $i'));
}

Simulator.registerAction(100, () => deltaFunc(Simulator.time, 0));
Simulator.registerAction(100, () => deltaFunc(Simulator.time, 1));

await Simulator.run();

// ignore: omit_local_variable_types
final List<String> expectedLog = [
'wake up 0',
'wake up 1',
'delta 0',
'delta 1'
];
expect(testLog, expectedLog);
});

test('simulator supports end of delta one shot callbacks', () async {
var callbackCount = 0;

// add a self cancelling listener
Simulator.registerAction(
100, () => Simulator.injectAction(() => callbackCount++));
Simulator.registerAction(200, () {});

await Simulator.run();
expect(callbackCount, 1);
});

test('deltas occur after end of delta', () async {
// ignore: omit_local_variable_types
final List<String> testLog = [];

void deltaFunc(int t, int i) {
testLog.add('first delta $i');

Simulator.registerAction(t, () {
Simulator.registerAction(
Simulator.time, () => testLog.add('next delta $i'));
Simulator.injectAction(() => testLog.add('end delta $i'));
});
}

Simulator.registerAction(100, () => deltaFunc(Simulator.time, 0));
Simulator.registerAction(100, () => deltaFunc(Simulator.time, 1));

await Simulator.run();

// ignore: omit_local_variable_types
final List<String> expectedLog = [
'first delta 0',
'first delta 1',
'end delta 0',
'end delta 1',
'next delta 0',
'next delta 1'
];

expect(testLog, expectedLog);
});
});
}