fix(timers): apply machine accesses at bus completion
This commit is contained in:
parent
8930ea93b4
commit
bfecc87e84
20 changed files with 765 additions and 137 deletions
|
|
@ -56,7 +56,7 @@ The loaded value takes precedence over the updated base. Addresses and register
|
|||
Pre-indexed loads access the adjusted address; post-indexed loads access the original address.
|
||||
The discarded post-indexed writeback address is not a second memory access.
|
||||
Normal alignment, word rotation, odd-halfword behavior, and sign extension still apply.
|
||||
Loads preserve CPSR and use normal load timing. Device clocks advance after the read, with IRQ sampling on the next step.
|
||||
Loads preserve CPSR and use normal load timing. Machine timer reads sample at data-access completion; IRQ delivery waits until the next step.
|
||||
Read diagnostics leave CPU state and device clocks unchanged under the existing atomic-error policy.
|
||||
Stores with matching base/source still store the original base value before writeback.
|
||||
PC writeback, byte/halfword PC destinations, and unimplemented user-transfer encodings remain diagnostics.
|
||||
|
|
@ -359,7 +359,8 @@ The new settings apply to subsequent code accesses.
|
|||
Timed CPU execution uses an ordered `CpuTiming` transaction: source fetch, actual data accesses, internal work, then target-pair fetches.
|
||||
A failed instruction discards the entire transaction. Target samples do not add duplicate fetch or data costs.
|
||||
IRQ entry records its source fetch and vector pair in the same path.
|
||||
Device clocks still advance once after the successful step; this is not per-cycle scheduling.
|
||||
Machine timer state advances through these events and commits only after success. Other device clocks still advance in bulk afterward.
|
||||
This is not a complete per-cycle scheduler.
|
||||
The transaction also stages the nominal Game Pak prefetch queue described below.
|
||||
|
||||
### Game Pak opcode prefetch
|
||||
|
|
@ -387,7 +388,7 @@ Host reads and `Memory::advance_cycles` do not advance the queue; machine steps
|
|||
These are source-backed nominal rules, not a hardware conformance claim.
|
||||
See [queue evidence, regression coverage, and limits](../research/gamepak-prefetch.md).
|
||||
The [original cancellation probe](../research/prefetch-cancellation.md) matches published read interval totals at instruction boundaries.
|
||||
Its unadjusted timer samples still fail because timer reads precede the instruction's device-clock update.
|
||||
Its unadjusted timer samples now also match after [ordered timer accesses](../research/ordered-timer-access.md).
|
||||
|
||||
### Nominal CPU resume after DMA
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ CPU delivery waits until no DMA is ready. IRQ entry does not acknowledge IF.
|
|||
Timing uses independent source and destination bus costs, including WAITCNT and 128 KiB ROM boundaries.
|
||||
The first unit uses non-sequential accesses plus two internal cycles. Later units use sequential accesses.
|
||||
Each step reports zero code cycles. Overlapping transfers read each unit after the preceding write; they are not bulk host copies.
|
||||
Timers stage progress through startup, source, and destination phases. Timer reads sample after source completion; writes apply after destination completion.
|
||||
Timer IF acknowledgements use that same ordering. Success commits the staged bank; failure discards it.
|
||||
Other devices then advance in bulk without ticking timers twice. Exact physical DMA startup timing remains unverified.
|
||||
A successful unit breaks the next nominal CPU code-access sequence, even if DMA accessed only RAM.
|
||||
The resumed instruction requests N once; enabled opcode prefetch can still satisfy a matching request.
|
||||
RAM accesses and the nominal startup cycles advance an active prefetch stream; cartridge accesses cancel it.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Entry charges the discarded incoming-state PC+8/PC+4 fetch and the ARM vector pa
|
|||
DMA resume can make the discarded fetch non-sequential. Missing discarded bytes do not prevent IRQ entry.
|
||||
See [refill timing](../research/refill-fetch-timing.md) for history effects and remaining limits.
|
||||
A device event during a step can trigger IRQ entry once no DMA is ready and CPU masks allow delivery.
|
||||
Bus writes take effect before the step's device-clock update.
|
||||
Timer accesses use ordered bus-completion phases. Other device writes still take effect before their bulk clock update.
|
||||
`StepKind::Dma { channel }` identifies a DMA unit. `StepKind::HaltIdle` identifies one bounded HALT clock advance.
|
||||
`StepKind::StopIdle` identifies a stopped system with no clock progress.
|
||||
The other step kinds remain `Instruction` and `IrqEntry`.
|
||||
|
|
@ -23,14 +23,24 @@ The other step kinds remain `Instruction` and `IrqEntry`.
|
|||
Failed steps do not advance the clock or partially change CPU or device state. Earlier successful steps remain committed.
|
||||
|
||||
**The clock now uses instruction-specific costs, but it is not cycle-accurate.**
|
||||
Device clocks update in batches after instructions, exception entries, DMA units, or HALT idle intervals.
|
||||
Reads observe register state before that batch.
|
||||
Writes, including timer start/stop and IF acknowledgement, take effect before the entire batch.
|
||||
This does not reproduce bus-access timing within an instruction or IRQ synchronization delays.
|
||||
The [prefetch cancellation probe](../research/prefetch-cancellation.md) makes this limitation measurable.
|
||||
Its timer load omits its own source-fetch time, which differs between the read and control runs.
|
||||
Nominal instruction-boundary totals match published observations; the actual timer samples do not.
|
||||
The diagnostic reports both values and returns failure rather than correcting the timer sample.
|
||||
Timers now use a staged clock during machine instructions, IRQ entry, and DMA units.
|
||||
Source-fetch cycles advance that clock before data work. Each data access advances it before reading or writing timer registers.
|
||||
All byte lanes of one access share the same completion time. Internal and target-fetch cycles follow in order.
|
||||
A timer load therefore includes its source and data cycles, but not its trailing internal cycle.
|
||||
Timer-start writes do not count preceding cycles. Timer-stop writes count through their own bus completion.
|
||||
Block transfers preserve distinct word phases; combined reload/control writes merge at one phase, with reload bytes first.
|
||||
|
||||
The transaction also owns timer IF bits. Reads see overflows through the current access; acknowledgements clear earlier timer requests.
|
||||
A later internal cycle can raise an acknowledged timer request again. Non-timer IF bits retain their existing behavior.
|
||||
IRQ delivery still waits until the next machine step. A committed timer request can wake HALT, but not STOP.
|
||||
|
||||
Successful steps commit timers and timer IF bits. Failed steps discard staged counters, prescaler phases, reload/control writes, and requests.
|
||||
Display and other device clocks then advance once, without advancing timers again.
|
||||
Those devices still use the bulk scheduler. Mid-instruction display events, general bus arbitration, and IRQ synchronization delays remain unmodeled.
|
||||
Host clock advances and HALT idle batches retain ordinary bulk timer advancement; STOP idle does not advance timers.
|
||||
|
||||
The unchanged [prefetch cancellation probe](../research/prefetch-cancellation.md) now matches both published interval totals and actual timer samples.
|
||||
No offset is added to its timer values. See [clock ownership and evidence](../research/ordered-timer-access.md) for the bounded scope.
|
||||
|
||||
`Cpu::step` remains a CPU-only API. It does not honor HALT/STOP, execute DMA, advance device clocks, or sample device IRQs.
|
||||
`Cpu::step_timed` executes the same operation and returns `StepTiming`, without advancing devices.
|
||||
|
|
@ -78,9 +88,11 @@ Audio events and interrupt sources other than timers, display events, DMA comple
|
|||
`--timer-demo` executes original ARM code that configures Timer 0, IE, and IME.
|
||||
The timer overflows after 16 supplied cycles and enters the original handler through vector `0x18`.
|
||||
The handler stops Timer 0, acknowledges IF, increments r10, and returns with `SUBS pc, lr, #4`.
|
||||
The demo ends after 21 steps and 151 nominal cycles with one handler call, using default WAITCNT settings.
|
||||
The demo completes with one handler call, using default WAITCNT settings.
|
||||
Its initial ARM ROM fetch uses PC+8 sequential timing; cold pipeline filling has no separate startup charge.
|
||||
These counts test the timing model; they are not hardware timing measurements.
|
||||
The stopped counter is now `0xfff4`, rather than the earlier bulk-write result `0xfffb`.
|
||||
Timer start excludes its preceding nine cycles; timer stop includes its two bus cycles.
|
||||
These are nominal regression results, not hardware startup-delay measurements.
|
||||
Its test handler deliberately changes r2 and r10; it does not implement the Nintendo BIOS calling convention.
|
||||
|
||||
## Keypad interrupt control
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue