Zero to Photon:
AFIFO.v: a Verilog asynchronous FIFO
2024 . 4 . 27
When capturing a photo, Photon's ICE40 FPGA is responsible for reading live image data from Photon's image sensor and writing that image data to SDRAM, and then transferring the image data from SDRAM to the SD card.

This process involves three independent clocks:

  • img_dclk: image sensor clock (98 MHz); generated by the image sensor
  • ram_clk: SDRAM clock (108 MHz); generated by ICE40 PLL
  • sd_clk: SD card clock (102 MHz); generated by ICE40 PLL
Analogous to crossing thread boundaries in the software world, special care is needed to safely pass data between clock domains in the hardware world. To safely pass data between its three clock domains, Photon uses AFIFO.v.

AFIFO.v is designed for the ICE40 FPGA and Yosys/icestorm toolchain. While AFIFO.v relies on the ICE40-specific SB_RAM40_4K dual-port RAM primitive to store its data, its logic should generalize to other FPGAs. However using AFIFO.v on a different FPGA requires updating the RAM instantiation to the RAM primitive offerred by the target FPGA.


AFIFO.v Overview

AFIFO.v is a Verilog module that implements an asynchronous first-in-first-out queue. Asynchronous in this context means that the read domain and the write domain use independent clocks; AFIFO.v allows data to be pushed into the queue with one clock signal, and popped from the queue with a different, independent clock signal, thereby allowing safe transmission of data between two clock domains.

AFIFO.v stores its data in a RAM primitive provided by the target FPGA. The RAM must be dual-port so that reads and writes can occur in parallel.

AFIFO.v is based on Cliff Cummings' 2002 paper.


AFIFO.v Interface

AFIFO.v groups its IO signals into three ports, described below.

Read Port

The Read port reads data from the FIFO, one word at a time:
input wire r_clk,
input wire r_trigger,
output wire[N:0] r_data,
output wire r_ready,
  • input r_clk: the clock for the Read port
  • input r_trigger: whether a word should be popped from the FIFO, if r_ready is 1
  • output r_data: the front word being popped from the FIFO
  • output r_ready: whether a word can be popped from the FIFO (ie whether the FIFO has at least one word available for reading, and r_data is therefore valid)

Write Port

The Write port writes data into the FIFO, one word at a time:
input wire w_clk,
input wire w_trigger,
input wire[N:0] w_data,
output wire w_ready,
  • input w_clk: the clock for the Write port
  • input w_trigger: whether w_data should be pushed into the FIFO, if w_ready is 1
  • input w_data: the word to be written into the FIFO
  • output w_ready: whether a word can be written into the FIFO (ie whether the FIFO has at least one empty slot)

Reset Port

The Reset port contains a single input signal:
input wire rst_,
  • input rst_: asynchronous reset; clears the FIFO so that it's empty