【数字IC手撕代码】Verilog同步FIFO|题目|原理|设计|仿真

简介: 【数字IC手撕代码】Verilog同步FIFO|题目|原理|设计|仿真

cb39282b8fb147aaa3a300f70d43c278.png


一、前言


本系列旨在提供100%准确的数字IC设计/验证手撕代码环节的题目,原理,RTL设计,Testbench和参考仿真波形,每篇文章的内容都经过仿真核对。快速导航链接如下:


1.奇数分频

2.偶数分频

3.半整数分批

4.小数/分数分频

5.序列检测器

6.模三检测器

7.饮料机

8.异步复位,同步释放

9.边沿检测(上升沿,下降沿,双边沿)

10.全加器,半加器

11.格雷码转二进制

12.单bit跨时钟域(打两拍,边沿同步,脉冲同步)

13.奇偶校验

14.伪随机数生成器[线性反馈移位寄存器]

15.同步FIFO

16.无毛刺时钟切换电路


应当说,手撕代码环节是面试流程中既重要又简单的一个环节,跟软件类的岗位相比起来,数字IC的手撕代码题目固定,数量有限,属于整个面试中必得分的一个环节,在这个系列以外,笔者同样推荐数字IC求职者使用“HdlBits”进行代码的训练

链接如下

HDLBits — Verilog Practice


这篇开始之前,先安利大家两篇文章,都是Clifford E. Cummings写的,分别是《Simulation and Synthesis Techniques for Asynchronous FIFO Design》和《Simulation and Synthesis Techniques for Asynchronous FIFO Design with Asynchronous Pointer Comparisons》,感兴趣的朋友可以baidu或者google搜来看看,这两篇文章涵盖了有关FIFO的所有关键问题,语言生动,深入浅出,加一起一共四十页左右。

而作为《数字IC手撕代码篇》,我们想要讨论的同步FIFO一方面继承自异步FIFO,另一方面也应该是所有面试中可能会遇到的最为复杂的手撕代码题,具体的内容如下


二、题目


1.使用verilog,完成同步FIFO的设计,其中数据位宽为8位,FIFO的深度为16,其中输入端口为clk,rst_n(复位信号),write_en(写使能),read_en(读使能),data_in,输出端口为empty(空信号),full(满信号),data_out。


三、原理


有关什么是FIFO之类的问题就不讨论了,我们重点讨论几个关键的问题


1.同步FIFO和异步FIFO的结构差异

image.png


以上是异步FIFO的结构图,可以发现,对于异步FIFO来说,它由几部分组成,最中间的是一块双口RAM,RAM的左边是写控制模块, 右边是读控制模块,下面的两级寄存器起到了格雷码状态下的地址信号跨时钟域作用,而对于同步FIFO来说,因为读写共用同一个时钟频率,因此不需要寄存器同步的那一部分。为了最大程度的贴近于异步FIFO的设计,作者依旧采用格雷码的比较来产生空满信号,当然,使用一个计数器来判断空满信号也是可行的,读者感兴趣可以在评论区讨论一下。


2.FIFO深度的约束

在格雷码转二进制中,我们已经讨论了“gray_code= binary_code ^ (binary_code >> 1)”这种格雷码的产生方式的限制,即只适用于2^N的情况,这里对FIFO的深度约束依旧成立,假如我们设计一个14位深度的fifo,地址由14跳回1不满足单bit变化,会产生采样错误的问题,因此本篇我们依旧对偶数情况的格雷码按下不表,只讨论2的N次方的深度问题


3.空满信号如何产生

设置读指针与写指针,在格雷码的情况下,若读写指针相等,证明FIFO为空,这个很好理解,那么什么情况下,FIFO为满呢?写指针转了一圈,追上了读指针的时候,FIFO应该是满状态,这里如果拿格雷码表格来举例的话,假设FIFO的深度为8位,只需要3位二进制编码就能表示,我们多使用一位,用4位的二进制编码来额外加上写指针追读指针的情况,那么以下的截图,读写指针为1+9,2+10,3+11以此类推的情况都是应该产生满信号的情况,找下规律,发现对于4bit格雷码来说,前两位相反,之后的位相同,就是verilog逻辑中判断满信号产生的条件

239c5d2bf344424b94b1b914e153cf7e.png


4.数据通路和控制通路是否都需要复位

在作者的设计中,并不是所有的寄存器块都进行了复位的,比如说描述data_in所在的块,作者没有进行复位,但这并不影响FIFO的正常工作,这是因为我们设计追寻的原则为“控制通路必须包含复位”,数据通路“是否复位可以选择”,即读写指针所在的通路rst_n到来的时候都需要复位,但data_in作为数据通路可以选择不带复位信号。具体涉及到的代码如下:

always@(posedge clk)
if(write_en && !full)
data[wr_point] <= data_in;
else
data[wr_point] <= data[wr_point];


四、RTL设计


module fifo(clk,rst_n,write_en,read_en,data_in,empty,full,data_out);
input clk;
input rst_n;
input write_en;
input read_en;
input [7:0] data_in;
output empty;
output full;
output  reg [7:0] data_out;
reg [7:0] data  [0:15];
reg [4:0] wr_point;
reg [4:0] rd_point;
wire [4:0] wr_gray_point;
wire [4:0] rd_gray_point;
assign wr_gray_point = wr_point ^ (wr_point>>1);
assign rd_gray_point = rd_point ^ (rd_point>>1);
assign empty = (wr_gray_point == rd_gray_point) ? 1 : 0;
assign full =  (wr_gray_point[2:0] == rd_gray_point[2:0]
                && wr_gray_point[4] == !rd_gray_point[4]
                && wr_gray_point[3] == !rd_gray_point[3] ) ? 1 : 0 ;
always@(posedge clk or negedge rst_n)
if(!rst_n)
        wr_point <= 5'b0_0000;
else if (write_en && !full)
        begin
        if(wr_point < 5'b1_1111)
        wr_point <= wr_point + 1'b1;
        else
        wr_point <= 5'b0_0000;
        end
else
        wr_point <= wr_point;
always@(posedge clk or negedge rst_n)
if(!rst_n)
        rd_point <= 5'd0;
else if (read_en && !empty)
        begin
        if(rd_point <5'b1_1111)
        rd_point <= rd_point + 1'b1;
        else
        rd_point <= 5'b0_0000;
        end
else
        rd_point <= rd_point;
always@(posedge clk)
if(write_en && !full)
data[wr_point] <= data_in;
else
data[wr_point] <= data[wr_point];
always@(posedge clk or negedge rst_n)
if(!rst_n)
data_out <= 8'h00;
else if(read_en && !empty)
data_out <= data[rd_point];
else
data_out <= 8'h00;
endmodule


五、Testbench设计


module fifo_tb();
reg clk;
reg rst_n;
reg write_en;
reg read_en;
reg [7:0] data_in;
wire empty;
wire full;
wire [7:0] data_out;
fifo u1(.clk(clk),
.rst_n(rst_n),
.write_en(write_en),
.read_en(read_en),
.data_in(data_in),
.empty(empty),
.full(full),
.data_out(data_out)
);
always #5 clk = !clk;
always #10.01 data_in = $random;
initial
begin
clk = 0;
rst_n = 1;
#10
rst_n =0;
#14
rst_n = 1;
write_en = 1;
#200
write_en = 0;
read_en = 1;
#400
$stop;
end
endmodule


六、仿真分析


4d2524a012dd4b8886112f97652be126.jpg

我们可以看到,之前按顺序输入的数据,之后按照相同顺序输出,同时空满信号的产生正常,设计成立。

相关文章
【期末不挂科-单片机考前速过系列P6】(第六章:10题速过定时计数器的结构和工作方式例题)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P6】(第六章:10题速过定时计数器的结构和工作方式例题)经典例题盘点(带图解析)
【数字IC手撕代码】Verilog小数分频|题目|原理|设计|仿真
【数字IC手撕代码】Verilog小数分频|题目|原理|设计|仿真
【数字IC手撕代码】Verilog小数分频|题目|原理|设计|仿真
【数字IC手撕代码】Verilog自动售卖饮料机|题目|原理|设计|仿真
【数字IC手撕代码】Verilog自动售卖饮料机|题目|原理|设计|仿真
【数字IC手撕代码】Verilog自动售卖饮料机|题目|原理|设计|仿真
【数字IC手撕代码】Verilog异步复位同步释放|题目|原理|设计|仿真
【数字IC手撕代码】Verilog异步复位同步释放|题目|原理|设计|仿真
【数字IC手撕代码】Verilog异步复位同步释放|题目|原理|设计|仿真
【数字IC手撕代码】Verilog轮询仲裁器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog轮询仲裁器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog轮询仲裁器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
|
自然语言处理 算法
【数字IC手撕代码】Verilog固定优先级仲裁器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog固定优先级仲裁器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog固定优先级仲裁器|题目|原理|设计|仿真
|
人工智能
【数字IC手撕代码】Verilog全加器半加器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog全加器半加器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog全加器半加器|题目|原理|设计|仿真
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog半整数分频|题目|原理|设计|仿真
【数字IC手撕代码】Verilog半整数分频|题目|原理|设计|仿真
【数字IC手撕代码】Verilog半整数分频|题目|原理|设计|仿真