本系列分为以下部分:
1、FIFO深度计算
2、同步fifo设计
3、fifo与格雷码
4、异步fifo设计(新增)
计划分三次更新完毕,本次为同步FIFO设计。
由于本次过于简单,第四次增加异步FIFO设计,异常有趣!
关于同步fifo的设计疑惑了半天,本以为这个代码是错的,后来自己又写了一遍,但是写到最后又觉得这个是正确的,主要是wr_cnt和rd_cnt的理解。
在本实例中,wr_cnt并非读写数据的计数器,不是说fifo中写入了多少个数据,而是指的写数据指针,在每次写入数据后写指针自动加一,写入16个数据后fifo写指针数值为4'b1111。
同样,rd_cnt也不是指的读了多少个数据,指的是读取数据的读指针,每读完一次后,改指针自动加一,即指向下一个要读取的地址。
没什么好讲的,异步fifo的设计才是有趣,异步fifo的设计代码在第三节格雷码更新完毕后更新。
代码:
module SYN_FIFO
#(
parameter DATA_WIDTH=8,
parameter ADDR_WIDTH=4,
parameter DATA_DEPTH=1<<ADDR_WIDTH
)
(
input clk, //时钟
input rst_n, //复位信号输入
input rd_en, //读取使能信号
input wr_en, //写使能信号
input [DATA_WIDTH-1'b1:0]data_w, //写入数据
output reg [DATA_WIDTH-1:0]data_r, //读出数据
output reg [ADDR_WIDTH-1:0] wr_cnt, //写计数器
output reg [ADDR_WIDTH-1:0] rd_cnt, //读计数器
output fifo_full, //fifo满
output reg [ADDR_WIDTH-1'b1:0]status_cnt,
output fifo_empty //fifo空标志
);
reg [DATA_WIDTH-1:0]data_ram;
//--------------
assign fifo_full = (status_cnt==DATA_DEPTH-1);
assign fifo_empty = (status_cnt==0);
//读取使能寄存
reg rd_en_r;
always@(posedge clk or negedge rst_n)
if(~rst_n)
rd_en_r<=1'b0;
else
rd_en_r<=rd_en;
//写入数据计数器
always@(posedge clk or negedge rst_n)
if(~rst_n)
wr_cnt<=4'hf;
else if(wr_cnt==DATA_DEPTH-1'b1)
wr_cnt<=0;
else if(wr_en)
wr_cnt<=wr_cnt+1'b1;
//读取计数器
always@(posedge clk or negedge rst_n)
if(~rst_n)
rd_cnt<=0;
else if(rd_cnt==DATA_DEPTH-1'b1)
rd_cnt<=0;
else if(rd_en)
rd_cnt<=rd_cnt+1'b1;
always@(posedge clk or negedge rst_n)
if(~rst_n)
data_r<=4'hf;
else if(rd_en_r)
data_r<=data_ram;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
status_cnt <= 0;
end
else if(rd_en && !wr_en && (status_cnt != 0))begin
status_cnt <= status_cnt - 1;
end
else if(wr_en && !rd_en && (status_cnt != DATA_DEPTH-1))
status_cnt <= status_cnt + 1;
else
status_cnt <= status_cnt;
end
//Syn_Dual_Port_RAM
integer i;
reg [DATA_WIDTH-1:0] register[DATA_DEPTH-1:0];
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < DATA_DEPTH; i = i + 1)
register[i] <= 0;
end
else if(wr_en == 1'b1)
register[wr_cnt] <= data_w;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_ram <= 0;
end
else if(rd_en == 1'b1)
data_ram <= register[rd_cnt];
else
data_ram <= data_ram;
end
endmodule
仿真:
`timescale 1ns/1ps
module fifo_tb;
parameter DATA_WIDTH=8;
parameter ADDR_WIDTH=4;
parameter DATA_DEPTH=1<<ADDR_WIDTH;
reg clk, rst_n,rd_en, wr_en;
reg [15:0]data_w; //写入数据
wire [DATA_WIDTH-1:0]data_r; //读出数据
wire [ADDR_WIDTH-1:0] wr_cnt; //写计数器
wire [ADDR_WIDTH-1:0] rd_cnt; //读计数器
wire fifo_full;
wire[ADDR_WIDTH-1'b1:0]ptr;
wire fifo_empty ;
SYN_FIFO u0(
clk, //时钟
rst_n, //复位信号输入
rd_en, //读取使能信号
wr_en, //写使能信号
data_w, //写入数据
data_r, //读出数据
wr_cnt, //写计数器
rd_cnt, //读计数器
fifo_full, //fifo满
ptr,
fifo_empty //fifo空标志
);
initial begin
clk=0;
forever #10 clk=~clk;
end
initial begin
rst_n=0;
data_w=16'd0;
rd_en=0;
wr_en=0;
#20;
rst_n=1;
#20;
wr(0); wr(1); wr(2); wr(3); wr(4); wr(5); wr(6); wr(7);
wr(8); wr(9); wr(10); wr(11); wr(12); wr(13); wr(14); wr(15);
#20;
rd;
#20;
$stop;
end
task wr;
input[3:0] i;
begin
wr_en=1'b1;
data_w=i;
#20;
wr_en=1'b0;
end
endtask
task rd;
begin
rd_en=1'b1;
#(20*17);
rd_en=1'b0;
end
endtask
endmodule
~~感谢阅读~~周末愉快