feat(timing): break CPU sequential access after DMA
Retain one pending non-sequential resume after successful DMA. Apply it to the next nominal non-refill code access and consume it only on successful CPU or IRQ progress. Preserve failed-step atomicity, host and idle isolation, existing refill costs, and WAITCNT ordering. Add 13 original regressions for ARM/Thumb widths, ROM wait settings, sources, preemption, retries, mixed APIs, idle states, and interrupt timing. Record pinned hardware-test source and emulator evidence without claiming physical-hardware or full-pipeline conformance. Validated on Darwin arm64: 952 workspace and 883 core/demo tests per debug/release profile, 39 Python tests, native ROM windows, ten window smoke modes, fmt, clippy, and rustdoc. Public ARM/Thumb/memory/BIOS reports remain unchanged.
This commit is contained in:
parent
663b25e5d1
commit
16286e0cbd
11 changed files with 437 additions and 4 deletions
|
|
@ -47,7 +47,8 @@ The repository is a Cargo workspace with three crates:
|
|||
| `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 |
|
||||
| `crates/gba-core/src/timing.rs` | Bus widths, wait-state costs, and timing breakdowns |
|
||||
| `crates/gba-core/src/cpu/timing.rs` | ARM/Thumb cycle summaries and data-access accounting |
|
||||
| `crates/gba-core/src/cpu/timing.rs` | ARM/Thumb cycle summaries, nominal DMA resume, and data-access accounting |
|
||||
| `crates/gba-core/src/cpu/dma_resume_tests.rs` | One-shot non-sequential DMA resume costs, wait settings, failures, idle, IRQs, and API isolation |
|
||||
| `crates/gba-core/src/cpu/timing_tests.rs` | Instruction timing and semantic-equivalence checks |
|
||||
| `crates/gba-demos/src/timer_demo.rs` | Original timer-configuration program and IRQ handler |
|
||||
| `Cargo.toml` | Workspace members and shared package settings |
|
||||
|
|
|
|||
|
|
@ -288,6 +288,24 @@ The writable mask is `0x5fff`; the Game Pak type flag reads as GBA, and the uppe
|
|||
A CPU write to WAITCNT does not retroactively change that instruction's code-access cost.
|
||||
The new settings apply to subsequent code accesses.
|
||||
|
||||
### Nominal CPU resume after DMA
|
||||
|
||||
A successful DMA unit marks the next CPU instruction's nominal code access non-sequential.
|
||||
This applies even when DMA only accesses RAM. Consecutive units retain one pending resume, not multiple penalties.
|
||||
A non-refill instruction uses its normal width and current WAITCNT with N instead of S.
|
||||
ARM word accesses retain an S cost for their second halfword.
|
||||
Stores and ROM boundaries that already use N do not receive an additional cost.
|
||||
|
||||
Successful timed or untimed CPU execution consumes pending resume, including skipped conditions and internal-memory instructions.
|
||||
Successful machine IRQ entry also consumes it. Failed CPU/DMA steps preserve it.
|
||||
Host access, clock-only advancement, and HALT/STOP idle do not consume it.
|
||||
A failed DMA unit cannot create pending resume.
|
||||
DMA WAITCNT writes apply before resume; CPU WAITCNT writes still use the previous code-access settings.
|
||||
|
||||
Branch and exception refill summaries retain their existing destination-based `1N+2S` costs, without an added resume access.
|
||||
The old-PC fetch is not modeled separately. This remains nominal timing, not a per-access pipeline.
|
||||
See [the evidence and limitations](../research/dma-resume-timing.md).
|
||||
|
||||
Important timing limits:
|
||||
|
||||
- Code S/N counts follow instruction summaries, not a simulated fetch pipeline.
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ 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.
|
||||
Startup scheduling delays, channel-resumption costs, CPU fetch-state effects, and display-bus contention remain unmodeled.
|
||||
A successful unit breaks the next nominal CPU code-access sequence, even if DMA accessed only RAM.
|
||||
The resumed non-refill instruction uses N code timing once; later instructions return to their normal summaries.
|
||||
See [CPU resume timing](cpu.md#nominal-cpu-resume-after-dma) for failure, idle, and refill rules.
|
||||
Startup scheduling delays, channel-resumption costs, a full CPU fetch pipeline, and display-bus contention remain unmodeled.
|
||||
|
||||
Special timing modes (sound FIFO/video capture), Game Pak DRQ, and prohibited source mode 3 return `DmaError::UnsupportedControl`.
|
||||
DMA reads below work RAM reuse known channel data as described below. They never expose BIOS bytes.
|
||||
|
|
|
|||
|
|
@ -139,6 +139,14 @@ DMA IWRAM continuation research:
|
|||
See [the bounded DMA continuation extension](research/iwram-bus-history.md#dma-continuation-extension).
|
||||
These source reviews do not establish sub-instruction arbitration, exact CPU resume timing, or hardware conformance.
|
||||
|
||||
DMA resume-timing research:
|
||||
|
||||
- [NanoBoyAdvance force-nseq-access test at cc3f4a28](https://codeberg.org/nba-emu/hw-test/src/commit/cc3f4a286cdef823980d9b353bd70befc9927d28/dma/force-nseq-access/source/main.c), for equal timer expectations after RAM-source and ROM-source DMA sequences.
|
||||
- The pinned NanoBoyAdvance bus and ares DMA/bus implementations above explicitly break sequential CPU access after DMA ownership.
|
||||
|
||||
See [non-sequential CPU resume](research/dma-resume-timing.md) for the nominal subset and unmodeled refill/prefetch details.
|
||||
The public test was reviewed, not executed. No hardware timing pass is claimed.
|
||||
|
||||
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.
|
||||
|
|
|
|||
74
docs/research/dma-resume-timing.md
Normal file
74
docs/research/dma-resume-timing.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Non-sequential CPU resume after DMA
|
||||
|
||||
## Finding and evidence
|
||||
|
||||
DMA can break the CPU's sequential bus access even when the transfer uses only work RAM.
|
||||
The prior instruction timing path always used its instruction summary, without retaining DMA ownership changes.
|
||||
An ordinary ROM instruction after DMA therefore still received a sequential code-access cost.
|
||||
|
||||
Sources reviewed:
|
||||
|
||||
- [NanoBoyAdvance force-nseq-access test at cc3f4a28](https://codeberg.org/nba-emu/hw-test/src/commit/cc3f4a286cdef823980d9b353bd70befc9927d28/dma/force-nseq-access/source/main.c) compares EWRAM-source and ROM-source transfers during a short instruction sequence.
|
||||
Both tests specify the same timer result. This is a whole-sequence expectation, not an isolated first-fetch measurement.
|
||||
- [NanoBoyAdvance bus at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/bus.cc) forces non-sequential ROM access when the previous access was DMA and the current access is CPU.
|
||||
The condition does not require the DMA source or destination to be ROM.
|
||||
- [ares DMA at 6f6786e0](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/dma.cpp) marks ARM7 accesses non-sequential when DMA takes bus ownership.
|
||||
Its [bus dispatch](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/bus.cpp) uses that state for CPU burst selection.
|
||||
|
||||
These sources support a sequence break. They do not establish exact timing for our instruction-boundary scheduler.
|
||||
The public test source was reviewed outside the repository. Its ROM was not imported, built, or executed here.
|
||||
No physical-hardware measurement or external-emulator differential run was performed.
|
||||
|
||||
## Bounded implementation
|
||||
|
||||
A successful DMA unit sets a pending non-sequential CPU resume in `Memory`.
|
||||
Further units and channel changes keep that single pending state. They do not queue additional costs.
|
||||
A failed unit cannot create or consume it. A blocked-source unit that successfully writes known channel data sets it normally.
|
||||
Register configuration, host reads/writes, and clock-only advancement do not consume it.
|
||||
|
||||
Before instruction execution, `Cpu::step_timed` captures the pending state and WAITCNT.
|
||||
For a non-refill instruction, pending resume changes the nominal code-access kind to non-sequential.
|
||||
The existing bus-cost function applies the code width and ROM-window wait settings.
|
||||
An ARM word still uses a non-sequential first halfword and a sequential second halfword.
|
||||
This is a change of access kind, not a fixed positive surcharge: some wait settings make N cheaper than S.
|
||||
|
||||
Successful CPU execution consumes the pending state in timed and untimed APIs.
|
||||
Skipped conditions consume it too. Failed fetches, unsupported instructions, and failed data accesses preserve it for retry.
|
||||
Execution in internal memory consumes it even when N and S costs are equal.
|
||||
The state does not wait for a later ROM instruction.
|
||||
|
||||
Machine IRQ entry consumes it through successful CPU-step completion.
|
||||
HALT/STOP idle does not consume it. Normal device progress, wake-up, and IRQ sampling rules still apply.
|
||||
A CPU WAITCNT store uses the pre-instruction settings for its code access.
|
||||
A completed DMA WAITCNT store changes the settings before CPU resume.
|
||||
|
||||
## Limits
|
||||
|
||||
The model has no persistent fetch pipeline or per-access DMA arbitration.
|
||||
It charges the current instruction address, not a separately scheduled PC+4/PC+8 pipeline fetch.
|
||||
DMA still executes only between instructions, not between data accesses or during CPU internal cycles.
|
||||
|
||||
Existing refill summaries remain destination-based `1N+2S` costs.
|
||||
A resumed branch or exception consumes pending state without adding another nominal access.
|
||||
This prevents double-counting the existing N component, but does not model the discarded old-PC fetch separately.
|
||||
Stores and 128 KiB ROM boundaries already use N and receive no duplicate cost.
|
||||
Game Pak prefetch remains unimplemented; WAITCNT bit 14 does not alter this nominal timing path.
|
||||
|
||||
This change does not select a DMA value for CPU open-bus reads.
|
||||
DMA channel data, bounded IWRAM lane history, and protected BIOS history remain separate.
|
||||
General bus-data ownership and exact resume timing still need independent per-access validation.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
Tests cover ARM/Thumb widths, all three ROM windows, every N/S wait setting, and stored prefetch-bit values.
|
||||
They check RAM/ROM DMA sources, repeated units, channel preemption, blocked-source completion, and configuration-only isolation.
|
||||
Other checks cover CPU/DMA errors, mixed timed/untimed APIs, host isolation, WAITCNT writes, skipped conditions, stores, and ROM boundaries.
|
||||
Refills, IRQ entry, internal-memory execution, HALT/STOP idle, timer advancement, and subsequent IRQ delivery have explicit checks.
|
||||
The new resume cases failed on the prior sequential-only path before implementation.
|
||||
|
||||
## Validation result
|
||||
|
||||
Workspace and core/demo tests pass in debug and release on Darwin arm64.
|
||||
Formatting, lint checks, rustdoc, preparation tests, native ROM windows, and graphics smoke modes pass.
|
||||
Public ARM, Thumb, memory, and BIOS reports match their previous passing results exactly.
|
||||
These suites are regression checks, not independent conformance tests for DMA resume timing.
|
||||
|
|
@ -37,6 +37,7 @@ Update this file when a feature lands or a limit is removed.
|
|||
- Four direct memory access (DMA) channels, with immediate, VBlank, and visible-line HBlank triggers.
|
||||
- Halfword/word DMA, address controls, repeat, channel priority, CPU pausing, and completion IRQs.
|
||||
- Separate retained DMA channel data, with known-value reuse for blocked sources below work RAM.
|
||||
- One-shot non-sequential nominal CPU code timing after successful DMA, including RAM-only transfers.
|
||||
- HALT with enabled-request wake-up, continued device clocks, and bounded idle steps.
|
||||
- BIOS-only CPU writes to POSTFLG/HALTCNT, with HALT and keypad-wake STOP.
|
||||
- STOP freezes CPU/DMA/device clocks, retains device phases, and reports a stopped frame runner without spinning.
|
||||
|
|
@ -150,7 +151,7 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
|
|||
|
||||
## Next steps
|
||||
|
||||
1. Extend bounded IWRAM DMA continuations toward general bus ownership with independent per-access resume tests.
|
||||
1. Extend bounded IWRAM DMA history and nominal non-sequential resume timing with independent per-access bus tests.
|
||||
General DMA open-bus reads and sub-instruction arbitration remain separate from retained channel data and local IWRAM lanes.
|
||||
Keep unknown history diagnostic until fetch ordering and latch ownership have independent coverage.
|
||||
Validate BIOS snapshots with independent hardware tests before claiming exact refill or data-access history.
|
||||
|
|
|
|||
|
|
@ -210,6 +210,9 @@ Original channel-data tests cover word retention, halfword duplication, blocked-
|
|||
They check cold/zero history, cancel/re-enable, source-counter crossings, repeats, priority, failed-unit rollback, and BIOS snapshot isolation.
|
||||
These tests do not establish general DMA open-bus reads or DMA-to-CPU handoff. See [the research scope](research/dma-data-latches.md).
|
||||
Tests verify channel priority/preemption, CPU pausing, display/timer progress, IRQ acknowledgement/return, and WAITCNT costs.
|
||||
CPU resume tests cover ARM/Thumb widths, all ROM-window wait settings, one-shot N timing, and RAM-only DMA.
|
||||
They check errors, host isolation, mixed timed/untimed APIs, WAITCNT updates, refills, IRQ entry, HALT/STOP, and timer delivery.
|
||||
These verify the [nominal resume subset](research/dma-resume-timing.md), not a full fetch pipeline or physical-hardware timing.
|
||||
Diagnostic tests check failed-unit isolation, retained earlier transfers, unsupported modes, and CPU block-store atomicity.
|
||||
Graphics startup tests verify that DMA3, not CPU stores or host writes, fills every background pixel.
|
||||
Display tests cover exact scanline boundaries, line-227 VBlank behavior, all 228 HBlank events, and read-only status fields.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue