功能图
空满判别
对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