由于平台已经有很多介绍关于异步FIFO的文章,在这里我就不说了,我直接上模块介绍和模块代码和测试代码。
感谢老师提供的课程,课程链接异步电路
对于异步FIFO,其结构图如下
其中,ASYN_FIFO_CTRL模块内部模块如下
对于我的代码,在ASYN_FIFO_CTRL模块中,我将计数器转成格雷码同步到别的时钟后没有再转到二进制,格雷码也可以比较输出空满信号,此计数器的位宽比地址位宽多一位,很多文章讲过,懂的都懂!对于空信号,就是判断读写计数器是否一样,而满信号在格雷码下则是,
assign full = ({最高位取反,次高位取反,剩下位} == 写计数器) ? 1‘b1 : 1’b0;
整个框图如下
对于二进制转格雷码,格雷码转二进制码,如下
接下来就是上代码
TOP代码
module TOP #(
parameter AW = 4,
parameter DW = 4,
parameter DATA_DEEPTH = 16
)(
input wr_clk ,//写时钟
input wr_rst_n ,//写时钟复位
input wr_en ,//写使能,高有效
input [DW-1:0] wr_data ,
output wr_full ,
input rd_clk ,
input rd_rst_n ,
input rd_en ,
output [DW-1:0] rd_data ,
output rd_empty
);
wire wr_en_mem;
wire rd_en_mem;
wire [AW-1:0] wr_addr;
wire [AW-1:0] rd_addr;
ASYN_FIFO_CTRL #(
.AW(AW),
.DW(AW),
.DATA_DEEPTH(DATA_DEEPTH)
)
ASYN_FIFO_CTRL_inst(
.wr_clk (wr_clk ),//写时钟
.wr_rst_n (wr_rst_n),//写时钟复位
.wr_en (wr_en ),//写使能,高有效
.wr_en_mem (wr_en_mem),//送给ram
.wr_addr (wr_addr),
.wr_full (wr_full),
.rd_clk (rd_clk),
.rd_rst_n (rd_rst_n),
.rd_en (rd_en),
.rd_en_mem (rd_en_mem),//送给ram
.rd_addr (rd_addr),
.rd_empty (rd_empty)
);
RAM #(
.AW (AW ),//地址位宽
.DW (DW ),//数据位宽
.DATA_DEEPTH(DATA_DEEPTH)//数据深度
)
RAM_inst(
.clk_w (wr_clk),
.clk_w_en (wr_en_mem),
.w_addr (wr_addr),
.w_data (wr_data),
.clk_r (rd_clk),
.clk_r_en (rd_en_mem),
.r_addr (rd_addr),
.r_data (rd_data)
);
endmodule
ASYN_FIFO_CTRL代码
module ASYN_FIFO_CTRL #(
parameter AW = 4,//地址位宽
parameter DW = 4,//数据位宽
parameter DATA_DEEPTH = 16//数据深度
)(
input wr_clk ,//写时钟
input wr_rst_n ,//写时钟复位
input wr_en ,//写使能,高有效
output wr_en_mem ,//送给ram
output [AW-1:0] wr_addr ,
output wr_full ,
input rd_clk ,
input rd_rst_n ,
input rd_en ,
output rd_en_mem ,//送给ram
output [AW-1:0] rd_addr ,
output rd_empty
);
reg [AW:0] rd_cnt;
wire [AW:0] rd_cnt_gray;
reg [AW:0] wr_cnt;
wire [AW:0] wr_cnt_gray;
reg [AW:0] rd_cnt_gray_r1_r2w;
reg [AW:0] rd_cnt_gray_r2_r2w;//同步到写时钟域 打2拍
reg [AW:0] wr_cnt_gray_r1_w2r;
reg [AW:0] wr_cnt_gray_r2_w2r;//同步到读时钟 打2拍
//读计数器,读地址产生
always @(posedge rd_clk or negedge rd_rst_n)
if(rd_rst_n == 1'b0)begin
rd_cnt <= 'b0;
end
else if(rd_en && (~rd_empty))begin
rd_cnt <= rd_cnt + 1'b1;
end
assign rd_addr = rd_cnt[AW-1:0];
//读计数器二进制转格雷码
assign rd_cnt_gray = rd_cnt[AW:0] ^ {1'b0,rd_cnt[AW:1]};
//读计数器的格雷码同步到写时钟
always @(posedge wr_clk or negedge wr_rst_n)
if(wr_rst_n == 1'b0)begin
{rd_cnt_gray_r2_r2w,rd_cnt_gray_r1_r2w} <= 'b0;
end
else begin
{rd_cnt_gray_r2_r2w,rd_cnt_gray_r1_r2w} <= {rd_cnt_gray_r1_r2w,rd_cnt_gray};
end
//写计数器,写地址产生
always @(posedge wr_clk or negedge rd_rst_n)
if(wr_rst_n == 1'b0)begin
wr_cnt <= 'b0;
end
else if(wr_en && (~wr_full))begin
wr_cnt <= wr_cnt + 1'b1;
end
assign wr_addr = wr_cnt[AW-1:0];
//写计数器二进制转格雷码
assign wr_cnt_gray = wr_cnt[AW:0] ^ {1'b0,wr_cnt[AW:1]};
//写计数器的格雷码同步到读时钟域
always @(posedge rd_clk or negedge rd_rst_n)
if(rd_rst_n == 1'b0)begin
{wr_cnt_gray_r2_w2r,wr_cnt_gray_r1_w2r} <= 'b0;
end
else begin
{wr_cnt_gray_r2_w2r,wr_cnt_gray_r1_w2r} <= {wr_cnt_gray_r1_w2r,wr_cnt_gray};
end
//空信号产生
assign rd_empty = (wr_cnt_gray_r2_w2r == rd_cnt_gray) ? 1'b1 : 1'b0;
//满信号产生
assign wr_full = ({~rd_cnt_gray_r2_r2w[AW],~rd_cnt_gray_r2_r2w[AW-1],rd_cnt_gray_r2_r2w[AW-2:0]} == wr_cnt_gray) ? 1'b1 : 1'b0;
//送给ram的使能
assign rd_en_mem = (rd_empty == 1'b0) ? 1'b1 : 1'b0;
assign wr_en_mem = (wr_full == 1'b0) ? 1'b1 : 1'b0;
endmodule
双端口RAM代码
module RAM #(
parameter AW = 4,//地址位宽
parameter DW = 4,//数据位宽
parameter DATA_DEEPTH = 16//数据深度
)(
input rst_n ,
input clk_w ,
input clk_w_en ,
input [AW-1:0] w_addr ,
input [DW-1:0] w_data ,
input clk_r ,
input clk_r_en ,
input [AW-1:0] r_addr ,
output reg [DW-1:0] r_data
);
reg [DW-1:0] mem[DATA_DEEPTH-1:0];
always @(posedge clk_w)
if(clk_w_en) begin
mem[w_addr] <= w_data;
end
always @(posedge clk_r)
if(clk_r_en)begin
r_data <= mem[r_addr];
end
endmodule
TOP_tb代码
`timescale 1ns/1ps
module TOP_tb;
parameter AW = 4;//地址位宽
parameter DW = 4;//数据位宽
parameter DATA_DEEPTH = 16;//数据深度
reg wr_clk;
reg wr_rst_n;
reg wr_en;
reg [DW-1:0] wr_data;
wire wr_full;
reg rd_clk;
reg rd_rst_n;
reg rd_en;
wire [DW-1:0] rd_data;
wire rd_empty;
always #2 wr_clk = ~wr_clk;
always #6 rd_clk = ~rd_clk;
initial begin
wr_en = 1'b0;
wr_data = 'b0;
rd_en = 1'b0;
wr_rst_n = 1'b0;
rd_rst_n = 1'b0;
wr_clk = 1'b0;
rd_clk = 1'b0;
#20;
wr_rst_n = 1'b1;
rd_rst_n = 1'b1;
#20;
wr_en = 1'b1;
rd_en = 1'b1;
forever begin
@( posedge wr_clk )
if( !wr_full )
wr_data = wr_data + 1'b1;
end
end
TOP #(
.AW (AW),
.DW (DW),
.DATA_DEEPTH(DATA_DEEPTH)
)
TOP_inst(
.wr_clk (wr_clk ),//写时钟
.wr_rst_n (wr_rst_n),//写时钟复位
.wr_en (wr_en ),//写使能,高有效
.wr_data (wr_data),
.wr_full (wr_full ),
.rd_clk (rd_clk ),
.rd_rst_n (rd_rst_n),
.rd_en (rd_en ),
.rd_data (rd_data),
.rd_empty (rd_empty )
);
endmodule
modelsim仿真如下
因为写时钟比读时钟快,所以该fifo的满信号比较多,且不可能产生空信号。谢谢!