异步fifo要求用verilog编写

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步fifo要求用verilog编写相关的知识,希望对你有一定的参考价值。

请设计一个异步FIFO,完成数据平滑功能,FIFO的深度为256,宽度为8位,实时给出读空和溢出指示,写时钟为带间隔的100MHz,读时钟为5MHz。
不要网上COPY的,要自己写的。 发我邮箱,wg_135721@qq.com 谢谢
注意是异步FIFO

module FIFO(Wr_Clk,//write FIFO clock
nWr, //write FIFO signal
Din, //write FIFO data
Rd_Clk,//read FIFO clock
nRd, //read FIFO signal
Dout, //read FIFO data

Full, // 1 = FIFO full
Empty);// 1 = FIFO empty

input Wr_Clk, nWr, Rd_Clk, nRd;
input [Bsize-1:0] Din;
output [Bsize-1:0] Dout;
output Full, Empty;

reg Full, Empty;
reg [Bsize-1:0] Buff [Dsize-1:0];
reg [Asize:0] Wr_Addr_Bin, Rd_Addr_Bin;
reg [Asize:0] Sync_Wr_Addr0_Gray, Sync_Wr_Addr1_Gray, Sync_Wr_Addr2_Gray;
reg [Asize:0] Sync_Rd_Addr0_Gray, Sync_Rd_Addr1_Gray, Sync_Rd_Addr2_Gray;

wire [Asize-1:0] FIFO_Entry_Addr, FIFO_Exit_Addr;
wire [Asize:0] Wr_NextAddr_Bin, Rd_NextAddr_Bin;
wire [Asize:0] Wr_NextAddr_Gray, Rd_NextAddr_Gray;
wire Asyn_Full, Asyn_Empty;

parameter
Dsize = 256, Asize = 8,
Bsize = 8;

initial
begin
Full = 0;
Empty = 1;

Wr_Addr_Bin = 0;
Rd_Addr_Bin = 0;

Sync_Wr_Addr0_Gray = 0;
Sync_Wr_Addr1_Gray = 0;
Sync_Wr_Addr2_Gray = 0;
Sync_Rd_Addr0_Gray = 0;
Sync_Rd_Addr1_Gray = 0;
Sync_Rd_Addr2_Gray = 0;
end
////////////////////FIFO数据的写入与输出//////////////////////////////////////
assign FIFO_Exit_Addr = Rd_Addr_Bin[Asize-1:0];
assign FIFO_Entry_Addr = Wr_Addr_Bin[Asize-1:0];

assign Dout = Buff[FIFO_Exit_Addr];
always @ (posedge Wr_Clk)
begin
if (~nWr & ~Full) Buff[FIFO_Entry_Addr] <= Din;
else Buff[FIFO_Entry_Addr] <= Buff[FIFO_Entry_Addr];
end
///////////////////FIFO读写的地址生成器///////////////////////////////////////
assign Wr_NextAddr_Bin = (~nWr&~Full) ?Wr_Addr_Bin[Asize:0]+1:Wr_Addr_Bin[Asize:0];
assign Rd_NextAddr_Bin = (~nRd&~Empty)?Rd_Addr_Bin[Asize:0]+1:Rd_Addr_Bin[Asize:0];

assign Wr_NextAddr_Gray = (Wr_NextAddr_Bin >> 1) ^ Wr_NextAddr_Bin;
assign Rd_NextAddr_Gray = (Rd_NextAddr_Bin >> 1) ^ Rd_NextAddr_Bin;

always @ (posedge Wr_Clk)
begin
Wr_Addr_Bin <= Wr_NextAddr_Bin;
Sync_Wr_Addr0_Gray <= Wr_NextAddr_Gray;
end

always @ (posedge Rd_Clk)
begin
Rd_Addr_Bin <= Rd_NextAddr_Bin;
Sync_Rd_Addr0_Gray <= Rd_NextAddr_Gray;
end
///////////////////采用双锁存器把异步信号同步起来/////////////////////////////
always @ (posedge Wr_Clk)
begin
Sync_Rd_Addr2_Gray <= Sync_Rd_Addr1_Gray;//读信号同步到写时钟
Sync_Rd_Addr1_Gray <= Sync_Rd_Addr0_Gray;
end

always @ (posedge Rd_Clk)
begin
Sync_Wr_Addr2_Gray <= Sync_Wr_Addr1_Gray;//写信号同步到读时钟
Sync_Wr_Addr1_Gray <= Sync_Wr_Addr0_Gray;
end
/////////////////将产生的Full信号和Empty信号同步的各自的时钟域上//////////////
assign Asyn_Empty = (Rd_NextAddr_Gray==Sync_Wr_Addr2_Gray);
assign Asyn_Full = (Wr_NextAddr_Gray==~Sync_Rd_Addr2_Gray[Asize:Asize-1],
Sync_Rd_Addr2_Gray[Asize-2:0]);

always @ (posedge Wr_Clk)
begin
Full <= Asyn_Full;
end

always @ (posedge Rd_Clk)
begin
Empty <= Asyn_Empty;
end
//////////////////////////////////////////////////////////////////////////////
endmodule
参考技术A `timescale 1ns / 100ps

`define SC_FIFO_ASYNC_RESET // Uncomment for Syncr. reset

module generic_fifo_sc (clk, rst, clr, din, we, dout, re,
full, empty, full_r, empty_r,
full_n, empty_n, full_n_r, empty_n_r,
level);

parameter dw=8;
parameter aw=8;
parameter n=32;
parameter max_size = 1<<aw;

input clk, rst, clr;
input [dw-1:0] din;
input we;
output [dw-1:0] dout;
input re;
output full, full_r;
output empty, empty_r;
output full_n, full_n_r;
output empty_n, empty_n_r;
output [1:0] level;

// Local Wires

reg [aw:0] wp;
wire [aw:0] wp_pl1;
reg [aw:0] rp;
wire [aw:0] rp_pl1;
reg full_r;
reg empty_r;
wire [aw:0] diff;
reg [aw:0] diff_r;
reg re_r, we_r;
wire full_n, empty_n;
reg full_n_r, empty_n_r;
reg [1:0] level;

// Memory Block

generic_dpram #(aw,dw) u0(
.rclk( clk ),
.rrst( !rst ),
.rce( 1'b1 ),
.oe( 1'b1 ),
.raddr( rp[aw-1:0] ),
.do( dout ),
.wclk( clk ),
.wrst( !rst ),
.wce( 1'b1 ),
.we( we ),
.waddr( wp[aw-1:0] ),
.di( din )
);

// Misc Logic

always @(posedge clk `SC_FIFO_ASYNC_RESET)
if(!rst) wp <= #1 aw+11'b0;
else
if(clr) wp <= #1 aw+11'b0;
else
if(we) wp <= #1 wp_pl1;

assign wp_pl1 = wp + aw1'b0, 1'b1;

always @(posedge clk `SC_FIFO_ASYNC_RESET)
if(!rst) rp <= #1 aw+11'b0;
else
if(clr) rp <= #1 aw+11'b0;
else
if(re) rp <= #1 rp_pl1;

assign rp_pl1 = rp + aw1'b0, 1'b1;

// Combinatorial Full & Empty Flags

assign empty = (wp == rp);
assign full = (wp[aw-1:0] == rp[aw-1:0]) & (wp[aw] != rp[aw]);

// Registered Full & Empty Flags

always @(posedge clk)
empty_r <= #1 (wp == rp) | (re & (wp == rp_pl1));

always @(posedge clk)
full_r <= #1 ((wp[aw-1:0] == rp[aw-1:0]) & (wp[aw] != rp[aw])) |
(we & (wp_pl1[aw-1:0] == rp[aw-1:0]) & (wp_pl1[aw] != rp[aw]));

// Combinatorial Full_n & Empty_n Flags

assign diff = wp-rp;
assign empty_n = diff < n;
assign full_n = !(diff < (max_size-n+1));

always @(posedge clk)
level <= #1 2diff[aw] | diff[aw-1:aw-2];

// Registered Full_n & Empty_n Flags

always @(posedge clk)
re_r <= #1 re;

always @(posedge clk)
diff_r <= #1 diff;

always @(posedge clk)
empty_n_r <= #1 (diff_r < n) | ((diff_r==n) & (re | re_r));

always @(posedge clk)
we_r <= #1 we;

always @(posedge clk)
full_n_r <= #1 (diff_r > max_size-n) | ((diff_r==max_size-n) & (we | we_r));

// Sanity Check

always @(posedge clk)
if(we & full)
$display("%m WARNING: Writing while fifo is FULL (%t)",$time);

always @(posedge clk)
if(re & empty)
$display("%m WARNING: Reading while fifo is EMPTY (%t)",$time);

endmodule
参考技术B 语法看夏宇文的书,设计fifo看verilog HDL 高级数字设计888页~

异步fifo的Verilog实现

 一、分析

由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
  跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较
  解决方法加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
 
1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接崩溃。

 

2.将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

那么,多位二进制码如何转化为格雷码?

 

 

 

 

 

 

 

 

 

 

 

换一种描述方法:

verilog代码实现就一句:assign  gray_code = (bin_code>>1)  ^  bin_code;

 使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。 

这里直接给出结论:

  判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针rd2_wp每一位完全相同;

  判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针wr2_rp高两位不相同,其余各位完全相同;

 

 

二、Verilog实现

module  fifo
#(
  parameter WSIZE = 8;
  parameter DSIZE = 32;
)
(
  input wr_clk,
  input rst,
  input wr_en,
  input [WSIZE-1 : 0]din,
  input rd_clk,
  input rd_en,
  output [WSIZE-1 : 0]dout,
  output reg rempty,
  output reg wfull
);

//定义变量
reg [WSIZE-1 :0] mem [DSIZE-1 : 0];
reg [WSIZE-1 : 0] waddr,raddr;
reg [WSIZE : 0] wbin,rbin,wbin_next,rbin_next;
reg [WSIZE : 0] wgray_next,rgray_next;
reg [WSIZE : 0] wp,rp;
reg [WSIZE : 0] wr1_rp,wr2_rp,rd1_wp,rd2_wp;
wire rempty_val,wfull_val;

//输出数据
assign dout = mem[raddr];

//输入数据
always@(posedge wr_clk)
  if(wr_en && !wfull)
    mem[waddr] <= din;

//1.产生存储实体的读地址raddr; 2.将普通二进制转化为格雷码,并赋给读指针rp
always@(posedge rd_clk or negedge rst_n)
  if(!rst_n)
    {rbin,rp} <= 0;
  else
    {rbin,rp} <= {rbin_next,rgray_next};

assign raddr = rbin[WSIZE-1 : 0];
assign rbin_next = rbin + (rd_en & ~rempty);
assign rgray_next = rbin_next ^ (rbin_next >> 1);

//1.产生存储实体的写地址waddr; 2.将普通二进制转化为格雷码,并赋给写指针wp
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    {wbin,wp} <= 0;
  else
    {wbin,wp} <= {wbin_next,wgray_next};

assign waddr = wbin[WSIZE-1 : 0];
assign wbin_next = wbin + (wr_en & ~wfull);
assign wgray_next = wbin_next ^ (wbin_next >> 1);

//将读指针rp同步到写时钟域
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    {wr2_rp,wr1_rp} <= 0;
  else
    {wr2_rp,wr1_rp} <= {wr1_rp,rp};

//将写指针wp同步到读时钟域
always@(posedge rd_clk or negedge rst_n)
  if(!rst_n)
    {rd2_wp,rd1_wp} <= 0;
  else
    {rd2_wp,rd1_wp} <= {rd1_wp,wp};

//产生读空信号rempty
assign rempty_val = (rd2_wp == rgray_next);
always@(posedge rd_clk or negedge rst_n)
  if(rst_n)
    rempty <= 1\'b1;
  else
    rempty <= rempty_val;

//产生写满信号wfull
assign wfull_val = ((~(wr2_rp[WSIZE : WSIZE-1]),wr2_rp[WSIZE-2 : 0]) == wgray_next);
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    wfull <= 1\'b0;
  else
    wfull <= wfull_val;

endmodule

 

以上是关于异步fifo要求用verilog编写的主要内容,如果未能解决你的问题,请参考以下文章

同步FIFO与异步FIFO的Verilog实现

异步FIFO的Verilog实现

异步FIFO的Verilog实现

异步fifo的Verilog实现

调试成功的简单异步FIFO--verilog实现+testbench

Verilog设计之异步fifo设计