On real hardware (FPGA)

The thesis proposed the algorithm for hardware but never built it. This page is the missing half: SweepLSD running live on 2009-era silicon.

What runs. A Digilent Atlys (Xilinx Spartan-6 LX45, 2009) takes HDMI in, detects line segments on-chip, and puts HDMI out with every frame's segments overlaid in green — at Full-HD 1080p30 (and 720p60), in a single recovered-pixel-clock domain at 74.25 MHz, with no frame buffer and no external memory. The detector's entire state is a few line buffers in on-chip block RAM (~70 KiB — the budget the 2014 thesis targeted).

Live demo: 1080p30 HDMI video passes through the board while segments are detected and overlaid on-chip. (direct video link if the embed does not play.)

1. Two hardware implementations, one contract

The C++ library was re-expressed twice, and both forms are held to the same acceptance gate as the software:

formwhat it isstatus
hls/the full detector as synthesizable HLS C++ (Vitis HLS), plus a tool-free g++ shim so the model runs as ordinary C++Artix-7 reports: front-end II=1 at 100 MHz; C-sim and C/RTL co-sim clean
rtl/hand-written, portable Verilog RTL: streaming front-end → sparse event FIFO → labelling back-end → integer judgeruns on the Atlys; verified as below

The contract is SW == HLS == RTL, bit-exact: golden-vector parity testbenches (Icarus Verilog) hold the RTL to the HLS C model and the C++ detect() reference, including 1920×1080 photographs — all 150 photographs of the project's Full-HD test corpus match segment-for-segment in the hardware configuration. The refinements that fit the streaming/integer model — strict NMS, half-pixel lattice, bounding-box endpoints, streaming hysteresis, curve rejection — are all in the hardware (sub-pixel NMS stays software-only: it does not fit the judge's 128-bit envelope).

What it costs. Both realizations, side by side. The HLS core proves the streaming schedule (front-end II=1, one pixel per clock) but, left to instantiate a moment multiplier per product, spends 79 DSP blocks and 95% of the small Artix-7's LUTs on the detector alone. The hand-written RTL folds every product onto one shared sequential multiplier, collapsing 79 DSP blocks to 14 — so the complete live system (detector + HDMI receive/transmit + overlay drawer) fits the 2009-era Spartan-6 well within budget, with every timing constraint met (74.8 MHz achieved against the 74.25 MHz pixel clock).

HLS core (Artix-7 xc7a35t)RTL system (Spartan-6 xc6slx45)
clock100 MHz, II=174.25 MHz pixel
slice LUTs19,789 (95%)12,664 (46%)
slice registers14,684 (35%)8,813 (16%)
occupied slices4,109 (60%)
block RAM79 (79%)74 RAMB16 + 26 RAMB8
DSP blocks79 (87%)14 (24%)
timingmet (slack 0.02 ns)all met (74.8 MHz)

The RTL column is the complete design that runs on the board; block RAM is counted as allocated primitives (the logical footprint is ≈140 KiB of 261 KiB, see footer). The single shared multiplier is what collapses the DSP count from 79 to 14.

2. Honest limits: dense-frame overload

The back-end labels events (edge pixels), so a maximally dense row can outrun it. The event FIFO absorbs bursts and, when saturated, sheds data events rather than stalling live video. After a sequence of measured drain optimizations (each bit-exact, each verified by a ground-truth burst simulation at real 1080p30 timing), corpus-wide loss went from ~52 % of segments on the first live build to ≈0.2 %, concentrated in a single pathologically dense frame; at a deeper FIFO the whole corpus is provably lossless in simulation. The remaining step on this 2009 part is a BRAM-based FIFO to afford that depth. Details and the measurement methodology: rtl/DESIGN.md, "Overflow reality check".

3. The bug that was not in the RTL

The hardest bug of the bring-up never appeared in simulation: the live board lost all detections below a per-image "cut row" on dense scenes, deterministically, with timing closed at Score 0 and RTL simulation clean. Per-pass UART telemetry showed the board's judge handshakes dying where simulation's did not — and a gate-level simulation of the synthesized netlist reproduced the board exactly: XST's FSM re-encoding had mis-synthesized the back-end's large FSM. One synthesis option (-fsm_extract NO) restores RTL-identical netlist behaviour; every build script now sets it, with a warning.

Two portable lessons. (1) "Timing Score 0 + RTL sim clean" does not imply the netlist computes what the RTL does — for deterministic board-only anomalies, run the netlist in simulation before blaming physics. (2) Cheap always-on instrumentation (six per-pass counters over UART) turns "the board misbehaves" into a one-line diff against the same counters in simulation. The full war story is in rtl/DESIGN.md.

4. Reproduce it

The RTL parity suite runs with the open-source Icarus Verilog on the tracked synthetic vectors: sh rtl/tb/run_tb.sh tb_sweep_core core/*.v (per-stage and full-chain testbenches; Full-HD golden vectors are regenerated locally with sweeplsd_dump_vectors since the photographs are not redistributed). The board bitstream needs Xilinx ISE 14.7 and the XAPP495 HDMI PHY, fetched separately — build steps, licensing notes, and the diagnostic LED/UART map are in rtl/boards/atlys/README.md; architecture and the verification story in rtl/DESIGN.md.