├── LICENSE ├── README.md └── src ├── asyn_fifo_TB.v └── asyn_fifo.v /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Angelo Jacobo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Created by: Angelo Jacobo 2 | Date: August 14,2021 3 | 4 | # Inside the src folder are: 5 | * asyn_fifo.v -> Asynchronous fifo module. Specs are given below. 6 | * asyn_fifo_TB.v -> Testbench for asyn_fifo module. Test cases are: 7 |            - write data until full 8 |            - read data until empty 9 |            - read data while simultaneously writing data 10 | 11 | # Waveform[WRITING]: 12 | ![write](https://user-images.githubusercontent.com/87559347/129466377-127dbd75-4217-4378-8a58-3c9cb801a970.png) 13 | 14 | # Waveform[READING]: 15 | ![read](https://user-images.githubusercontent.com/87559347/129466378-5e9abc88-6936-4518-991d-77ef2b3dd21a.png) 16 | 17 | # About: 18 | This project implemented a FIFO with separate clock domains for read and write(i.e. Asynchronous FIFO). 19 | Specs are: 20 | * Reconfigurable memory width and depth 21 | * Infers block ram resource of the FPGA 22 | * Provides data count of the words available for reading. Sync to either read/write clock domains 23 | * Read mode is First-Word Fall-Through 24 | * Reset type is asynchronous 25 | 26 | # Donate 27 | Support these open-source projects by donating 28 | 29 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=GBJQGJNCJZVRU) 30 | 31 | 32 | # Inquiries 33 | Connect with me at my linkedin: https://www.linkedin.com/in/angelo-jacobo/ 34 | -------------------------------------------------------------------------------- /src/asyn_fifo_TB.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | 4 | module asyn_fifo_TB; 5 | 6 | // Inputs 7 | reg rst_n; 8 | reg clk_write; 9 | reg clk_read; 10 | reg write; 11 | reg read; 12 | reg [7:0] data_write; 13 | 14 | // Outputs 15 | wire [7:0] data_read; 16 | wire full; 17 | wire empty; 18 | wire[4:0] data_count_w,data_count_r; 19 | 20 | // Instantiate the Unit Under Test (UUT) 21 | asyn_fifo #(.DATA_WIDTH(8),.FIFO_DEPTH_WIDTH(5)) uut ( //32x8 FIFO mem 22 | .rst_n(rst_n), 23 | .clk_write(clk_write), 24 | .clk_read(clk_read), 25 | .write(write), 26 | .read(read), 27 | .data_write(data_write), 28 | .data_read(data_read), 29 | .full(full), 30 | .empty(empty), 31 | .data_count_w(data_count_w), 32 | .data_count_r(data_count_r) 33 | ); 34 | always begin //100MHz 35 | clk_write=1; 36 | #5; 37 | clk_write=0; 38 | #5; 39 | end 40 | always begin //13MHz 41 | clk_read=1; 42 | #7.69; 43 | clk_read=0; 44 | #7.69; 45 | end 46 | 47 | integer i; 48 | initial begin 49 | // Initialize Inputs 50 | rst_n = 1; 51 | write = 0; 52 | read = 0; 53 | data_write = 0; 54 | #100; 55 | 56 | for(i=0;i<=40;i=i+1) begin //write data until full 57 | @(negedge clk_write); 58 | data_write=i; 59 | write=1; 60 | end 61 | write=0; 62 | #100; 63 | 64 | for(i=0;i<=40;i=i+1) begin //read data until empty 65 | @(negedge clk_read); 66 | read=1; 67 | end 68 | read=0; 69 | #100; 70 | 71 | read=1; 72 | for(i=0;i<=50;i=i+1) begin //read data WHILE writing data 73 | @(negedge clk_write); 74 | data_write=i; 75 | write=1; 76 | end 77 | 78 | #1000 $stop; 79 | 80 | end 81 | 82 | endmodule 83 | 84 | -------------------------------------------------------------------------------- /src/asyn_fifo.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | module asyn_fifo 4 | #( 5 | parameter DATA_WIDTH=8, 6 | FIFO_DEPTH_WIDTH=11 //total depth will then be 2**FIFO_DEPTH_WIDTH 7 | ) 8 | ( 9 | input wire rst_n, 10 | input wire clk_write,clk_read, //clock input from both domains 11 | input wire write,read, 12 | input wire [DATA_WIDTH-1:0] data_write, //input FROM write clock domain 13 | output [DATA_WIDTH-1:0] data_read, //output TO read clock domain 14 | output reg full,empty, //full=sync to write domain clk , empty=sync to read domain clk 15 | output reg[FIFO_DEPTH_WIDTH-1:0] data_count_w,data_count_r //counts number of data left in fifo memory(sync to either write or read clk) 16 | ); 17 | 18 | 19 | /* 20 | async_fifo #(.DATA_WIDTH(16),.FIFO_DEPTH_WIDTH(10)) m2 //1024x16 FIFO mem 21 | ( 22 | .rst_n(rst_n), 23 | .clk_write(), 24 | .clk_read(), //clock input from both domains 25 | .write(), 26 | .read(), 27 | .data_write(), //input FROM write clock domain 28 | .data_read(), //output TO read clock domain 29 | .full(), 30 | .empty(), //full=sync to write domain clk , empty=sync to read domain clk 31 | ..data_count_w(), 32 | .data_count_r() //counts number of data left in fifo memory(sync to either write or read clk) 33 | ); 34 | */ 35 | 36 | 37 | localparam FIFO_DEPTH=2**FIFO_DEPTH_WIDTH; 38 | 39 | initial begin 40 | full=0; 41 | empty=1; 42 | end 43 | 44 | 45 | ///////////////////WRITE CLOCK DOMAIN////////////////////////////// 46 | reg[FIFO_DEPTH_WIDTH:0] w_ptr_q=0; //binary counter for write pointer 47 | reg[FIFO_DEPTH_WIDTH:0] r_ptr_sync; //binary pointer for read pointer sync to write clk 48 | reg[FIFO_DEPTH_WIDTH:0] r_grey_sync; //grey counter for the read pointer synchronized to write clock 49 | reg[3:0] i; //log_2(FIFO_DEPTH_WIDTH) 50 | 51 | wire[FIFO_DEPTH_WIDTH:0] w_grey,w_grey_nxt; //grey counter for write pointer 52 | wire we; 53 | 54 | assign w_grey=w_ptr_q^(w_ptr_q>>1); //binary to grey code conversion for current write pointer 55 | assign w_grey_nxt=(w_ptr_q+1'b1)^((w_ptr_q+1'b1)>>1); //next grey code 56 | assign we= write && !full; 57 | 58 | //register operation 59 | always @(posedge clk_write,negedge rst_n) begin 60 | if(!rst_n) begin 61 | w_ptr_q<=0; 62 | full<=0; 63 | end 64 | else begin 65 | if(write && !full) begin //write condition 66 | w_ptr_q<=w_ptr_q+1'b1; 67 | full <= w_grey_nxt == {~r_grey_sync[FIFO_DEPTH_WIDTH:FIFO_DEPTH_WIDTH-1],r_grey_sync[FIFO_DEPTH_WIDTH-2:0]}; //algorithm for full logic which can be observed on the grey code table 68 | end 69 | else full <= w_grey == {~r_grey_sync[FIFO_DEPTH_WIDTH:FIFO_DEPTH_WIDTH-1],r_grey_sync[FIFO_DEPTH_WIDTH-2:0]}; 70 | 71 | for(i=0;i<=FIFO_DEPTH_WIDTH;i=i+1) r_ptr_sync[i]=^(r_grey_sync>>i); //grey code to binary converter 72 | data_count_w <= (w_ptr_q>=r_ptr_sync)? (w_ptr_q-r_ptr_sync):(FIFO_DEPTH-r_ptr_sync+w_ptr_q); //compares write pointer and sync read pointer to generate data_count 73 | end 74 | end 75 | 76 | ///////////////////////////////////////////////////////////////////// 77 | 78 | 79 | ///////////////////READ CLOCK DOMAIN////////////////////////////// 80 | reg[FIFO_DEPTH_WIDTH:0] r_ptr_q=0; //binary counter for read pointer 81 | reg[FIFO_DEPTH_WIDTH:0] w_ptr_sync; //binary counter for write pointer sync to read clk 82 | reg[FIFO_DEPTH_WIDTH:0] w_grey_sync; //grey counter for the write pointer synchronized to read clock 83 | 84 | wire[FIFO_DEPTH_WIDTH:0] r_grey,r_grey_nxt; //grey counter for read pointer 85 | wire[FIFO_DEPTH_WIDTH:0] r_ptr_d; 86 | 87 | 88 | assign r_grey= r_ptr_q^(r_ptr_q>>1); //binary to grey code conversion 89 | assign r_grey_nxt= (r_ptr_q+1'b1)^((r_ptr_q+1'b1)>>1); //next grey code 90 | assign r_ptr_d= (read && !empty)? r_ptr_q+1'b1:r_ptr_q; 91 | 92 | //register operation 93 | always @(posedge clk_read,negedge rst_n) begin 94 | if(!rst_n) begin 95 | r_ptr_q<=0; 96 | empty<=1; 97 | end 98 | else begin 99 | r_ptr_q<=r_ptr_d; 100 | if(read && !empty) empty <= r_grey_nxt==w_grey_sync;//empty condition 101 | else empty <= r_grey==w_grey_sync; 102 | 103 | for(i=0;i<=FIFO_DEPTH_WIDTH;i=i+1) w_ptr_sync[i]=^(w_grey_sync>>i); //grey code to binary converter 104 | data_count_r = (w_ptr_q>=r_ptr_sync)? (w_ptr_q-r_ptr_sync):(FIFO_DEPTH-r_ptr_sync+w_ptr_q); //compares read pointer to sync write pointer to generate data_count 105 | end 106 | end 107 | //////////////////////////////////////////////////////////////////////// 108 | 109 | 110 | /////////////////////CLOCK DOMAIN CROSSING////////////////////////////// 111 | reg[FIFO_DEPTH_WIDTH:0] r_grey_sync_temp; 112 | reg[FIFO_DEPTH_WIDTH:0] w_grey_sync_temp; 113 | always @(posedge clk_write) begin //2 D-Flipflops for reduced metastability in clock domain crossing from READ DOMAIN to WRITE DOMAIN 114 | r_grey_sync_temp<=r_grey; 115 | r_grey_sync<=r_grey_sync_temp; 116 | end 117 | always @(posedge clk_read) begin //2 D-Flipflops for reduced metastability in clock domain crossing from WRITE DOMAIN to READ DOMAIN 118 | w_grey_sync_temp<=w_grey; 119 | w_grey_sync<=w_grey_sync_temp; 120 | end 121 | 122 | ////////////////////////////////////////////////////////////////////////// 123 | 124 | 125 | 126 | //instantiation of dual port block ram 127 | dual_port_sync #(.ADDR_WIDTH(FIFO_DEPTH_WIDTH) , .DATA_WIDTH(DATA_WIDTH)) m0 128 | ( 129 | .clk_r(clk_read), 130 | .clk_w(clk_write), 131 | .we(we), 132 | .din(data_write), 133 | .addr_a(w_ptr_q[FIFO_DEPTH_WIDTH-1:0]), //write address 134 | .addr_b(r_ptr_d[FIFO_DEPTH_WIDTH-1:0] ), //read address ,addr_b is already buffered inside this module so we will use the "_d" ptr to advance the data(not "_q") 135 | .dout(data_read) 136 | ); 137 | 138 | endmodule 139 | 140 | 141 | 142 | //inference template for dual port block ram 143 | module dual_port_sync 144 | #( 145 | parameter ADDR_WIDTH=11, //2k by 8 dual port synchronous ram(16k block ram) 146 | DATA_WIDTH=8 147 | ) 148 | ( 149 | input wire clk_r, 150 | input wire clk_w, 151 | input wire we, 152 | input wire[DATA_WIDTH-1:0] din, 153 | input wire[ADDR_WIDTH-1:0] addr_a,addr_b, //addr_a for write, addr_b for read 154 | output wire[DATA_WIDTH-1:0] dout 155 | ); 156 | 157 | reg[DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0]; 158 | reg[ADDR_WIDTH-1:0] addr_b_q; 159 | 160 | always @(posedge clk_w) begin 161 | if(we) ram[addr_a]<=din; 162 | end 163 | always @(posedge clk_r) begin 164 | addr_b_q<=addr_b; 165 | end 166 | assign dout=ram[addr_b_q]; 167 | 168 | endmodule 169 | 170 | 171 | 172 | --------------------------------------------------------------------------------