-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The simulator clock was previously tied to the host system time. This causes inconsistency across simulation sessions, and also meant that the internal timer accuracy was low, as the timer resolution was limited by the host OS scheduling. This changes the simulator clock to run independently from the system clock, and increases the internal clock resolution from microseconds to nanosecond. It also introduces a new `Simulator` class, which manages the execution of the simulated core (previously, this functionality has been part of the `RP2040` class).
- Loading branch information
Showing
21 changed files
with
405 additions
and
359 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,23 @@ | ||
import * as fs from 'fs'; | ||
import { RP2040 } from '../src/index.js'; | ||
import { GDBTCPServer } from '../src/gdb/gdb-tcp-server.js'; | ||
import { Simulator } from '../src/simulator.js'; | ||
import { bootromB1 } from './bootrom.js'; | ||
import { loadHex } from './intelhex.js'; | ||
import { GDBTCPServer } from '../src/gdb/gdb-tcp-server.js'; | ||
|
||
// Create an array with the compiled code of blink | ||
// Execute the instructions from this array, one by one. | ||
const hex = fs.readFileSync('hello_uart.hex', 'utf-8'); | ||
const mcu = new RP2040(); | ||
const simulator = new Simulator(); | ||
const mcu = simulator.rp2040; | ||
mcu.loadBootrom(bootromB1); | ||
loadHex(hex, mcu.flash, 0x10000000); | ||
|
||
const gdbServer = new GDBTCPServer(mcu, 3333); | ||
const gdbServer = new GDBTCPServer(simulator, 3333); | ||
console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`); | ||
|
||
mcu.uart[0].onByte = (value) => { | ||
process.stdout.write(new Uint8Array([value])); | ||
}; | ||
|
||
mcu.core.PC = 0x10000000; | ||
mcu.execute(); | ||
simulator.rp2040.core.PC = 0x10000000; | ||
simulator.execute(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,12 @@ | ||
export interface IClockTimer { | ||
pause(currentMicros: number): void; | ||
resume(currentMicros: number): void; | ||
export type AlarmCallback = () => void; | ||
|
||
export interface IAlarm { | ||
schedule(deltaNanos: number): void; | ||
cancel(): void; | ||
} | ||
|
||
export interface IClock { | ||
readonly micros: number; | ||
|
||
pause(): void; | ||
|
||
resume(): void; | ||
|
||
createTimer(deltaMicros: number, callback: () => void): IClockTimer; | ||
readonly nanos: number; | ||
|
||
deleteTimer(timer: IClockTimer): void; | ||
createAlarm(callback: AlarmCallback): IAlarm; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,7 @@ | ||
import { IClock, IClockTimer } from './clock.js'; | ||
|
||
export class MockClockTimer implements IClockTimer { | ||
constructor( | ||
readonly micros: number, | ||
readonly callback: () => void, | ||
) {} | ||
|
||
pause() { | ||
/* intentionally empty */ | ||
} | ||
|
||
resume() { | ||
/* intentionally empty */ | ||
} | ||
} | ||
|
||
export class MockClock implements IClock { | ||
micros: number = 0; | ||
|
||
readonly timers: MockClockTimer[] = []; | ||
|
||
pause() { | ||
/* intentionally empty */ | ||
} | ||
|
||
resume() { | ||
/* intentionally empty */ | ||
} | ||
import { SimulationClock } from './simulation-clock.js'; | ||
|
||
export class MockClock extends SimulationClock { | ||
advance(deltaMicros: number) { | ||
const { timers } = this; | ||
const targetTime = this.micros + Math.max(deltaMicros, 0.01); | ||
while (timers[0] && timers[0].micros <= targetTime) { | ||
const timer = timers.shift(); | ||
if (timer) { | ||
this.micros = timer.micros; | ||
timer.callback(); | ||
} | ||
} | ||
} | ||
|
||
createTimer(deltaMicros: number, callback: () => void) { | ||
const timer = new MockClockTimer(this.micros + deltaMicros, callback); | ||
this.timers.push(timer); | ||
this.timers.sort((a, b) => a.micros - b.micros); | ||
return timer; | ||
} | ||
|
||
deleteTimer(timer: IClockTimer) { | ||
const timerIndex = this.timers.indexOf(timer as MockClockTimer); | ||
if (timerIndex >= 0) { | ||
this.timers.splice(timerIndex, 1); | ||
} | ||
this.tick(this.nanos + deltaMicros * 1000); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { AlarmCallback, IAlarm, IClock } from './clock.js'; | ||
|
||
type ClockEventCallback = () => void; | ||
|
||
export class ClockAlarm implements IAlarm { | ||
next: ClockAlarm | null = null; | ||
nanos: number = 0; | ||
scheduled = false; | ||
|
||
constructor( | ||
private readonly clock: SimulationClock, | ||
readonly callback: AlarmCallback, | ||
) {} | ||
|
||
schedule(deltaNanos: number): void { | ||
if (this.scheduled) { | ||
this.cancel(); | ||
} | ||
this.clock.linkAlarm(deltaNanos, this); | ||
} | ||
|
||
cancel(): void { | ||
this.clock.unlinkAlarm(this); | ||
this.scheduled = false; | ||
} | ||
} | ||
|
||
export class SimulationClock implements IClock { | ||
private nextAlarm: ClockAlarm | null = null; | ||
|
||
private nanosCounter = 0; | ||
|
||
constructor(readonly frequency = 125e6) {} | ||
|
||
get nanos() { | ||
return this.nanosCounter; | ||
} | ||
|
||
get micros() { | ||
return this.nanos / 1000; | ||
} | ||
|
||
createAlarm(callback: ClockEventCallback) { | ||
return new ClockAlarm(this, callback); | ||
} | ||
|
||
linkAlarm(nanos: number, alarm: ClockAlarm) { | ||
alarm.nanos = this.nanos + nanos; | ||
let alarmListItem = this.nextAlarm; | ||
let lastItem = null; | ||
while (alarmListItem && alarmListItem.nanos < alarm.nanos) { | ||
lastItem = alarmListItem; | ||
alarmListItem = alarmListItem.next; | ||
} | ||
if (lastItem) { | ||
lastItem.next = alarm; | ||
alarm.next = alarmListItem; | ||
} else { | ||
this.nextAlarm = alarm; | ||
alarm.next = alarmListItem; | ||
} | ||
alarm.scheduled = true; | ||
return alarm; | ||
} | ||
|
||
unlinkAlarm(alarm: ClockAlarm) { | ||
let alarmListItem = this.nextAlarm; | ||
if (!alarmListItem) { | ||
return false; | ||
} | ||
let lastItem = null; | ||
while (alarmListItem) { | ||
if (alarmListItem === alarm) { | ||
if (lastItem) { | ||
lastItem.next = alarmListItem.next; | ||
} else { | ||
this.nextAlarm = alarmListItem.next; | ||
} | ||
return true; | ||
} | ||
lastItem = alarmListItem; | ||
alarmListItem = alarmListItem.next; | ||
} | ||
return false; | ||
} | ||
|
||
tick(deltaNanos: number) { | ||
const targetNanos = this.nanosCounter + deltaNanos; | ||
let alarm = this.nextAlarm; | ||
while (alarm && alarm.nanos <= targetNanos) { | ||
this.nextAlarm = alarm.next; | ||
this.nanosCounter = alarm.nanos; | ||
alarm.callback(); | ||
alarm = this.nextAlarm; | ||
} | ||
this.nanosCounter = targetNanos; | ||
} | ||
|
||
get nanosToNextAlarm() { | ||
if (this.nextAlarm) { | ||
return this.nextAlarm.nanos - this.nanos; | ||
} | ||
return 0; | ||
} | ||
} |
Oops, something went wrong.