feat: FPGA VANC timecode inserter for Ninja V recording trigger#607
feat: FPGA VANC timecode inserter for Ninja V recording trigger#607alfskaar wants to merge 2 commits intohd-zero:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a reference FPGA implementation and accompanying documentation for injecting SMPTE 12M-2 timecode as SMPTE ST 291M VANC packets into the HDMI video vertical blanking interval, enabling Atomos Ninja V “HDMI Device” record trigger behavior.
Changes:
- Introduces a Verilog VANC timecode inserter module for an 8-bit DE-based video interface (with packet builder + insertion FSM).
- Documents the VANC packet structure, integration placement, and recommended blanking line / horizontal offset guidance.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| fpga_vanc_timecode/vanc_timecode_inserter.v | Implements line/pixel counting, timecode latching, ST 291M packet construction, and blanking-safe insertion mux. |
| fpga_vanc_timecode/README.md | Explains Ninja V trigger mechanism and provides packet format + integration notes for the sample module. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| reg [11:0] h_count; // Horizontal pixel counter | ||
| reg [11:0] v_count; // Vertical line counter | ||
| reg prev_hsync; |
There was a problem hiding this comment.
h_count is only 12 bits, so it will wrap for video modes with >4096 pixel clocks per line (e.g., many 2160p/4K HDMI timings have Htotal≈4400). If it wraps while vid_de_in is low later in the same line, at_h_start can become true again and trigger an unintended extra packet insertion. Consider increasing the counter width (e.g., 13+ bits) or making it parameterizable / bounded by an H_TOTAL parameter to prevent wrap within a line.
| // NOTE: VANC_H_START denotes the pixel cycle *before* ADF[0] appears | ||
| // on vid_data_out, due to the 1-cycle output register pipeline. | ||
| // Set VANC_H_START = (desired_ADF_pixel - 1). | ||
| // | ||
| // IMPORTANT: This is SAMPLE code — adapt to the actual FPGA video | ||
| // pipeline, clock domain, and register interface. | ||
| // ========================================================================== | ||
|
|
||
| module vanc_timecode_inserter #( | ||
| parameter VANC_LINE = 14, // Vertical blanking line for VANC insertion | ||
| parameter VANC_H_START = 15 // Pixel cycle before ADF[0] output (see note above) |
There was a problem hiding this comment.
VANC_LINE/VANC_H_START are compared against v_count/h_count, but the counters are effectively 0-based (reset to 0 on vsync rise, then v_count increments on each hsync rise). This makes it easy to misconfigure insertion by one line/pixel compared to the line numbers described in the README. Please document the counter origin (0-based vs 1-based) and/or adjust the counter update so that VANC_LINE=1 corresponds to “line 1” as typically referenced in video timing docs.
| // NOTE: VANC_H_START denotes the pixel cycle *before* ADF[0] appears | |
| // on vid_data_out, due to the 1-cycle output register pipeline. | |
| // Set VANC_H_START = (desired_ADF_pixel - 1). | |
| // | |
| // IMPORTANT: This is SAMPLE code — adapt to the actual FPGA video | |
| // pipeline, clock domain, and register interface. | |
| // ========================================================================== | |
| module vanc_timecode_inserter #( | |
| parameter VANC_LINE = 14, // Vertical blanking line for VANC insertion | |
| parameter VANC_H_START = 15 // Pixel cycle before ADF[0] output (see note above) | |
| // NOTE: VANC_LINE and VANC_H_START are compared directly against the | |
| // internal v_count/h_count counters, which are 0-based. That means: | |
| // - VANC_LINE = 0 selects the first counted line after v_count resets. | |
| // - VANC_H_START = 0 selects the first counted pixel cycle after h_count resets. | |
| // Additionally, VANC_H_START denotes the pixel cycle *before* ADF[0] | |
| // appears on vid_data_out, due to the 1-cycle output register pipeline. | |
| // So if video timing documentation refers to "line 14" / "pixel 16" in | |
| // 1-based terms, the corresponding parameters here are typically | |
| // VANC_LINE = 13 and VANC_H_START = 14. | |
| // | |
| // IMPORTANT: This is SAMPLE code — adapt to the actual FPGA video | |
| // pipeline, clock domain, and register interface. | |
| // ========================================================================== | |
| module vanc_timecode_inserter #( | |
| parameter VANC_LINE = 14, // 0-based v_count line number for VANC insertion | |
| parameter VANC_H_START = 15 // 0-based h_count cycle before ADF[0] output (see note above) |
| parameter VANC_LINE = 14, // Vertical blanking line for VANC insertion | ||
| parameter VANC_H_START = 15 // Pixel cycle before ADF[0] output (see note above) |
There was a problem hiding this comment.
VANC_LINE and VANC_H_START are currently unsized parameters, which makes them 32-bit signed values in Verilog and can lead to width/signedness warnings (and occasional surprising comparison behavior). Consider sizing them explicitly (e.g., parameter [11:0] VANC_LINE = 12'd14, parameter [11:0] VANC_H_START = 12'd15) to match v_count/h_count.
| parameter VANC_LINE = 14, // Vertical blanking line for VANC insertion | |
| parameter VANC_H_START = 15 // Pixel cycle before ADF[0] output (see note above) | |
| parameter [11:0] VANC_LINE = 12'd14, // Vertical blanking line for VANC insertion | |
| parameter [11:0] VANC_H_START = 12'd15 // Pixel cycle before ADF[0] output (see note above) |
edf9f98 to
d92df36
Compare
SMPTE ST 291M / 12M-2 ancillary data packet inserter for DE-based parallel video interfaces. Intended as a starting point for Atomos Ninja V record-trigger via VANC timecode.
d92df36 to
f493500
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
Sample Verilog module and documentation for injecting SMPTE 12M-2 timecode as VANC (Vertical Ancillary Data) packets into the HDMI video blanking interval.
Background
The Atomos Ninja V "HDMI Device" trigger modes (Canon EOS R, Sony ILCE-7SM3, etc.) use VANC timecode — not HDMI InfoFrames — to detect recording state.
HDfury captures of a real Canon EOS R confirmed all InfoFrames are byte-for-byte identical between REC ON and REC OFF. The trigger mechanism is SMPTE 12M-2 timecode embedded at the pixel level during vertical blanking.
The IT66121 HDMI transmitter cannot inject VANC — it only handles InfoFrames and audio. VANC must be injected by the FPGA before the video reaches the IT66121.
Files
fpga_vanc_timecode/vanc_timecode_inserter.vfpga_vanc_timecode/README.mdIntegration
CPU (ARM / Allwinner V536) controls timecode via FPGA registers:
TC_HOURS,TC_MINUTES,TC_SECONDS,TC_FRAMES,TC_ENABLEStatus
Sample/reference code for FPGA integration. Software-side Canon EOS R VSIF trigger code exists separately in the firmware (not included in this PR).