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

Feature 93 #95

Merged
merged 3 commits into from
Jan 1, 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
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@
public class AccurateFrequencyRunner {
private final static Logger LOGGER = LoggerFactory.getLogger(AccurateFrequencyRunner.class);

private final long slotNanos = SleepUtils.SLEEP_PRECISION;
private final double slotNanos = SleepUtils.SLEEP_PRECISION;
private final double slotMicros = slotNanos / 1000.0;

private final AtomicLong executedCyclesPerSlot = new AtomicLong();
private final AtomicLong executedCycles = new AtomicLong();

/**
* Runs the CPU.
Expand All @@ -55,38 +55,49 @@ public class AccurateFrequencyRunner {
*/
@SuppressWarnings("BusyWait")
public CPU.RunState run(Supplier<Double> getTargetFrequencyKHz, Supplier<CPU.RunState> runInstruction) {
final int cyclesPerSlot = Math.max(1, (int) (slotMicros * getTargetFrequencyKHz.get() / 1000.0));
// We need to compensate for 0
final double cyclesPerSlot = Math.max(1, slotMicros * getTargetFrequencyKHz.get() / 1000.0);

LOGGER.debug("Running CPU with {} cycles per slot", cyclesPerSlot);

CPU.RunState currentRunState = CPU.RunState.STATE_RUNNING;
long delayNanos = SleepUtils.SLEEP_PRECISION;
long targetCycles = (long) Math.ceil(cyclesPerSlot);

long emulationStartTime = System.nanoTime();
executedCyclesPerSlot.set(0);
executedCycles.set(0);
while (!Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
double computationStartTime = System.nanoTime();
while ((executedCycles.get() < targetCycles) &&
!Thread.currentThread().isInterrupted() &&
(currentRunState == CPU.RunState.STATE_RUNNING)) {
currentRunState = runInstruction.get();
}

double computationTime = System.nanoTime() - computationStartTime;

// Delay is actually the thing which "moves" the emulation forwards. Since the host CPU is much faster than
// the emulated one, th computationTime << slotNanos. Hence, we will have additional time we haven't used yet,
// to execute additional cycles, which will - again - run in a slotNanos time slot. And the situation repeats.
long delayNanos = (long) (slotNanos - computationTime);

try {
if (delayNanos > 0) {
// We do not require precise sleep here!
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayNanos));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}

long computationStartTime = System.nanoTime();

// We take into consideration real sleep time
long targetCycles = (computationStartTime - emulationStartTime) / slotNanos * cyclesPerSlot;
double slotEndTime = System.nanoTime();

while ((executedCyclesPerSlot.get() < targetCycles) &&
!Thread.currentThread().isInterrupted() &&
(currentRunState == CPU.RunState.STATE_RUNNING)) {
currentRunState = runInstruction.get();
}
final long tc = targetCycles;
executedCycles.updateAndGet(c -> c - tc);

long computationTime = System.nanoTime() - computationStartTime;
delayNanos = slotNanos - computationTime;
// After the slot is finished, we know not enough cycles were performed. Hence, we need to compensate
// with additional cycles - target cycles.
// We do not enforce precise sleep here, because we will take into account real sleep time.
targetCycles = (long) Math.ceil((slotEndTime - computationStartTime) / slotNanos * cyclesPerSlot);
}
return currentRunState;
}
Expand All @@ -99,6 +110,6 @@ public CPU.RunState run(Supplier<Double> getTargetFrequencyKHz, Supplier<CPU.Run
* @param cycles number of cycles
*/
public void addExecutedCycles(long cycles) {
executedCyclesPerSlot.addAndGet(cycles);
executedCycles.addAndGet(cycles);
}
}
Loading