feat(cpu): account for source fetches in branch and IRQ refills
This commit is contained in:
parent
0650b60908
commit
2d7eb6e4eb
27 changed files with 638 additions and 87 deletions
|
|
@ -69,7 +69,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 later [refill timing extension](refill-fetch-timing.md) replaces destination-only costs with source-fetch and target-pair costs.
|
||||
IRQ entry also samples its discarded old-state fetch before the vector pair.
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ A failed unit cannot create or consume it. A blocked-source unit that successful
|
|||
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.
|
||||
Pending resume changes the source 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.
|
||||
|
|
@ -50,9 +50,9 @@ The [fetch-address timing extension](fetch-address-timing.md) now charges non-re
|
|||
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.
|
||||
The later [refill timing extension](refill-fetch-timing.md) replaces destination-only summaries with source-fetch and target-pair costs.
|
||||
Resume changes the source access to N; the first target access independently remains N.
|
||||
No extra access is appended. A resumed ROM branch can therefore cost more than its non-resumed refill.
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -63,18 +63,18 @@ Timer tests and the original timer demo now reflect this bounded rule.
|
|||
|
||||
## Deliberately retained limits
|
||||
|
||||
The later [refill timing extension](refill-fetch-timing.md) also charges source fetches and target pairs for branches and IRQ entry.
|
||||
|
||||
- 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.
|
||||
- Refills now charge one source fetch plus the target N+S pair, not an additional fetch beside the old destination-only summary.
|
||||
- Thumb BL prefixes use their lookahead address; suffixes add the target pair.
|
||||
- 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.
|
||||
The next timing step is verified access-kind sequencing between instructions and data accesses.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ A retry takes a new lookahead sample from current memory.
|
|||
- All data reads within an instruction still use one entry snapshot.
|
||||
|
||||
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.
|
||||
The [refill timing extension](refill-fetch-timing.md) also charges source fetches and target pairs individually.
|
||||
Instruction-local access kinds and cold-start costs remain nominal.
|
||||
Per-access device updates, video-bus contention, general DMA handoff, and Game Pak prefetch remain incomplete.
|
||||
No boundary timing accuracy is claimed.
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ Debugger invalidation clears only instruction retention, but the following cold
|
|||
A successful instruction commits its staged lanes regardless of its resulting state, PC, or region.
|
||||
A failed instruction discards every staged change, including cold-fill samples.
|
||||
Successful PC-writing instructions then apply target fetches after their data accesses.
|
||||
Other-region execution, BIOS IRQ entry, and unavailable targets do not erase known local lanes.
|
||||
Other-region execution, BIOS vector fetches, and unavailable targets do not erase known local lanes.
|
||||
The later [IRQ fetch extension](refill-fetch-timing.md) updates addressed IWRAM lanes through its discarded old-state source fetch.
|
||||
Host inspection and setup never drive the latch.
|
||||
|
||||
Initial lanes remain unknown until observed accesses establish them.
|
||||
|
|
|
|||
122
docs/research/refill-fetch-timing.md
Normal file
122
docs/research/refill-fetch-timing.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Source-fetch and target-pair refill timing
|
||||
|
||||
## Finding and evidence
|
||||
|
||||
The instruction buffer already sampled a branch's old-state lookahead and its two target instructions.
|
||||
However, timing charged three destination accesses: N(T), S(T+W), and S(T+2W).
|
||||
That omitted the source-region cost and charged the third target fetch before target execution.
|
||||
The totals often matched within one memory region, hiding cross-region and instruction-state errors.
|
||||
Machine IRQ entry also omitted the discarded source sample entirely.
|
||||
|
||||
The [ARM7TDMI branch timing description](https://support.arm.com/documentation/ddi0029/g/instruction-cycle-timings/branch-and-branch-with-link) specifies three accesses:
|
||||
|
||||
1. Prefetch from the current PC while calculating the destination. The decision cannot prevent this fetch.
|
||||
2. Fetch the branch destination.
|
||||
3. Fetch destination plus one instruction length to refill the pipeline.
|
||||
|
||||
The [software-interrupt and exception-entry description](https://support.arm.com/documentation/ddi0029/g/instruction-cycle-timings/software-interrupt-and-exception-entry) likewise describes a forced-address construction cycle followed by pipeline refill.
|
||||
Its first address is `pc+2L`, using the incoming instruction state.
|
||||
The vector pair uses ARM state.
|
||||
|
||||
[NanoBoyAdvance at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) provides a source cross-check:
|
||||
|
||||
- `Run` fetches the old-state lookahead before instruction execution.
|
||||
- `ReloadPipeline16` and `ReloadPipeline32` read the target N and the next instruction S.
|
||||
- `SignalIRQ` first performs a discarded halfword/word fetch using the incoming state and access kind.
|
||||
It then changes mode/state and reloads the ARM vector pair.
|
||||
|
||||
This supports source-address and target-pair accounting, not complete chronological bus sequencing in this emulator.
|
||||
The existing [DMA resume evidence](dma-resume-timing.md) supports changing the next source access to N.
|
||||
No physical-hardware measurement or external-emulator differential run was performed.
|
||||
Tests and tracing code are original. No external source code, ROM, or BIOS was added to the repository.
|
||||
|
||||
## Implemented subset
|
||||
|
||||
For an instruction executing at P, `Cpu::step_timed` charges its sampled lookahead once:
|
||||
|
||||
- ARM source: P+8, word width.
|
||||
- Thumb source: P+4, halfword width.
|
||||
- Access kind: the existing instruction-local summary, overridden to N after successful DMA.
|
||||
- WAITCNT: captured before instruction effects.
|
||||
|
||||
If the instruction refills, timing also charges N(T) and S(T+W) using the resulting state.
|
||||
The existing pipeline samples supply those instructions and update supported local history.
|
||||
T+2W is sampled and charged when the first target instruction executes, not during the refill.
|
||||
The same rule covers taken branches, BX, PC loads/writes, SWI, saved-state returns, and Thumb BL suffixes.
|
||||
Untaken conditions and BL prefixes do not add a target pair.
|
||||
|
||||
Accepted machine IRQ entry samples the discarded incoming-state P+2W fetch, then the ARM vector pair.
|
||||
It does not fetch or execute the interrupted current opcode, nor invent a cold current/decode fill.
|
||||
The discarded sample drives its addressed IWRAM lanes when mapped; other regions leave that local history unchanged.
|
||||
An unavailable discarded sample cannot prevent entry and is not retained as a current-slot diagnostic.
|
||||
Unavailable vector samples fail only when execution reaches them.
|
||||
IRQ sampling does not commit a BIOS instruction snapshot because no BIOS instruction executed.
|
||||
The separate successful-BIOS-instruction history policy remains unchanged.
|
||||
|
||||
Masked IRQ requests have no fetch, history, CPU, or resume-state effects.
|
||||
DMA still takes priority at machine-step boundaries.
|
||||
Successful IRQ entry consumes pending DMA resume through normal machine-step completion.
|
||||
Devices advance once by the total entry cost, not between its accesses.
|
||||
The public memory-free `Cpu::enter_exception` and `Cpu::take_interrupt` APIs remain unchanged.
|
||||
|
||||
## Examples with default WAITCNT
|
||||
|
||||
| Operation | Source | Target pair | Code cycles |
|
||||
| --- | --- | --- | --- |
|
||||
| IWRAM ARM BX to Thumb WS1 | 1 | 5 + 5 | 11 |
|
||||
| IWRAM Thumb BX to ARM WS0 | 1 | 8 + 6 | 15 |
|
||||
| ARM WS0 branch to IWRAM | 6 | 1 + 1 | 8 |
|
||||
| ARM WS0 branch within WS0 | 6 | 8 + 6 | 20 |
|
||||
| Same branch after DMA | 8 | 8 + 6 | 22 |
|
||||
| IRQ from ARM WS0 | 6 | 1 + 1 | 8 |
|
||||
| IRQ from ARM WS0 after DMA | 8 | 1 + 1 | 10 |
|
||||
|
||||
These examples exclude ROM page boundaries.
|
||||
Every access independently selects its actual region, wait window, and forced-N 128 KiB boundary cost.
|
||||
A target pair can cross a region or wait-window boundary.
|
||||
A boundary at T+2W is charged on target execution, not during the preceding branch.
|
||||
DMA can produce two N accesses: the source fetch and the first target fetch. No fourth fetch is added.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
Tests cover:
|
||||
|
||||
- Incoming/outgoing ARM and Thumb widths, all ROM wait settings, and the stored prefetch bit.
|
||||
- Boundaries in the first, second, and third target slots, plus split wait-window target pairs.
|
||||
- ROM source boundaries and DMA resume before a RAM refill, with no delayed resume penalty.
|
||||
- Cross-region source lookahead, data/internal costs for PC loads, and saved-state returns.
|
||||
- IRQ source widths, missing ROM, masked requests, LR/SPSR preservation, and device-clock totals.
|
||||
- IRQ IWRAM word and halfword lane updates, cold entry without invented fills, and retained Thumb entry.
|
||||
- Missing-vector rollback after accepted entry, BIOS-history isolation, and timed/untimed CPU equivalence.
|
||||
|
||||
## Validation and report changes
|
||||
|
||||
Workspace and core/demo tests pass in debug and release on Darwin arm64.
|
||||
Formatting, clippy, rustdoc, Python preparation tests, native ROM windows, and all graphics smoke modes pass.
|
||||
The release executable is native Mach-O arm64.
|
||||
Public ARM, Thumb, memory, and BIOS checkpoint assertions pass. Debug and release reports match.
|
||||
|
||||
Compared with the preceding fetch-address change, the ARM, Thumb, and memory reports each lose five nominal cycles.
|
||||
Their step counts and all other report fields remain unchanged.
|
||||
The BIOS report gains three instructions and 29 nominal cycles; all other fields remain unchanged.
|
||||
|
||||
A temporary original trace runner compared the preceding and updated cores against the pinned BIOS fixture.
|
||||
The changed VBlank arrival position causes one extra three-instruction polling loop, adding 34 cycles.
|
||||
Cross-region firmware transfers and IRQ entry together remove five cycles, giving the observed net increase of 29.
|
||||
The trace still reaches the same checkpoint with r12=0 and one IRQ entry.
|
||||
This trace comparison is a regression audit between our core versions, not external hardware validation.
|
||||
|
||||
The original timer demo retains its total nominal duration and IRQ step.
|
||||
IRQ entry gains five cycles before the handler stops the timer; the return loses five cycles afterward.
|
||||
The stopped counter therefore increases by five even though the final machine-cycle total is unchanged.
|
||||
|
||||
## Remaining limits
|
||||
|
||||
- Source access kinds still use instruction-local summaries, including the existing store rule.
|
||||
Neighboring instruction/data-access sequencing is not a complete chronological bus trace.
|
||||
- Cold current/decode fills have no separate startup timing charge.
|
||||
- BIOS retained history is not a complete refill/data-access bus latch.
|
||||
- Missing instruction bytes remain diagnostics, not hardware prefetch aborts.
|
||||
- Game Pak prefetch, per-access device updates, display contention, configurable EWRAM timing, and sub-instruction DMA arbitration remain unimplemented.
|
||||
|
||||
The next timing work is independently verified access-kind sequencing between instructions and data accesses.
|
||||
|
|
@ -39,6 +39,7 @@ It does not replay the bus observations of retained current/decode instructions.
|
|||
The consumer performs no mapped memory read.
|
||||
After successful control flow, the same target samples fill the instruction buffer and update local IWRAM history in either state.
|
||||
Machine IRQ entry uses this refill path; BIOS vector fetches leave IWRAM lanes unchanged.
|
||||
The later [refill timing extension](refill-fetch-timing.md) first samples the discarded source fetch, which can update IWRAM lanes.
|
||||
|
||||
Instruction buffers and BIOS/IWRAM latches remain separate retained state.
|
||||
Sampling alone changes no CPU access context, latch, device clock, DMA resume marker, or data-timing trace.
|
||||
|
|
@ -56,7 +57,7 @@ Strict instruction reads never use protected BIOS or unused-memory data fallback
|
|||
- 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.
|
||||
- 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.
|
||||
- Refills now charge the source fetch and target pair; instruction-local source access kinds remain nominal.
|
||||
- Per-access device updates, Game Pak prefetch, and exact refill/arbitration timing remain incomplete.
|
||||
|
||||
## Original tests
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ 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 remain unchanged.
|
||||
The later [refill timing extension](refill-fetch-timing.md) charges the incoming-state source fetch and outgoing-state target pair.
|
||||
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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue