大家好,又见面了,我是你们的朋友全栈君。
FIFO是一种先进先出数据缓存器,它与普通存储器的区别是没有外部读写地址线,使用起来非常简单,缺点是只能顺序读写,而不能随机读写。
同步FIFO:指读时钟和写时钟是同一个时钟 异步FIFO:指读写时钟是不同的时钟。
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测
对于“空”的判断:依然依据二者完全相等(包括MSB); 对于“满”的判断:如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。剩下的其余位完全相等6. 同步FIFO实现
参考链接:https://blog.csdn.net/HengZo/article/details/49683707
代码的核心部分主要是data_count,并且full 信号是当data_count == DATA_DEPTH时拉高,除了data_count之外,还可以同过判断r_ptr和w_ptr两个指针是否相等来判断空,满信号 ssign empty = (w_ptr == r_ptr) ? 1 : 0; assign full = (w_ptr[2:0] == r_ptr[2:0] && (w_ptr[3] == ~r_ptr[3])) ? 1 : 0;
这里还有一个注意的点,也就是w_ptr++会越界,同步FIFO就算用data_cnt来判断空满条件,在存数据和写数据时还是应该用w_ptr来表示,如果直接用data_cnt来表示,那么设计的就是一个栈而不是FIFO了。module syn_fifo( clk, rst_n, data_in, w_en, full, data_out, r_en, empty); parameter DATA_WIDTH = 8; parameter DATA_DEPTH = 16; parameter ADDR_WIDTH = 4; input wire clk, rst_n; input wire [DATA_WIDTH-1:0] data_in; input wire w_en, r_en; output wire empty, full; output reg [DATA_WIDTH-1:0] data_out; reg [ADDR_WIDTH : 0] data_count; reg [ADDR_WIDTH-1 : 0] w_ptr, r_ptr; reg [DATA_WIDTH-1 : 0] mem[0 : DATA_DEPTH-1]; assign empty = (data_count == 'd0) ? 1 : 0; assign full = (data_count == DATA_DEPTH) ? 1 : 0; //data_count == DATA_DEPTH always @ (posedge clk or negedge rst_n) begin if (!rst_n) data_count <= 'd0; else if (w_en && r_en) data_count <= data_count; else if (!full && w_en) data_count <= data_count + 1'b1; else if (!empty && r_en) data_count <= data_count - 1'b1; else data_count <= data_count; end always @ (posedge clk or negedge rst_n) begin if (!rst_n) mem[w_ptr] <= 'd0; else if (!full && w_en) mem[w_ptr] <= data_in; end always @ (posedge clk or negedge rst_n) begin if (!rst_n) w_ptr <= 'd0; else if (w_en && !full) w_ptr <= w_ptr + 1'b1; else w_ptr <= w_ptr; end always @ (posedge clk or negedge rst_n) begin if (!rst_n) r_ptr <= 'd0; else if (r_en && !empty) r_ptr <= r_ptr + 1'b1; else r_ptr <= r_ptr; end always @ (posedge clk or negedge rst_n) begin if (!rst_n) data_out <= 'd0; else if (!empty && r_en) data_out <= mem[r_ptr]; end endmodule
设计难点:
跨时钟域数据比较,需要用到同步器,减少亚稳态的传递用到gray码,进一步减少亚稳态的产生gray码相等信号的比较 空:两个gray码相等 满:高两位相反,其余位相同。指针计数需要比ADDR的位宽多一位,这一点和同步FIFO的设计是一样的。https://www.cnblogs.com/BitArt/archive/2013/04/10/3010073.html
module asyn_fifo( clk_w, clk_r, rst_n, r_en, w_en, data_in, data_out, full, empty ); input wire clk_r, clk_w, rst_n; input wire r_en, w_en; input wire [7:0] data_in; output wire full, empty; output reg [7:0] data_out; parameter DATA_DEPTH = 8; parameter DATA_WIDTH = 8; parameter ADDR_WIDTH = 3; reg [3:0] w_ptr, r_ptr; reg [7:0] mem[DATA_DEPTH-1 : 0]; always @ (posedge clk_w or negedge rst_n) begin if (~rst_n) w_ptr <= 'd0; else if (w_en && !full) w_ptr <= w_ptr + 1'b1; else w_ptr <= w_ptr; end wire [3:0] w_ptr_gray, r_ptr_gray; assign w_ptr_gray = w_ptr ^ (w_ptr >> 1); assign r_ptr_gray = r_ptr ^ (r_ptr >> 1); // reg [ADDR_WIDTH:0] rd1_wp, rd2_wp; always @ (posedge clk_r or negedge rst_n) begin if (!rst_n) begin rd1_wp <= 'd0; rd2_wp <= 'd0; end else begin rd1_wp <= w_ptr_gray; rd2_wp <= rd1_wp; end end assign empty = (rd2_wp == r_ptr_gray) ? 1 : 0; always @ (posedge clk_r or negedge rst_n) begin if (~rst_n) r_ptr <= 'd0; else if (r_en && !empty) r_ptr <= r_ptr + 1'b1; else r_ptr <= r_ptr; end //wire [ADDR_WIDTH:0] r_ptr_gray; assign r_ptr_gray = r_ptr ^ (r_ptr >> 1); reg [ADDR_WIDTH:0] wd1_rp, wd2_rp; always @ (posedge clk_w or negedge rst_n) begin if (~rst_n) begin wd1_rp <= 'd0; wd2_rp <= 'd0; end else begin wd1_rp <= r_ptr_gray; wd2_rp <= wd1_rp; end end assign full = ({(~wd2_rp[ADDR_WIDTH:ADDR_WIDTH-1]),wd2_rp[ADDR_WIDTH-2:0]} == w_ptr_gray) ? 1:0; always @ (posedge clk_w or negedge rst_n) begin if (~rst_n) mem[w_ptr] <= 'd0; else if (!empty && rd_en) mem[w_ptr] <= data_in; end always @ (posedge clk_r or negedge rst_n) begin if (~rst_n) data_out <= 'd0; else if (!full && w_en) data_out <= mem[r_ptr]; end endmodule
set_false_path 在设计中,不需要满足setup/hold时序的数据路径需要设置成false path set_disable_timing:可以使库单元的时间弧(timing arc)无效 总的来说,set_false_path 只对data path起作用, EDA 工具还会分析计算这条时序路径, 只是不报出来是否有时序违例。 set_disable_timing 对timing arc起作用,完全不去分析这条timing arc
FIFO仅在数据突发时才有效,不使用与连续的数据输出和输入。如果存在连续的数据流,那么所需要的FIFO大小因该是无限的。因此需要知道突发速率,突发大小,频率等,才能确定FIFO的深度。 最小深度的计算流程
确定读时钟fr和写时钟的频率fw, 一般情况fw>fr的根据fr和fw计算读写一次数据的周期Tr 和Tw,根据T= 1/f根据突发写长度的大小,计算这么多数据需要写多少时间 tw = Tw*len根据写的时间tw计算读了多少数据 n = tw/TrFIFO的最小深度等于 len-n分析过程: 写时钟周期Tw = 1000/80 ns = 12.5ns;同理读时钟周期为20ns; 突发写长度为120个数据,写120个数据耗时120 * 12.5 = 1500ns; 1500ns时间内读出数据1500/20ns = 75个; 故最小FIFO深度为120 – 75 = 45;
分析: 写时钟周期T_A = 12.5ns,读时钟周期为T_B = 20ns; 两个写时钟写一个数据,也就是写一个数据需要时间2*T_A = 25ns,那么由于突发写数据个数为120个,写这么多数据需要时间120 * 25ns = 3000ns; 4个读时钟周期读一个数据,因此读一个数据需要时间80ns,3000ns读了3000/80 = 37.5个数据(0.5不算一个数据,没读完整),约等于37个数据。 所以,FIFO的最小深度为120 – 37 = 83;
分析: 这种情况下永远也不会发生数据丢失的情况; fifo的深度为1
分析: 写时钟周期1000/30 ns = 100/3 ns;读时钟周期 20ns; 写一个数据需要2个时钟,也就是200/3 ns;读一个数据需要4个时钟,也就是80 ns; 写120个数据需要时间8000ns,这段时间内读出数据8000/80 = 100个; 因此,FIFO的最小深度为120 – 100 = 20;
分析: 如果读写时钟之间没有相位差,则不需要FIFO就可以进行读写; 如果二者存在相位差,只需要FIFO的深度为1即可。
分析: 两个时钟写一个数据,需要时间40ns; 4个时钟读一个数据,需要80ns; 由于突发长度为120,需要120*40 = 4800ns写完;这段时间读出数据个数:4800/80 = 60; 所以,FIFO最小深度为120 – 60 = 60;
首先,从条件可知,写频率等于读频率; 其次,读写可以在如下限制下的任意时刻发生:
为了获得更安全的FIFO深度,我们需要考虑最坏的情况,以防数据丢失; 对于最坏的情况,写入和读取之间的数据速率之间的差异应该是最大的。 因此,对于写操作,应考虑最大数据速率,对于读操作,应考虑最小数据速率。从上表可以看出,最快的写数据速率应该为第4种情况,写操作在最小的时间内完成; 由于突发写长度为160,所以160个时钟写160个数据; 由于读速度为10个时钟读8个数据,因此一个数据需要10/8个时钟; 所以160个时钟读了160*8/10 = 128个数据; 所以FIFO的最小深度为160-128=32.
假如clkA = 25MHz,则CLKB = 100MHz;
TA= 40ns, TB = 10ns; en_B = 100*40 = 4000ns;占空比为1/4; 我们认为B为写时钟,写使能时间为4000/4 = 1000ns,则突发写长度为1000/10 = 100个数据; 在1000ns内读出数据为1000/40 = 25个数据,所以FIFO最小深度为100 – 25 = 75
输入时钟频率炜250MHz,输入数据率8Gbps,输出的时钟频率200MHz,输出的数据率为5Gbps,单位时间内输入的数据总量为4Gb,在保证数据不丢失的情况下,最少需要多大的缓冲空间,并给出分析步骤。 解析:解答1,不考虑两个包之间的背靠背的情况 4Gb/8Gbps * 5Gbps = 2.5Gb,因此缓冲空间=4Gb-2.5Gb = 1.5Gb; 解答2:考虑背靠背的情况 突发长度=8G, 写应该是0440的情况,读用最慢的情况,2.5, 2.5,0.5, 2.5,因此FIFO= 8-3 = 5G
注意同步化synchronization stages
这个值用于表示FIFOempty拉低的时间长度,同时要注意FIFO的读一定要有empty控制,并且发现empty并不是一写入数据就拉低的。
参考链接: https://blog.csdn.net/Reborn_Lee/article/details/100127937 [https://hardwaregeeksblog.files.wordpress.com/2016/12/fifodepthcalculationmadeeasy2.pdf] https://zhuanlan.zhihu.com/p/47847664
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/170181.html原文链接:https://javaforall.cn
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!本文分享自作者个人站点/博客:https://javaforall.cn复制如有侵权,请联系 本站 删除。