前言:

本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。

目录如下:

1.数字IC手撕代码-分频器(任意偶数分频)

2.数字IC手撕代码-分频器(任意奇数分频)

3.数字IC手撕代码-分频器(任意小数分频)

4.数字IC手撕代码-异步复位同步释放

5.数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

6.数字IC手撕代码-序列检测(状态机写法)

7.数字IC手撕代码-序列检测(移位寄存器写法)

8.数字IC手撕代码-半加器、全加器

9.数字IC手撕代码-串转并、并转串

10.数字IC手撕代码-数据位宽转换器(宽-窄,窄-宽转换)

11.数字IC手撕代码-有限状态机FSM-饮料机

12.数字IC手撕代码-握手信号(READY-VALID)

13.数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

14.数字IC手撕代码-泰凌微笔试真题

15.数字IC手撕代码-平头哥技术终面手撕真题

16.数字IC手撕代码-兆易创新笔试真题

17.数字IC手撕代码-乐鑫科技笔试真题(4倍频)

18.数字IC手撕代码-双端口RAM(dual-port-RAM)

...持续更新

更多手撕代码题可以前往 数字IC手撕代码--题库。


目录

原理介绍

同步FIFO的工作方式

FIFO空满的产生

计数器判断空满

代码

Dual Port RAM

sync_FIFO

testbench

波形图


原理介绍

在设计系统时,会包含工作在不同时钟频率下的元件,例如处理器和外设。数据在这些元件之间传输时先进先出(FIFO first in first out)阵列起到了重要作用。FIFO是用于对在通信总线上传输的数据进行排列的简单存储结构。

因此,FIFO常用来传输跨不同时钟域的数据。

本节介绍简单的同步FIFO架构,读写使用同样的时钟,为我们后续写异步FIFO(读写时钟非同源)做铺垫。

下面给出了一个同步FIFO的通用架构,DPRAM(Dual Port RAM 双端口RAM)作为存储器来存储信息,在此之上添加判断DPRAM空满信息的组件后,整个模块就是一个同步FIFO了,读写分别使用不同的使能和地址信号(读、写使能,读、写地址分立),使得整个模块可以进行同时读写。

通过读、写指针产生各自的读、写地址,送到读、写端口。写指针指向下一个要写入的地址,读指针指向下一个要读取的地址。有效写使能使得写指针递增, 有效的读使能使读指针递增。

图中的“状态模块”产生FIFO的空满信号,如果“fifo_full”有效,则说明FIFO内部的空间已满,不能再写入数据。如果“fifo_empty”有效,则说明FIFO内乜有可供读取的下一个有效数据。通过对读写指针位置的判断,该模块也可以指示出任意时刻FIFO中空或满区域的个数。

同步FIFO的工作方式

复位后,读写指针都归0。此时“fifo_empty”信号置为有效而“fifo_full”保持低电平。因为FIFO为空,所以阻止对FIFO的读操作,只能进行写操作。后续的写操作会增加写指针的值,并将“fifo_empty”信号置为无效。在写到最后一个数据时,写指针等于RAM_SIZE-1。此时进行一个写指针会使写指针滚回到0,并将“fifo_full”信号置为高电平。

总之,在读、写指针相等时,FIFO要么空要么满,所以需要对两种情况进行区分。

FIFO空满的产生

以深度为4的FIFO为例,一开始读写指针指向同一个位置,FIFO为空。写入三个数据之后,写指针指向RAM_SIZE-1=3的位置,此时再写入一个数据,写指针(wr_ptr)滚回0,和读指针指向同一个位置,此时FIFO为满。

根据这种逻辑,很容易推导出这么一个结论:无论读写指针此时指向什么位置,当wr_ptr+1==rd_ptr时,FIFO再写入一个数据就满了,所以有:

fifo_full = (rd_ptr == (wr_ptr + 1'b1))&& wr_fifo

从而有判断FIFO为满的RTL 代码:

always@(posedge clk or negedge rstn)beginif(!rstn)fifo_full <= 1'b0;else if(wr_fifo && rd_fifo);//do nothingelse if(rd_fifo)fifo_full <= 1'b0;else if((rd_ptr = wr_ptr + 1'b1) && wr_fifo)fifo_full <= 1'b1;
end

类似的,当读操作使得两个指针在下一个时钟相等时,FIFO变空,产生“fifo_empty”信号。有如下关系:无论读写指针此时指向什么位置,当rd_ptr+1==wr_ptr时,FIFO再读出一个数据就空了。

fifo_empty = (wr_ptr == (rd_ptr + 1'b1))&& rd_fifo

从而有判断FIFO为空的RTL 代码:

always @(posedge clk or negedge rstn)beginif(!rstn)fifo_empty <= 1'b1;else if(wr_fifo && rd_fifo);//do nothingelse if(wr_fifo)fifo_empty <= 1'b0;else if((wr_ptr = rd_ptr + 1'b1) && rd_fifo)fifo_empty <= 1'b1;
end

计数器判断空满

FIFO还有另一种利用计数器来指示FIFO空满的方法。

计数器的宽度要与FIFO的深度相等,这样计数器才能记录FIFO数据的最大个数。计数器在复位时初始化为0,随后的任何写操作会将其递增1,任何读操作会使其递减1。

在计数器为0时,很容易判断FIFO处于空状态,而当计数器的值等于FIFO的大小时,就能判断FIFO处于满状态。

对于这种采用计数器来判断空满的方式实现比较简单,但是和上一个比较读写指针位置的方法相比资源占用会高一些。因为这种方法要求增加额外的硬件(计数器)来进行计数。

代码

简单来说,FIFO就是一个有判断空满逻辑的双端口RAM,下面我们来写一下以指针循环一周期判断空满的方式的同步FIFO,但是在之前我们先要写一个双端口RAM来存储数据。

Dual Port RAM

module dual_port_ram#(parameter DEPTH = 16,parameter WIDTH = 8
)(input                       wr_clk      ,input                       wr_en       ,input   [$clog(DEPTH)-1:0]  wr_addr     ,input   [WIDTH-1:0]         wr_data     , input                       rd_clk      ,input                       rd_en       ,input   [$clog(DEPTH)-1:0]  rd_addr     ,output  [WIDTH-1:0]         rd_data
);
reg [WIDTH-1:0] RAM_MEM [DEPTH-1:0];always @(posedge wr_clk)beginif(wr_en)RAM_MEM[wr_addr] <= wr_data;
endalways @(posedge rd_clk)beginif(rd_en)RAM_MEM[rd_addr] <= rd_data;
endendmodule

整个双端口RAM其实就是一个简单的写使能时,把数据写入输入的写地址。读使能时,把数据从地址里读出来的那么一个功能,后续在FIFO中例化该Dual Port RAM模块。

sync_FIFO

`include "clog.v"module sync_fifo#(parameter WIDTH = 8     ,parameter DEPTH = 16
)(input                       clk         ,       input                       rstn        ,   // reset while rstn is negative//write interfaceinput       [WIDTH-1:0]     data_in     ,   // input data input                       wr_en       ,   // write enable //read interfaceinput                       rd_en       ,   // read enableoutput      [WIDTH-1:0]     data_out    ,output  reg                 fifo_empty  ,output  reg                 fifo_full
);//signal define
reg [clog(DEPTH)-1:0]    wr_ptr;
reg [clog(DEPTH)-1:0]    rd_ptr;wire wr_fifo;
wire rd_fifo;//write data opration
always @(posedge clk or negedge rstn)beginif(!rstn)wr_ptr <= 1'b0;else if(wr_fifo)wr_ptr <= wr_ptr + 1'b1;
endassign wr_fifo = wr_en && !fifo_full;//read data opration
always @(posedge clk or negedge rstn)beginif(!rstn)rd_ptr <= 1'b0;else if(rd_fifo)rd_ptr <= rd_ptr + 1'b1;
endassign rd_fifo = rd_en && !fifo_empty;//full signal judgment
always@(posedge clk or negedge rstn)beginif(!rstn)fifo_full <= 1'b0;else if(wr_fifo && rd_fifo);//do nothingelse if(rd_fifo)fifo_full <= 1'b0;else if((rd_ptr == wr_ptr + 1'b1) && wr_fifo)fifo_full <= 1'b1;
end//empty signal judgment
always @(posedge clk or negedge rstn)beginif(!rstn)fifo_empty <= 1'b1;else if(wr_fifo && rd_fifo);//do nothingelse if(wr_fifo)fifo_empty <= 1'b0;else if((wr_ptr == rd_ptr + 1'b1) && rd_fifo)fifo_empty <= 1'b1;
enddual_port_ram #(.DEPTH      (DEPTH)     ,.WIDTH      (WIDTH)
)u_dual_port_ram
(.wr_clk      (clk)       ,       //sync FIFO ,wr_clk = rd_clk.wr_en       (wr_fifo)   ,.wr_addr     (wr_ptr)    ,.wr_data     (data_in)   ,.rd_clk      (clk)       ,.rd_en       (rd_fifo)   ,.rd_addr     (rd_ptr)    ,.rd_data     (data_out)
);endmodule

此外,还写了一个判断位宽的函数clog.v

`ifndef MY_CLOG
`define MY_CLOGfunction integer clog (input integer depth);beginfor (clog=0; depth-1>0; clog=clog+1) depth = depth >>1;                          end
endfunction`endif

testbench

`timescale 1ns/1nsmodule sync_fifo_tb();parameter WIDTH = 8;
parameter DEPTH = 16;reg                     clk         ;
reg                     rstn        ;reg     [WIDTH-1:0]     data_in     ;
reg                     rd_en       ;
reg                     wr_en       ;wire    [WIDTH-1:0]     data_out    ;
wire                    empty       ;
wire                    full        ;always #5 clk = ~clk;initial beginclk     <= 1'b0;rstn    <= 1'b0;data_in <= 'd0;rd_en   <= 1'b0;wr_en   <= 1'b0;//write 16 times to make fifo full#10rstn    <= 1'b1;repeat(16)begin@(negedge clk)beginwr_en   <= 1'b1;data_in <= $random; // generate 8bit random number data_inendend//read 16 times to make fifo emptyrepeat(16)begin@(negedge clk)beginwr_en   <= 1'b0;rd_en   <= 1'b1;endend//read and write 8 timesrepeat(8)begin@(negedge clk)beginwr_en   <= 1'b1;data_in <= $random; rd_en   <= 1'b0;endend//Continuous read and writeforever begin@(negedge clk)beginwr_en   <= 1'b1;data_in <= $random;rd_en   <= 1'b1;endend
endinitial begin#800$finish();
endinitial begin$fsdbDumpfile("sync_fifo.fsdb");$fsdbDumpvars(0);
endsync_fifo #(.WIDTH      (WIDTH)     ,.DEPTH      (DEPTH)
)u_sync_fifo
(.clk        (clk)       ,.rstn       (rstn)      ,.data_in    (data_in)   ,.rd_en      (rd_en)     ,.wr_en      (wr_en)     ,.data_out   (data_out)  ,.fifo_empty (empty)     ,.fifo_full  (full)
);endmodule

波形图

仿真结果和分析的结果一致, 写入16个数据将FIFO写满,此时full信号拉高;读出16个数据将FIFO读空,此时empty信号拉高;写入8个数据之后,同时读写,写入数据和读出数据均保持一致,功能正确。

了解了同步FIFO的设计方法后,再进行异步FIFO的设计就比较简单了,下篇博客记录如何写一个异步FIFO,解决FIFO中跨时钟域的问题。


更多手撕代码题可以前往 数字IC手撕代码--题库。

数字IC手撕代码-同步FIFO相关推荐

  1. 数字IC手撕代码---百题斩

    前言: 本篇导览目录,用来索引笔者写的其他手撕代码文章 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC ...

  2. 数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  3. 数字IC手撕代码-兆易创新笔试真题

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  4. 数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  5. 数字IC手撕代码-乐鑫科技笔试真题(4倍频)

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  6. 数字IC手撕代码-有限状态机FSM-饮料机

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  7. 数字IC手撕代码--小米科技(除法器设计)

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  8. 数字IC手撕代码-泰凌微笔试真题

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

  9. 数字IC手撕代码-平头哥技术终面手撕真题

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

最新文章

  1. centos下为firefox安装flash插件的几种方法
  2. linux检查文件一致性,3.20 fsck(检查并修复Linux 文件系统)
  3. 5分钟搜索120个平台10年数据!监视软件细节曝光:人人“裸奔”,预测警务歧视严重...
  4. 30-hadoop-hbase-安装squirrel工具
  5. beats 耳机 android,Beats耳机app
  6. ASP.net中的validaterequest
  7. 快速mysql导入sql文件_mysql肿么快速从sql文件导入数据库
  8. 【机器学习基础】数学推导+纯Python实现机器学习算法17:XGBoost
  9. 工业以太网的优点有哪些?
  10. 华为智能计算发布FusionServer Pro智能服务器
  11. Hbase 架构(未完待续)
  12. Maven : Log4j2 could not find a logging implementation
  13. oracle实例创建失败,【求助】急!!!!oracle客户端安装时创建实例失败
  14. Python调用shell命令方式
  15. bootstrap引入文件方法
  16. rgmii clock delay
  17. XML中的大于等于小于等于
  18. Sketch Learning - SVG 是什么?
  19. ffmeg将多段视频合成一个视频
  20. 利用遗传算法库DEAP优化交易策略

热门文章

  1. Michael Kors 完成了对 Versace 的收购,集团还正式改名成了 Capri
  2. 洛谷 P1862 输油管道问题
  3. 在python中_语法-和在Python中是什么意思?
  4. c语言第4份实验报告,C语言实验报告(四)
  5. win32crypt :安装pywin32并解决模块找不到问题
  6. 音视频开发---基础概念总结
  7. php composer 安装
  8. 出现最多的字母——python
  9. Python基础047:Pycharm debug时设置断点但是不起作用怎么回事?
  10. 计算机科学 高中研究项目,2013全国第28届青少年科技创新大赛高中《计算机科学》作品《项目简介》...