目录

  • 1. wr_logic 与 rd_logic
    • 1.1. RAM 读使能与写使能
    • 1.2. full与empty判定:读写指针的扩展位
      • 位宽相同的 读写指针
      • 读写指针的 扩展位
    • 1.3. RAM 读指针 与 写指针
      • 跨时钟域 中间态问题:带扩展位的Grey码
      • 跨时钟域 亚稳态问题:电平同步 带来的延迟
      • 跨时钟域 快采慢:无影响
      • 跨时钟域 慢采快:无影响
    • 1.4. 代码
  • 2. sdram
    • 2.1. 代码
  • 3. async_fifo
  • 4. async_fifo_tb
  • 5. param_def

逻辑和代码设计部分是异步FIFO的关键部分。

按照从外到内的顺序设计逻辑(外部模块的端口是确定的,内部模块的要求可动态调整)

所以先读写逻辑,最后是SDRAM

1. wr_logic 与 rd_logic

三个功能,生成对RAM的读写地址和使能,同时根据写指针和读指针判断FIFO是为full状态还是empty状态

先上一张思维导读,以理解整个设计思路,不是拍脑袋

1.1. RAM 读使能与写使能

这个好说,联合FIFO读写使能和空满标志即可

assign wr_en_ram = wr_en & (!full);
assign rd_en_ram = rd_en & (!empty);

1.2. full与empty判定:读写指针的扩展位

位宽相同的 读写指针

首先我们需要两个指针,分别表示下次写入的RAM地址,即写指针和下次读出的RAM地址,即读指针,且这两种指针的位宽相同

相同的地址位宽是为了便于full和empty判定,后文的SDRAM访问也方便,所以暂使位宽相同。
注意在这种情况下,读指针走的路程永远不会超过写指针。

● 那么如何判断空满呢?认为当再执行一次访问时,出现数据覆盖or空读时,此时RAM要么写满要么读空

注意地址位宽相同但数据位宽是不同的!

例如设置FIFO写数宽16bit,FIFO读数宽8bit,FIFO深度为64bit。
若RAM每个地址的数据位宽选择8bit,此时每次读直接读出对应地址的数据,而每次写则需要写入两个地址的数据。
如下图所示由于每次写要写两个地址的数据,所以如果发现这次写会出现数据覆盖时,就full有效。
而读是每次读一个地址,所以如果发现这次读会出现空读时,就empty有效。

读写指针的 扩展位

此处SDRAM数据位宽选择读、写数据位宽中较小的那个

这必然导致一种访问可以一个地址一个地址得访问,另一种访问必须每次访问多个地址。

这就可以根据指针之间的相隔的地址个数判断空满。

但有一个问题,如果两个指针指向相同的地址,此时是空还是满呢?

解决方法是,将读写指针的位数扩展一位,每当走过一个FIFO深度时,该bit翻转

扩展位相同,则读空empty有效,扩展位不同,则写满full有效。

注意full和empty都是组合逻辑。如果FIFO满了还写就会覆盖,FIFO空了还读就是空数

1.3. RAM 读指针 与 写指针

复位时,俩指针指向SDRAM的第一个地址。

● 之后每当wr_en有效时,检查full信号是否有效,若无效就将wdata的数据写入RAM,同时写指针移动。否则不能向RAM写入,可通过控制RAM的wr_en端口阻止写入

● 之后每当rd_en有效时,检查empty信号是否有效,若无效就从RAM读出rdata的数据,同时读指针移动。否则不能从RAM读出,可通过控制RAM的rd_en端口阻止读出

之后根据上述full和empty的逻辑判断空满即可。但是注意读写指针均会涉及跨时钟域传输,该如何处理?

跨时钟域 中间态问题:带扩展位的Grey码

多bit跨时钟域传输

由于读写指针是多bit二进制变化,所以很可能由于时钟偏斜,时钟沿不同时到达各触发器,导致那几个bit位的变化不是同时的

所以异步时钟很可能采样到跳变过程的中间态

例如读指针从01跳变10,由于时钟偏斜第一位posedge rclk先到、第二位posedge rclk后到,所以wclk处第一个触发器D端变成1早、第二个触发器D端变成0晚,故wclk就可能采样到11,这会导致full判断出错以及其它问题。

而该问题的本质是每个时钟周期都涉及多个触发器的电平变化,那么能不能减少每个时钟周期电平变化的触发器个数?

Grey码计数器

有,Grey码计数器,每个时钟沿只有1bit发生变化,最多采样到一种错值,即旧值。

那问题来了,怎么实现Grey码计数器?直接实现似乎比较困难。

我的实现方式是将二进制计数器的组合逻辑部分和触发器之间加一个Binary2Grey的码制转换。

SDRAM那边不出意外的话应该是用二进制计数器,直接使用Grey码计数器的二进制组合逻辑部分的输出,然后打一拍再连地址端。

即Grey码计数器的组成是:二进制组合逻辑 + Binary2Grey的码制转换 + 触发器
这里打一拍是因为这里的触发器的初始值为0,是指触发器Q端初始是0,而D端初始应该是1。所以二进制组合逻辑部分的输出初始值也是1,所以要先打一拍。
或者是Grey码计数器出来再接一个Grey2Binary转换模块,再连到SDRAM的地址端。但这样耗费资源多一些

带扩展位的Grey码

别忘了full和empty要求计数器要扩展一位的。

纯用Grey码有个问题就是,指针跑完一圈后,扩展位翻转,但其他位也会翻转呀,那么在该时钟沿有两个触发器发生了电平变化。

例如0000(第一位是扩展位)走到最后一个地址是0100,然后回到第一个地址,按照之前我们的逻辑,此时指针是1000。1000相比0100翻转了两位!

改进的思路是,指针走动方向不变,扩展位为0时其余位递增、扩展位为1时其余位递减,如下

RAM内存 第一圈 第二圈 第三圈 第四圈 ...
指针顺序为地址一到八 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码
地址一 0 000 0 000 1 100 1 000 0 000 0 000 1 100 1 000 ... ...
地址二 0 001 0 001 1 101 1 001 0 001 0 001 1 101 1 001 ... ...
地址三 0 011 0 010 1 111 1 010 0 011 0 010 1 111 1 010 ... ...
地址四 0 010 0 011 1 110 1 011 0 010 0 011 1 110 1 011 ... ...
地址五 0 110 0 100 1 010 1 100 0 110 0 100 1 010 1 100 ... ...
地址六 0 111 0 101 1 011 1 101 0 111 0 101 1 011 1 101 ... ...
地址七 0 101 0 110 1 001 1 110 0 101 0 110 1 001 1 110 ... ...
地址八 0 100 0 111 1 000 1 111 0 100 0 111 1 000 1 111 ... ...

以wr_logic为例模块组成如下

跨时钟域 亚稳态问题:电平同步 带来的延迟

OK方案就此敲定,现在来分析一下最后一个问题,电平同步产生的延迟会不会导致full和empty不是实时响应,导致FIFO满写or空读?

基于打拍导致的延迟,一点点推导证明就可以

empty有效,但FIFO非空

以读时钟域为准,假如说t时刻empty拉高,表示此时rptr_g和电平同步之后wptr_g_d2满足指针对应关系。

rptr_g表示格雷码版本的读指针
wptr_g_d2表示格雷码版本的写指针,且经过读时钟电平同步

由于wptr_g_d2是写逻辑中wptr_g电平同步的结果,所以wptr_g_d2是wptr延迟一段时间的结果。

因此t时刻wptr_g要么与wptr_g_d2相同,要么已经发生移动,即已经写了几个数据入FIFO,即“虚空”

虚空的情况确实会出现,但不会有任何影响。

empty无效,但FIFO已空

以读时钟域为准,假如说t时刻empty无效,表示此时rptr_g和电平同步之后wptr_g_d2不满足指针对应关系,那么wptr_g_d2一定要比rptr_g走的路程长。

即在读时钟域看来,此时写的数据一定比读的数据多

而wptr_g_d2还是wptr延迟一段时间的结果,那么t时刻写逻辑中的wptr一定比wptr_g_d2走的路程更长,那么此时FIFO一定有数据,且比读逻辑中看到的的数据要多。

full有效,但FIFO非满

分析思路与“empty有效,但FIFO非空”相同

以写时钟域为准,假如说t时刻full拉高,表示此时wptr_g和电平同步之后rptr_g_d2满足指针对应关系。

但rptr_g_d2是rptr延迟一段时间的结果,故此时FIFO可能不是满的,这不会有什么影响。

full无效,但FIFO已满

以写时钟域为准,假如说t时刻full无效,表示此时wptr_g和rptr_g_d2不满足指针对应关系,即还没满。

而rptr_g_d2还是rptr延迟一段时间的结果,那么t时刻读逻辑中的rptr一定比rptr_g_d2走的路程更长,那么此时FIFO一定比写逻辑中看到的的数据要少。

跨时钟域 快采慢:无影响

快采慢带来的问题无非就是采样好几个周期嘛,因为读写逻辑中不涉及对读写指针的计数,所以拉高多次没关系。

主要关注的是当前这一拍异步指针到底在哪

跨时钟域 慢采快:无影响

慢采快的问题就是遗漏嘛,但是读写逻辑并不关心你异步指针是怎么走的,所以漏就漏了。

还是那句话,主要关注的是,在当前这一拍,你这个异步指针是多少,我不管你怎么走的。

1.4. 代码

module wr_logic#(parameter ASYNC_FIFO_DEPTH      =  4096,parameter WDATA_WIDTH          =  16,parameter RDATA_WIDTH            =  8,parameter PROG_DEPTH          =  2048,parameter ADDR_WIDTH           =  8)(input                        rstn,input                      wclk,input                      wr_en,input     [WDATA_WIDTH-1:0]   wdata,  input   [ADDR_WIDTH:0]      rptr_g,                     //包含扩展位output                       wr_en_ram,output    [ADDR_WIDTH-1:0]    waddr_ram,                  //不包含扩展位output  [WDATA_WIDTH-1:0]   wdata_ram,output                        full,output                         pfull,output    [ADDR_WIDTH:0]      wptr_g                      //包含扩展位);localparam AVAIL_NUM_FIFO = (WDATA_WIDTH < RDATA_WIDTH)?(PROG_DEPTH/WDATA_WIDTH):(PROG_DEPTH/RDATA_WIDTH);         //FIFO中空余多少个数reg [ADDR_WIDTH:0]     rptr_g_d1;
reg [ADDR_WIDTH:0]  rptr_g_d2;                              //Grey读指针
reg [ADDR_WIDTH:0]  rptr_b;                                 //二进制读指针
reg [ADDR_WIDTH:0]  wptr_b;                                 //二进制写指针
reg [ADDR_WIDTH:0]  wptr_g_r;                               //Grey写指针
reg [ADDR_WIDTH:0]  wptr_g_r_d;
integer i;assign wr_en_ram = wr_en && (!full);
assign wdata_ram = wdata;always@(posedge wclk) beginif(!rstn) beginrptr_g_d1 <= 0;rptr_g_d2 <= 0;endelse beginrptr_g_d1 <= rptr_g;rptr_g_d2 <= rptr_g_d1;end
end//二进制写指针的第一位是扩展位
assign waddr_ram = wptr_b[ADDR_WIDTH-1:0];always@(posedge wclk) beginif(!rstn)wptr_g_r_d <= 0;else wptr_g_r_d <= wptr_g_r;
endassign wptr_g = wptr_g_r_d;//二进制写指针 转换为 Grey写指针
always@(*) beginwptr_g_r[ADDR_WIDTH] = wptr_b[ADDR_WIDTH];if(wptr_g_r[ADDR_WIDTH]) begin                          //扩展位是1,Grey码需要做对称wptr_g_r[ADDR_WIDTH-1:0] = (wptr_b[ADDR_WIDTH-1:0] >> 1) ^ wptr_b[ADDR_WIDTH-1:0];wptr_g_r[ADDR_WIDTH-1:0] = {!wptr_g_r[ADDR_WIDTH-1],wptr_g_r[ADDR_WIDTH-2:0]};endelse                                                    //扩展位是0wptr_g_r[ADDR_WIDTH-1:0] = (wptr_b[ADDR_WIDTH-1:0] >> 1) ^ wptr_b[ADDR_WIDTH-1:0];
end//Grey读指针 转换为 二进制读指针
always@(*) beginfor(i=0;i<=ADDR_WIDTH;i=i+1)rptr_b[i] = ^(rptr_g_d2[ADDR_WIDTH-1:0] >> i);
endgenerate if(WDATA_WIDTH < RDATA_WIDTH) begin                  //写数据位宽小,每posedge wclk写入SDRAM一个地址always@(posedge wclk) beginif(!rstn)wptr_b <= 0;else if(wr_en_ram)wptr_b <= wptr_b + 1;elsewptr_b <= wptr_b;end//二进制写指针和二进制读指针:扩展位不同、其余位相同,full有效assign full = (wptr_b[ADDR_WIDTH]^rptr_b[ADDR_WIDTH]) && (wptr_b[ADDR_WIDTH-1:0] == rptr_b[ADDR_WIDTH-1:0]);
end
else beginlocalparam SHRINK = WDATA_WIDTH/RDATA_WIDTH;         //写数据位宽大,每posedge wclk写入SDRAM SHRINK个地址always@(posedge wclk) beginif(!rstn)wptr_b <= 0;else if(wr_en_ram) wptr_b <= wptr_b + SHRINK;elsewptr_b <= wptr_b;end//二进制写指针和二进制读指针:扩展位不同时差一轮、扩展位相同时位于同一轮//认为FIFO内空余的空间不能写入一个WDATA_WIDTH的数据,就认为满assign full = (wptr_b[ADDR_WIDTH]^rptr_b[ADDR_WIDTH])?((rptr_b[ADDR_WIDTH-1:0] - wptr_b[ADDR_WIDTH-1:0]) < SHRINK):(rptr_b[ADDR_WIDTH-1:0]+({ADDR_WIDTH{1'b1}} - wptr_b[ADDR_WIDTH-1:0] + 1'b1) < SHRINK);    end
endgenerate//二进制写指针和二进制读指针:扩展位不同时差一轮、扩展位相同时位于同一轮
assign pfull = (wptr_b[ADDR_WIDTH]^rptr_b[ADDR_WIDTH])?(wptr_b[ADDR_WIDTH-1:0]+({ADDR_WIDTH{1'b1}} - rptr_b[ADDR_WIDTH-1:0] + 1'b1) >= AVAIL_NUM_FIFO):((wptr_b[ADDR_WIDTH-1:0] - rptr_b[ADDR_WIDTH-1:0]) >= AVAIL_NUM_FIFO);endmodule
module rd_logic#(parameter ASYNC_FIFO_DEPTH      =  4096,parameter WDATA_WIDTH          =  16,parameter RDATA_WIDTH            =  8,parameter PROG_DEPTH          =  2048,parameter ADDR_WIDTH           =  8)(input                        rstn,input                      rclk,input                      rd_en,input     [RDATA_WIDTH-1:0]   rdata_ram,input     [ADDR_WIDTH:0]      wptr_g,                     //包含扩展位output                       rd_en_ram,output    [ADDR_WIDTH-1:0]    raddr_ram,                  //不包含扩展位output  [RDATA_WIDTH-1:0]   rdata,output                        valid,output                        empty,output    [ADDR_WIDTH:0]      rptr_g                      //包含扩展位);localparam SHRINK = RDATA_WIDTH/WDATA_WIDTH;  reg [ADDR_WIDTH:0]  wptr_g_d1;
reg [ADDR_WIDTH:0]  wptr_g_d2;                              //Grey写指针
reg [ADDR_WIDTH:0]  wptr_b;                                 //二进制读指针
reg [ADDR_WIDTH:0]  rptr_b;                                 //二进制读指针
reg [ADDR_WIDTH:0]  rptr_g_r;                               //Grey读指针
reg [ADDR_WIDTH:0]  rptr_g_r_d;
reg                 valid_r;
integer i;assign rd_en_ram = rd_en && (!empty);
assign rdata = rdata_ram;always@(posedge rclk) beginif(!rstn) beginwptr_g_d1 <= 0;wptr_g_d2 <= 0;endelse beginwptr_g_d1 <= wptr_g;wptr_g_d2 <= wptr_g_d1;end
end//二进制读指针的第一位是扩展位
assign raddr_ram = rptr_b[ADDR_WIDTH-1:0];always@(posedge rclk) beginif(!rstn)rptr_g_r_d <= 0;else rptr_g_r_d <= rptr_g_r;
endassign rptr_g = rptr_g_r_d;//二进制读指针 转换为 Grey读指针
always@(*) beginrptr_g_r[ADDR_WIDTH] = rptr_b[ADDR_WIDTH];if(rptr_g_r[ADDR_WIDTH]) begin                          //扩展位是1,Grey码需要做对称rptr_g_r[ADDR_WIDTH-1:0] = (rptr_b[ADDR_WIDTH-1:0] >> 1) ^ rptr_b[ADDR_WIDTH-1:0];rptr_g_r[ADDR_WIDTH-1:0] = {!rptr_g_r[ADDR_WIDTH-1],rptr_g_r[ADDR_WIDTH-2:0]};endelse                                                    //扩展位是0rptr_g_r[ADDR_WIDTH-1:0] = (rptr_b[ADDR_WIDTH-1:0] >> 1) ^ rptr_b[ADDR_WIDTH-1:0];
end//Grey写指针 转换为 二进制写指针
always@(*) beginfor(i=0;i<=ADDR_WIDTH;i=i+1)wptr_b[i] = ^(wptr_g_d2[ADDR_WIDTH-1:0] >> i);
endgenerate
if(RDATA_WIDTH < WDATA_WIDTH)    begin               //读数据位宽小,每posedge rclk读出SDRAM一个地址always@(posedge rclk) beginif(!rstn)rptr_b <= 0;else if(rd_en_ram)rptr_b <= rptr_b + 1;elserptr_b <= rptr_b;endalways@(posedge rclk) beginif(!rstn)valid_r <= 1'b0;else if(rd_en_ram)valid_r <= 1'b1;elsevalid_r <= 1'b0;endassign valid = valid_r;//二进制读指针和二进制写指针:完全相同,empty有效assign empty = (wptr_b[ADDR_WIDTH:0] == rptr_b[ADDR_WIDTH:0]);
end
else begin              //读数据位宽大,每posedge rclk读出SDRAM SHRINK个地址always@(posedge rclk) beginif(!rstn)rptr_b <= 0;else if(rd_en_ram)rptr_b <= rptr_b + SHRINK;elserptr_b <= rptr_b;endalways@(posedge rclk) beginif(!rstn)valid_r <= 1'b0;else if(rd_en_ram)valid_r <= 1'b1;elsevalid_r <= 1'b0;endassign valid = valid_r;//二进制读指针和二进制写指针:扩展位不同时差一轮、扩展位相同时位于同一轮//认为FIFO内空余的空间不能读入一个RDATA_WIDTH的数据,就认为空assign empty = (rptr_b[ADDR_WIDTH]^wptr_b[ADDR_WIDTH])?(wptr_b[ADDR_WIDTH-1:0]+({ADDR_WIDTH{1'b1}} - rptr_b[ADDR_WIDTH-1:0] + 1'b1) < SHRINK):((wptr_b[ADDR_WIDTH-1:0] - rptr_b[ADDR_WIDTH-1:0]) < SHRINK);
end
endgenerateendmodule

2. sdram

前文已经定好了SDRAM的读写地址位宽相同且为二进制码、读写数据位宽不同、每个地址的数据位宽为读写数据位宽中较小的那个。

那么如何实现位宽不同的数据访问呢?

该模块下会有一个寄存器变量作为内存reg [DATA_WIDTH-1:0] mem [(1<<ADDR_WIDTH)-1:0];,这里的ADDR_WIDTH已知了,那么DATA_WIDTH是多少呢?

无非有两个选项,'WDATA_WIDTH和’RDATA_WIDTH,此处选择min('WDATA_WIDTH,'RDATA_WIDTH)。所以低数据位宽访问,直接访问。高数据位宽访问,多地址拼接

例如设置FIFO写数宽16bit,FIFO读数宽8bit,FIFO深度为64bit。
那么RAM写地址应该有64/16=4个,读地址有64/8=8个。此处SDRAM地址位宽选择 读地址个数对应的位宽,为3。
所以FIFO读直接读出对应地址的数据,FIFO写则需要写入两个地址的数据。

2.1. 代码

module sdram#(parameter ASYNC_FIFO_DEPTH         =  4096,parameter WDATA_WIDTH          =  16,parameter RDATA_WIDTH            =  8,parameter PROG_DEPTH          =  2048,parameter ADDR_WIDTH           =  8)(input                     rstn,input                      wclk,input                      wr_en,input [WDATA_WIDTH-1:0]  wdata,input [ADDR_WIDTH-1:0]     waddr,                         input                    rclk,input                      rd_en,input [ADDR_WIDTH-1:0]    raddr,                         output [RDATA_WIDTH-1:0] rdata);reg [RDATA_WIDTH-1:0] rdata_r;
integer i,j;assign rdata = rdata_r;generate if(WDATA_WIDTH < RDATA_WIDTH) begin                                         //写数据位宽小localparam SHRINK = RDATA_WIDTH/WDATA_WIDTH;reg [WDATA_WIDTH-1:0] mem [(1<<ADDR_WIDTH)-1:0];always@(posedge wclk) beginif(wr_en)                    mem[waddr] <= wdata;                                                    //直接写入endalways@(posedge rclk) beginif(rd_en) begin                                                                //读多个地址的数据,凑数据位宽,先高位后低位for(j = 0;j <= SHRINK - 1;j = j + 1) beginrdata_r[RDATA_WIDTH - j*WDATA_WIDTH-1 -:WDATA_WIDTH] <= mem[raddr + j];  mem[raddr + j] <= 0;   endendendalways@(negedge rstn) beginif(!rstn) beginfor(i = 0;i < (1<<ADDR_WIDTH);i = i + 1)mem[i] <= 0;rdata_r <= 0;endendendelse begin                                                                            //读数据位宽小localparam SHRINK = WDATA_WIDTH/RDATA_WIDTH;reg [RDATA_WIDTH-1:0] mem [(1<<ADDR_WIDTH)-1:0];always@(posedge wclk) beginif(wr_en) begin                                                              //写多个地址的数据,凑数据位宽,先高位后低位                           for(j = 0;j <= SHRINK - 1;j = j + 1)mem[waddr + j] <= wdata[WDATA_WIDTH - j*RDATA_WIDTH-1 -:RDATA_WIDTH];           endendalways@(posedge rclk) beginif(rd_en) begin                                                               //直接读出即可rdata_r <= mem[raddr];mem[raddr] <= 0;endendalways@(negedge rstn) beginif(!rstn) beginfor(i = 0;i < (1<<ADDR_WIDTH);i = i + 1)mem[i] <= 0;rdata_r <= 0;endendend
endgenerateendmodule

3. async_fifo

顶层模块将各模块按照设计的架构连接起来即可

module async_fifo#(parameter ASYNC_FIFO_DEPTH        =  4096,parameter WDATA_WIDTH          =  16,parameter RDATA_WIDTH            =  8,parameter PROG_DEPTH          =  2048,parameter ADDR_WIDTH           =  8)(input                        rstn,input                      wclk,input                      wr_en,input [WDATA_WIDTH-1:0]   wdata,input                         rclk,input                      rd_en,output                        full,output                         pfull,output [RDATA_WIDTH-1:0]  rdata,output                        valid,output                        empty);//localparam ADDR_WIDTH = max(sqrt(`ASYNC_FIFO_DEPTH/`WDATA_WIDTH),sqrt(`ASYNC_FIFO_DEPTH/`RDATA_WIDTH));wire [ADDR_WIDTH:0] rptr_g;
wire [ADDR_WIDTH:0] wptr_g;
wire wr_en_ram;
wire rd_en_ram;
wire [ADDR_WIDTH-1:0] waddr_ram;
wire [ADDR_WIDTH-1:0] raddr_ram;
wire [WDATA_WIDTH-1:0] wdata_ram;
wire [RDATA_WIDTH-1:0] rdata_ram;wr_logic #(.ASYNC_FIFO_DEPTH       (ASYNC_FIFO_DEPTH),.WDATA_WIDTH             (WDATA_WIDTH),.RDATA_WIDTH          (RDATA_WIDTH),.PROG_DEPTH           (PROG_DEPTH),.ADDR_WIDTH                (ADDR_WIDTH))u_wr_logic(.rstn                   (rstn),.wclk                    (wclk),.wr_en                   (wr_en),.wdata                  (wdata),    .rptr_g                 (rptr_g),   .wr_en_ram              (wr_en_ram),.waddr_ram              (waddr_ram),                        .wdata_ram              (wdata_ram),.full                   (full),.pfull                   (pfull),.wptr_g                 (wptr_g)                        );rd_logic #(.ASYNC_FIFO_DEPTH      (ASYNC_FIFO_DEPTH),.WDATA_WIDTH             (WDATA_WIDTH),.RDATA_WIDTH          (RDATA_WIDTH),.PROG_DEPTH           (PROG_DEPTH),.ADDR_WIDTH                (ADDR_WIDTH))u_rd_logic(.rstn                   (rstn),.rclk                    (rclk),.rd_en                   (rd_en),.rdata_ram              (rdata_ram),    .wptr_g                 (wptr_g),                       .rd_en_ram              (rd_en_ram),.raddr_ram              (raddr_ram),                    .rdata                  (rdata),.valid                  (valid),.empty                  (empty),.rptr_g                 (rptr_g));sdram #(.ASYNC_FIFO_DEPTH     (ASYNC_FIFO_DEPTH),.WDATA_WIDTH             (WDATA_WIDTH),.RDATA_WIDTH          (RDATA_WIDTH),.PROG_DEPTH           (PROG_DEPTH),.ADDR_WIDTH                (ADDR_WIDTH))u_sdram(.rstn                  (rstn),.wclk                    (wclk),.wr_en                   (wr_en_ram),.wdata                  (wdata_ram),.waddr                  (waddr_ram),                            .rclk                   (rclk),.rd_en                   (rd_en_ram),.raddr                  (raddr_ram),                            .rdata                  (rdata_ram));endmodule

4. async_fifo_tb

这是tb文件。

注意SDRAM地址宽度要根据数据位宽小的算,即一定要满足

ADDR_WIDTH = max(sqrt(ASYNC_FIFO_DEPTH/WDATA_WIDTH),sqrt(ASYNC_FIFO_DEPTH/RDATA_WIDTH))

`timescale 1ns/1ps`include "../rtl/param_def.v"module async_fifo_tb();logic wclk;
logic rclk;
logic rstn;
logic wr_en;
logic rd_en;
logic [`WDATA_WIDTH-1:0] wdata;
logic [`RDATA_WIDTH-1:0] rdata;
logic valid;
logic full;
logic pfull;
logic empty;initial beginwclk = 0;forever #5 wclk = !wclk;                    //100MHZ
endinitial beginrclk = 0;forever #10 rclk = !rclk;                    //50MHZ
endinitial beginrstn = 1;#50 rstn = 0;#100 rstn = 1;
endinitial beginwr_en = 0;rd_en = 0;wdata = 0;fifo_write_read_test();
endtask fifo_write_read_test();#200;fifo_wr_sequence();fifo_rd_sequence();
endtasktask fifo_wr_sequence();automatic bit [`WDATA_WIDTH-1:0] wr_data = 0;repeat(2048) begin@(posedge wclk);wr_data = $urandom_range(0,0-1);#1;if(!full) beginwr_en = 1;wdata = wr_data;endelsewr_en = 0;endwr_en = 0;
endtasktask fifo_rd_sequence();automatic bit [`RDATA_WIDTH-1:0] rd_data = 0;repeat(2048) begin@(posedge rclk);#1;if(!empty) beginrd_en = 1;rd_data = rdata;endelserd_en = 0;endrd_en = 0;
endtaskasync_fifo#(.ASYNC_FIFO_DEPTH        (`ASYNC_FIFO_DEPTH),.WDATA_WIDTH           (`WDATA_WIDTH),.RDATA_WIDTH            (`RDATA_WIDTH),.PROG_DEPTH             (`PROG_DEPTH),.ADDR_WIDTH              (`ADDR_WIDTH)                      //保证ADDR_WIDTH = max(sqrt(`ASYNC_FIFO_DEPTH/`WDATA_WIDTH),sqrt(`ASYNC_FIFO_DEPTH/`RDATA_WIDTH));)u_async_fifo(.rstn        (rstn),.wclk        (wclk),.wr_en       (wr_en),.wdata      (wdata),.rclk       (rclk),.rd_en       (rd_en),.full       (full),.pfull       (pfull),.rdata      (rdata),.valid      (valid),.empty      (empty));endmodule

5. param_def

`define ASYNC_FIFO_DEPTH    4096
`define WDATA_WIDTH        16
`define RDATA_WIDTH        4
`define PROG_DEPTH             2048
`define ADDR_WIDTH         10

异步FIFO逻辑设计部分相关推荐

  1. 异步fifo的设计(FPGA)

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) fifo位宽(每个 ...

  2. 异步FIFO的设计详解(格雷码计数+两级DFF同步)

    文章目录 一.异步FIFO介绍 1.1.空满判断 1.2.跨时钟域问题 1.3.格雷码转换 1.4.格雷码计数器 二.代码code 一.异步FIFO介绍   FIFO有同步和异步两种,同步即读写时钟相 ...

  3. 74ls390设计任意进制计数器_异步FIFO:设计原理及Verliog源码

    1.  异步FIFO的概念 异步FIFO为读取与写入采用不同的时钟,使用异步FIFO用于在不同的时钟域传输数据,主要用于跨时钟域传输多bit数据. 2.  异步FIFO的设计难点 同步异步信号,避免亚 ...

  4. (87)FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计

    1.1 FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-同步FIFO与异步FIFO区 ...

  5. 异步fifo的设计与验证

    书接上文,上一篇介绍了略简单的同步fifo,接下来开始较为复杂的异步fifo. 1.同步fifo与异步fifo的区别 当设计中只有一个时钟时,所有的寄存器否用同一个,不会出现传输速度不匹配的情况:但是 ...

  6. 同步FIFO + 异步FIFO 【设计详解及代码分享】

    FIFO表示先入先出,是一种存储结构.可满足一下需求: 1.当输入数据速率和输出速率不匹配时.可作为临时存储单元. 2.用于不同时钟域之间的同步. 3.输入数据路径和输出数据路径之间的数据宽度不匹配时 ...

  7. 从蓄水池问题思考异步FIFO深度设计

    文章目录 前言 蓄水池问题 情形一:进水口,出水口一直工作 情形二:出水口一直工作 FIFO深度问题 举例 前言 前段时间面试,因为没考虑过FIFO深度的问题,被面试官吊打一番,痛定思痛,写下这些. ...

  8. 异步fifo设计及验证verilog代码

    论文参考:The Design and Verification of a Synchronous First-In First-Out 博客参考:[原创]异步FIFO设计原理详解 (含RTL代码和T ...

  9. FPGA基础知识极简教程(4)从FIFO设计讲起之异步FIFO篇

    博文目录 写在前面 正文 同步FIFO回顾 $clog2()系统函数使用 综合属性控制资源使用 异步FIFO设计 FIFO用途回顾 异步FIFO原理回顾 异步FIFO设计 异步FIFO仿真 参考资料 ...

最新文章

  1. LeetCode-106-Construct Binary Tree from Inorder and Postorder Traversal
  2. HDU 3068 最长回文
  3. 野火stm32呼吸灯程序_说一说STM32启动过程
  4. pppoe拨号的外网ip无法ping通_【思唯网络学院】 五大网络概念:IP地址、子网掩码、网关、DHCP服务和PPPoE拨号...
  5. (软件工程复习核心重点)第六章实现和测试-第八节:调试
  6. CSS学习笔记--CSS语法与选择器
  7. 吴裕雄--天生自然 高等数学学习:微分方程的幂级数解法
  8. 吃一堑长一智,作为程序员的我们记住这几点,2021年Android开发实战
  9. modbus协议的常用测试工具
  10. 力扣刷题 DAY_67 回溯
  11. 【附源码】Python计算机毕业设计企业员工考勤管理系统
  12. 【DASCTF2023】Misc mp3
  13. HTML怎么跟随页面缩放,如何让网页跟着 浏览器全比例缩小(示例代码)
  14. 无限循环小数转化分数
  15. 当年也是翩翩少年,如今落得秃顶大叔,程序员秃顶算工伤吗?
  16. 驼峰命名法(camelCase)
  17. 艾宾浩斯记忆曲线(遗忘曲线)
  18. javascript jc脚本语言
  19. [轻笔记]蛙跳积分法
  20. php $this-%3euid,码 | 保利威帮助中心 - Part 3

热门文章

  1. 计算机用户名怎么改好听,电脑维修店名字好听易记
  2. python导入本地模块报错之终极解决方案
  3. 【给自己看的笔记,随便写写】如何去调整游戏数值(新手为例)
  4. 思必驰DUI集成指南
  5. UE4 3DUI显示与交互案例
  6. 从书法中窥测字体的演变
  7. 跟着我从零开始入门FPGA(一周入门系列)第二天
  8. 使用python在雅虎财经提取数据的创捷有效边界
  9. 超级App成为Gartner预测的战略技术,软件降本增效是否能进一步提速?
  10. ZOJ 3716 Ribbon Gymnastics 解题报告