一、前言
应聘IC前端相关岗位时,FIFO是最常考也是最基本的题目。FIFO经常用于数据缓存、位宽转换、异步时钟域处理。随着芯片规模的快速增长,灵活的system verilog成为设计/验证人员的基本功。本文从简易版的同步FIFO开始,熟悉IP设计与验证的基础技能。
二、IP设计
FIFO这一IP核已经相当成熟,因此网上资料也是一抓一大把。其中笔者认为较好的一个在文末附录中,需要详细了解FIFO工作原理的朋友可以仔细看看。这里简单介绍下本文设计FIFO的原理与结构。FIFO的内部存储单元是常见的双口RAM,这个IP的精髓在于读写地址的对外屏蔽与自动管理。避免写满、读空至关重要。本文设计的FIFO顶层例化双口RAM和FIFO控制两大模块:前者仅作为存储单元响应读写信号,后者根据读写计数器产生读写指针和重要的空满指示信号。
代码如下:
存储模块:
1 `timescale 1ns/1ps 2 module dpram 3 #(parameter D_W=8, 4 A_W=8) 5 ( 6 input clk, 7 input rst_n, 8 //write ports 9 input wr_en, 10 input [D_W-1:0] wr_data, 11 input [A_W-1:0] wr_addr, 12 //read ports 13 input rd_en, 14 input [A_W-1:0] rd_addr, 15 output reg [D_W-1:0] rd_data 16 ); 17 //RAM 18 reg [D_W-1:0] memory [0:2**A_W-1]; 19 20 //write operation 21 always@(posedge clk)begin 22 if(wr_en)begin 23 memory[wr_addr] <= wr_data; 24 end 25 end 26 27 //read operation 28 always@(posedge clk or negedge rst_n)begin 29 if(~rst_n) 30 rd_data <= 0; 31 else if(rd_en)begin 32 rd_data <= memory[rd_addr]; 33 end 34 else if(rd_addr == 1) 35 rd_data <= memory[0]; 36 end 37 38 endmodule
FIFO控制模块:
1 `timescale 1ns/1ps 2 module fifo_ctrl 3 #(parameter A_W = 8, 4 parameter [0:0] MODE = 0//0- standard read 1- first word fall through 5 ) 6 ( 7 input clk, 8 input rst_n, 9 10 output [A_W-1:0] wr_addr, 11 output [A_W-1:0] rd_addr, 12 13 output empty, 14 output full, 15 input wr_en, 16 input rd_en 17 ); 18 localparam MAX_CNT = 2**A_W; 19 localparam FD_W = A_W; 20 21 function [FD_W-1:0] abs; 22 input signed [FD_W-1:0] data; 23 begin 24 assign abs = data >= 0 ? data : -data; 25 end 26 endfunction 27 28 reg [A_W-1:0] wr_cnt; 29 wire add_wr_cnt,end_wr_cnt; 30 reg wr_flag; 31 reg [A_W-1:0] rd_cnt; 32 wire add_rd_cnt,end_rd_cnt; 33 reg rd_flag; 34 wire [A_W+1-1:0] wr_ptr,rd_ptr; 35 36 always@(posedge clk or negedge rst_n)begin 37 if(~rst_n)begin 38 wr_cnt <= 0; 39 end 40 else if(add_wr_cnt)begin 41 if(end_wr_cnt) 42 wr_cnt <= 0; 43 else 44 wr_cnt <= wr_cnt + 1'b1; 45 end 46 end 47 48 assign add_wr_cnt = wr_en & ~full; 49 assign end_wr_cnt = add_wr_cnt && wr_cnt == MAX_CNT - 1; 50 51 always@(posedge clk or negedge rst_n)begin 52 if(~rst_n)begin 53 wr_flag <= 0; 54 end 55 else if(end_wr_cnt)begin 56 wr_flag <= ~wr_flag; 57 end 58 end 59 60 always@(posedge clk or negedge rst_n)begin 61 if(~rst_n)begin 62 rd_cnt <= 0; 63 end 64 else if(add_rd_cnt)begin 65 if(end_rd_cnt) 66 rd_cnt <= 0; 67 else 68 rd_cnt <= rd_cnt + 1'b1; 69 end 70 end 71 72 assign add_rd_cnt = rd_en & ~empty; 73 assign end_rd_cnt = add_rd_cnt && rd_cnt == MAX_CNT - 1; 74 75 always@(posedge clk or negedge rst_n)begin 76 if(~rst_n)begin 77 rd_flag <= 0; 78 end 79 else if(end_rd_cnt)begin 80 rd_flag <= ~rd_flag; 81 end 82 end 83 84 assign wr_ptr = {wr_flag,wr_cnt}; 85 assign rd_ptr = {rd_flag,rd_cnt}; 86 87 assign wr_addr = wr_cnt; 88 assign rd_addr = rd_cnt + MODE; 89 90 assign empty = wr_ptr == rd_ptr; 91 assign full = (abs(wr_ptr[A_W-1:0] - rd_ptr[A_W-1:0]) < 1) && (wr_ptr[A_W] != rd_ptr[A_W]); 92 93 endmodule
同步FIFO顶层:
1 `timescale 1ns/1ps 2 module fifo_sync 3 #(parameter D_W = 8, 4 LOG_2_DEPTH = 8,//2^8 = 256 5 parameter