自用,含详细注释
代码模块
module Asyn_fifo
#(
parameter data_width = 16,
parameter data_depth = 8,
parameter ram_depth = 256
)
(
input rst_n,
//写时钟域
input wr_clk,
input wr_en,
input [data_width-1:0] data_in,
output full,
//读时钟域
input rd_clk,
input rd_en,
output reg [data_width-1:0] data_out,
output empty
);
wire [data_depth-1:0] wr_adr; //写地址
wire [data_depth-1:0] rd_adr; //读地址
reg [data_depth:0] wr_adr_ptr; //写指针 指向fifo_ram
reg [data_depth:0] rd_adr_ptr; //读指针 同上
wire [data_depth:0] wr_adr_gray; //写地址格雷码
reg [data_depth:0] wr_adr_gray1; //延时第一拍
reg [data_depth:0] wr_adr_gray2; //延时第二拍
wire [data_depth:0] rd_adr_gray;
reg [data_depth:0] rd_adr_gray1;
reg [data_depth:0] rd_adr_gray2;
//双端口读写RAM
assign wr_adr = wr_adr_ptr[data_depth-1:0];
assign rd_adr = rd_adr_ptr[data_depth-1:0];
integer i;
reg [data_width-1:0] ram_fifo[ram_depth-1:0];
//读写数据
always@(posedge wr_clk, negedge rst_n)begin //写
if(~rst_n) begin
for(i=0;i<ram_depth;i=i+1)
ram_fifo[i] <= 1'b0;
end
else if(wr_en && ~full)
ram_fifo[wr_adr] <= data_in;
else
ram_fifo[wr_adr] <= ram_fifo[wr_adr];
end
always@(posedge rd_clk, negedge rst_n)begin//读
if(~rst_n) begin
data_out <= 'd0;
end
else if(rd_en && ~empty)
data_out <= ram_fifo[rd_adr];
else
data_out <= 'd0;
end
//读写数据时,相应地址指针+1
always@(posedge wr_clk, negedge rst_n)begin //写指针+1
if(~rst_n) begin
wr_adr_ptr <= 'd0;
end
else if(wr_en && ~full)
wr_adr_ptr <= wr_adr_ptr + 1'b1;
else
wr_adr_ptr <= wr_adr_ptr;
end
always@(posedge rd_clk, negedge rst_n)begin //读指针+1
if(~rst_n) begin
rd_adr_ptr <= 'd0;
end
else if(rd_en && ~empty)
rd_adr_ptr <= rd_adr_ptr + 1'b1;
else
rd_adr_ptr <= rd_adr_ptr;
end
//二进制转格雷码
//公式:G = (B>>1) ^ B;
assign wr_adr_gray = (wr_adr_ptr >> 1) ^ wr_adr_ptr;
assign rd_adr_gray = (rd_adr_ptr >> 1) ^ rd_adr_ptr;
//打两拍操作
always@(posedge wr_clk, negedge rst_n)begin //读指针打拍
if(~rst_n) begin
rd_adr_gray1 <= 'd0;
rd_adr_gray2 <= 'd0;
end
else begin
rd_adr_gray1 <= rd_adr_gray;
rd_adr_gray2 <= rd_adr_gray1;
end
end
always@(posedge rd_clk, negedge rst_n)begin //写指针打拍
if(~rst_n) begin
wr_adr_gray1 <= 'd0;
wr_adr_gray2 <= 'd0;
end
else begin
wr_adr_gray1 <= wr_adr_gray;
wr_adr_gray2 <= wr_adr_gray1;
end
end
//空判断:读写指针指向同一地址,且两指针在同一圈
assign empty = (rd_adr_gray == wr_adr_gray2)?1'b1:1'b0;
//满判断:读写指针指向同一地址
//但写地址已经套了读地址一圈,即最高位和读地址不同
//注意 由于转格雷码时有位移+按位与操作,地址最高位不同 = 格雷码最高二位不同
assign full = (wr_adr_gray[data_depth:data_depth-1] != rd_adr_gray2[data_depth:data_depth-1]) && (wr_adr_gray[data_depth-2:0] == rd_adr_gray2[data_depth-2:0]);
endmodule
Testbench
module asyn_fifo_tb;
reg rst_n;
reg wr_clk;
reg wr_en;
reg [15:0] data_in;
wire full;
reg rd_clk;
reg rd_en;
wire [15:0] data_out;
wire empty;
Asyn_fifo asyn_fifo_inst
(
.rst_n (rst_n),
.wr_clk (wr_clk),
.wr_en (wr_en),
.data_in (data_in),
.full (full),
.rd_clk (rd_clk),
.rd_en (rd_en),
.data_out (data_out),
.empty (empty)
);
initial wr_clk = 0;
always #10 wr_clk = ~wr_clk;
initial rd_clk = 0;
always #30 rd_clk = ~rd_clk;
always@(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
data_in <= 'd0;
else if(wr_en)
data_in <= data_in + 1'b1;
else
data_in <= data_in;
end
initial begin
rst_n = 0;
wr_en = 0;
rd_en = 0;
# 200;
rst_n = 1;
wr_en = 1;
# 20000;
wr_en = 0;
rd_en = 1;
# 20000;
rd_en = 0;
$stop;
end