首先要理解fifo的写满和读空,用多出来的一位来扩充地址
使用格雷码判断当,写指针追上读指针时候就是写满,为了方便判断当写满时候即,写地址地最高和次高位与读地址相反其他位相同时就是写满,
当读地址的所有位和写地址的所有位相等就是读空
代码根据图可以轻松得到:
下图是地址计数器的增加图
写控制器:
module w_ctrl(
input wire w_clk,//写时钟
input wire rst_n,//复位
input wire w_en,//写使能
input wire [8:0] r_gaddr,//读时钟域过来的格雷码读地址指针
output reg w_full,//写满标志
output wire [8:0] w_addr,//256深度的FIFO写二进制地址
output wire [8:0] w_gaddr//写FIFO地址格雷码编码
);
reg [8:0] addr;
reg [8:0] gaddr;
wire [8:0] addr_wire;
wire [8:0] gaddr_wire;
reg [8:0] r_gaddr_d1,r_gaddr_d2;
wire w_full_wire;
//打两拍进行时钟同步
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
{r_gaddr_d2,r_gaddr_d1} <= 18'd0;
else
{r_gaddr_d2,r_gaddr_d1} <= {r_gaddr_d1,r_gaddr};
//产生些ram的地址指针二进制
assign w_addr = addr;
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
addr <= 9'd0;
else
addr <= addr_wire;
assign addr_wire = addr + ((~w_full)&w_en);
//转换格雷码编码地址
assign gaddr_wire=(addr_wire>>1)^addr_wire;
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
gaddr<=9'd0;
else gaddr <= gaddr_wire;
assign w_gaddr = gaddr;
//写满标志产生完成
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
w_full <= 1'b0;
else if({~gaddr_wire[8:7],gaddr_wire[6:0]}==r_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
w_full <=1'b1; //w_full_wire;
else w_full <=1'b0;
endmodule
在这里插入代码片
读数据:
module r_ctrl (
input wire r_clk,//读时钟
input wire rst_n,
input wire r_en,//读使能
input wire [8:0] w_gaddr,//写时钟域中的写地址指针
output reg r_empty,//读空标志
output wire [8:0] r_addr,//读地址二进制
output wire [8:0] r_gaddr//读格雷码地址
);
reg [8:0] addr;
reg [8:0] gaddr;
wire [8:0] addr_wire;
wire [8:0] gaddr_wire;
reg [8:0] w_gaddr_d1,w_gaddr_d2;
wire r_empty_wire;
//打两拍进行时钟同步
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
{w_gaddr_d2,w_gaddr_d1} <= 18'd0;
else
{w_gaddr_d2,w_gaddr_d1} <= {w_gaddr_d1,w_gaddr};
//二进制的读地址
assign r_addr = addr;
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
addr <=9'd0;
else
addr <= addr_wire;
assign addr_wire = addr + ((~r_empty)&r_en);
//格雷码的读地址
assign r_gaddr = gaddr;
assign gaddr_wire = (addr_wire >>1 )^ addr_wire;
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
gaddr <= 9'd0;
else
gaddr <= gaddr_wire;
//读空标志的产生
assign r_empty_wire =(gaddr_wire == w_gaddr_d2);
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
r_empty<=1'b1;
else //if(gaddr_wire == w_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
r_empty <= r_empty_wire;
//else
//r_empty <= 1'b0;
endmodule
下面是fifo_mem实现:
module fifomem(
input wire w_clk,
input wire r_clk,
input wire w_en,//来自于FIFO的写控制模块
input wire w_full,//来自于FIFO的写控制模块
input wire [7:0] w_data,//来自于外部数据源
input wire [8:0] w_addr,//来自于FIFO的写控制模块
input wire r_empty,//来自于FIFO的读控制模块
input wire [8:0] r_addr,//来自于FIFO的读控制模块
input wire rst_n,
output reg [7:0] r_data//读数据是从内部ram中读取
);
reg [7:0]data_mem[255:0];
wire [7:0]w_addr_r;//定义写入地址
wire [7:0]r_addr_r;//定义写入地址
integer i;
assign w_addr_r = w_addr[7:0];
assign r_addr_r = r_addr[7:0];
//写入数据
always@(posedge w_clk or negedge rst_n)
if(!rst_n)begin
for(i = 0;i < 256;i = i + 1)begin
data_mem[i] <= 8'b0;
end
end
else if(w_en &&(!w_full))//防止写溢出,如果已经满了禁止写数据
data_mem[w_addr_r] <= w_data;
//读出数据
always@(posedge r_clk or negedge rst_n)
if(!rst_n)
r_data <= 0;
else
r_data <=data_mem[r_addr_r];
endmodule
下面是顶层模块:
module ex_fifo(
input wire w_clk,
input wire r_clk,
input wire rst_n,
input wire w_en,
input wire [7:0] w_data,
output wire w_full,
input wire r_en,
output wire [7:0] r_data,
output wire r_empty
);
wire [8:0] r_gaddr;
wire [8:0] w_addr;
wire [8:0] w_gaddr;
wire [8:0] r_addr;
w_ctrl w_ctrl_inst(
.w_clk (w_clk),//写时钟
.rst_n (rst_n),//复位
.w_en (w_en),//写使能
.r_gaddr (r_gaddr),//读时钟域过来的格雷码读地址指针
.w_full (w_full),//写满标志
.w_addr (w_addr),//256深度的FIFO写二进制地址
.w_gaddr (w_gaddr)//写FIFO地址格雷码编码
);
fifomem fifomem_inst(
.w_clk (w_clk),
.r_clk (r_clk),
.w_en (w_en),//来自于FIFO的写控制模块
.w_full (w_full),//来自于FIFO的写控制模块
.w_data (w_data),//来自于外部数据源
.w_addr (w_addr),//来自于FIFO的写控制模块
.r_empty (r_empty),//来自于FIFO的读控制模块
.r_addr (r_addr),//来自于FIFO的读控制模块
.r_data (r_data),//读数据是从内部ram中读取
.rst_n(rst_n)
);
r_ctrl r_ctrl_inst(
.r_clk (r_clk),//读时钟
.rst_n (rst_n),
.r_en (r_en),//读使能
.w_gaddr (w_gaddr),//写时钟域中的写地址指针
.r_empty (r_empty),//读空标志
.r_addr (r_addr),//读地址二进制
.r_gaddr (r_gaddr)//读格雷码地址
);
endmodule
下面是testbench:
`timescale 1ns/1ns
module tb_ex_fifo;
reg r_clk,w_clk,rst_n;
reg w_en;
reg [7:0] w_data;
reg r_en;
wire w_full;
wire r_empty;
wire [7:0] r_data;
parameter CLK_P=20;
initial begin
rst_n<=0;
r_clk<=0;
w_clk<=0;
#200
rst_n=1;
end
//写的初始化模块
initial begin
w_en=0;
w_data=0;
#300
write_data(256);
end
//读的初始化模块
initial begin
r_en =0;
@(posedge w_full);
#40;
read_data(256);
end
always #(CLK_P/2) r_clk =~r_clk;
always #(CLK_P/2) w_clk =~w_clk;
ex_fifo ex_fifo_inst(
.w_clk (w_clk),
.r_clk (r_clk),
.rst_n (rst_n),
.w_en (w_en),
.w_data (w_data),
.w_full (w_full),
.r_en (r_en),
.r_data (r_data),
.r_empty (r_empty)
);
task write_data(len);
integer i,len;
begin
for (i=0;i<len;i=i+1)
begin
@(posedge w_clk);
w_en=1'b1;
w_data=i;
end
@(posedge w_clk);
w_en = 1'b0;
w_data =0;
end
endtask
task read_data(len);
integer i,len;
begin
for (i=0;i<len;i=i+1)
begin
@(posedge r_clk);
r_en=1'b1;
end
@(posedge r_clk);
r_en = 1'b0;
end
endtask
endmodule
自己用modelsim仿真脚本如下:
quit -sim
.main clear
vlib work
vlog ./tb_ex_fifo.v
vlog ./../design/*.v
vsim -voptargs=+acc work.tb_ex_fifo
add wave tb_ex_fifo/*
add wave tb_ex_fifo/ex_fifo_inst/*
add wave -divider {w}
add wave tb_ex_fifo/ex_fifo_inst/w_ctrl_inst/*
add wave -divider {R}
add wave tb_ex_fifo/ex_fifo_inst/r_ctrl_inst/*
run 25us
qq_43210117
发布了4 篇原创文章 · 获赞 0 · 访问量 12
私信
关注