数字IC笔面常考,跨时钟域神器。 您所在的位置:网站首页 读写神器 数字IC笔面常考,跨时钟域神器。

数字IC笔面常考,跨时钟域神器。

2024-07-16 02:50| 来源: 网络整理| 查看: 265

异步FIFO 写在前面的话异步FIFO相关知识点FIFO简介FIFO结构应用场景(来源小梅哥 《FPGA 系统设计与验证实战指南》 章节4.4)相关参数异步FIFO 内部组成 异步FIFO的Verilog代码(强烈建议手敲,不要复制粘贴!)顶层模块双端口RAM写满信号判断模块读空信号判断模块信号同步模块testbench波形截图 总结

写在前面的话

掌握基本的数字模块是数字IC工程师的基本要求,最近几年在笔试和面试的时候会遇到要求手撕代码,一方面是考察面试者有没有良好的coding style, 重要的则是考察面试者对常用模块的了解程度。面对这种问题,没有比较好的解决方法,只能是多看、多写,时常复习复习。之所以要把异步FIFO放到分享的第一篇技术博客,是因为本人在学习过程中,从项目中简单了解异步FIFO的IP核、到学习空满信号判断再到照着资料手敲代码,最后到面试中针对异步FIFO有问必答,一路走过来,越发了解到异步FIFO的重要性。而在和经验丰富的老工程师交谈的时候,发现他们那个年代是需要手撕异步FIFO的,当时真的是人人都会,反观现在的面试者,好多人只是了解个IP核就无了。

异步FIFO相关知识点 FIFO简介

FIFO(First In First Out),先进先出。 ,对数据的存储具有先进先出的特性,FPGA 或者 ASIC 中常使用的数据缓存器,常被用于数据的缓存或者高速异步数据的交互。

注意点: (1)它与普通存储器的区别是没有外部读写地址线,使用相对简单。 (2)缺点是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加 1 完成,不能读取指定位置的数据。 (3)有同步和异步FIFO,同步指的是读写时钟是同一个时钟,注意这里时钟的概念,是同一个,不是同频时钟。 异步指的是读写时钟不同。 (4)同步FIFO一般做数据缓冲,主要作用就是buffer。 (5)异步FIFO主要作用就是跨时钟域处理,除此之外可以实现两个不同数据宽度的数据连接。

FIFO结构

FIFO从结构来区分,有两类。分别是:单时钟 FIFO(SCFIFO)和双时钟 FIFO (DCFIFO),其中双时钟FIFO又可以分为 普通双时钟(DCFIFO) 和 混合宽度双时钟 FIFO (DCFIFO_MIXED_WIDTHS)。

单时钟FIFO和双时钟FIFO的输入输出信号如图所示:(来源于Intel FPGA FIFO IP User Guide) (1)单时钟FIFO所有输入输出信号都是在Clock的上升沿进行的,均同步于Clock信号。 (2)双时钟FIFO结构中,读写端口分别有独立的时钟,所有写相关信号同步于写时钟 wrclk ,所有读相关信号同步于读时钟 rdclk 。 单时钟和双时钟FIFO

应用场景(来源小梅哥 《FPGA 系统设计与验证实战指南》 章节4.4)

(1)同步FIFO 单时钟FIFO常用于片内数据交互,例如,FPGA读取传感器数据,先写入FIFO,之后再通过串口发送出去。传感器的读取和串口数据发送都可以基于同一个时钟,因此使用单时钟FIFO做同步。

(2)异步FIFO 异步FIFO典型应用就是异步数据收发,例如高速ADC采集,采集后数据通过千兆以太网发送回PC机器。 利用异步FIFO完成跨时钟域处理

相关参数

(1)宽度(WIDTH),FIFO一次读写的数据位宽。 (2)深度(DEEPTH),FIFO可以存储多少个数据。 (3)满标志:FIFO 已满或将要满时送出的信号,停止写操作而造成溢出(overflow)。 (4)空标志:FIFO 已空或将要空时送出的信号,停止读出数据而造成无效数据的读出(underflow)。 (5)读时钟:读取数据端的时钟信号。 (6)写时钟:写入数据端的时钟信号。 (7)读指针:内部读地址,时钟有效自动加一。 (8)写指针:内部写地址,时钟有效自动加一。 (9)读使能:读取端数据有效。 (10)写使能:写入端数据有效。

异步FIFO 内部组成

如下图所示,简单的异步FIFO内部主要包含以下五个模块,分别是双端口RAM、写满标志判断write_full、读空标志判断read_empty、读时钟到写时钟同步、写时钟到读时钟同步。

这里常考的几个关键点是: (1)双端口RAM相关知识。 (2)写满和读空信号的判断。 用格雷码怎么判断,二进制码怎么判断,有何优缺点? (3)同步模块的作用? 二进制转格雷码怎么做?同步之后的写满和读空是真满 真空吗? 在这里插入图片描述

异步FIFO的Verilog代码(强烈建议手敲,不要复制粘贴!)

下面将附上异步FIFO的Verilog代码,这里建议初学者一定要手敲,用心理解各个模块的作用以及信号连接。同时,也能作为基础的代码练习,培养手速和coding style。最后,这里附上的代码仅供学习,重点是学习异步FIFO的内部结构,感兴趣的同学可以自己再找找异步FIFO相关的代码。

注:这里代码借鉴了猪肉白菜的博客园,各位同学一定要去看看这篇博客。 另外,还有一篇菜鸟教程的博客,也很不错

(1)猪肉白菜博客链接: link

(2)菜鸟教程博客: link

顶层模块

顶层包含了模块例化、内部连线、端口声明等。

// ----------------------------------------------------------------------------- // Copyright (c) 2014-2022 All rights reserved // ----------------------------------------------------------------------------- // Author : hfut904 // File : FIFO_async.v // Create : 2022-03-04 09:47:04 // Revise : 2022-03-04 21:03:52 // Editor : sublime text4, tab size (4) // ----------------------------------------------------------------------------- //异步fifo设计 包括五个部分 RAM、write_full、read_empty、synchronization(写到读、读到写) module FIFO_async #( //------------------paramter------------------------------ parameter FIFO_data_size = 6 , parameter FIFO_addr_size = 5 )( //write signals input clk_w , input rst_w , input w_en , //read signals input clk_r , input rst_r , input r_en , //data in & out input [FIFO_data_size-1:0] data_in , output [FIFO_data_size-1:0] data_out , //key signals output wire empty , output wire full ); //============================================================== //------------paramter reg wire ------------------------------ wire [FIFO_addr_size:0] r_pointer_gray_sync ; wire [FIFO_addr_size:0] w_pointer_gray_sync ; wire [FIFO_addr_size:0] r_pointer_gray ; wire [FIFO_addr_size:0] w_pointer_gray ; wire [FIFO_addr_size-1:0] w_addr ; wire [FIFO_addr_size-1:0] r_addr ; //inst model RAM #( .FIFO_data_size(FIFO_data_size), .FIFO_addr_size(FIFO_addr_size) ) inst_RAM ( .clk_w (clk_w ), .rst_w (rst_w ), .clk_r (clk_r ), .rst_r (rst_r ), .full (full ), .empty (empty ), .w_en (w_en ), .r_en (r_en ), .r_addr (r_addr ), .w_addr (w_addr ), .data_in (data_in ), .data_out (data_out ) ); write_full #( .FIFO_addr_size(FIFO_addr_size) ) inst_write_full ( .clk_w (clk_w ), .rst_w (rst_w ), .w_en (w_en ), .r_pointer_gray_sync (r_pointer_gray_sync ), .w_pointer_gray (w_pointer_gray ), .w_addr (w_addr ), .full (full ) ); read_empty #( .FIFO_addr_size(FIFO_addr_size) ) inst_read_empty ( .clk_r (clk_r ), .rst_r (rst_r ), .r_en (r_en ), .w_pointer_gray_sync (w_pointer_gray_sync ), .r_pointer_gray (r_pointer_gray ), .r_addr (r_addr ), .empty (empty ) ); synchronization #( .FIFO_addr_size(FIFO_addr_size) ) inst1_synchronization ( .clk (clk_r ), .rst (rst_r ), .din (r_pointer_gray ), .dout (r_pointer_gray_sync ) ); synchronization #( .FIFO_addr_size(FIFO_addr_size) ) inst2_synchronization ( .clk (clk_w ), .rst (rst_w ), .din (w_pointer_gray ), .dout (w_pointer_gray_sync ) ); endmodule 双端口RAM

双口RAM是FIFO的重要组成部分,用来完成数据存储。

// ----------------------------------------------------------------------------- // Copyright (c) 2014-2022 All rights reserved // ----------------------------------------------------------------------------- // Author : hfut904 // File : RAM.v // Create : 2022-03-04 10:02:58 // Revise : 2022-03-04 17:03:43 // Editor : sublime text4, tab size (4) // ----------------------------------------------------------------------------- //双口RAM模块 module RAM #( //------------------------paramter------------------- parameter FIFO_data_size = 3 , parameter FIFO_addr_size = 2 )( //----------------------port define ----------------- //write clock & reset input clk_w , input rst_w , //read clock & reset input clk_r , input rst_r , //key signals input full , input empty , //enable input w_en , input r_en , //wr rd addr input [FIFO_addr_size-1:0] w_addr , input [FIFO_addr_size-1:0] r_addr , input [FIFO_data_size-1:0] data_in , output reg [FIFO_data_size-1:0] data_out ); //============================================================== //------------paramter reg wire ------------------------------ reg [FIFO_data_size-1:0] mem [{FIFO_addr_size{1'b1}}:0 ] ; integer i ; /*------------------------------------------------------------------------------ -- wire flag_wr ; wire flag_rd ; ------------------------------------------------------------------------------*/ //always block always @(posedge clk_w or negedge rst_w) begin if(~rst_w) begin for ( i = 0; i 1'b0}} ; end end else if ((w_en == 1) && (full == 0))begin mem[w_addr] 1'b0}} ; end end //rd always @(posedge clk_r or negedge rst_r) begin if(~rst_r) begin data_out 1'b0}} ; //'d0 end else if ((r_en == 1) && (empty == 0))begin data_out 1'b0}} ; end end //assign /* assign flag_wr = (w_en == 1) && (full == 0) ; assign flag_rd = (r_en == 1) && (empty == 0) ; */ endmodule 写满信号判断模块

输出当前FIFO写满标志。

// ----------------------------------------------------------------------------- // Copyright (c) 2014-2022 All rights reserved // ----------------------------------------------------------------------------- // Author : hfut904 // File : write_full.v // Create : 2022-03-04 10:32:09 // Revise : 2022-03-04 15:25:14 // Editor : sublime text4, tab size (4) // ----------------------------------------------------------------------------- module write_full #( //-----------------paramter------------------------------- parameter FIFO_addr_size = 2 )( //================== port define =========================== input clk_w , input rst_w , input w_en , input [FIFO_addr_size:0] r_pointer_gray_sync , output full , output wire [FIFO_addr_size-1:0] w_addr , output wire [FIFO_addr_size:0] w_pointer_gray ); //========================reg wire ================================= reg [FIFO_addr_size:0] w_pointer_bin ; wire flag_wr ; //===================Main Code===================================== //always block always @(posedge clk_w or negedge rst_w) begin : proc_ if(~rst_w) begin w_pointer_bin FIFO_addr_size{1'b0}} ; end else if ((r_en == 1) &&(empty == 0)) begin r_pointer_bin >1)^r_pointer_bin ; assign r_addr = r_pointer_bin[FIFO_addr_size-1:0] ; assign empty = r_pointer_gray == w_pointer_gray_sync? 1: 0 ; //MSB 最高位相等就判断为空 endmodule 信号同步模块

这里用到的是单bit同步的方式,结构简单,在高速应用中则采用其他的同步方式。

// ----------------------------------------------------------------------------- // Copyright (c) 2014-2022 All rights reserved // ----------------------------------------------------------------------------- // Author : hfut904 // File : synchronization.v // Create : 2022-03-04 14:26:40 // Revise : 2022-03-04 20:55:24 // Editor : sublime text4, tab size (4) // ----------------------------------------------------------------------------- ///同步模块 打两拍进行同步 module synchronization #( //=======================paramter========================================== parameter FIFO_addr_size = 2 )( //====================port define======================================= input clk , input rst , input [FIFO_addr_size:0] din , output reg [FIFO_addr_size:0] dout ); //====================reg wire ========================================== reg [FIFO_addr_size:0] dout_t ; //always block always @(posedge clk or negedge rst) begin if(~rst) begin dout 1'b0}} ; dout_t 1'b0}} ; end else begin dout_t 1'b0}} ; #15 rst_w = 0 ; #20 rst_w = 1 ; end //clk_r 读模块信号 initial begin clk_r = 0 ; rst_r = 1 ; r_en = 0 ; #25 rst_r = 0 ; #50 rst_r = 1 ; end //w_en 写使能 initial begin w_en = 0 ; #450 w_en = 1 ; #400 w_en = 0 ; #750 w_en = 1 ; end //r_en 读使能 initial begin r_en = 0 ; #900 r_en = 1 ; #400 r_en = 0 ; #300 r_en = 1 ; end initial begin for ( i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有