feat(cpu): charge non-refill code at pipeline fetch addresses
This commit is contained in:
parent
0a696ea3e7
commit
0650b60908
26 changed files with 413 additions and 46 deletions
|
|
@ -70,7 +70,8 @@ Failed-instruction tests continue to assert full-state preservation.
|
|||
This implements persistent ARM instruction words, not a complete cycle-by-cycle pipeline.
|
||||
Cold fill and target-pair samples do not add nominal cycles or data accesses.
|
||||
The existing destination-based refill summaries and one-shot DMA resume timing remain unchanged.
|
||||
The current instruction address still selects nominal code cost rather than a separately scheduled fetch address.
|
||||
The later [fetch-address timing extension](fetch-address-timing.md) selects non-refill code cost from the newly sampled address.
|
||||
Access kinds, cold-fill startup costs, and refill scheduling remain incomplete.
|
||||
|
||||
ARM PC+8 open bus and protected BIOS readback remain separate snapshots.
|
||||
The [shared fetch sample](shared-fetch-samples.md) supplies the instruction-buffer entry and supported bus snapshots without duplicate reads.
|
||||
|
|
|
|||
|
|
@ -45,14 +45,15 @@ A completed DMA WAITCNT store changes the settings before CPU resume.
|
|||
## Limits
|
||||
|
||||
The timing model has no per-access fetch scheduling or DMA arbitration.
|
||||
The later [ARM](arm-instruction-buffer.md) and [Thumb](thumb-instruction-buffer.md) instruction buffers retain instructions without changing these nominal costs.
|
||||
It charges the current instruction address, not a separately scheduled PC+4/PC+8 pipeline fetch.
|
||||
The later [ARM](arm-instruction-buffer.md) and [Thumb](thumb-instruction-buffer.md) instruction buffers retain instructions.
|
||||
The [fetch-address timing extension](fetch-address-timing.md) now charges non-refill code at PC+4/PC+8 rather than the executing address.
|
||||
A resumed access selects that fetch region's wait settings. It is still not a separately scheduled bus event.
|
||||
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.
|
||||
Stores and fetches at 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.
|
||||
|
|
|
|||
102
docs/research/fetch-address-timing.md
Normal file
102
docs/research/fetch-address-timing.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Non-refill timing at the actual fetch address
|
||||
|
||||
## Finding and evidence
|
||||
|
||||
The instruction buffer already sampled ARM P+8 or Thumb P+4 before executing the current instruction at P.
|
||||
However, `Cpu::step_timed` charged non-refill code at P.
|
||||
Near a memory-region, ROM wait-window, or 128 KiB page boundary, that selected the wrong bus cost.
|
||||
It also charged a page-boundary access when execution later reached an already fetched instruction.
|
||||
|
||||
The [ARM7TDMI data-operation timing table](https://support.arm.com/documentation/ddi0029/g/instruction-cycle-timings/data-operations) identifies the normal instruction prefetch address as `pc+2L`.
|
||||
Here L is the instruction length: four bytes in ARM state and two bytes in Thumb state.
|
||||
An ordinary data operation overlaps this fetch; a register-controlled shift adds an internal cycle.
|
||||
The [instruction-pipeline description](https://developer.arm.com/documentation/dvi0027/b/arm7tdmi/instruction-pipeline) provides the corresponding fetch/decode/execute model.
|
||||
|
||||
[NanoBoyAdvance at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) provides an implementation cross-check.
|
||||
Its `Run` method takes the current retained opcode and fetches the next halfword/word at the advanced r15 before execution.
|
||||
Its refill helpers advance visible r15 by two instruction widths after loading the target pair.
|
||||
This supports using the new fetch address rather than the current retained opcode's address.
|
||||
|
||||
Existing [bus-cost rules](../hardware/cpu.md#instruction-and-bus-timing) supply the region, ROM wait-window, and forced page-boundary costs.
|
||||
Existing [DMA resume evidence](dma-resume-timing.md) supports the retained one-shot sequence break.
|
||||
This change uses those rules without claiming complete access-kind sequencing or refill timing.
|
||||
No physical-hardware measurement or external-emulator differential run was performed.
|
||||
All programs are original; no external source code or ROM was imported.
|
||||
|
||||
## Implemented subset
|
||||
|
||||
`Cpu::step_timed` captures the address of `Fetched.lookahead` before execution.
|
||||
For a non-refill instruction, it passes that address to `bus_cycles` with the incoming instruction width.
|
||||
The existing instruction summary selects sequential or non-sequential access, with the pending DMA resume override.
|
||||
WAITCNT is captured before CPU execution, so a CPU store cannot retroactively change its own fetch cost.
|
||||
|
||||
The same rule applies to cold and retained pipelines.
|
||||
Sampling alone adds no data-timing entry or device-clock update.
|
||||
The returned code cost accounts for one new lookahead access, not an additional copy of it.
|
||||
Cold current/decode fills still have no separate startup charge.
|
||||
Successful untimed execution still consumes DMA resume without advancing clocks.
|
||||
Failed execution retains the existing CPU/buffer, timing, and resume-state rollback rules.
|
||||
|
||||
A deferred lookahead error does not fail the current instruction early.
|
||||
Its nominal code cost uses the attempted fetch address, even when supplied ROM bytes are missing there.
|
||||
The strict instruction diagnostic occurs only when the missing slot becomes current.
|
||||
An unmapped region retains the bus-cost helper's one-cycle fallback; this is diagnostic policy, not hardware abort timing.
|
||||
|
||||
## Examples with default WAITCNT
|
||||
|
||||
| Executing instruction | New fetch | Non-refill code cost |
|
||||
| --- | --- | --- |
|
||||
| ARM at `0x02fffff8` | IWRAM `0x03000000` word | 1 cycle, not EWRAM's 6 |
|
||||
| Thumb at `0x02fffffc` | IWRAM `0x03000000` halfword | 1 cycle, not EWRAM's 3 |
|
||||
| ARM at `0x07fffffc` | ROM `0x08000004` word | 6 cycles, not OAM's 1 |
|
||||
| ARM at `0x0801fff8` | ROM page boundary `0x08020000` | 8 cycles for N+S |
|
||||
| ARM at `0x08020000` | ROM `0x08020008` word | 6 cycles; no repeated page-boundary charge |
|
||||
|
||||
For ARM/Thumb ROM words/halfwords, a boundary cost now occurs two executed instruction positions earlier.
|
||||
Crossing into another wait-state window selects that window's settings at the new fetch.
|
||||
A completed DMA changes the access kind at this address; it does not append a second code access.
|
||||
|
||||
An ordinary ARM instruction starting directly at ROM base now costs 6 code cycles rather than 8.
|
||||
The charged fetch is at ROM base plus eight, not at the page boundary.
|
||||
This does not model the full hardware cost of initializing an empty pipeline.
|
||||
Timer tests and the original timer demo now reflect this bounded rule.
|
||||
|
||||
## Deliberately retained limits
|
||||
|
||||
- Instruction-local S/N summaries remain in use, including the existing store rule.
|
||||
Neighboring instruction/data-access sequencing is not a complete chronological bus trace.
|
||||
- Branches, PC loads/writes, SWI, exception returns, and IRQ entry retain destination-based `1N+2S` summaries.
|
||||
The old-PC fetch and target pair are not yet charged individually.
|
||||
- The new lookahead cost is not added to those refill summaries; doing so would count an extra access.
|
||||
- Thumb BL prefixes use their lookahead address; suffixes retain the nominal refill summary.
|
||||
- Cold fills have no separate startup charge, even though their samples update supported bus history.
|
||||
- Devices advance once after the whole successful instruction. No per-access timer/IRQ update is introduced.
|
||||
- Game Pak prefetch, display contention, configurable EWRAM timing, and sub-instruction DMA arbitration remain unimplemented.
|
||||
|
||||
This is a fetch-address correction within the existing timing model, not cycle-accurate emulation.
|
||||
The next timing step is verified source-fetch/target-pair accounting for refills and IRQ entry.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
New tests cover:
|
||||
|
||||
- ARM/Thumb memory-region crossings with cold and retained pipelines, compared with untimed execution.
|
||||
- ROM 128 KiB boundaries using independent N/S tables for every wait setting and both prefetch-bit values.
|
||||
- Wait-window crossings using the newly selected window's settings.
|
||||
- DMA resume at a cross-region lookahead without duplicate code cost.
|
||||
- CPU WAITCNT stores using old fetch settings and new settings on the next instruction.
|
||||
- Deferred missing-lookahead diagnostics and unchanged nominal refill totals.
|
||||
- Machine timer advancement from the corrected fetch cost before the next IRQ sample.
|
||||
|
||||
The address-sensitive tests failed with current-PC charging and pass with the new fetch address.
|
||||
Existing ROM-start, boundary, BIOS-access, load-alias, and timer assertions were recalculated from PC+8/PC+4 costs.
|
||||
Data results, access counts, flags, writeback, and failed-step preservation checks remain intact.
|
||||
|
||||
## 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 the prior fetch-boundary reports exactly, including cycle counts.
|
||||
Debug and release reports also match for the same fixture paths.
|
||||
The original timer demo's nominal total changes from 153 to 151 cycles because its initial ROM fetch is now charged at PC+8.
|
||||
These are regression checks, not physical-hardware timing conformance results.
|
||||
|
|
@ -76,8 +76,8 @@ A retry takes a new lookahead sample from current memory.
|
|||
- Protected BIOS history remains separate and image-derived.
|
||||
- All data reads within an instruction still use one entry snapshot.
|
||||
|
||||
Nominal timing remains based on the current instruction address and existing refill summaries.
|
||||
Crossing a wait-state window does not yet schedule code cost at the actual lookahead address.
|
||||
The later [fetch-address timing extension](fetch-address-timing.md) charges non-refill code at the actual lookahead address.
|
||||
Refill summaries remain destination-based; the instruction-local access kinds and cold-start costs are still nominal.
|
||||
Per-access device updates, video-bus contention, general DMA handoff, and Game Pak prefetch remain incomplete.
|
||||
No boundary timing accuracy is claimed.
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ Strict instruction reads never use protected BIOS or unused-memory data fallback
|
|||
- BIOS history commits after successful BIOS instruction execution; target refills do not establish additional BIOS history.
|
||||
- All unused-memory data reads within an instruction still use its entry snapshot.
|
||||
- DMA remains scheduled between whole instructions. Sampling does not add an interleaving point.
|
||||
- Current-instruction and destination-based nominal timing remain unchanged.
|
||||
- Non-refill timing now consumes the sample's address in the [fetch-address extension](fetch-address-timing.md).
|
||||
- Destination-based refill summaries and instruction-local access kinds remain nominal.
|
||||
- Per-access device updates, Game Pak prefetch, and exact refill/arbitration timing remain incomplete.
|
||||
|
||||
## Original tests
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ Refill bus history and instruction buffering now consume the same captured targe
|
|||
|
||||
Buffer samples add no nominal cycles or data accesses.
|
||||
Timed and untimed execution use the same buffer logic.
|
||||
Destination-based refill summaries and one-shot non-sequential CPU resume costs remain unchanged.
|
||||
Destination-based refill summaries remain unchanged.
|
||||
The later [fetch-address timing extension](fetch-address-timing.md) selects non-refill and DMA-resume code costs from the new fetch address.
|
||||
Mapped boundary snapshots now follow the [actual fetch region](fetch-region-boundaries.md).
|
||||
Complete bus history, exact boundary timing, Game Pak prefetch, and sub-instruction DMA arbitration remain incomplete.
|
||||
Instruction-state-changing compare quirks also remain hardware-unverified.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue