Skip to content
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

Refactored tick() in simulator.dart #475

Merged
merged 5 commits into from
Apr 3, 2024
Merged
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
104 changes: 74 additions & 30 deletions lib/src/simulator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ abstract class Simulator {
_pendingTimestamps.isNotEmpty || _injectedActions.isNotEmpty;

/// Sorted storage for pending functions to execute at appropriate times.
static final SplayTreeMap<int, List<dynamic Function()>> _pendingTimestamps =
SplayTreeMap<int, List<dynamic Function()>>();
static final SplayTreeMap<int, ListQueue<dynamic Function()>>
_pendingTimestamps = SplayTreeMap<int, ListQueue<dynamic Function()>>();

/// The list of actions to be performed in this timestamp
static ListQueue<dynamic Function()> _pendingList = ListQueue();
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved

/// Functions to be executed as soon as possible by the [Simulator].
///
Expand Down Expand Up @@ -192,7 +195,7 @@ abstract class Simulator {
' Current time is ${Simulator.time}');
}
if (!_pendingTimestamps.containsKey(timestamp)) {
_pendingTimestamps[timestamp] = [];
_pendingTimestamps[timestamp] = ListQueue();
}
_pendingTimestamps[timestamp]!.add(action);
}
Expand Down Expand Up @@ -246,58 +249,99 @@ abstract class Simulator {
/// If there are no timestamps pending to execute, nothing will execute.
static Future<void> tick() async {
if (_injectedActions.isNotEmpty) {
// injected actions will automatically be executed during tickExecute
await tickExecute(() {});
// case 1 : ( the usual Rohd case )
// The previous delta cycle did NOT do
// 'registerAction( _currentTimeStamp );'.
// In that case, _pendingTimestamps[_currentTimestamp] is null so we will
// add a new empty list, which will trigger a new delta cycle.
//
// case 2 :
// The previous delta cycle DID do 'registerAction( _currentTimestamp );'.
// In that case, there is *already* another tick scheduled for
// _currentTimestamp, and the injected actions will get called in
// the normal way.
//
// Either way, the end result is that a whole new tick gets scheduled for
// _currentTimestamp and any outstanding injected actions get executed.

// ignore: unnecessary_lambdas
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
_pendingTimestamps.putIfAbsent(_currentTimestamp, () => ListQueue());
}

// don't continue through the tick for injected actions, come back around
return;
// the main event loop
if (_updateTimeStamp()) {
_preTick();
await _mainTick();
_clkStable();
await _outOfTick();
}
}

/// Updates [_currentTimestamp] with the next time stamp.
///
/// Returns true iff there is a next time stamp.
///
/// Also updates [_pendingList] with the list of actions scheduled for this
/// timestamp.
///
/// If any of the actions in [_pendingList] schedule an action for
/// [_currentTimestamp], then this action is registered in the next delta
/// cycle. The next delta cycle is modelled as a new list of actions with the
/// same time as [_currentTimestamp].
static bool _updateTimeStamp() {
final nextTimeStamp = _pendingTimestamps.firstKey();

if (nextTimeStamp == null) {
return;
return false;
}

_currentTimestamp = nextTimeStamp;

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

await tickExecute(() async {
for (final func in pendingList) {
await func();
}
});
}

/// Executes all pending injected actions.
static Future<void> _executeInjectedActions() async {
while (_injectedActions.isNotEmpty) {
final injectedFunction = _injectedActions.removeFirst();
await injectedFunction();
}
// remove current list of actions but keep it for use in the mainTick phase
_pendingList = _pendingTimestamps.remove(_currentTimestamp)!;
return true;
}

/// Performs the actual execution of a collection of actions for a [tick()].
static Future<void> tickExecute(dynamic Function() toExecute) async {
/// Executes the preTick phase.
static void _preTick() {
_phase = SimulatorPhase.beforeTick;

// useful for flop sampling
_preTickController.add(null);
}

/// Executes the mainTick phase.
///
/// After [_startTickController] is notified, this method awaits all the
/// actions registered with this tick, removing the action from [_pendingList]
/// as it goes.
static Future<void> _mainTick() async {
_phase = SimulatorPhase.mainTick;

// useful for things that need to trigger every tick without other input
_startTickController.add(null);
await toExecute();

// execute the actions for this timestamp
while (_pendingList.isNotEmpty) {
await _pendingList.removeFirst()();
}
}

/// Executes the clkStable phase
static void _clkStable() {
_phase = SimulatorPhase.clkStable;

// useful for flop clk input stability
_clkStableController.add(null);
}

await _executeInjectedActions();
/// Executes the outOfTick phase
////
/// Just before we end the current tick, we execute the injected actions,
/// removing them from [_injectedActions] as we go.
static Future<void> _outOfTick() async {
while (_injectedActions.isNotEmpty) {
final injectedFunction = _injectedActions.removeFirst();
await injectedFunction();
}

_phase = SimulatorPhase.outOfTick;

Expand Down
Loading