【verilog】 异步FIFO设计(格雷码转换,跨时钟域)

功能图

在这里插入图片描述

空满判别

对FIFO的空满判断采用地址位扩展的方法
waddr=raddr,fifo为空
waddr[最高bit]≠raddr[最高bit],waddr[次高bit:0]=raddr[次高bit:0],fifo为满

若一个异步FIFO深度为8,则所需地址线宽3bit
将其地址位扩展为4bit,最高位用于辅助空满计算
在最初时,waddr和raddr都为b0000,此时waddr=raddr,fifo为空
下面对其进行读写操作:
进行8次写后:waddr = b1000 raddr = b0000
             此时waddr[4]≠raddr[4],waddr[3:0]=raddr[3:0],fifo为满
进行8次读后:waddr = b1000 raddr = b1000
             此时waddr = raddr fifo为空
进行8次写后:waddr = b0000 raddr = b1000
             此时waddr[4]≠raddr[4],waddr[3:0]=raddr[3:0],fifo为满
进行8次读后:waddr = b0000 raddr = b0000
             此时waddr = raddr fifo为空
进行3次写后:waddr = b0011 raddr = b0000
进行1次读后:waddr = b0011 raddr = b0001
进行1次写后:waddr = b0100 raddr = b0001
进行3次读后:waddr = b0100 raddr = b0100 waddr = raddr fifo空

可以知道,fifo的读写是转圈的,连续地址写,也连续地址读,不会因为你把前面的地址读完了,
我的写就回来重新写。

顶层


module asyn_fifo #(
   parameter DEPTH   = 256,       
   parameter WIDTH_A = 8,       
   parameter WIDTH_D = 16       
)(                                 
   input                     w_clk,
   input                     rst_n,
   input                     w_req,
   input [WIDTH_D-1:0]       w_data,
                     
   input                     r_clk, 
   input                     r_req, 
                     
   output [WIDTH_D-1:0]      r_data,
   
   output                    w_full ,  
   output                    r_empty 
   
);

wire [WIDTH_A:0]  w_addr;
wire [WIDTH_A:0]  r_gaddr_syn;
wire [WIDTH_A:0]  r_addr;
wire [WIDTH_A:0]  w_gaddr;
wire [WIDTH_A:0]  r_gaddr;
wire [WIDTH_A:0]  w_gaddr_syn;

write_part #(
   .WIDTH_A(WIDTH_A)     
)write_control(            
  .w_clk  ( w_clk   ),     
  .w_rst  ( rst_n   ),     
  .w_req  ( w_req   ),     
                   
  .r_gaddr( r_gaddr_syn ),   
                   
  .w_full ( w_full  ),   
  .w_addr ( w_addr  ),    
                   
  .w_gaddr( w_gaddr )    
);                     

RAM #(                                 
   .DEPTH  (DEPTH  ),                     
   .WIDTH_A(WIDTH_A),                     
   .WIDTH_D(WIDTH_D)                                                                  
)
RAM_inst
(                                            
    .r_clk ( r_clk  ) , 
    .w_clk ( w_clk  ) ,             
    .rst_n ( rst_n  ) ,             
                   
    .w_addr( w_addr[WIDTH_A-1:0] ) ,   //!!ram的读写地址需要丢弃掉扩展位         
    .w_data( w_data ) ,     
    .w_en  ( w_req & (!w_full)   ) ,              
                   
    .r_addr( r_addr[WIDTH_A-1:0] ) ,            
    .r_en  ( r_req & (!r_empty)   ) ,              
                   
    .r_data( r_data )      
);                                            
    

syn #(                          
  .WIDTH_D(WIDTH_A)                
)
syn_w_2_r                                      
(                                      
    .syn_clk ( r_clk ) ,  
    .syn_rst ( rst_n ) ,  
    .data_in ( w_gaddr ) ,  
             
    .syn_data( w_gaddr_syn )  
);        

syn #(                     
  .WIDTH_D(WIDTH_A)                                          
)                          
syn_r_2_w                  
(                          
   .syn_clk ( w_clk ) ,    
   .syn_rst ( rst_n ) ,    
   .data_in ( r_gaddr ),  
                           
   .syn_data( r_gaddr_syn )  
);     

                    
read_part  #(
  .WIDTH_A(WIDTH_A)   
)read_control(                               
  .r_clk   ( r_clk   ),              
  .r_rst   ( rst_n   ),              
  .r_req   ( r_req   ),              
                              
  .w_gaddr ( w_gaddr_syn ),            
                            
  .r_empty ( r_empty ),           
  .r_addr  ( r_addr  ),            
                             
  .r_gaddr ( r_gaddr )            
);                      


endmodule                   

子模块

/*
 * 模块1:二进制码转格雷码模块
 * 模块2:双端口RAM模块
 * 模块3:数据(格雷码)同步模块 
 * 模块4:读地址控制器
 * 模块5:写地址控制器
 */


/*模块:二进制码->转->格雷码*/
module bin_to_gray #(
  parameter  WIDTH_D = 5
)(
   input  [WIDTH_D-1:0] bin_c,
   output [WIDTH_D-1:0] gray_c
);                 


/*---------产生格雷码的最高位---------*/
wire gray_msb;
assign gray_msb = bin_c[WIDTH_D-1]; //保留二进制码的最高位作为格雷码的最高位  


/*---------产生格雷码的其他位---------*/
//除最高位,格雷码的其他位均服从:
//格雷码的第n位 = 二进制码的第n+1位 异或 二进制码的第n位
reg [WIDTH_D-2:0] gray_others_bit;
integer i;     
always @( * )begin
  for(i=0;i<WIDTH_D-1;i=i+1)//为了代码的通用性,这里确实是用for产生组合逻辑比较好·
    gray_others_bit[i] = bin_c[i]^bin_c[i+1]; 
    //等价于	  
    // assign gray_others_bit[0] = bin_c[0]^bin_c[1]; 
    // assign gray_others_bit[1] = bin_c[1]^bin_c[2]; 
    // assign gray_others_bit[2] = bin_c[2]^bin_c[3]; 
    // assign gray_others_bit[3] = bin_c[3]^bin_c[4]; 
    // assign gray_others_bit[4] = bin_c[4]^bin_c[5]; 
end
    
assign gray_c = {gray_msb,gray_others_bit};


endmodule



/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/



/*模块:双口RAM*/
module RAM #(
   parameter DEPTH = 256,
   parameter WIDTH_A = 8,
   parameter WIDTH_D = 16

)(
  input                    r_clk,
  input                    w_clk,
  input                    rst_n,
                           
  input [WIDTH_A-1:0]      w_addr,   
  input [WIDTH_D-1:0]      w_data,
  input                    w_en,
  
  input [WIDTH_A-1:0]      r_addr,
  input                    r_en,
  
  output reg[WIDTH_D-1:0]  r_data
);


reg [15:0]  mem[0:DEPTH-1];

integer i;
always @( posedge w_clk )
  if( !rst_n )
    for(i=0;i<DEPTH;i=i+1)  //同步复位时,将mem里全刷上0
      mem[i] <= 'h0000;     //
  else if( w_en )
    mem[w_addr] <= w_data;
    
always @( posedge r_clk )
  if( !rst_n )
    r_data <= 'h0000;
  else if( r_en )
    r_data <= mem[r_addr];
    
endmodule  



/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/



/*模块:同步模块,将输入的并行数据,打两拍到指定时钟域*/
module syn #(
  parameter WIDTH_D = 5
)
(
    input                    syn_clk,
    input                    syn_rst,
    input [WIDTH_D:0]      data_in,
    
    output [WIDTH_D:0]     syn_data
);

reg [WIDTH_D:0] syn_reg_1,syn_reg_2;

always @( posedge syn_clk )
  if( !syn_rst ) begin
  	syn_reg_1 <= 'h00;
  	syn_reg_2 <= 'h00;
  end
  else begin
    syn_reg_1 <= data_in;
    syn_reg_2 <= syn_reg_1; //普通的数据可不敢这样同步,得全是亚稳态,连续的格雷码这么做才能
  end			    //相对稳定一些
  
assign syn_data = syn_reg_2;

endmodule





/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/




//读地址控制器
//功能1:产生读地址的格雷码和二进制码
//       格雷码将在顶层中送给写控制器
//       二进制码将在顶层中送给RAM
//功能2:将产生的格雷码读地址,与输入进来的已经同步到读时钟域
//       的写时钟域发来的格雷码写地址相对比产生empty信号
//
//注意:在此module中,无论是input的地址,还是output的地址
//      都含有1bit的扩展位
module read_part #(
   parameter WIDTH_A = 8 //读地址[8:0],最高位[8]为扩展位,其他位[7:0]为真实地址位 
)(
      input            r_clk,          
      input            r_rst,          
      input            r_req,         //读请求
                                              
      input      [WIDTH_A:0] w_gaddr, //输入的是同步到读时钟域之后的
				      //写时钟域发过来的写地址的格雷码      
                                       
      output           	      r_empty,//空指示信号  
      output reg [WIDTH_A:0]  r_addr, //此控制器产生的二进制读地址
      output reg [WIDTH_A:0]  r_gaddr //经由此控制器产生的格雷码读地址(调用了格雷码转换模块)
);

/*有读请求且不为空,地址向前走*/
always @( posedge r_clk )                                                          
  if( !r_rst )                                                                     
    r_addr <= 'h00;                                                                
  else if( r_req&&(!r_empty) )                                                      
    r_addr <= r_addr + 1'b1;                                                       
       
/*将生成的地址转码成格雷码*/
wire [WIDTH_A:0] r_gaddr_w;									    
bin_to_gray #(                                                                     
  .WIDTH_D(WIDTH_A+1)                                                                      
)                                                                                  
bin_to_gray_inst                                                                   
(                                                                                  
   .bin_c ( r_addr ),                                                              
   .gray_c( r_gaddr_w )                                                              
); 

/*复位格雷码*/
always @( posedge r_clk )
  if( !r_rst )
    r_gaddr <= 'h0;
  else
    r_gaddr <= r_gaddr_w;	  

/*生成empty指示信号*/                                                                           
assign r_empty = (w_gaddr==r_gaddr_w)?1'b1:1'b0;       

endmodule



/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------*/



//写地址控制模块,与读地址控制模块大同小异
module write_part #(
  parameter WIDTH_A = 8 
)(
  input            w_clk,
  input            w_rst,
  input            w_req,
             
  input      [WIDTH_A:0] r_gaddr,
             
  output           w_full ,
  output reg[WIDTH_A:0]  w_addr ,
  
  output reg [WIDTH_A:0]  w_gaddr       
);                                   

//reg [4:0] w_addr;
always @( posedge w_clk )
  if( !w_rst )
    w_addr <= 'h00;
  else if( w_req&&(!w_full) )
    w_addr <= w_addr + 1'b1;

wire [WIDTH_A:0] w_gaddr_w;
bin_to_gray #(             
  .WIDTH_D(WIDTH_A+1)              
)
bin_to_gray_inst
(                                
   .bin_c ( w_addr ),    
   .gray_c( w_gaddr_w )    
);        

always @( posedge w_clk )
  if( !w_rst )
    w_gaddr <= 'h0;
  else
    w_gaddr <= w_gaddr_w;	  



assign w_full = ({~w_gaddr_w[WIDTH_A],~w_gaddr_w[WIDTH_A-1],w_gaddr_w[WIDTH_A-2:0]}==r_gaddr)?1'b1:1'b0;

endmodule                  


测试

`timescale 1ns/1ps

module tb;

logic         w_clk ;
logic         w_req ;
logic [7:0]   w_data;
      
logic         r_clk; 
logic         rst_n; 
logic         r_req; 

logic [7:0]   r_data;
logic         w_full;
logic         r_empty;

always #10 w_clk = ~w_clk;
always #20 r_clk = ~r_clk; //读的慢写的快

initial begin
  w_req = 0;
  w_data = 0;
  r_req = 0;
  w_clk = 0;
  r_clk = 0;
  rst_n = 0;
  #30; 
  rst_n = 1;
  #20;
  r_req = 1;
  w_req = 1;
  forever begin
    @( posedge w_clk )
    if( !w_full )
    w_data = w_data + 'd5;
  end     
end

asyn_fifo #(
  .DEPTH   (256),
  .WIDTH_A (8  ),  
  .WIDTH_D (8 ) 
)
asyn_fifo_inst(             
     .w_clk  ( w_clk   ),   
     .rst_n  ( rst_n   ),   
     .w_req  ( w_req   ),   
     .w_data ( w_data  ),  
                      
     .r_clk  ( r_clk   ),   
     .r_req  ( r_req   ),  
                      
     .r_data ( r_data  ),  
                      
     .w_full ( w_full  ), 
     .r_empty( r_empty )                                       
);   

endmodule                         

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞IC的那些年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值