Skip to content

rain: rewrite matrix mode text pixel pipeline - #947

Open
0xa000 wants to merge 1 commit into
MEGA65:developmentfrom
0xa000:fix/matrix-mode-text-pipeline
Open

rain: rewrite matrix mode text pixel pipeline#947
0xa000 wants to merge 1 commit into
MEGA65:developmentfrom
0xa000:fix/matrix-mode-text-pipeline

Conversation

@0xa000

@0xa000 0xa000 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Authorship notice: investigation and patch developed with substantial AI assistance (Claude), reviewed and hardware-verified by me.

Symptom. In matrix mode, glyph rendering has visible defects: jagged (zig-zag) vertical edges on all glyphs, some glyphs one pixel wider than they should be (easy to see on e and p), and every glyph shifted one pixel to the right with the rightmost font column wrapped around to the left edge of its character cell (visible on m and p, whose rows use font bit 0). We found this while porting the core to the open-source openXC7 toolchain, but the defects are pure RTL behaviour in rain.vhdl — deterministic given the raster geometry and independent of the toolchain used to build the core.

Root cause. The rotate+stretch shifter that expands 8-bit font rows into the 16-tick character cells has three independent defects.

  • The 2x pixel-stretch phase (char_bit_stretch) free-runs across raster lines; because the character-reload branch preempts its toggle branch, the per-line toggle count is odd and the phase flips every scanline, displacing alternate scanlines of each glyph row by one pixel — the zig-zag.
  • 8 font pixels x 2 ticks cannot be fit into the 16-tick cell by the rotate scheme without a seam: depending on phase, either the last font pixel lingers for a third tick (glyph grows a pixel on the right) or the first font column ghosts again at the end of the cell.
  • The rotate wraps bit 7 into displayed bit 0, so display order is b0-first — one-pixel right shift with wrap-around.

Fix. Replace the shifter entirely: reload the character on the strobe-aligned tick (exact 16-tick cells, reload takes the place of the decrement-to-zero branch) and select the font bit by indexing with the upper bits of the tick counter, msb-first.

Evidence. Verified in a differential RTL simulation harness built for this hunt: the matrix_rain_compositor + termmem + lfsr16 leaf driven with a synthetic PAL raster (pixel strobe every 3rd 81 MHz cycle, 864-strobe lines), banner text injected through the monitor char handshake, and the green channel dumped as PGM frames. The unmodified RTL reproduces all three defects (matching board photos); with the fix, an automated per-cell comparison of the rendered frame against the font ROM shows all 320 header character cells pixel-exact, zero mismatched scanline pairs, zero odd-width strokes. Also verified on hardware: matrix mode renders crisp, correctly-placed glyphs on an openXC7 build of the core for an xc7a100t board (QMTech Wukong port).

Deliberately not included. The same investigation produced a CDC hardening for the monitor char handshake (2-FF synchroniser on monitor_char_valid, registered ack). It is omitted here because on stock builds uartclock (= cpuclock) and pixelclock come from the same MMCM and sit in the same set_clock_groups group, so Vivado times the crossing and the raw handshake is safe by construction; the hardening only matters under toolchains that do not time inter-clock paths. It belongs with a future series enabling the core on such toolchains.

The rotate+stretch shifter that expands font bytes into the 16-tick
character cells has three defects, visible as rendering artifacts in
matrix mode:

1. the 2x pixel-stretch phase free-runs across raster lines; with an
   odd toggle count per line it flips every scanline, displacing
   alternate scanlines of each glyph row by one pixel (jagged,
   zig-zag glyph edges);
2. 8 font pixels x 2 ticks never fit the 16-tick cell without a seam:
   depending on phase, either the last font pixel lingers for a third
   tick (glyphs grow an extra pixel on the right) or the first font
   column ghosts at the end of the cell;
3. display order is b0-first: every glyph is shifted one pixel right,
   with the rightmost font column wrapped around to the cell's left
   edge (visible on glyphs like 'm' and 'p' whose rows use bit 0).

Replace the shifter with a strobe-aligned character reload (exact
16-tick cells) and an indexed, msb-first font-bit select.

Verified in a differential RTL simulation harness (synthetic PAL
raster, banner text injected through the monitor char handshake,
rendered frames dumped as images): all 320 header character cells now
match the font ROM pixel-exactly, with no mismatched scanline pairs
and no odd-width strokes. Also verified on hardware (matrix mode on
an openXC7 build of the core for an xc7a100t board).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant