校招Verilog——同步FIFO和异步FIFO
一、同步FIFO
1、代码
1 //************************************************************************** 2 // *** 名称 : sFIFO.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2020年6月 6 // *** 描述 : 同步FIFO,读写位宽相同,默认写先读后 7 //************************************************************************** 8 module sFIFO 9 //========================< 参数 >========================================== 10 #( 11 parameter DATA_W = 8 , //数据位宽 12 parameter ADDR_W = 4 //地址位宽 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input clk , //时钟 17 input rst_n , //复位 18 input wr_en , //写使能 19 input [DATA_W-1:0] wr_data , //写数据 20 input rd_en , //读使能 21 output reg [DATA_W-1:0] rd_data , //读数据 22 output full , //写满 23 output empty //读空 24 ); 25 //========================< 信号 >========================================== 26 reg [ADDR_W-1:0] wr_addr ; //写地址 27 reg [ADDR_W-1:0] rd_addr ; //读地址 28 reg [DATA_W-1:0] ram[2**ADDR_W-1:0] ; //ram,地址位宽4,共16个 29 reg [ADDR_W :0] cnt ; //计数器 30 //========================================================================== 31 //== 读写地址 32 //========================================================================== 33 //-- 写地址 34 //--------------------------------------------------- 35 always @(posedge clk or negedge rst_n) begin 36 if(!rst_n) begin 37 wr_addr <= 0; 38 end 39 else if(wr_en & (!full)) begin 40 wr_addr <= wr_addr + 1; 41 end 42 end 43 44 //-- 读地址 45 //--------------------------------------------------- 46 always @(posedge clk or negedge rst_n) begin 47 if (!rst_n) begin 48 rd_addr <= 0; 49 end 50 else if(rd_en & (!empty)) begin 51 rd_addr <= rd_addr + 1; 52 end 53 end 54 //========================================================================== 55 //== 读写数据 56 //========================================================================== 57 //-- 写数据 58 //--------------------------------------------------- 59 integer i; 60 always @(posedge clk or negedge rst_n) begin 61 if(!rst_n) begin 62 for(i=0; i<16; i=i+1) 63 ram[i] <= 0; 64 end 65 else if(wr_en) begin 66 ram[wr_addr] <= wr_data; 67 end 68 end 69 70 //-- 读数据 71 //--------------------------------------------------- 72 always @(posedge clk or negedge rst_n) begin 73 if (!rst_n) begin 74 rd_data<=0; 75 end 76 else if (rd_en) begin 77 rd_data <= ram[rd_addr]; 78 end 79 end 80 //========================================================================== 81 //== 空满标志 82 //========================================================================== 83 //-- 辅助计数 84 //--------------------------------------------------- 85 always @(posedge clk or negedge rst_n) begin 86 if (!rst_n) begin 87 cnt <= 0; 88 end 89 else if(wr_en && (!rd_en) && (cnt!=16)) begin 90 cnt <= cnt + 1; 91 end 92 else if(rd_en && (!wr_en) && (cnt!=0)) begin 93 cnt <= cnt - 1; 94 end 95 end 96 97 //-- 写满、读空 98 //--------------------------------------------------- 99 assign full = (cnt==16); 100 assign empty = (cnt==0 ); 101 102 103 endmodule
2、仿真
1 `timescale 1ns/1ns 2 module sFIFO_tb; 3 //========================< 参数 >========================================== 4 parameter DATA_W = 8 ; //数据位宽 5 parameter ADDR_W = 4 ; //地址位宽 6 //========================< 信号 >========================================== 7 reg clk ; 8 reg rst_n ; 9 reg wr_en ; 10 reg [DATA_W-1:0] wr_data ; 11 reg rd_en ; 12 //========================================================================== 13 //== 例化 14 //========================================================================== 15 sFIFO 16 #( 17 .DATA_W (DATA_W ), 18 .ADDR_W (ADDR_W ) 19 ) 20 u_sFIFO 21 ( 22 .clk (clk ), 23 .rst_n (rst_n ), 24 .wr_en (wr_en ), 25 .wr_data (wr_data ), 26 .rd_en (rd_en ), 27 .rd_data ( ), 28 .full ( ), 29 .empty ( ) 30 ); 31 //========================================================================== 32 //== 时钟 33 //========================================================================== 34 always #10 clk=~clk; 35 //========================================================================== 36 //== 设计 37 //========================================================================== 38 initial begin 39 clk = 1; 40 rst_n = 0; 41 wr_en = 0; 42 wr_data = 0; 43 rd_en = 0; 44 45 #101; 46 rst_n = 1; 47 48 #20; 49 gen_data; 50 51 @(posedge clk); 52 rd_en=1; 53 54 repeat(16)@(posedge clk); 55 rd_en=0; 56 end 57 58 task gen_data; 59 integer i; 60 begin 61 for(i=0; i<16; i=i+1) begin 62 wr_en = 1; 63 wr_data = i; 64 #20; 65 end 66 wr_en = 0; 67 wr_data = 0; 68 end 69 endtask 70 71 72 endmodule
二、异步FIFO
1、分析
(1)格雷码
比较空满时,需要读写地址进行判断,二者属于跨时钟域,需要进行打拍的同步处理,未避免亚稳态,采用格雷码,因为格雷码相邻只有一位变化,这样同步多位时更不容易产生问题。
格雷码公式:gray = (binary>>1) ^ binary;
(2)读空判断
默认是先写后读,读追上了写,之后就是读空了。因此读空标志为【读写地址相同】。
(3)写满判断
默认是先写后读,写在前面,超过了一轮地址后,又追上了读,之后就是写满了。因此写满标志也是【读写地址相同】吗?
答案错误!显然,这样的思维会导致写满和读空的标志相同,无法确定【读写地址相同】时到底是写满还是读空,因此可以设置一个写指针 wr_addr_ptr 和 读指针 rd_addr_ptr,其位宽比读写地址多1位,整个指针的长度是地址的 2 倍。假设前半段为 A,后半段为 B。
- 读在 A,最高位为0,剩余位为100;
- 写在 B,最高位为1,剩余位为100;
我们便可以判定,这时写越过了一轮,又到了读的位置,这便是真正的写满标志。提炼一下就是:“读写的最高位不同,其余位相同”时,处于写满状态。
以上是当地址编码为普通二进制码时的分析,但是格雷码是不一样的,他的排列有些不同,前半段 A 和后半段 B 是镜像对称的,如下图所示:
通过观察格雷码的特点,我们可以这样判断:“读写的最高2位不同,其余位相同”时,处于写满状态。
2、代码
1 //************************************************************************** 2 // *** 名称 : asFIFO.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2020年6月 6 // *** 描述 : 异步FIFO,读写位宽相同,默认写先读后 7 //************************************************************************** 8 module asFIFO 9 //========================< 参数 >========================================== 10 #( 11 parameter DATA_W = 8 , //数据位宽 12 parameter ADDR_W = 4 //地址位宽 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input rst_n , //复位 17 //FIFO写 ---------------------------------------- 18 input wr_clk , //写时钟 19 input wr_en , //写使能 20 input [DATA_W-1:0] wr_data , //写数据 21 output wr_full , //写满 22 //FIFO读 ---------------------------------------- 23 input rd_clk , //读时钟 24 input rd_en , //读使能 25 output reg [DATA_W-1:0] rd_data , //读数据 26 output rd_empty //读空 27 ); 28 //========================< 信号 >========================================== 29 reg [ADDR_W :0] wr_addr_ptr ; //写指针,多1位 30 reg [ADDR_W :0] rd_addr_ptr ; //读指针,多1位 31 wire [ADDR_W :0] wr_addr_gray ; //写地址_格雷码 32 reg [ADDR_W :0] wr_addr_gray_r ; //写地址打拍 33 reg [ADDR_W :0] wr_addr_gray_rr ; //写地址打拍 34 wire [ADDR_W :0] rd_addr_gray ; //读地址_格雷码 35 reg [ADDR_W :0] rd_addr_gray_r ; //读地址打拍 36 reg [ADDR_W :0] rd_addr_gray_rr ; //读地址打拍 37 //----------------------------------------------- 38 wire [ADDR_W-1:0] wr_addr ; //写地址 39 wire [ADDR_W-1:0] rd_addr ; //读地址 40 reg [DATA_W-1:0] ram[2**ADDR_W-1:0] ; //ram,地址位宽4,共16个 41 //========================================================================== 42 //== 地址指针 43 //========================================================================== 44 always @(posedge wr_clk or negedge rst_n) begin 45 if(!rst_n) begin 46 wr_addr_ptr <= 0; 47 end 48 else if(wr_en & (!wr_full)) begin 49 wr_addr_ptr <= wr_addr_ptr + 1; 50 end 51 end 52 53 always @(posedge rd_clk or negedge rst_n) begin 54 if(!rst_n) begin 55 rd_addr_ptr <= 0; 56 end 57 else if(rd_en & (!rd_empty)) begin 58 rd_addr_ptr <= rd_addr_ptr + 1; 59 end 60 end 61 //========================================================================== 62 //== 空满信号 63 //========================================================================== 64 //-- 格雷码转换 65 //--------------------------------------------------- 66 assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr; 67 assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr; 68 69 //-- 跨时钟域,打两拍 70 //--------------------------------------------------- 71 always @(posedge wr_clk) begin 72 rd_addr_gray_r <= rd_addr_gray; 73 rd_addr_gray_rr <= rd_addr_gray_r; 74 end 75 76 always @(posedge rd_clk) begin 77 wr_addr_gray_r <= wr_addr_gray; 78 wr_addr_gray_rr <= wr_addr_gray_r; 79 end 80 81 //-- 写满标志:高2位不同,其余位相同 82 //--------------------------------------------------- 83 assign wr_full = (wr_addr_gray == ({~rd_addr_gray_rr[ADDR_W-:2],rd_addr_gray_rr[ADDR_W-2:0]})); 84 85 //-- 读空标志:读写地址相同 86 //--------------------------------------------------- 87 assign rd_empty = (rd_addr_gray == wr_addr_gray_rr); 88 //========================================================================== 89 //== ram读写 90 //========================================================================== 91 //-- 读写地址 92 //--------------------------------------------------- 93 assign wr_addr = wr_addr_ptr[ADDR_W-1:0]; 94 assign rd_addr = rd_addr_ptr[ADDR_W-1:0]; 95 96 //-- 写数据 97 //--------------------------------------------------- 98 integer i; 99 always @(posedge wr_clk or negedge rst_n) begin 100 if(!rst_n) begin 101 for(i=0; i<2**ADDR_W; i=i+1) 102 ram[i] <= 0; 103 end 104 else if(wr_en & (!wr_full)) begin 105 ram[wr_addr] <= wr_data; 106 end 107 end 108 109 //-- 读数据 110 //--------------------------------------------------- 111 always @(posedge rd_clk or negedge rst_n) begin 112 if(!rst_n) begin 113 rd_data <= 0; 114 end 115 else if(rd_en & (!rd_empty)) begin 116 rd_data <= ram[rd_addr]; 117 end 118 end 119 120 121 endmodule
3、仿真
1 `timescale 1ns/1ns 2 module asFIFO_tb; 3 //========================< 参数 >========================================== 4 parameter DATA_W = 8 ; //数据位宽 5 parameter ADDR_W = 4 ; //地址位宽 6 //========================< 信号 >========================================== 7 reg wr_clk ; 8 reg rd_clk ; 9 reg rst_n ; 10 reg wr_en ; 11 reg [DATA_W-1:0] wr_data ; 12 reg rd_en ; 13 //========================================================================== 14 //== 例化 15 //========================================================================== 16 asFIFO 17 #( 18 .DATA_W (DATA_W ), 19 .ADDR_W (ADDR_W ) 20 ) 21 u_asFIFO 22 ( 23 .wr_clk (wr_clk ), 24 .rd_clk (rd_clk ), 25 .rst_n (rst_n ), 26 .wr_en (wr_en ), 27 .wr_data (wr_data ), 28 .rd_en (rd_en ), 29 .rd_data ( ), 30 .wr_full ( ), 31 .rd_empty ( ) 32 ); 33 //========================================================================== 34 //== 时钟 35 //========================================================================== 36 always #10 wr_clk=~wr_clk; 37 always #5 rd_clk=~rd_clk; 38 //========================================================================== 39 //== 数据 40 //========================================================================== 41 initial begin 42 wr_clk = 1; 43 rd_clk = 0; 44 rst_n = 0; 45 wr_en = 0; 46 wr_data = 0; 47 rd_en = 0; 48 49 #101; 50 rst_n = 1; 51 52 #20; 53 gen_data; 54 55 @(posedge rd_clk); 56 rd_en = 1; 57 58 repeat(16)@(posedge rd_clk); 59 rd_en=0; 60 end 61 62 task gen_data; 63 integer i; 64 begin 65 for(i=0; i<16; i=i+1) begin 66 wr_en = 1; 67 wr_data = i; 68 #20; 69 end 70 wr_en = 0; 71 wr_data = 0; 72 end 73 endtask 74 75 76 endmodule
异步FIFO设计可以查看文章:异步FIFO设计原理与设计方法以及重要问题汇总(包含verilog代码|Testbench|仿真结果)