├── Quartus RTL view.pdf ├── README.md ├── rtl ├── bin2gray.v ├── dualport_ram.v ├── gray2bin.v ├── rlogic.v ├── top_dual_fifo.v └── wlogic.v ├── sim ├── osc.v ├── sim.v └── tester.v └── top.png /Quartus RTL view.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akhan3/async-fifo/6c65b10cb74a77600f0bf942f5c6404cb17af392/Quartus RTL view.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-fifo 2 | Asynchronous FIFO for transferring data between two asynchronous clock domains 3 | 4 | ![alt tag](https://raw.githubusercontent.com/akhan3/async-fifo/master/top.png) 5 | -------------------------------------------------------------------------------- /rtl/bin2gray.v: -------------------------------------------------------------------------------- 1 | module bin2gray # 2 | ( 3 | parameter SIZE = 4 4 | ) 5 | ( 6 | input [SIZE-1:0] bin, 7 | output [SIZE-1:0] gray 8 | ); 9 | 10 | assign gray = bin ^ (bin >> 1); 11 | 12 | endmodule 13 | -------------------------------------------------------------------------------- /rtl/dualport_ram.v: -------------------------------------------------------------------------------- 1 | module dualport_ram # 2 | ( 3 | parameter AWIDTH = 9, 4 | parameter DWIDTH = 16 5 | ) 6 | ( 7 | input arst_n, // asynchronous reset 8 | input wclk, // writing circuit's clock 9 | input wen, // enable signal to register the write data to FIFO buffer 10 | input [AWIDTH-1:0] waddr, // write address for the FIFO buffer 11 | input [DWIDTH-1:0] wdata, // data to be written by the writing circuit 12 | 13 | input rclk, // reading circuit's clock 14 | input ren, // enable signal to update the read bus from FIFO buffer 15 | output reg rdv, // data valid signal. Reading circuit can safely register the data on the data bus 16 | input [AWIDTH-1:0] raddr, // read address for the FIFO buffer 17 | output reg [DWIDTH-1:0] rdata // data to be read by the reading circuit 18 | ); 19 | 20 | reg [DWIDTH-1:0] mem [0:2**AWIDTH-1]; // the actual memory block 21 | 22 | always @(posedge wclk) begin 23 | if (wen) 24 | mem[waddr] <= wdata; 25 | end 26 | 27 | always @(posedge rclk, negedge arst_n) begin 28 | if (!arst_n) 29 | rdv <= 1'b0; 30 | else 31 | rdv <= ren; 32 | end 33 | 34 | // assign rdata = mem[raddr]; 35 | always @(posedge rclk, negedge arst_n) begin 36 | if (!arst_n) 37 | rdata <= 'b0; 38 | else if (ren) 39 | rdata <= mem[raddr]; 40 | end 41 | 42 | endmodule 43 | -------------------------------------------------------------------------------- /rtl/gray2bin.v: -------------------------------------------------------------------------------- 1 | module gray2bin # 2 | ( 3 | parameter SIZE = 4 4 | ) 5 | ( 6 | input [SIZE-1:0] gray, 7 | output reg [SIZE-1:0] bin 8 | ); 9 | 10 | integer k; 11 | 12 | always @* begin // combinational logic 13 | for (k = 0; k < SIZE; k = k+1) begin 14 | bin[k] = ^(gray >> k); 15 | end 16 | end 17 | 18 | 19 | endmodule 20 | -------------------------------------------------------------------------------- /rtl/rlogic.v: -------------------------------------------------------------------------------- 1 | module rlogic # 2 | ( 3 | parameter AWIDTH = 4 4 | ) 5 | ( 6 | input rclk, // reading circuit's clock 7 | input arst_n, // asynchronous reset 8 | input rrq, // data request signal. Reading circuit must assert this to indicate that it can accept the data on the data bus 9 | input [AWIDTH:0] wgray, // gray write pointer from writer to reader (for safe synchronization across clock domains) 10 | output [AWIDTH:0] rgray, // gray read pointer from reader to writer (for safe synchronization across clock domains) 11 | output [AWIDTH-1:0] raddr, // read address for the FIFO buffer 12 | output reg rempty, // FIFO empty indicator for the reading circuit 13 | output ren // enable signal to update the read bus from FIFO buffer 14 | ); 15 | 16 | 17 | // internal nets 18 | reg [AWIDTH:0] wgray_step1; // sync version 19 | reg [AWIDTH:0] wgray_rsync; // double-sync version 20 | wire [AWIDTH:0] wgray_rsync_bin; // binary converted 21 | reg [AWIDTH:0] rbincounter; // binary read pointer (extra MSB tracks wrapping around) 22 | wire [AWIDTH:0] rbincounter_next; // next state combinational signal 23 | 24 | assign raddr = rbincounter[AWIDTH-1:0]; // read address for the FIFO buffer (lower bits of binary read pointer) 25 | assign ren = rrq && !rempty; // enable when the reader is requesting and FIFO is not empty 26 | 27 | 28 | // Synchronize wgray in rclk domain 29 | // wgray --> FF --> FF --> wgray_rsync 30 | always @(posedge rclk, negedge arst_n) begin 31 | if (!arst_n) begin 32 | wgray_step1 <= 0; 33 | wgray_rsync <= 0; 34 | end else begin 35 | wgray_step1 <= wgray; 36 | wgray_rsync <= wgray_step1; 37 | end 38 | end 39 | 40 | 41 | // binary counter 42 | always @(posedge rclk, negedge arst_n) begin 43 | if (!arst_n) 44 | rbincounter <= 0; 45 | else 46 | rbincounter <= rbincounter_next; 47 | end 48 | 49 | assign rbincounter_next = (rrq && !rempty) ? (rbincounter + 1'b1) : rbincounter; 50 | // assign rbincounter_next = rbincounter + (rrq && !rempty); 51 | 52 | 53 | // binary to gray conversion 54 | bin2gray #(.SIZE(AWIDTH+1)) 55 | bin2gray ( 56 | .bin (rbincounter ), 57 | .gray (rgray ) 58 | ); 59 | 60 | // gray to binary conversion 61 | gray2bin #(.SIZE(AWIDTH+1)) 62 | gray2bin ( 63 | .gray (wgray_rsync ), 64 | .bin (wgray_rsync_bin) 65 | ); 66 | 67 | 68 | // Empty logic 69 | always @(posedge rclk, negedge arst_n) begin 70 | if (!arst_n) 71 | rempty <= 0; 72 | else begin 73 | // if the read pointer catches upto the write pointer 74 | // if both the pointers have wrapped around the same number of times 75 | rempty <= (wgray_rsync_bin == rbincounter_next); 76 | end 77 | end 78 | 79 | 80 | 81 | 82 | endmodule 83 | -------------------------------------------------------------------------------- /rtl/top_dual_fifo.v: -------------------------------------------------------------------------------- 1 | // TOP-level module 2 | module top_dual_fifo # 3 | ( 4 | parameter AWIDTH = 3, 5 | parameter DWIDTH = 16 6 | ) 7 | ( 8 | input arst_n, // asynchronous reset 9 | 10 | input wclk, // writing circuit's clock 11 | input wdv, // data valid signal. Writing circuit must assert this signa to have the data registered 12 | input [DWIDTH-1:0] wdata, // data to be written by the writing circuit 13 | output wfull, // FIFO full indicator for the writing circuit 14 | 15 | input rclk, // reading circuit's clock 16 | input rrq, // data request signal. Reading circuit must assert this to indicate that it can accept the data on the data bus 17 | output [DWIDTH-1:0] rdata, // data to be read by the reading circuit 18 | output rempty, // FIFO empty indicator for the reading circuit 19 | output rdv // data valid signal. Reading circuit can safely register the data on the data bus 20 | ); 21 | 22 | 23 | // internal nets 24 | wire [AWIDTH:0] rgray; 25 | wire [AWIDTH:0] wgray; 26 | wire [AWIDTH-1:0] waddr; 27 | wire [AWIDTH-1:0] raddr; 28 | wire wen; 29 | wire ren; 30 | 31 | 32 | // Write logic 33 | wlogic #(.AWIDTH(AWIDTH)) 34 | wlogic 35 | ( 36 | .wclk (wclk ), 37 | .arst_n (arst_n ), 38 | .wdv (wdv ), 39 | .rgray (rgray ), 40 | .wgray (wgray ), 41 | .waddr (waddr ), 42 | .wfull (wfull ), 43 | .wen (wen ) 44 | ); 45 | 46 | // Read logic 47 | rlogic #(.AWIDTH(AWIDTH)) 48 | rlogic 49 | ( 50 | .rclk (rclk ), 51 | .arst_n (arst_n ), 52 | .rrq (rrq ), 53 | .wgray (wgray ), 54 | .rgray (rgray ), 55 | .raddr (raddr ), 56 | .rempty (rempty ), 57 | .ren (ren ) 58 | ); 59 | 60 | 61 | // Dual-port block RAM for FIFO buffer 62 | dualport_ram #( 63 | .AWIDTH(AWIDTH), 64 | .DWIDTH(DWIDTH) 65 | ) 66 | dualport_ram 67 | ( 68 | .arst_n (arst_n ), 69 | .wclk (wclk ), 70 | .wen (wen ), 71 | .waddr (waddr ), 72 | .wdata (wdata ), 73 | .rclk (rclk ), 74 | .raddr (raddr ), 75 | .rdata (rdata ), 76 | .ren (ren ), 77 | .rdv (rdv ) 78 | ); 79 | 80 | 81 | endmodule 82 | -------------------------------------------------------------------------------- /rtl/wlogic.v: -------------------------------------------------------------------------------- 1 | module wlogic # 2 | ( 3 | parameter AWIDTH = 4 4 | ) 5 | ( 6 | input wclk, // writing circuit's clock 7 | input arst_n, // asynchronous reset 8 | input wdv, // data valid signal. Writing circuit must assert this signa to have the data registered 9 | input [AWIDTH:0] rgray, // gray read pointer from reader to writer (for safe synchronization across clock domains) 10 | output [AWIDTH:0] wgray, // gray write pointer from writer to reader (for safe synchronization across clock domains) 11 | output [AWIDTH-1:0] waddr, // write address for the FIFO buffer 12 | output reg wfull, // FIFO full indicator for the writing circuit 13 | output wen // enable signal to register the write data to FIFO buffer 14 | ); 15 | 16 | 17 | // internal nets 18 | reg [AWIDTH:0] rgray_step1; // sync version 19 | reg [AWIDTH:0] rgray_wsync; // double-sync version 20 | wire [AWIDTH:0] rgray_wsync_bin; // binary converted 21 | reg [AWIDTH:0] wbincounter; // binary write pointer (extra MSB tracks wrapping around) 22 | wire [AWIDTH:0] wbincounter_next; // next state combinational signal 23 | 24 | assign waddr = wbincounter[AWIDTH-1:0]; // write address for the FIFO buffer (lower bits of binary write pointer) 25 | assign wen = wdv && !wfull; // enable when the writer has valid data and FIFO is not full 26 | 27 | 28 | // Synchronize rgray in wclk domain 29 | // rgray --> FF --> FF --> rgray_wsync 30 | always @(posedge wclk, negedge arst_n) begin 31 | if (!arst_n) begin 32 | rgray_step1 <= 0; 33 | rgray_wsync <= 0; 34 | end else begin 35 | rgray_step1 <= rgray; 36 | rgray_wsync <= rgray_step1; 37 | end 38 | end 39 | 40 | 41 | // binary counter 42 | always @(posedge wclk, negedge arst_n) begin 43 | if (!arst_n) 44 | wbincounter <= 0; 45 | else 46 | wbincounter <= wbincounter_next; 47 | end 48 | 49 | assign wbincounter_next = (wdv && !wfull) ? (wbincounter + 1'b1) : wbincounter; 50 | // assign wbincounter_next = wbincounter + (wdv && !wfull); 51 | 52 | 53 | // binary to gray conversion 54 | bin2gray #(.SIZE(AWIDTH+1)) 55 | bin2gray ( 56 | .bin (wbincounter ), 57 | .gray (wgray ) 58 | ); 59 | 60 | // gray to binary conversion 61 | gray2bin #(.SIZE(AWIDTH+1)) 62 | gray2bin ( 63 | .gray (rgray_wsync ), 64 | .bin (rgray_wsync_bin) 65 | ); 66 | 67 | 68 | // Full logic 69 | always @(posedge wclk, negedge arst_n) begin 70 | if (!arst_n) 71 | wfull <= 0; 72 | else begin 73 | // if the write pointer catches upto the read pointer 74 | // if the write pointer has wrapped around and reaches the read pointer 75 | // The extra MSB will be different in such case 76 | wfull <= (rgray_wsync_bin == {~wbincounter_next[AWIDTH], wbincounter_next[AWIDTH-1:0]}); 77 | end 78 | end 79 | 80 | 81 | 82 | 83 | endmodule 84 | -------------------------------------------------------------------------------- /sim/osc.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/100ps 2 | 3 | module osc ( 4 | input enable, 5 | input [31:0] period, 6 | input [31:0] phase_offset, 7 | output reg clk 8 | ); 9 | 10 | real half_period; 11 | 12 | always @(period) begin 13 | half_period = period / 2; 14 | end 15 | 16 | 17 | initial begin 18 | clk = 1'b0; 19 | repeat(2) #(phase_offset); 20 | $display ("SIM %0t ns: initial clock phase offset = %d", $time, phase_offset); 21 | forever begin 22 | if (enable) 23 | #(half_period) clk = ~clk; 24 | else 25 | #(half_period); 26 | end 27 | end 28 | 29 | 30 | endmodule 31 | -------------------------------------------------------------------------------- /sim/sim.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/100ps 2 | 3 | module sim; 4 | 5 | parameter DWIDTH = 16; 6 | parameter AWIDTH = 5; 7 | 8 | // connecting nets 9 | wire arst_n; 10 | wire wclk; 11 | wire rclk; 12 | wire fifo_wdv; 13 | wire fifo_rdv; 14 | wire [DWIDTH-1:0] fifo_wdata; 15 | wire fifo_full; 16 | wire fifo_ren; 17 | wire [DWIDTH-1:0] fifo_rdata; 18 | wire fifo_empty; 19 | 20 | top_dual_fifo #( 21 | .AWIDTH(AWIDTH), 22 | .DWIDTH(DWIDTH) 23 | ) 24 | dut 25 | ( 26 | .arst_n (arst_n ), 27 | .wclk (wclk ), 28 | .rclk (rclk ), 29 | .wdv (fifo_wdv ), 30 | .rdv (fifo_rdv ), 31 | .wdata (fifo_wdata ), 32 | .wfull (fifo_full ), 33 | .rrq (fifo_rrq ), 34 | .rdata (fifo_rdata ), 35 | .rempty (fifo_empty ) 36 | ); 37 | 38 | tester #( 39 | .AWIDTH(AWIDTH), 40 | .DWIDTH(DWIDTH) 41 | ) 42 | tester 43 | ( 44 | .wclk (wclk ), 45 | .rclk (rclk ), 46 | .arst_n (arst_n ), 47 | .fifo_full (fifo_full ), 48 | .fifo_wdv (fifo_wdv ), 49 | .fifo_rdv (fifo_rdv ), 50 | .fifo_wdata (fifo_wdata ), 51 | .fifo_empty (fifo_empty ), 52 | .fifo_rdata (fifo_rdata ), 53 | .fifo_rrq (fifo_rrq ) 54 | ); 55 | 56 | 57 | 58 | endmodule 59 | 60 | -------------------------------------------------------------------------------- /sim/tester.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/100ps 2 | 3 | module tester # 4 | ( 5 | parameter AWIDTH = 4, 6 | parameter DWIDTH = 8 7 | ) 8 | ( 9 | output wclk, 10 | output rclk, 11 | output reg arst_n, 12 | 13 | input fifo_full, 14 | input fifo_rdv, 15 | output reg fifo_wdv, 16 | output reg [DWIDTH-1:0] fifo_wdata, 17 | 18 | input fifo_empty, 19 | input [DWIDTH-1:0] fifo_rdata, 20 | output reg fifo_rrq 21 | ); 22 | 23 | 24 | 25 | task sync_wclk; 26 | begin 27 | @(posedge wclk); 28 | end 29 | endtask 30 | 31 | task sync_rclk; 32 | begin 33 | @(posedge rclk); 34 | end 35 | endtask 36 | 37 | 38 | // data will fill the FIFO buffer many times over 39 | parameter data_length = 2**AWIDTH * 107 + 5; 40 | 41 | integer watchdog_timer; 42 | reg [31:0] period_wclk; 43 | reg [31:0] period_rclk; 44 | reg [31:0] phase_offset_wclk; 45 | reg [31:0] phase_offset_rclk; 46 | 47 | 48 | // Slow production (write), fast consumption (read) 49 | initial begin 50 | period_wclk = 71 * 2; // both half-periods are prime numbers 51 | period_rclk = 29 * 2; 52 | phase_offset_wclk = 0; 53 | phase_offset_rclk = 10; 54 | end 55 | 56 | osc osc_wclk ( 57 | .enable (1'b1 ), 58 | .period (period_wclk ), 59 | .phase_offset (phase_offset_wclk ), 60 | .clk (wclk ) 61 | ); 62 | 63 | osc osc_rclk ( 64 | .enable (1'b1 ), 65 | .period (period_rclk ), 66 | .phase_offset (phase_offset_rclk ), 67 | .clk (rclk ) 68 | ); 69 | 70 | // open file for reading and writing data 71 | integer fhw, fhr; 72 | initial begin 73 | fhw = $fopen("log_wdata.txt","w"); 74 | fhr = $fopen("log_rdata.txt","w"); 75 | end 76 | 77 | integer i, j, k, l, m; 78 | wire [31:0] long_period; 79 | assign long_period = (period_wclk > period_rclk) ? period_wclk : period_rclk; 80 | 81 | reg por_done; // status of power-on reset 82 | reg sim_done; // status of running simulation 83 | reg mismatch; // indicates verification failure 84 | reg [DWIDTH-1:0] testmem_r [0:data_length-1]; 85 | reg [DWIDTH-1:0] testmem_w [0:data_length-1]; 86 | 87 | // power-on reset 88 | initial begin 89 | $display ("SIM %0t ns: Waiting for power-on reset (POR)", $time); 90 | watchdog_timer = $time; 91 | arst_n = 1'bx; 92 | por_done = 1'bx; 93 | sim_done = 1'bx; 94 | repeat(2) #(long_period * 4); 95 | arst_n = 1'b0; 96 | por_done = 1'b0; 97 | sim_done = 1'b0; 98 | #(long_period * 3) 99 | arst_n = 1'b1; 100 | #(long_period * 5) 101 | por_done = 1; 102 | end 103 | 104 | 105 | initial begin 106 | fifo_wdv = 0; 107 | fifo_wdata = 'bx; 108 | fifo_rrq = 0; 109 | end 110 | 111 | initial begin 112 | wait(por_done); // wait for power-on reset to complete 113 | $display ("SIM %0t ns: Initialize and POR complete", $time); 114 | 115 | //=========================================================================== 116 | // Main testbench activity starts below 117 | //=========================================================================== 118 | repeat (5) #(long_period); 119 | 120 | fork 121 | 122 | // writing to FIFO 123 | begin 124 | for (i = 0; i < data_length; i = i+1) begin 125 | @(posedge wclk) 126 | fifo_wdv = 1; 127 | // fifo_wdata = i[DWIDTH-1:0]; // fifo_wdata = 8'd65 + i[DWIDTH-1:0]; 128 | fifo_wdata = $random; 129 | $display("SIM %0t ns: WR_data = %h", $time, fifo_wdata); 130 | $fdisplay(fhw, "%d", fifo_wdata); 131 | @(negedge wclk) 132 | 133 | // Do not write on the full buffer 134 | if(fifo_full) begin 135 | fifo_wdv = 0; 136 | wait(!fifo_full); 137 | fifo_wdv = 1; 138 | end 139 | 140 | // stop writing. Let the buffer drain (go empty) 141 | if(j == data_length/4 || i == (data_length/4)*3) begin 142 | fifo_wdv = 0; 143 | wait(fifo_empty); 144 | fifo_wdv = 1; 145 | end 146 | end 147 | 148 | @(posedge wclk) 149 | fifo_wdv = 0; 150 | end 151 | 152 | 153 | // reading from FIFO 154 | begin 155 | // wait a while before start reading 156 | wait(i > (1 + 2**AWIDTH / 2)); 157 | for (j = 0; j < data_length; j = j+1) begin 158 | @(posedge rclk); 159 | fifo_rrq = 1; 160 | wait(fifo_rdv); 161 | 162 | // Do not read from the empty buffer 163 | if(fifo_empty) begin 164 | fifo_rrq = 0; 165 | wait(!fifo_empty); 166 | fifo_rrq = 1; 167 | end 168 | 169 | // stop reading. Let the buffer fill 170 | if(i == (data_length/3)*2) begin 171 | fifo_rrq = 0; 172 | wait(fifo_full); 173 | fifo_rrq = 1; 174 | end 175 | 176 | // Swap the read/write speed 177 | // Fast production (write), slow consumption (read) 178 | if (j == data_length/2) begin 179 | period_wclk <= period_rclk; 180 | period_rclk <= period_wclk; 181 | end 182 | end 183 | 184 | #(long_period * 10); 185 | sim_done = 1; 186 | end 187 | 188 | 189 | // logging read_data to file 190 | begin 191 | forever begin 192 | @(posedge rclk) 193 | if(fifo_rdv) begin 194 | $display("SIM %0t ns: RD_data = %h", $time, fifo_rdata); 195 | $fdisplay(fhr, "%d", fifo_rdata); 196 | end 197 | end 198 | end 199 | 200 | // catch the sim_done signal and compare files 201 | begin 202 | wait(sim_done); 203 | 204 | // close the files and open again 205 | $fclose(fhw); 206 | $fclose(fhr); 207 | 208 | fhw = $fopen("log_wdata.txt","r"); 209 | fhr = $fopen("log_rdata.txt","r"); 210 | 211 | for (i = 0; i < data_length; i = i+1) begin 212 | k = $fscanf(fhw, "%d", testmem_w[i]); 213 | k = $fscanf(fhr, "%d", testmem_r[i]); 214 | $display("SIM: (WR, RD)[%2d] = (%h, %h) %s", i, testmem_w[i], testmem_r[i], (testmem_r[i] != testmem_w[i]) ? "MISMATCH" : "MATCH"); 215 | mismatch = mismatch || (testmem_r[i] != testmem_w[i]); 216 | end 217 | 218 | $fclose(fhr); 219 | $fclose(fhw); 220 | 221 | if(mismatch) 222 | $display("SIM ERROR: There was a mismatch between sent and received data. FIFO verification failed!"); 223 | else 224 | $display("SIM SUCCESS: Data received as sent. FIFO verified successfully!"); 225 | 226 | 227 | 228 | $display ("SIM %0t ns: Simulation completed", $time); 229 | // $stop; 230 | end 231 | 232 | 233 | join 234 | //=========================================================================== 235 | // Main testbench activity ends above 236 | //=========================================================================== 237 | 238 | 239 | 240 | 241 | $display ("SIM: Total Simulation time = %0d", $time); 242 | // $stop; 243 | end 244 | 245 | 246 | // reset watchdog timer on any activity 247 | always @(fifo_wdata, fifo_rdata, fifo_rdv, fifo_wdv, fifo_full, fifo_empty) begin 248 | watchdog_timer = $time; 249 | end 250 | 251 | // trigger watchdog timer after a long period of inactivity 252 | initial begin 253 | forever begin 254 | #(long_period*300) 255 | if ($time - watchdog_timer > long_period*300) begin 256 | $display ("SIM %0t ns: Watchdog timer activated", $time); 257 | $stop; 258 | end 259 | end 260 | 261 | end 262 | 263 | 264 | 265 | 266 | endmodule 267 | -------------------------------------------------------------------------------- /top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akhan3/async-fifo/6c65b10cb74a77600f0bf942f5c6404cb17af392/top.png --------------------------------------------------------------------------------