Stage 4 step 5 — the integration, written down before it is attempted¶
Every component exists and is independently proven. What remains is one coordinated edit across four files with no green state in between, which is why it is written out rather than started at the end of a session.
| component | cost | proven by |
|---|---|---|
sdram.v word-write mode |
— | on hardware, WD=0000 |
sdram_arb.v |
7 LUT4 | tb_sdram_arb.v |
gfx_mem.v |
152 LUT4 | tb_gfx_mem.v |
gfx_span.v |
96 LUT4 | tb_gfx_span.v, exhaustive |
sdram_model.v (sim) |
— | drop-in at sdram.v's interface |
The edit, file by file¶
fpga/rtl/gfx.v¶
- Ports. Drop
sc_en,sc_addr,sc_data— the scanout no longer reads through this module. Keepsc_pen/sc_rgb: the palette still lives here and the scanout still looks up through it. Add the arbiter port (e_req,e_we,e_word,e_addr,e_din,e_ack,e_ready,e_dout). - Delete
reg [7:0] fb[0:FBBYTES-1],fb_a,fb_we,fb_q,sc_en_d, thee_addr/e_we/e_wdata/e_rdataregs, and the always block that drives them. That is the 42 references counted at the outset. - Delete the pixel unit's four-phase FSM and instantiate
gfx_memin its place. The algorithms' interface is unchanged —px_go,px_busy,px_x,px_y,px_pen,px_read— which is the entire point of building it that way. Two knock-ons:px_penwidens from[1:0]to[7:0], andpx_goneeds apx_go <= 0;default at the top of the main always block, because the FSM being deleted is what used to clear it. - Geometry becomes mode-dependent.
GW/GH/GSTRIDEandpx_on,px_row,px_byte,px_shall move intogfx_mem, which already has them. - Add the mode register and command
$0CSETMODE (mode inGPARM), refusing anything but 0/1 via the ERR bit — matching the emulator, which is the golden model here. S_CLScurrently walksclsioverFBBYTESwritingcls_valdirectly. Re-express as onegfx_spanper row, which also makes it use the word path.- Fills —
S_FILL,S_CIRCF, and the ellipse fill — currently plot pixel by pixel. Point them atgfx_spanfor their runs. This is where the 4x comes from; correctness does not depend on it, so it can land second.
fpga/rtl/p8x_soc.v¶
Instantiate sdram_model + sdram_arb, wire gfx's new port to the arbiter's
engine side, and give the arbiter a refresh timer. The scanout side of the SoC
has no panel, so s_req ties low — but exercise it anyway, as the current
SoC already does with sc_lfsr: contention that is never simulated is
contention that is never tested.
fpga/tang-nano-20k/rtl/p8x_top.v¶
Same, with the real sdram.v, plus sdram_video replacing video_rgb and
taking the arbiter's scanout port.
fpga/sim/tb_gfx.v¶
It dumps the PPM by reading dut.gfx.fb[] directly. That array is gone, so it
must read sdram_model's mem[] instead, and honour the mode when deciding
geometry and depth. This is the acceptance test, so it has to be right: the
whole branch is judged by gfx.sh showing RTL and emulator identical.
Order, and where it can go wrong¶
Do 1–5 first and prove mode 0 still matches the emulator pixel-for-pixel via
gfx.sh — that is a pure substitution and should change no pixels at all. Only
then do 6–7 (spans) and re-run, then add mode-1 payloads.
The first full build is also the first time the whole thing is placed together.
Budget: ~255 LUT4 of new logic plus the controller's ~600, against 7,448 free —
but block RAM goes the other way, 4 framebuffer blocks returned against 1 for
the line buffer. On a design that failed to place from a logically-null change,
expect to find out the hard way, and check p8x_cpu.fs's mtime before
believing any result.