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
|
|
@ -48,6 +48,8 @@ The repository is a Cargo workspace with three crates:
|
|||
| `crates/gba-core/src/cpu/fetch_history_tests.rs` | Shared sample consumption, no execution-entry rereads, and failed-step retry |
|
||||
| `crates/gba-core/src/cpu/fetch_boundary_tests.rs` | Actual fetch-region snapshots, split refills, IWRAM lane requirements, ROM-window boundaries, and nominal timing |
|
||||
| `crates/gba-core/src/io.rs` | I/O registers, timers, HALT/STOP wake-up, next-event bounds, and IRQ latches |
|
||||
| `crates/gba-core/src/io/timer_step.rs` | Staged timer bank and timer IF bits at ordered CPU/DMA bus phases |
|
||||
| `crates/gba-core/src/memory/timer_step_tests.rs` | Timer access phases, coherent lanes, cascades, IF ordering, rollback, DMA, idle, and clock ownership |
|
||||
| `crates/gba-core/src/display.rs` | Display clock, scanline status, comparison edges, and display IRQ events |
|
||||
| `crates/gba-core/src/dma.rs` | DMA registers, internal pointers, retained channel data, trigger state, priority, and completion IRQs |
|
||||
| `crates/gba-core/src/machine.rs` | Timed CPU/DMA/device stepping and IRQ delivery |
|
||||
|
|
@ -69,7 +71,7 @@ The repository is a Cargo workspace with three crates:
|
|||
| `crates/gba-demos/src/timer_demo.rs` | Original timer-configuration program and IRQ handler |
|
||||
| `crates/gba-demos/src/prefetch_probe.rs` | Original bounded ARM read/control probes and pinned published cancellation observations |
|
||||
| `crates/gba-demos/examples/prefetch_cancellation.rs` | CSV comparison of published, instruction-boundary, and unadjusted timer intervals; fails on mismatches |
|
||||
| `crates/gba-core/tests/prefetch_cancellation.rs` | Published read comparisons, cancellation phase, disabled-prefetch controls, and the known timer sampling gap |
|
||||
| `crates/gba-core/tests/prefetch_cancellation.rs` | Published read comparisons, cancellation phase, disabled-prefetch controls, and unadjusted timer samples |
|
||||
| `Cargo.toml` | Workspace members and shared package settings |
|
||||
| `crates/gba-core/src/lib.rs` | Core modules |
|
||||
| `crates/gba-demos/src/lib.rs` | Demo modules, original instruction/exception bytes, and raw input-test ROM |
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ direnv exec . cargo run --locked -p gba-demos --example prefetch_cancellation >
|
|||
```
|
||||
|
||||
This headless original ARM probe compares published read-cancellation observations with two separate emulator measurements.
|
||||
Instruction-boundary totals match, but actual timer samples currently fail. The example therefore exits with status 1.
|
||||
Do not treat the matching totals as a hardware-timing pass.
|
||||
See [the protocol, source hashes, and timer sampling gap](research/prefetch-cancellation.md).
|
||||
Both instruction-boundary totals and actual timer samples now match; the example exits with status 0.
|
||||
This is a bounded comparison with published observations, not a complete hardware-timing pass.
|
||||
See [the protocol, source hashes, and timer sampling correction](research/prefetch-cancellation.md).
|
||||
The probe needs no downloaded ROM, assets, window, or external assembler.
|
||||
|
||||
## Open the affine raster demo
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -209,12 +209,20 @@ Game Pak prefetch research:
|
|||
- [Jgenesis prefetch at fab6e2cc](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/prefetch.rs) and its [bus integration](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/bus.rs), for independent halfword capacity, full-buffer pause/restart, CPU delivery, and cancellation rules.
|
||||
- [NanoBoyAdvance boundary test at cc3f4a28](https://codeberg.org/nba-emu/hw-test/src/commit/cc3f4a286cdef823980d9b353bd70befc9927d28/bus/128kb-boundary/source/main.c), reviewed for data LDM/DMA boundaries, not active opcode-queue conformance.
|
||||
- [PrefetchAbuse source at 9ca57c13](https://github.com/zaydlang/PrefetchAbuse/blob/9ca57c13da7e3c569937f99a42e7c1caca029a2d/src/main.c), its [README](https://github.com/zaydlang/PrefetchAbuse/blob/9ca57c13da7e3c569937f99a42e7c1caca029a2d/README.md), and [Makefile](https://github.com/zaydlang/PrefetchAbuse/blob/9ca57c13da7e3c569937f99a42e7c1caca029a2d/Makefile), for published read observations, actual multiply delays, and build assumptions. No upstream program or ROM was imported.
|
||||
See [the original cancellation comparison](research/prefetch-cancellation.md) for hashes, correspondence limits, matching nominal intervals, and failing timer samples.
|
||||
See [the original cancellation comparison](research/prefetch-cancellation.md) for hashes, correspondence limits, and the initial timer sampling failure.
|
||||
Both nominal intervals and unadjusted timer samples now match after ordered timer accesses.
|
||||
|
||||
See [prefetch behavior and evidence](research/gamepak-prefetch.md).
|
||||
The nominal queue is implemented. Source differences and live WAITCNT transitions remain validation questions.
|
||||
No hardware conformance result is claimed.
|
||||
|
||||
Ordered timer data accesses:
|
||||
|
||||
- The pinned [Jgenesis bus at fab6e2cc](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/bus.rs) charges one I/O cycle before register reads/writes and passes the resulting clock to timers.
|
||||
- The pinned PrefetchAbuse read observations above isolate the missing source-fetch interval in the previous bulk timer model.
|
||||
|
||||
See [staged timer clock ownership](research/ordered-timer-access.md). Relative probe agreement does not establish all absolute timer edges.
|
||||
|
||||
Use [GBATEK](https://problemkaputt.de/gbatek.htm) and ARM7TDMI documentation for further hardware work.
|
||||
Only use game ROMs that you may lawfully use. Do not commit game ROMs, BIOS files, or game assets.
|
||||
The ignore file excludes `roms/`, common game ROM extensions, and save files.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Background progress cannot cause an early ROM-file lookup or change instruction
|
|||
- [NanoBoyAdvance 128 KiB boundary test at cc3f4a28](https://codeberg.org/nba-emu/hw-test/src/commit/cc3f4a286cdef823980d9b353bd70befc9927d28/bus/128kb-boundary/source/main.c) tests data LDM and DMA boundaries.
|
||||
Reading this test does not validate active opcode-queue crossings. It was not run here.
|
||||
- [PrefetchAbuse at 9ca57c13](https://github.com/zaydlang/PrefetchAbuse/blob/9ca57c13da7e3c569937f99a42e7c1caca029a2d/src/main.c) supplies published hardware read-cancellation observations.
|
||||
A later [original probe](prefetch-cancellation.md) matches the instruction-boundary differences, but exposes incorrect bulk timer sampling.
|
||||
The [original probe](prefetch-cancellation.md) matches instruction-boundary differences and, after ordered timer accesses, actual timer samples.
|
||||
The source, README, and build assumptions were reviewed. No upstream implementation or ROM was imported.
|
||||
|
||||
No external implementation or ROM was added to the repository.
|
||||
|
|
@ -76,7 +76,7 @@ A later ROM fetch still needs an exact head match to use queued timing.
|
|||
|
||||
Success commits queue progress. A diagnostic discards all staged progress, including earlier successful data accesses.
|
||||
Missing lookahead or target bytes retain deferred diagnostics and nominal attempted-fetch costs.
|
||||
Device clocks still advance once after a successful machine step, not after each timing event.
|
||||
Machine timer transactions now follow these events and commit on success. Other devices still advance in bulk after the step.
|
||||
|
||||
`Cpu::step_timed` updates the queue and returns costs without advancing devices.
|
||||
`Cpu::step` uses the same path when prefetch is enabled, but discards returned costs.
|
||||
|
|
@ -135,8 +135,8 @@ Public ARM, Thumb, memory, and BIOS reports match the preceding ordered-timing c
|
|||
Debug and release reports also match. Original CPU and timer demo traces are unchanged.
|
||||
These public checkpoints do not independently validate active prefetch timing.
|
||||
|
||||
The later [cancellation comparison](prefetch-cancellation.md) matches published read observations only at instruction boundaries.
|
||||
Actual timer readings still fail because device updates occur after the timer-load instruction.
|
||||
Next, implement ordered timer observations and validate full-buffer restart and page boundaries independently.
|
||||
The [cancellation comparison](prefetch-cancellation.md) originally matched published read observations only at instruction boundaries.
|
||||
[Ordered timer accesses](ordered-timer-access.md) now make the unchanged timer readings match as well.
|
||||
Next, validate absolute timer edges, full-buffer restart, and page boundaries independently.
|
||||
Cold-start timing, live WAITCNT reconfiguration, per-access device scheduling, and sub-instruction DMA arbitration remain incomplete.
|
||||
Exact bus history, display contention, configurable EWRAM timing, and timer/IRQ delays remain separate work.
|
||||
|
|
|
|||
100
docs/research/ordered-timer-access.md
Normal file
100
docs/research/ordered-timer-access.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# Ordered timer data accesses
|
||||
|
||||
## Result
|
||||
|
||||
Machine timer reads and writes now use bus-access completion times instead of the instruction's initial clock.
|
||||
The unchanged [prefetch cancellation probe](prefetch-cancellation.md) matches both published interval totals and unadjusted timer readings.
|
||||
Its program, published observations, and measurement code were not changed to obtain this result.
|
||||
|
||||
This is a timer-specific extension to nominal timing, not a complete cycle-accurate device scheduler.
|
||||
Display events, DMA arbitration, timer startup delays, and IRQ synchronization remain separate limits.
|
||||
No physical-hardware measurement or external-emulator differential run was performed locally.
|
||||
|
||||
## Evidence and phase choice
|
||||
|
||||
The preceding probe isolated the error: its missing interval equaled the difference between the final source-fetch costs.
|
||||
The opcode queue computed those costs correctly, but the timer read occurred before they advanced device time.
|
||||
Moving only the probe's reported result would conceal that error rather than correct device behavior.
|
||||
|
||||
The reviewed [Jgenesis bus at fab6e2cc](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/bus.rs) charges the I/O cycle before register access.
|
||||
Its timer reads and writes receive the resulting clock value.
|
||||
This supports the implemented completion-phase rule for a one-cycle I/O access.
|
||||
No source code was imported from that implementation.
|
||||
|
||||
The published subtraction experiment validates relative read intervals.
|
||||
It does not independently establish every absolute timer edge, startup delay, or control-write phase.
|
||||
Those remain explicit validation limits.
|
||||
|
||||
## Staged clock ownership
|
||||
|
||||
`Memory` owns an optional `TimerStep` during each machine instruction, IRQ entry, or DMA unit.
|
||||
The transaction copies only the four timers and timer IF bits, not RAM, display state, or the entire I/O object.
|
||||
Its elapsed timestamp starts at zero and increases monotonically.
|
||||
It has no heap allocation or event log.
|
||||
|
||||
For a CPU instruction:
|
||||
|
||||
1. Stage timer progress through the source fetch.
|
||||
2. For each data access, stage its elapsed cycles before reading or writing timer registers.
|
||||
3. Stage internal cycles and any target-pair fetches in their existing order.
|
||||
4. On success, commit timers and timer IF bits at the final timestamp.
|
||||
5. Advance the other devices and the machine clock once, without ticking timers again.
|
||||
|
||||
One word access has one sampling time for all four byte lanes.
|
||||
Reload bytes merge before control bytes at that same time.
|
||||
Block transfers have separate completion times for successive words.
|
||||
A start write cannot count earlier source cycles. A stop write includes clocks through its completion.
|
||||
Prescaler remainders, cascaded overflow pulses, and reload behavior use the existing timer implementation.
|
||||
|
||||
Timer IF reads include requests raised through the access phase.
|
||||
Acknowledgement clears timer requests already present at that phase; later work can raise them again.
|
||||
Committing timer flags preserves non-timer IF bits.
|
||||
Those other sources still follow the existing bulk event model, so IF is not a fully cycle-accurate cross-device snapshot.
|
||||
IRQ delivery remains at machine-step boundaries, never halfway through an instruction.
|
||||
A committed enabled timer request wakes HALT. STOP does not use timer requests as a wake source.
|
||||
|
||||
### DMA
|
||||
|
||||
Each unit uses the existing nominal two-cycle startup on its first transfer.
|
||||
Timer sampling follows startup plus source cost; destination writes follow the destination cost.
|
||||
A failed source or destination discards the timer transaction.
|
||||
A successful unit commits timer state before other device clocks and DMA completion IRQ handling.
|
||||
This extends the existing whole-unit scheduler; it does not establish physical DMA startup or preemption timing.
|
||||
|
||||
### Errors and API isolation
|
||||
|
||||
CPU diagnostics discard staged time, counter values, prescaler remainders, and pending timer requests.
|
||||
This includes failures before instruction fetch and failures after earlier block-load accesses.
|
||||
Existing store validation still prevents partial writes when a block transfer has an invalid destination.
|
||||
Previously completed machine steps remain committed.
|
||||
|
||||
`Cpu::step` and `Cpu::step_timed` remain CPU-only APIs.
|
||||
They update CPU state, ordinary register writes, instruction retention, and enabled opcode-prefetch timing as before.
|
||||
They do not create a timer transaction or advance device clocks.
|
||||
Host reads and writes likewise remain clock-free.
|
||||
|
||||
`Memory::advance_cycles` and HALT idle intervals use the existing bulk timer advancement.
|
||||
STOP idle freezes all clocks. The instruction entering STOP still pays its complete nominal duration.
|
||||
Instruction fetches from I/O are not given the data-access timer sampling rule.
|
||||
Other device reads, writes, rendering, and event scheduling retain their documented limits.
|
||||
|
||||
## Validation
|
||||
|
||||
Original tests cover ARM/Thumb loads, coherent byte lanes, start/stop stores, and ordered block transfers.
|
||||
They check old-reload overflow behavior, prescaler retention, cascades, timer IF reads, acknowledgement, and later reassertion.
|
||||
Other tests cover non-timer IF preservation, deferred IRQ delivery, DMA phases, rollback, and CPU-only isolation.
|
||||
Capture-enabled and capture-disabled steps check that display clock splits do not tick timers twice.
|
||||
BIOS HALT/STOP stores check final-cycle ownership and timer wake behavior.
|
||||
|
||||
The cancellation example exits successfully in debug and release on Darwin arm64.
|
||||
Its CSV reports match between build profiles. No correction is applied to loaded timer values.
|
||||
Workspace/core/demo tests, formatting, clippy, rustdoc, Python tests, native ROM windows, and graphics smoke modes pass.
|
||||
Pinned public ARM, Thumb, memory, and BIOS reports remain byte-identical to the preceding change.
|
||||
|
||||
The original timer demo retains its cycle count and IRQ-entry step.
|
||||
Its stopped counter changes from `0xfffb` to `0xfff4`: start excludes nine preceding cycles, while stop includes two bus cycles.
|
||||
The alias-load regression now samples `0x00c0fffb` after source and data cycles, instead of the previous `0x00c0fffa`.
|
||||
These expectation changes follow the access phases; the independent cancellation program and observations remain unchanged.
|
||||
|
||||
Next, validate absolute timer startup/control edges and independently test full-buffer prefetch restart and page boundaries.
|
||||
Extend other device observations only with explicit clock ownership and rollback coverage.
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
An original ARM probe reproduces the measured structure of the read cases in `zaydlang/PrefetchAbuse`.
|
||||
Its instruction-boundary cycle differences match every published read observation.
|
||||
Its actual emulated timer readings do **not** match those observations.
|
||||
The difference isolates an existing timer-sampling limitation, not a new queue discrepancy.
|
||||
No core timing behavior was changed for this comparison.
|
||||
Its actual emulated timer readings now also match, after [ordered timer accesses](ordered-timer-access.md).
|
||||
The initial comparison isolated a bulk timer-sampling error, not a queue discrepancy.
|
||||
The original program, published observations, and measurement code remain unchanged.
|
||||
|
||||
This is not a run or a port of the upstream ROM.
|
||||
The expected values are published measurement facts. The program builder and runner are original.
|
||||
|
|
@ -70,10 +70,10 @@ direnv exec . cargo test --locked -p gba-core --test prefetch_cancellation
|
|||
direnv exec . cargo run --locked -p gba-demos --example prefetch_cancellation > /tmp/prefetch-cancellation.csv
|
||||
```
|
||||
|
||||
**The example currently exits with status 1 because the timer comparisons fail.**
|
||||
That status is expected evidence of a known limit, not a passing hardware-timing test.
|
||||
A matching instruction-boundary total cannot override a failed timer comparison.
|
||||
Status 0 requires both comparisons to match for every case. Status 2 reports invalid arguments or execution errors.
|
||||
**The example now exits with status 0 because both comparisons match.**
|
||||
It previously exited with status 1, exposing the bulk timer-sampling error described below.
|
||||
A matching instruction-boundary total still cannot override a failed timer comparison.
|
||||
Status 0 requires both comparisons to match for every case. Status 1 reports mismatches; status 2 reports invalid arguments or execution errors.
|
||||
The example takes no arguments and needs no downloaded files, window, or external assembler.
|
||||
|
||||
Use `--release` to repeat the same checks in a release build.
|
||||
|
|
@ -90,11 +90,11 @@ The CSV columns are:
|
|||
No timing correction is applied to `timer_delta`.
|
||||
The runner does not hide, patch, or reinterpret failing timer values.
|
||||
|
||||
## Root cause of the timer mismatch
|
||||
## Historical timer mismatch and correction
|
||||
|
||||
The opcode queue sees ordered source, data, and internal costs.
|
||||
The timer device still updates only after the entire instruction completes.
|
||||
Consequently, the timer load reads the old counter before its own source-fetch cost advances device time.
|
||||
Before the correction, the opcode queue saw ordered source, data, and internal costs.
|
||||
The timer device updated only after the entire instruction completed.
|
||||
Consequently, the timer load read the old counter before its own source-fetch cost advanced device time.
|
||||
|
||||
The read run cancels prefetch with ROM data traffic.
|
||||
Its subsequent timer load must fetch code from ROM again.
|
||||
|
|
@ -107,22 +107,24 @@ For a one-cycle multiply and WAITCNT `0x4000`:
|
|||
| --- | ---: |
|
||||
| Published interval difference | 17 |
|
||||
| Machine-clock boundary difference | 17 |
|
||||
| Actual timer-sample difference | 14 |
|
||||
| Previous timer-sample difference | 14 |
|
||||
| Current timer-sample difference | 17 |
|
||||
| Timer-load source fetch in the read run | 8 |
|
||||
| Timer-load source fetch in the control run | 5 |
|
||||
|
||||
The missing three cycles equal `8 - 5`.
|
||||
For every compared case, the boundary-minus-timer discrepancy equals the final source-fetch cost difference.
|
||||
Before the correction, each boundary-minus-timer discrepancy equaled the final source-fetch cost difference.
|
||||
The sample instructions have identical data and internal costs, so those costs cancel in the subtraction.
|
||||
|
||||
With prefetch disabled, the two final source-fetch costs match.
|
||||
The timer and boundary differences then match each other, and the cancellation-specific extra cycle disappears.
|
||||
This negative control helps separate the sampling problem from timer subtraction or multiply decoding errors.
|
||||
|
||||
The regression tests explicitly mark the bulk-sampling expectation as a known gap.
|
||||
Replace that expectation when timer I/O moves to ordered bus phases.
|
||||
Do not make the probe pass by adding an offset to its loaded timer value.
|
||||
Timer startup/stop placement, other device events, and atomic failure behavior also need coverage before changing clock ownership.
|
||||
Timer data accesses now sample staged device state at bus completion, including the source and data cycles.
|
||||
Timer writes use the same phase. Successful steps commit that state; failed steps discard it.
|
||||
The regression now requires actual timer values to match the unchanged published observations.
|
||||
No offset was added to the loaded value or diagnostic report.
|
||||
See [ordered timer accesses](ordered-timer-access.md) for start/stop, IF, DMA, API isolation, and rollback coverage.
|
||||
|
||||
## Coverage and limits
|
||||
|
||||
|
|
@ -139,5 +141,6 @@ The largest multiply delay here does not establish eight-halfword capacity behav
|
|||
Live WAITCNT changes, DMA, HALT/STOP edges, and exact hardware timer startup delays remain outside this probe.
|
||||
See [the queue model](gamepak-prefetch.md) for those separate limits.
|
||||
|
||||
Next, move timer observations toward ordered bus-access phases with explicit rollback and no duplicate device advancement.
|
||||
Ordered timer observations now retain rollback and avoid duplicate device advancement.
|
||||
Absolute timer startup and control-edge validation remain necessary.
|
||||
Independent full-buffer and page-boundary measurements remain necessary before claiming complete prefetch timing accuracy.
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ Update this file when a feature lands or a limit is removed.
|
|||
- Persistent fetch access kinds follow CPU data/internal work, compose with DMA resume, and reset after target refills.
|
||||
- Game Pak wait-state control (`WAITCNT`) for all three ROM windows.
|
||||
- Bit 14 enables a nominal eight-halfword opcode prefetch queue, with partial fetches, cancellation, and DMA/idle integration.
|
||||
- An original ARM cancellation probe matches published read interval totals, while separately reporting incorrect bulk timer samples.
|
||||
- Staged timer clocks follow CPU and DMA bus-completion phases, including timer IF reads/acknowledgements and failed-step rollback.
|
||||
- The unchanged original ARM cancellation probe matches both published read interval totals and its unadjusted timer samples.
|
||||
- Sequential/non-sequential data accesses, branch refill costs, and variable multiply timing.
|
||||
- Optional caller-supplied 16 KiB BIOS mapping, with CPU read protection based on retained ARM PC+8 or aligned Thumb PC+4 words.
|
||||
- Optional original BIOS replacement: minimal boot, SoftReset, selective RegisterRamReset, IRQ dispatch, interrupt waits, memory copy/fill, integer/fixed-point arithmetic, affine matrices, bit unpacking, LZ77/run-length/Huffman decompression, and differential filters.
|
||||
|
|
@ -121,7 +122,7 @@ CPU/bus costs are nominal estimates, not a cycle-accurate implementation.
|
|||
The instruction buffer retains the next fetch kind; cold startup costs and per-access device scheduling remain unmodeled.
|
||||
Game Pak prefetch uses source-backed queue rules, not hardware-verified timing.
|
||||
Live WAITCNT transitions, page-boundary behavior, and exact DMA/idle ordering still need independent timing validation.
|
||||
Timer reads occur before the current instruction's device update. Prefetch cancellation probes expose the resulting sample error.
|
||||
Timer data accesses follow ordered nominal bus phases. Other devices remain instruction-batched; exact timer startup and IRQ delays remain unverified.
|
||||
Graphics support includes Mode 0–5 debug snapshots and HBlank row capture with sprites, mosaic, windows, and color effects.
|
||||
Internal affine origins, vertical window flags, and vertical mosaic counters track line boundaries.
|
||||
Horizontal window comparators retain four-cycle event history.
|
||||
|
|
@ -173,8 +174,8 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
|
|||
Validate BIOS snapshots with independent hardware tests before claiming exact refill or data-access history.
|
||||
The desktop runner still uses only the original BIOS. Keep protected reads image-derived for all supplied images.
|
||||
2. Validate the active nominal Game Pak prefetch queue against independent timing tests.
|
||||
The [cancellation probe](research/prefetch-cancellation.md) isolates a timer sampling gap despite matching nominal interval totals.
|
||||
Move timer observations to ordered bus phases without losing rollback or advancing devices twice.
|
||||
The [cancellation probe](research/prefetch-cancellation.md) now matches after [ordered timer accesses](research/ordered-timer-access.md).
|
||||
Validate absolute timer startup/control edges and extend ordered observations to other devices without duplicate clocks or lost rollback.
|
||||
Validate full-buffer restart, page boundaries, live WAITCNT transitions, and cold-start costs.
|
||||
See [prefetch behavior and evidence](research/gamepak-prefetch.md); exact bus arbitration and timer/IRQ delays remain separate work.
|
||||
3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available.
|
||||
|
|
|
|||
|
|
@ -276,13 +276,17 @@ Integration tests check RAM/internal progress, ROM data stalls, branch and IRQ f
|
|||
WAITCNT CPU/host/DMA writes, RAM/cart DMA, HALT/STOP, failed steps, and missing ROM bytes have separate checks.
|
||||
See [prefetch evidence and limits](research/gamepak-prefetch.md). These tests validate the nominal model, not physical-hardware accuracy.
|
||||
An [original cancellation probe](research/prefetch-cancellation.md) compares read/control intervals with pinned, published hardware observations.
|
||||
Its instruction-boundary totals match; its actual timer samples fail. The example returns nonzero for those failures.
|
||||
Regressions identify the missing source-fetch interval without applying a correction to the timer values.
|
||||
Both its instruction-boundary totals and actual timer samples now match. Any mismatch still returns a nonzero status.
|
||||
Regressions retain the original program and published observations without correcting the loaded timer values.
|
||||
Disabled-prefetch controls, delay bounds, completion PCs, and fresh-run determinism have separate checks.
|
||||
Instruction timing tests cover arithmetic, shifts, branches, transfers, block operations, multiply, and exception returns.
|
||||
Timed and untimed execution are compared across all 65,536 Thumb encodings with fixed initial registers.
|
||||
Timer/IRQ tests verify that changing WAITCNT changes when an interrupt is sampled.
|
||||
Timer tests cover all prescalers, reload changes, start/stop behavior, byte writes, large clock advances, and cascades.
|
||||
Ordered timer tests cover ARM/Thumb sampling, coherent byte lanes, block-transfer phases, reload/control ordering, and prescaler retention.
|
||||
They verify timer IF reads/acknowledgements, preservation of other IF bits, deferred IRQ delivery, DMA phases, and HALT/STOP ownership.
|
||||
Failures before fetch and after partial data work must discard staged time and requests. CPU-only APIs must not advance timer devices.
|
||||
Display-capture splits must not tick committed timers a second time. See [ordered timer accesses](research/ordered-timer-access.md).
|
||||
Bulk timer advancement is compared against an independent cycle-by-cycle reference for each prescaler and cascade configuration.
|
||||
Machine tests cover ARM/Thumb IRQ return, pending-request gating, acknowledgement, and the complete timer demo.
|
||||
Failed I/O block transfers are checked for partial register writes, hidden reload changes, and unintended IF acknowledgement.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue