feat(cpu): retain fetch access kinds across instruction execution
This commit is contained in:
parent
2d7eb6e4eb
commit
86321f0071
21 changed files with 637 additions and 61 deletions
|
|
@ -72,7 +72,8 @@ Cold fill and target-pair samples do not add nominal cycles or data accesses.
|
|||
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.
|
||||
The later [sequencing extension](fetch-access-sequencing.md) stores the next fetch kind in the instruction buffer.
|
||||
Cold-fill startup costs and per-access 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.
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ DMA still executes only between instructions, not between data accesses or durin
|
|||
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.
|
||||
The later [sequencing extension](fetch-access-sequencing.md) applies each instruction's data/internal effects to the following source fetch.
|
||||
CPU history, DMA resume, and ROM page boundaries can all require N without duplicating an access.
|
||||
A completed transfer after DMA can establish a new N requirement for the following fetch.
|
||||
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.
|
||||
|
|
|
|||
129
docs/research/fetch-access-sequencing.md
Normal file
129
docs/research/fetch-access-sequencing.md
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# Persistent CPU fetch access kinds
|
||||
|
||||
S means sequential access; N means non-sequential access. W is the instruction width.
|
||||
|
||||
## Finding and evidence
|
||||
|
||||
Source-fetch addresses and refill target pairs were already timed separately.
|
||||
However, the current instruction still selected its own source access kind.
|
||||
Stores selected N immediately; loads, register shifts, and multiplies did not affect the following instruction.
|
||||
IRQ entry always selected S unless DMA had completed.
|
||||
This lost the sequence break between instruction execution and the next fetch.
|
||||
|
||||
The [ARM7TDMI load-multiple description](https://support.arm.com/documentation/ddi0210/c/Instruction-Cycle-Timings/Load-multiple-registers) separates the initial prefetch, data transfers, and final internal cycle.
|
||||
Instruction cycle totals alone do not identify which neighboring fetch receives the bus-sequence change.
|
||||
Two pinned implementations provide a source-level cross-check:
|
||||
|
||||
- [NanoBoyAdvance ARM handlers at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/handlers/handler32.inl) set the next pipeline access to N after transfers, swaps, register shifts, and multiplies.
|
||||
- Its [Thumb handlers](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/handlers/handler16.inl) do the same for corresponding Thumb operations.
|
||||
- Its [CPU loop and refill helpers](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) consume that kind before execution, including discarded IRQ fetches.
|
||||
Failed ARM conditions set the following access to S. Target refills end with S.
|
||||
- Its [bus timing](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/timing.cc) charges internal cycles and requested ROM wait costs separately when prefetch is inactive.
|
||||
- [ares memory helpers at 6f6786e0](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/component/processor/arm7tdmi/memory.cpp) end a burst on internal cycles and data loads/stores.
|
||||
Its [instruction loop](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/component/processor/arm7tdmi/instruction.cpp) fetches before execution and restarts target refills with N followed by S.
|
||||
Its [ARM handlers](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/component/processor/arm7tdmi/instructions-arm.cpp) supply the corresponding internal cycles.
|
||||
|
||||
This is agreement between source implementations, not a physical-hardware timing result.
|
||||
No external emulator was executed. No external implementation or ROM was imported into the repository.
|
||||
Game Pak prefetch and within-instruction bus scheduling remain outside this subset.
|
||||
|
||||
## Implemented subset
|
||||
|
||||
The CPU instruction buffer retains the next source-fetch kind alongside its expected PC and instruction state.
|
||||
The current source fetch consumes the incoming kind, with the existing one-shot DMA override.
|
||||
Successful execution then selects the following kind:
|
||||
|
||||
| Completed operation | Following fetch |
|
||||
| --- | --- |
|
||||
| Arithmetic without an internal cycle, immediate shifts, status transfers, skipped conditions, BL prefix | S |
|
||||
| Loads/stores, block/stack transfers, swaps | N |
|
||||
| Register-controlled shifts and multiplies | N |
|
||||
| Branch, PC load/write, SWI, or machine IRQ target refill | S |
|
||||
|
||||
Register-controlled shifts break the sequence even when the shift amount is zero.
|
||||
Data operations break it even when data lies in RAM or I/O rather than ROM.
|
||||
A PC load finishes its data/internal work before its target pair; the pair therefore establishes S for target execution.
|
||||
The target pair itself remains N(T), S(T+W).
|
||||
|
||||
`Cpu::step` and `Cpu::step_timed` commit the same sequence state.
|
||||
Clones and full CPU equality include it. Failed instructions preserve it with the existing instruction buffer.
|
||||
A failed current-slot fetch cannot consume it. Deferred lookahead errors do not prevent a successful current instruction from updating it.
|
||||
IRQ entry captures the incoming kind before exception entry discards the old buffer.
|
||||
Masked IRQ requests leave it unchanged.
|
||||
|
||||
DMA history stays in `Memory`, separate from CPU continuation state.
|
||||
Either source can require N; their combination does not add a second fetch or a fixed surcharge.
|
||||
A completed load after DMA can establish a new N requirement for the following fetch.
|
||||
Host reads/writes, clock advancement, and HALT/STOP idle do not consume the CPU continuation.
|
||||
|
||||
Cold startup still uses nominal S and has no separate current/decode fill charge.
|
||||
Debugger invalidation and PC/state mismatches deliberately return to that cold policy.
|
||||
They cannot reuse another continuation's access kind. Debugger invalidation does not clear Memory's DMA override.
|
||||
This policy is not a claim about hardware reset/startup timing.
|
||||
A status-restoration quirk that changes instruction state without a normal refill also retains the existing cold-buffer policy.
|
||||
|
||||
## Example with default WS0 settings
|
||||
|
||||
For ARM code away from ROM boundaries, S costs 6 cycles and N costs 8 cycles.
|
||||
Consider a RAM word load followed by two ordinary arithmetic instructions:
|
||||
|
||||
1. The load consumes its incoming S source fetch, then performs its data read and internal cycle.
|
||||
2. The first arithmetic instruction consumes N because of the preceding load.
|
||||
3. The second arithmetic instruction consumes S again.
|
||||
|
||||
A store follows the same source-kind sequence, without the load's internal cycle.
|
||||
The store no longer changes its own source fetch retroactively.
|
||||
A WAITCNT store uses old settings for its source fetch, then new settings and N for the following fetch.
|
||||
A branch after a load consumes N at the source and still charges the target N+S pair.
|
||||
|
||||
N is an access kind, not always a positive cycle penalty.
|
||||
Some wait settings make N cheaper than S. Tests use independent N/S tables rather than adding a fixed cost.
|
||||
ROM page boundaries can force N independently; a forced boundary and a retained N do not duplicate an access.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
Instruction-pair tests cover:
|
||||
|
||||
- Supported ARM/Thumb transfer families, block/stack operations, signed loads, swaps, shifts, multiplies, and status operations.
|
||||
- Skipped conditions, zero shift amounts, BL prefixes, and ordinary arithmetic.
|
||||
- Every ROM window and wait setting, both instruction widths, and stored prefetch-bit values.
|
||||
- Repeated transfers, branches after loads, and PC-load refills that restore S.
|
||||
- Timed/untimed equivalence, cloning, failed data accesses, missing current slots, and masked IRQ requests.
|
||||
- DMA and CPU sequence-break composition, IRQ source timing, and handler refill state.
|
||||
- Host and HALT isolation, debugger invalidation, PC mismatches, and page/wait-window crossings.
|
||||
|
||||
Existing rollback, instruction semantics, load data/internal costs, and deferred diagnostics remain unchanged.
|
||||
WAITCNT and DMA tests now distinguish a consumed resume from a new sequence break caused by the completed instruction.
|
||||
|
||||
## Validation and report audit
|
||||
|
||||
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 refill-timing change:
|
||||
|
||||
- ARM nominal cycles increase by 208; Thumb increases by 106; memory increases by 32.
|
||||
Their other report fields remain unchanged.
|
||||
- BIOS executes 963 fewer instructions and steps, with the same final nominal cycle total and all other report fields unchanged.
|
||||
|
||||
An original temporary trace runner compared both core versions against the pinned BIOS fixture.
|
||||
The VBlank polling loop now costs 36 rather than 34 cycles because its load makes the following test instruction fetch N.
|
||||
The updated core executes 321 fewer three-instruction polling loops.
|
||||
IRQ entry moves to another position in the loop; the final checkpoint still has r12=0 and one IRQ entry.
|
||||
This is an audit between our core versions, not external-emulator or hardware timing validation.
|
||||
|
||||
The original timer demo keeps its IRQ step and total duration.
|
||||
Moving store fetch costs to following instructions shifts timer start two cycles earlier.
|
||||
The timer therefore runs two additional cycles before the handler stops it.
|
||||
|
||||
## Remaining limits
|
||||
|
||||
- Cold-start costs and state-changing test/compare quirks are not hardware-validated.
|
||||
- Data timing still uses an instruction-local access trace. This is not a complete bus-event scheduler.
|
||||
- Device clocks still update once per instruction, IRQ entry, or DMA unit.
|
||||
- Game Pak prefetch, configurable EWRAM timing, display contention, and DMA during internal cycles remain unimplemented.
|
||||
- IRQ synchronization delays and complete BIOS/general-bus latch history remain incomplete.
|
||||
|
||||
The next timing work is Game Pak prefetch, with explicit startup and invalidation rules and independent tests.
|
||||
|
|
@ -27,7 +27,8 @@ All programs are original; no external source code or ROM was imported.
|
|||
|
||||
`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.
|
||||
The later [sequencing extension](fetch-access-sequencing.md) retains the incoming access kind from the preceding instruction.
|
||||
The pending DMA resume still overrides that source access to N.
|
||||
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.
|
||||
|
|
@ -65,8 +66,8 @@ Timer tests and the original timer demo now reflect this bounded rule.
|
|||
|
||||
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.
|
||||
- Source S/N kinds now follow the preceding instruction's data/internal work.
|
||||
Data timing and device scheduling still do not form a complete chronological bus trace.
|
||||
- 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.
|
||||
|
|
@ -74,7 +75,7 @@ The later [refill timing extension](refill-fetch-timing.md) also charges source
|
|||
- 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 access-kind sequencing between instructions and data accesses.
|
||||
The next timing step is Game Pak prefetch with verified startup and invalidation rules.
|
||||
|
||||
## Original regression coverage
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ A retry takes a new lookahead sample from current memory.
|
|||
|
||||
The later [fetch-address timing extension](fetch-address-timing.md) charges non-refill code at the actual lookahead address.
|
||||
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.
|
||||
The later [sequencing extension](fetch-access-sequencing.md) retains source kinds across CPU data/internal work.
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ For an instruction executing at P, `Cpu::step_timed` charges its sampled lookahe
|
|||
|
||||
- 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.
|
||||
- Access kind: now retained from the preceding instruction by the [sequencing extension](fetch-access-sequencing.md), with DMA override.
|
||||
- WAITCNT: captured before instruction effects.
|
||||
|
||||
If the instruction refills, timing also charges N(T) and S(T+W) using the resulting state.
|
||||
|
|
@ -112,11 +112,11 @@ The stopped counter therefore increases by five even though the final machine-cy
|
|||
|
||||
## 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.
|
||||
- The later [sequencing extension](fetch-access-sequencing.md) retains the next source kind after CPU data/internal work.
|
||||
Data timing and device scheduling still do not form 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.
|
||||
The next timing work is Game Pak prefetch with verified startup and invalidation rules.
|
||||
|
|
|
|||
|
|
@ -57,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).
|
||||
- Refills now charge the source fetch and target pair; instruction-local source access kinds remain nominal.
|
||||
- Refills now charge the source fetch and target pair; the [sequencing extension](fetch-access-sequencing.md) retains incoming source kinds.
|
||||
- Per-access device updates, Game Pak prefetch, and exact refill/arbitration timing remain incomplete.
|
||||
|
||||
## Original tests
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ CPU stores, DMA transfers, and host writes never replace retained instructions a
|
|||
`Cpu::invalidate_pipeline()` explicitly discards retained instructions for debugger repair or attachment to different memory.
|
||||
It does not clear separate BIOS or IWRAM bus history.
|
||||
CPU clones own independent buffers; full CPU equality includes them.
|
||||
The later [sequencing extension](fetch-access-sequencing.md) also retains the next fetch kind in each buffer.
|
||||
|
||||
## Separation from bus history and timing
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue