Skip to content

Commit

Permalink
Merge pull request #95 from emustudio/feature-93
Browse files Browse the repository at this point in the history
Feature 93
  • Loading branch information
vbmacher committed Jan 1, 2024
2 parents 74951d8 + bccee06 commit 12ff7fa
Showing 1 changed file with 29 additions and 18 deletions.
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);
}
}

0 comments on commit 12ff7fa

Please sign in to comment.