# Wave 2.6 Mini-Plan: Multi-Beat DMAC to GIF to GS

This document defines the next consolidation step after the completed
Wave 2.5 `memory-backed BGCOLOR via DMA/GIF` milestone.

Goal:

- prove the DMAC/GIF path across more than one qword,
- validate address stepping from `MADR`,
- validate end-of-packet signaling on the final beat only,
- preserve the visible Milestone A+ outcome through the existing GS/platform
  chain,
- do all of that without expanding packet-format scope prematurely.

Working milestone name:

`multi-beat BGCOLOR via DMA/GIF`

That means:

- DMAC channel 2 is programmed with `QWC = 2`,
- DMAC performs two memory-backed qword fetches,
- GIF accepts two qwords as two standalone register-write packets,
- GS observes two consecutive writes,
- platform video reflects the final written color,
- traces prove beat ordering, source-address stepping, and `gif_tag_last`
  behavior.

## Why this goes before memory-map routing

Wave 2.5 established the right ownership seam:

- DMAC is now a memory client,
- `MADR` is real,
- RAM is owned by the memory subsystem,
- the graphics-visible path is intact.

The next most valuable risk to retire is not topology, it is transfer shape.

Multi-beat transfer support is the first place where subtle bugs are likely to
hide:

- off-by-one address stepping,
- incorrect remaining-count bookkeeping,
- `gif_tag_last` asserted too early or too late,
- state-machine stalls between fetch and send,
- final-color behavior when multiple writes land back-to-back.

Once that is stable, routing the same traffic through `ee_memory_map_stub`
becomes a narrower structural refactor instead of a behavioral and structural
change combined.

## Deliverables

The first Wave 2.6 pass should land:

1. an updated `rtl/dmac/dmac_reg_stub.sv`
2. a small comment/README update in `rtl/gif_gs/gif_path_stub.sv` if needed to
   make multi-beat behavior explicit
3. an updated `sim/tb/integration/tb_bgcolor_via_dma.sv`
4. `sim/Makefile` updates only if the run flow changes
5. optional README note if the integration-TB expectations materially change

No new subsystem contracts or decision records are required for this step.

## Scope boundary

This plan is intentionally narrow.

It does not attempt to implement:

- real GIFtag decode,
- packed register lists,
- chain mode,
- linked-list DMA,
- path arbitration,
- full EE memory-map routing,
- any IOP or SIF behavior,
- GS drawing primitives or VRAM.

The purpose is to prove:

`QWC > 1` works correctly on the already-established temporary topology.

## Transfer length scope

Recommendation:

- sign off this phase on `QWC = 2`

Do not require `QWC = 4` for first signoff.

### Why two qwords is enough

Two beats cover every new behavior category this phase is meant to validate:

- beat 0 source address = `MADR`
- beat 1 source address = `MADR + 16`
- beat 0 has `gif_tag_last = 0`
- beat 1 has `gif_tag_last = 1`
- the transfer must remain active across an intermediate beat
- the final beat must retire cleanly into `DMA_DONE`

Four beats would add more distance but not a new category of behavior.
If the implementation naturally supports `QWC > 2`, that is welcome, but the
required directed proof for this phase should stay at `2`.

## Payload semantics

Recommendation:

- keep the current project-local packet model:
  - one qword = one standalone GS register write
- use the same destination register twice:
  - `BGCOLOR` on beat 0
  - `BGCOLOR` again on beat 1 with a different RGB value

This is the simplest high-signal option.

### Why this goes over the alternatives

Using one register twice keeps the focus on transport, not packet-model growth.

It gives us:

- two visible GS-side events,
- two visible GIF-side accepts,
- a deterministic final platform-visible color,
- no need to invent a tag-qword/data-qword pairing before we are ready to
  tackle real GIFtag structure.

Using two different GS registers would blur the phase boundary by making the
test partly about GS register-surface breadth. That coverage already exists in
the single-register-path and can expand later when GIFtag work begins.

### Recommended concrete payload

Preload two qwords at consecutive addresses:

- beat 0 packet:
  - register = `BGCOLOR`
  - value = a clearly non-final color, for example blue
- beat 1 packet:
  - register = `BGCOLOR`
  - value = the intended final visible color, for example red

That gives a clean final assertion:

- two `EV_BGCOLOR` events occurred,
- final `bg_*` equals the second payload.

## `dmac_reg_stub` exact scope

Status target:

- the current state machine becomes genuinely multi-beat for the signoff case

### Owns in this phase

- repeated qword fetches based on `QWC`
- source-address stepping by `16` bytes per beat
- repeated send/accept handshakes into `gif_path_stub`
- correct `gif_tag_last` behavior on the final beat only
- transfer completion only after the final beat is accepted

### Explicit non-goals

- channel arbitration
- chain-mode `TADR` behavior
- interrupt-driven completion semantics
- routing through `ee_memory_map_stub`

### Required behavioral rules

For `QWC = 2`:

1. `DMA_START.arg2` reports the latched `MADR`
2. beat 0 fetch/read uses `MADR`
3. beat 1 fetch/read uses `MADR + 16`
4. beat 0 emits `gif_tag_last = 0`
5. beat 1 emits `gif_tag_last = 1`
6. `DMA_DONE` occurs only after beat 1 is accepted downstream

Implementation note:

- if the RTL naturally supports arbitrary `QWC > 0`, that is fine
- the TB and acceptance criteria only need to lock `QWC = 2`

## `gif_path_stub` behavior in Wave 2.6

Recommendation:

- keep `gif_path_stub` stateless with respect to packet framing
- keep the current project-local interpretation:
  - every accepted qword is one standalone register write
- preserve `in_last` as trace-visible metadata only

This means no new internal "tag phase vs. data phase" state is added yet.

### Why this is the right boundary

Wave 2.6 is about transfer length, not packet-model realism.

If we introduce tag/data pairing now, we turn one scoped extension into two:

- multi-beat DMA sequencing
- a new packet parser contract

That is exactly the kind of scope expansion we have been avoiding well so far.

## Trace policy

No new event names are required for Wave 2.6.

Reuse the current vocabulary:

- `EV_DMA_CFG`
- `EV_DMA_START`
- `EV_DMA_BEAT`
- `EV_DMA_DONE`
- `EV_GIFTAG`
- `EV_BGCOLOR`

### Required trace-visible facts

The Wave 2.6 proof must make the following visible in traces:

- two DMAC-backed RAM reads from consecutive qword addresses
- two `EV_DMA_BEAT` events with:
  - beat index `0`, source address `MADR`
  - beat index `1`, source address `MADR + 16`
- two `EV_GIFTAG` events with:
  - `flags[0] = 0` on beat 0
  - `flags[0] = 1` on beat 1
- two GS-side `EV_BGCOLOR` events
- one `EV_DMA_DONE` after the second beat completes

Cycle-perfect alignment between subsystems is not the primary comparison rule.
Order and causality are the primary rules:

- read for beat 0 precedes send for beat 0
- read for beat 1 precedes send for beat 1
- done follows final acceptance

## Integration testbench shape

Recommendation:

- update the existing `sim/tb/integration/tb_bgcolor_via_dma.sv`

Do not create a second near-duplicate integration TB for first signoff.

### Why update instead of fork

Wave 2.6 is a strict extension of the current Milestone A+ proof:

- same subsystem chain
- same top-level outcome
- same trace sinks

Keeping one canonical integration TB reduces maintenance churn and makes the
current path's expectations clearer.

If later we want a minimal single-beat smoke test, that can be added as a
focused DMAC unit TB rather than cloning the whole platform chain.

### TB preload shape

The TB should preload two qwords into `ee_ram_stub`:

- first qword at `MADR`
- second qword at `MADR + 16`

Suggested values:

- beat 0: `BGCOLOR = blue`
- beat 1: `BGCOLOR = red`

### TB pass criteria

At minimum, the updated integration TB should require:

1. `mem_reads_dmac >= 2`
2. `dma_cfg_count >= 3`
3. `dma_start_count == 1`
4. `dma_beat_count == 2`
5. `dma_done_count == 1`
6. `giftag_count == 2`
7. `gs_bgcolor_count == 2`
8. first observed beat source address = `MADR`
9. second observed beat source address = `MADR + 16`
10. first GIF end-of-packet flag = `0`
11. second GIF end-of-packet flag = `1`
12. final `bg_*` equals the second payload color
13. platform still renders active pixels correctly after transfer completion

Optional but useful:

- count post-transfer frames as before
- assert that `DMA_DONE` is observed only after the second `EV_GIFTAG`

## Exit criteria

Wave 2.6 is complete when all of the following are true:

- `dmac_reg_stub` performs a two-beat memory-backed transfer
- `MADR` stepping is trace-visible and correct
- `gif_tag_last` is false on beat 0 and true on beat 1
- GIF accepts two qwords without new packet-state machinery
- GS observes two BGCOLOR writes
- platform video settles to the second color
- `make full_checks` remains green

## Next step after Wave 2.6

If this passes cleanly, the next recommended step is:

- route DMAC through `ee_memory_map_stub`

That is the right moment for topology cleanup, because the transfer behavior
itself will already be stable and trace-proven.
