Skip to content

feat: broadcast-standard native CRT video with PAL and 480i modes#4

Open
wizzomafizzo wants to merge 6 commits into
feat/dual-mode-native-fbfrom
fix/native-video-centering
Open

feat: broadcast-standard native CRT video with PAL and 480i modes#4
wizzomafizzo wants to merge 6 commits into
feat/dual-mode-native-fbfrom
fix/native-video-centering

Conversation

@wizzomafizzo

@wizzomafizzo wizzomafizzo commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

Implements the FPGA side of docs/native-video-plan.md: the native 15 kHz output moves from a single hand-calibrated 320x240 mode to broadcast-standard geometry with three switchable modes, a versioned DDR control contract, and no OSD video options. The zaparoo-launcher (ARM) side is a separate effort — see docs/native-video-frontend-brief.md.

Video modes (selected by the launcher via DDR, not the OSD)

Mode Active Timing Line rate
0 (default) 352x240p60 429x262 (Switchres NTSC) 15 734.27 Hz
1 720x480i60 858x525, half-line vsync interlace (CEA-861) 15 734.27 Hz
2 352x288p50 432x312 (Switchres PAL) 15 625.00 Hz
  • Exact 27.000000 MHz video clock from a new dedicated PLL (rtl/pll_video.v). The previous 27.027027 MHz gave a 15 750 Hz line rate (~0.1% off-spec). 27 MHz cannot come from the shared PLL: lcm(100 MHz clk_sys, 27 MHz) = 2700 MHz exceeds the Cyclone V VCO range — 27.027027 is exactly the closest sharable frequency, which is why stock uses it.
  • DDR control contract v2 (magic 0x5A50 in word1): mode, signed H/V centering trims (-8..+8 px / -8..+2 lines, clamped in RTL, latched at field wrap), double-buffered frames at +0x1000/+0x180000. Legacy writers (no magic) still display, centered with 16-px side bars. All OSD video options removed; publishing frames is the mode switch, and a zeroed control word (or DDR timeout) reverts to the noise pattern.
  • 480i: the app publishes progressive 720x480; the core fetches field lines (2*line + field) as two 180-beat bursts, drives VGA_F1, and alternates vsync phase by exactly 262.5 lines.
  • menu.sdc: the video clock is declared asynchronous to all other clocks (all crossings are 2FF/dual-clock-FIFO CDC). With it, the build meets timing on every domain (worst setup slack +0.53 ns, TNS 0) — the previous baseline silently shipped -4.8 ns / TNS -426.

Testing

  • Self-checking Icarus testbenches in tb/ (tb/run.sh): all mode timings verified in exact pixel ticks (line/field periods, sync widths, porch positions, offset trims and clamping, half-line interlace), plus a system-level reader TB against behavioral DDR/FIFO models covering the v2 and legacy fetch sequences, double buffering, both 480i field parities, FIFO occupancy, writer-stop reversion, and DDR-timeout recovery. 86 checks, all passing.
  • Full Quartus 17.0.2 compile in the CI docker image: 0 errors, timing met on all clock domains.
  • Still pending hardware verification per plan §8: real CRTs, capture-device line-rate readout, OSD overlay, HDMI lock in all modes.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58d06889-4c41-453d-b267-18cd7230a428

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/native-video-centering

Comment @coderabbitai help to get the list of available commands and usage tips.

Implements the FPGA side of docs/native-video-plan.md (all phases; the
zaparoo-launcher side comes separately).

- PLL output 1 retargeted 27.027027 -> 27.000000 MHz, giving exact NTSC
  (15734.27 Hz) and PAL (15625.00 Hz) line rates.
- native_video_timing rebuilt around per-mode parameter sets: 352x240p60
  (Switchres ntsc porches), 720x480i60 (CEA-861, 262+263-line fields with
  half-line vsync offset on the odd field), 352x288p50 (Switchres pal).
  Mode and trims latch at the field wrap; offsets clamp to -8..+8 px /
  -8..+2 lines. Field flips at the start of vblank so the reader's line
  preload always fetches the parity about to be displayed.
- native_video_reader parses DDR control word1: magic 0x5A50 selects the
  v2 layout (buffers +0x1000/+0x180000, tight stride) and carries mode and
  h/v offsets; without magic the legacy 320x240 layout is scanned centered
  with 16-px side bars. word0 == 0 and DDR timeouts now clear frame_ready
  so the core reverts to the noise pattern instead of scanning a dead
  buffer. 480i fetches source line 2*line+field as two 180-beat bursts;
  line FIFO deepened to 1024 words for the interlaced 2-line preload.
- menu.sv: OSD video options removed (CONF_STR back to stock), ce_pix
  divider switches /4 / /2 by mode, VGA_F1 driven by the field bit.
- Self-checking iverilog testbenches in tb/ (run via tb/run.sh) verify all
  mode timings in exact pixel ticks, the half-line interlace (both vsync
  intervals exactly 262.5 lines), offset clamping, the v2/legacy fetch
  sequences, double buffering, writer-stop reversion, and timeout recovery.
- Readme documents the native video output and the forced_scandoubler /
  vga_scaler=1 note.
- Self-contained handoff document for the zaparoo-launcher team: the v2
  DDR contract (word layout, buffer addresses, publish/stop protocol,
  write ordering), the 352x240 writer changes, safe-area and 480i
  flicker rules, the calibration screen spec, and the hardware
  verification checklist.
The fitter rejected 27.000000 MHz on the shared PLL: all outputs of one
PLL divide a common VCO, and lcm(100 MHz clk_sys, 27 MHz) = 2700 MHz is
beyond the Cyclone V's 600-1600 MHz VCO range. 27.027027 MHz (1000/37)
is exactly the closest sharable frequency, which is why stock MiSTer
uses it.

- Revert rtl/pll/pll_0002.v to stock; its 27.027 MHz output is now
  unconnected.
- Add rtl/pll_video.v (+ pll_video_0002.v, qip): dedicated PLL whose
  sole output is exact 27.000000 MHz (VCO 1350 MHz = 50 x 27, C = 50);
  CLK_VIDEO comes from it, and the native video path holds in reset
  until it locks.
- Add menu.sdc declaring the video clock asynchronous to all other
  clocks. It was absent from sys_top.sdc's exclusive clock groups, so
  TimeQuest analyzed the two-flop synchronizer and line-FIFO crossings
  as related paths (worst slack -10.4 ns, TNS -1529). All crossings are
  designed CDC structures. With the constraint, the full Quartus 17.0.2
  compile meets timing on every domain (worst setup slack +0.53 ns,
  TNS 0), which the previous 27.027 MHz baseline did not (-4.8 ns,
  TNS -426).
- Document the shared-VCO constraint in docs/native-video-plan.md.
@wizzomafizzo wizzomafizzo changed the title fix: widen native video centering range feat: broadcast-standard native CRT video with PAL and 480i modes Jun 11, 2026
- The app-level CRT mode (--crt: pixel fonts, CRT layout, DDR writer)
  stays; only the core-side status[9] enable and offset status bits are
  gone. The old wording ("no CRT mode toggle anywhere") read as if the
  concept itself was removed.
- Add section 3 documenting the existing Main_MiSTer coordination
  (config/zaparoo_launcher_crt.bin read at menu-core load, OSD toggle
  respawning only the frontend) and the required Main-fork edits: drop
  the dead status writes, move offset ownership to the launcher, update
  fb mode to 352x240/1408, and widen the DDR blank to 0x300000.
- Sketch a launcher-side toggle option via a reserved exit code so
  neither Main nor the system needs a restart.
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