System designExplainer

FPGA USB3 slave FIFO video streaming: size DMA buffers, read flags

FPGA video over a USB3 slave FIFO at 640 × 512, 16-bit, 50 fps is only 32.8 MB/s: erratic streaming points at DMA buffers and flags, not the bus.

LumaSpec Vision13 min read Share on LinkedIn

On this page 9 sections
  1. 01What a slave FIFO bridge is, and why firmware defines everything
  2. 02The flags: what each line reports and where wiring goes wrong
  3. 03Worked example: 640 × 512, 16-bit video at 50 fps
  4. 04Sizing DMA buffers and host reads
  5. 05Erratic reception: three faults, in this order
  6. 06UVC or private bulk: how the host should see the frames
  7. 07When to skip the bridge and buy the interface finished
  8. 08Checks before you commit the design
  9. 09Questions

You have an FPGA producing frames and a USB3 peripheral controller in slave FIFO mode sitting between it and the host. The data path comes up, but what arrives is torn, short or duplicated, and the first suspicion is USB bandwidth. Run the numbers instead: a 640 × 512, 16-bit stream at 50 fps needs about 32.8 MB/s, and a healthy USB3 link carries hundreds of megabytes per second. This guide computes that gap, shows what the slave FIFO flags really report, sizes DMA buffers against host read jitter, and ends with the decision for each case: fix the handshake, or skip the custom bridge and buy the interface finished.

What a slave FIFO bridge is, and why firmware defines everything

A USB3 peripheral controller configured in slave FIFO mode exposes a handful of parallel FIFOs to your FPGA. Your FPGA is the master of that interface: it drives a clock, data, a write strobe and a chip select. The controller forwards whatever lands in the FIFOs to the host as USB bulk data. Flag lines come back the other way and tell the FPGA whether the buffer behind a given thread has room, or holds readable data.

None of this behavior lives in the silicon. Which endpoints the host sees, how large each DMA buffer is, how many buffers exist and what the state machine does on every clock edge are all set by firmware you write, compile into an image and download to the board. An untouched part enumerates as nothing at all, so the perennial question — does the image file have to be downloaded — always has the same answer: yes. You build the image with the vendor's SDK and load it either at every power-up from the host utility, or once into the boot memory the board provides.

The state machine that drives the FIFO side is not written line by line either. You describe the interface in the vendor's graphical timing tool — bus width, signal mapping, flag behavior — and it generates the hardware description that the firmware wraps. Most streaming faults are born on that drawing board, which is why the rest of this guide spends more time on flags and buffers than on the bus. If your bridge sits on a carrier board next to a camera module, match the pair before you stream, as in this board-level camera and USB3 bridge board check.

The flags: what each line reports and where wiring goes wrong

The slave FIFO architecture runs several DMA threads, often called sockets, and every thread has its own flag outputs. The interface's application note states their job plainly: the controller asserts a flag to signal that a buffer has reached an empty or full condition. Two flags per thread matter for video:

  • the busy flag, which flips when the current buffer is full on a write thread or empty on a read thread;
  • the watermark flag, also called the partial flag, which toggles when the buffer fill crosses a threshold you program in the state machine.

Polarity is the first trap. The vendor's timing tool generates flags driven active low by default, so a line that idles high and pulses low means 'space available' or 'data available'. Connect it to FPGA logic that assumes active high and your design sees the opposite of reality while every waveform looks plausible. The second trap is thread mapping: with four threads in play, each flag pin belongs to one socket, and sampling the flag of thread 0 while writing thread 2 loses the plot silently.

The watermark level has a second job: it tells the FPGA how much it may push as one burst without the FIFO filling mid-burst. Set the margin smaller than your line burst and the FIFO fills between flag updates. The write then either stalls mid-line or keeps driving and overwrites committed bytes. Which pin carries which flag, for which thread and direction, is tabulated in the vendor's slave FIFO application note — read that one table against your generated design before the first power-up.

Worked example: 640 × 512, 16-bit video at 50 fps

The case is the classic custom-sensor stream: a mono sensor delivering 640 × 512 pixels per frame at 16 bits per pixel (2 bytes) and 50 frames per second, clocked by the FPGA into the bridge's write thread. At 50 fps the frame period is 20 ms, so 655,360 bytes must move within every 20 ms window. Substitute your own resolution and pixel depth; the three lines of arithmetic do not change.

bytes per frame = width × height × bytes per pixel stream rate = bytes per frame × frames per second bus occupancy = stream rate / sustained bus rate

Bytes per frame: 640 × 512 pixels × 2 B = 655,360 B. Stream rate: 655,360 B × 50 frames/s = 32,768,000 B/s, about 32.8 MB/s or 262.1 Mbit/s. Sustained bus rate: a 5 Gbit/s USB3 link spends a fifth of its signaling on 8b/10b encoding and carries 4 Gbit/s, and protocol overhead leaves roughly 3.2 to 3.8 Gbit/s for payload in a tuned bulk stream. Occupancy: 262.1 / 3,200 ≈ 8.2%, and only 6.6% even against the full 4,000 Mbit/s.

Decision: at 8% occupancy the bus cannot be the fault. Hold on to the threshold this arithmetic produces — below roughly 10% of sustained capacity a single stream has unlimited practical headroom; past about half the bus, per-camera budgeting starts, the way the USB3 bandwidth budget math works it for standard cameras. Erratic reception at 32.8 MB/s is a handshake fault, and the next sections go to it.

What moves the number and by how much: an 8-bit pixel stream halves the rate to 16.4 MB/s (131.1 Mbit/s); squeezing the frame into a 10 ms burst window doubles the instantaneous rate to 65.5 MB/s (524.3 Mbit/s, still 16.4% occupancy); stepping up to 800 × 600 at 60 Hz with 16-bit pixels gives 800 × 600 × 2 B × 60 /s = 57.6 MB/s (460.8 Mbit/s, 14.4% occupancy). Every plausible variant stays under a sixth of the bus, so the conclusion never changes.

Sizing DMA buffers and host reads

USB moves your video as a chain of DMA buffers: the bridge fills one, commits it to the wire and refills the next while the first is still in flight. Two sizes decide whether that chain ever runs dry: how many bytes are in flight on the device side (buffer count × buffer size) and how much the host reads per call. The rule in words: keep enough buffering to ride out the longest gap between host reads.

in-flight bytes ≥ stream rate × worst-case host stall

For the worked case with a 10 ms stall allowance — a pessimistic but realistic hiccup for an operating system also serving disk, display and network — the product is 32.8 MB/s × 0.010 s = 328 kB that must sit buffered somewhere. If the firmware builds 32 kB DMA buffers — an illustrative size; your build's limit governs — then 328 kB / 32 kB = 10 buffers just to survive one stall, so run 12 to 16 and watch the flags through a full frame: no assert means margin. The same count-over-size logic drives sizing DMA FIFO buffers on FPGA-targeted controllers, and why three streaming buffers beat one holds for every buffer chain: fewer than three in flight and each commit stalls the pipe.

On the host side, read whole buffers per call. A bulk transfer ends on a short packet — at SuperSpeed the bulk maximum packet size is 1024 bytes — so an end-of-frame partial buffer must be committed as a short packet on purpose, or the last piece of every frame waits in the FIFO. Keep host reads an exact multiple of the device buffer size, hand the same memory back each time instead of allocating per frame, and consume counted blocks the way FPGA DMA FIFO counted reads are meant to be serviced. Reusing image buffers instead of per-frame allocation is its own discipline. Under Linux the same squeeze appears as socket limits: see the GigE buffer timeout tuning for the pattern, or raising the host USB buffer once a second stream joins.

The host controller matters too. A chipset port shares its bandwidth tree with storage, displays and every other device; a dedicated controller keeps the video stream off that tree. The MV-GU2204 quad USB3.0 adaptor gives the bridge 4 dedicated USB3 ports.

Frame grabbersHikrobot MV-GU2204 MV-GU2200 Series Quad USB3.0 AdaptorMV-GU2204
Interface
USB3
Ports
4 ports
$469.00View partBuild a system with it

Erratic reception: three faults, in this order

With bandwidth excluded, reception faults live in the handshake — the same bandwidth-versus-buffers split the USB3 Vision transfer-error checklist automates for standard cameras. Work the table top to bottom; each test costs minutes.

Symptom at the host Likely fault Bench test
Stream starts, then the FPGA side hangs Wrong flag pin, or the wrong thread's flag, sampled Scope every flag pin at idle: the level before the first frame must match the generated state machine (active low by default)
Data valid but torn at a repeating offset Write burst longer than the watermark margin; FIFO fills mid-burst Halve the burst length or raise the watermark; the tear period should move with buffer size
Blocks repeat, or byte counters jump Host reads out of step with buffer boundaries; buffers lost or read twice Stream an incrementing 32-bit counter from the FPGA and check per-buffer monotonicity at the host

The counter pattern settles arguments. Load an incrementing 32-bit value into every word, capture one frame at the host and diff against the pattern. A jump means a lost buffer, a repeat means one read twice, and the byte offset names which buffer in the chain failed. If all three tests pass and frames still arrive short, the remaining suspect is end-of-frame commit — the partial buffer never sent as a short packet. That shows up as a one-frame lag with corruption that grows, and it is fixed in the state machine's end-of-frame handling, not on the host.

UVC or private bulk: how the host should see the frames

The firmware chooses how the host perceives the device, and that choice decides where your debugging time goes. USB video class firmware presents standard camera descriptors, and any operating system opens it without a custom driver. The descriptors must declare every frame size and frame interval the device will actually deliver; when the host negotiates a format the firmware cannot honor, the part enumerates and then delivers nothing, or a frame that looks like noise. A 640 × 512 stream at 50 fps works over UVC exactly as long as the descriptors list that size and interval and the timing engine honors them. The host-side expectations of a compliant device on embedded Linux are covered in UVC-compliant USB cameras on embedded Linux.

A private vendor-specific bulk interface takes the opposite trade. No class driver stands in the way, no descriptor has to advertise your frame geometry, and you control read sizes and buffer counts directly — exactly what a stream under debug wants. The cost: the host application is yours from end to end.

UVC firmware Private bulk firmware
Host driver Ships with the OS You write and maintain it
Viewer tools Any camera application Your own host code
Frame geometry Must be declared and honored Unrestricted, but no negotiation
Best phase Stable stream others need to open Bring-up, measurement, debugging

A practical sequence: bring the stream up on private bulk with raw dumps to disk, where every byte is inspectable. Move to UVC once frames are clean and someone other than you needs to open the device.

When to skip the bridge and buy the interface finished

Decide per project. Build the slave FIFO bridge when the sensor is custom or the timing is yours — your own VGA sequencer, a non-standard imager, deterministic latency, or data that is not camera-shaped. The work is bounded, and this guide is its checklist. Buy a camera when the sensor is a standard imager, because the device side — descriptors, DMA engine, enumeration, driver — arrives built and tested, and a board-level camera drops into your housing with the USB3 work already done.

The LS-BCM-0003 carries a 1/1.8" global-shutter sensor streaming at 120 fps over USB3, which frees your FPGA to do image processing instead of USB. The MV-CB060-10UM-B puts a 6 MP mono sensor on a bare board over USB3 when resolution matters more than frame rate.

Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
$690.00View part
Board-level camerasHikrobot MV-CB060-10UM-B 6 MP Board Level Camera, USB3, IMX178, Mono, Bare Board, V4MV-CB060-10UM-B
Sensor format
1/1.8"
Interface
USB3
$239.00View part

For standard inspection rather than embedded integration, the whole area scan camera range enumerates as an ordinary vision device. And if the custom path exists only because every camera seemed to need custom plumbing, walk the frame grabbers aisle first — standard interfaces with standard drivers, including the USB3 host adaptors of the kind above. The camera finder narrows either route in a few clicks.

Checks before you commit the design

  • Compute occupancy (stream Mbit/s ÷ sustained bus Mbit/s) and confirm it sits under about 10%. Above that, budget per camera before adding streams.
  • Scope every flag pin at idle and match level and polarity to the generated state machine before the first frame.
  • Set the watermark margin at least as large as your longest write burst.
  • Size the buffer chain from stream rate × worst-case host stall, then run several buffers more than the minimum.
  • Make host reads a whole multiple of the device buffer size, and reuse the memory between frames.
  • Commit the end-of-frame partial buffer as a short packet, or every frame loses its tail.
  • Pick UVC or private bulk before writing host code; switching later rewrites the host side.

When the checklist is green and frames still misbehave, the fault is almost always one flag line — and at 8% occupancy it is never the bus. If you would rather not own the firmware at all, send us the sensor, frame size and frame rate through the contact page, or start at the extras step of the configurator for the host-side parts. The systems topic page collects the rest of the host-and-transport debugging guides.

Questions

Does a slave FIFO bridge need firmware before it will stream anything?

Yes. The controller ships with no application in it: the slave FIFO state machine, the DMA channel geometry and the USB descriptors all live in the firmware image your build produces. Compile it with the vendor's SDK and download it to the board — at every power-up from the host utility, or once into boot memory where the board provides it. Only then does the part enumerate as your device and expose flags to the FPGA.

My data path works but frames come back torn or duplicated. Where is the fault?

Almost never bandwidth: a 640 × 512, 16-bit, 50 fps stream occupies about 8% of a healthy USB3 link. Work the handshake in this order — flag polarity and thread assignment against the generated state machine; watermark level versus write burst size; host read sizes versus DMA buffer boundaries. Streaming an incrementing counter pattern from the FPGA and diffing per buffer at the host finds lost or duplicated buffers in a single run.

What does the watermark (partial) flag actually tell the FPGA?

It toggles when the selected buffer's fill crosses a threshold you program in the state machine, and its meaning follows the direction: on a write thread it reports space for the next burst; on a read thread it reports data ready to take. The tool that generates the state machine sets both the assert level and the flag's polarity. If your burst is larger than the programmed margin, the FIFO fills mid-burst and words are lost or the line stalls.

When should I use UVC firmware instead of a vendor-specific bulk interface?

Choose UVC when the OS's own camera driver and any viewer application should open the stream: the firmware declares each frame size and frame interval in its descriptors, and those declarations must match what the timing engine delivers. Choose a private vendor-specific bulk interface while the stream is still moving — you control read sizes and buffer counts directly, and no descriptor negotiation can fail. Many teams debug on private bulk and ship UVC.

Is a ready-made USB3 camera the better buy when my sensor is a standard imager?

Usually, yes. A board-level camera contains the whole device side you would otherwise build: enumeration, buffering, descriptors and a proven host driver. You give up custom sensor timing but gain standard controls and software support. Keep the FPGA bridge for genuinely non-standard sensors, timing you generate yourself, deterministic low latency, or data that is not camera-shaped.