论文参考:The Design and Verification of a Synchronous First-In First-Out
博客参考:【原创】异步FIFO设计原理详解 (含RTL代码和Testbench代码)_锤王马加爵的博客-CSDN博客_fifo testbenchFIFO在硬件上是一种地址依次自增的Single Dul RAM,按读数据和写数据工作的时钟域是否相同分为同步FIFO和异步FIFO,其中同步FIFO是指读时钟和写时钟为同步时钟,常用于数据缓存和数据位宽转换;异步FIFO通常情况下是指读时钟和写时钟频率有差异,即由两个异步时钟驱动的FIFO,由于读写操作是独立的,故常用于多比特数据跨时钟域处理。本文仅讨论异步FIFO的设计。 因为FIFO的硬件本质是一块Single Dul RAM,无论它的内部结构和原理如何复杂,最核心的...https://blog.csdn.net/qq_40807206/article/details/109555162
代码中省略了转为格雷码的步骤,读者可自行添加。
1.write_control.v
//FIFO parameters
`define FIFO_DEPTH 64
`define FIFO_MEM_ADDR_WIDTH 6
`define FIFO_HALFFULL 4
module write_control (
input reset,
input clk_write_logic,
input wire [`FIFO_MEM_ADDR_WIDTH:0] read_pointer,
input write_request,
output reg w_enable,
output reg write_ack,
output reg [`FIFO_MEM_ADDR_WIDTH:0] write_pointer,
output reg full_fifo_status,
output reg halffull_fifo_status,
input wire wc_scan_in0,// test scan mode data input
// wc_scan_en, // test scan mode enable
// wc_test_mode, // test mode select
// output wc_scan_out0, // test scan mode data,
output wire [`FIFO_MEM_ADDR_WIDTH-1:0] mem_addr_read,
output reg [`FIFO_MEM_ADDR_WIDTH:0] rd_ff1, // 读地址同步寄存器1
output reg [`FIFO_MEM_ADDR_WIDTH:0] rd_ff2, // 读地址同步寄存器2
input reg init_done
);
wire [`FIFO_MEM_ADDR_WIDTH-1:0] mem_addr_write;
assign mem_addr_read = read_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
assign mem_addr_write = write_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
//full_fifo_status = (111 111=63) && (1),这是写满?出现(read_pointer=7'h66 write_pointer=7'h3f)、(7'h28 7'h7f)等为写满的情况
//assign full_fifo_status = ((mem_addr_write == `FIFO_DEPTH - 1) && (read_pointer[`FIFO_MEM_ADDR_WIDTH] ^ write_pointer[`FIFO_MEM_ADDR_WIDTH]));
// 读地址同步至写时钟域
always @(posedge clk_write_logic or posedge reset) begin
if(!reset) begin
rd_ff1 <= 'b0;
rd_ff2 <= 'b0;
end
else begin
rd_ff1 <= read_pointer;
rd_ff2 <= rd_ff1;
end
end
//写使能判断
// assign w_enable = (write_request && (full_fifo_status==0))?1:0;
always @(*) begin
// 写数据
if(init_done) begin
if(write_request == 1'b1 && full_fifo_status == 1'b0)begin
w_enable = 1;
end
else begin
w_enable = 0;
end
end
end
// 写地址自加一
always@(posedge clk_write_logic or posedge reset) begin
if(!reset) begin
write_pointer <= 0;
write_ack <= 0;
end
else begin
//将满也不是64-mem_addr_write[5:0],应该是本地写指针减去同步过来的读指针
//halffull_fifo_status = ((`FIFO_DEPTH - mem_addr_write)<=(`FIFO_HALFFULL))?1:0;//逻辑小于等于
// $display("halffull_fifo_status=%b",halffull_fifo_status);
if(w_enable==1'b1 && full_fifo_status==1'b0) begin
write_ack <= 1;
write_pointer <= write_pointer + 1'b1;
end
else
write_ack <= 0;
end
end
//将满判断
// assign full_fifo_status = (rd_ff2[`FIFO_MEM_ADDR_WIDTH]!=write_pointer[`FIFO_MEM_ADDR_WIDTH]) && (rd_ff2[`FIFO_MEM_ADDR_WIDTH-1:0]==write_pointer[`FIFO_MEM_ADDR_WIDTH-1:0])?1:0;
always @(*) begin
if(write_pointer[`FIFO_MEM_ADDR_WIDTH]==0 && rd_ff2[`FIFO_MEM_ADDR_WIDTH]==1)begin
halffull_fifo_status = (`FIFO_DEPTH - (write_pointer + 2*`FIFO_DEPTH - rd_ff2)<=(`FIFO_HALFFULL))?1:0;
$display("half_01=%b",halffull_fifo_status);
end
else begin
halffull_fifo_status = (`FIFO_DEPTH - (write_pointer - rd_ff2)<=(`FIFO_HALFFULL))?1:0;
$display("half=%b",halffull_fifo_status);
end
end
//写满判断
always @(*) begin
if((rd_ff2[`FIFO_MEM_ADDR_WIDTH]!=write_pointer[`FIFO_MEM_ADDR_WIDTH]) && (rd_ff2[`FIFO_MEM_ADDR_WIDTH-1:0]==write_pointer[`FIFO_MEM_ADDR_WIDTH-1:0])) begin
full_fifo_status = 1'b1;
end
else begin
full_fifo_status = 1'b0;
end
end
endmodule
2.read_control.v
//FIFO parameters
`define FIFO_DEPTH 64
`define FIFO_MEM_ADDR_WIDTH 6
`define FIFO_HALFEMPTY 4
module read_control (
input reset,
input clk_read_logic,
input wire [`FIFO_MEM_ADDR_WIDTH:0] write_pointer,
input read_request,
output reg r_enable,
output reg read_ack,
output reg [`FIFO_MEM_ADDR_WIDTH:0] read_pointer,
output reg empty_fifo_status,
output reg halfempty_fifo_status,
input wire rc_scan_in0,
input wire rc_scan_en,
input wire rc_test_mode,
output rc_scan_out0,
output reg [`FIFO_MEM_ADDR_WIDTH:0] wr_ff1, // 写地址同步寄存器1
output reg [`FIFO_MEM_ADDR_WIDTH:0] wr_ff2, // 写地址同步寄存器2
input reg init_done
);
wire [`FIFO_MEM_ADDR_WIDTH-1:0] mem_addr_read;
wire [`FIFO_MEM_ADDR_WIDTH-1:0] mem_addr_write;
assign mem_addr_read = read_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
assign mem_addr_write = write_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
// always @(*) begin
// $display("$time=%p, mem_addr_read=%h", $time, mem_addr_read);
// $display("$time=%p, mem_addr_write=%h",$time, mem_addr_write);
// end
always@(posedge clk_read_logic or posedge reset) begin
if(!reset) begin
read_pointer <= 0;
read_ack <= 0;
end
// else begin
//halfempty_fifo_status = ((mem_addr_read)<=(`FIFO_HALFEMPTY))?1:0;
//读指针自加1
if(r_enable) begin
read_ack <= 1;
read_pointer <= read_pointer + 1'b1;
end
else
read_ack <= 0;
// end
end
// 写地址同步至读时钟域
always @(posedge clk_read_logic or posedge reset) begin// rdclk是wrclk的两倍,因为写要快点,所以时间短节奏快,波形密
if(!reset) begin
wr_ff1 <= 'b0;
wr_ff2 <= 'b0;
end
else begin
wr_ff1 <= write_pointer;
wr_ff2 <= wr_ff1;
end
end
//读使能判断
// assign r_enable = (read_request && (empty_fifo_status==0))?1:0;
always @(*) begin
//读数据
if(init_done) begin
if( read_request == 1'b1 && empty_fifo_status == 1'b0 )begin
r_enable = 1;
end
else begin
r_enable = 0;
end
end
end
//将空判断
always @(*) begin
halfempty_fifo_status = ((wr_ff2 - read_pointer)<=(`FIFO_HALFEMPTY))?1:0;
end
//读空判断
// assign empty_fifo_status = ((mem_addr_read == 0) && (mem_addr_write == 0) && read_pointer[`FIFO_MEM_ADDR_WIDTH] == write_pointer[`FIFO_MEM_ADDR_WIDTH])?1:0;
always @(*) begin
if( wr_ff2==read_pointer ) begin
empty_fifo_status = 1'b1;
end
else begin
empty_fifo_status = 1'b0;
end
end
endmodule
3.memory_array.v
//FIFO parameters
`define MEM_ADDR_WIDTH 6
`define MEM_DEPTH 64
`define MEM_DATA_WIDTH 16
module memory_array (
input reset,
input clk_write_logic,
input clk_read_logic,
input [`MEM_ADDR_WIDTH-1:0] w_addr,
input [`MEM_ADDR_WIDTH-1:0] r_addr,
input w_enable,
input r_enable,
input reg [`MEM_DATA_WIDTH-1:0] w_data,
input reg mem_scan_in0,
input reg mem_scan_en,
input reg mem_test_mode,
output reg [`MEM_DATA_WIDTH-1:0] r_data,
output mem_scan_out0
);
reg [`MEM_DATA_WIDTH-1:0] memory [0:`MEM_DEPTH-1];
always @(posedge clk_write_logic) begin
if(w_enable)
memory[w_addr] <= w_data;
// $display("memory = 'p%p", memory);//64*16
end
always @(posedge clk_read_logic or posedge reset) begin
if(!reset)
r_data <= 'b0;
else if(r_enable)
r_data <= memory[r_addr];
end
endmodule
4.SFIFO.v
`define FIFO_DEPTH 64
`define FIFO_MEM_ADDR_WIDTH 6
`define FIFO_MEM_DATA_WIDTH 16
`define FIFO_HALFEMPTY 4
`define FIFO_HALFFULL 4
`include "write_control.v"
`include "read_control.v"
`include "memory_array.v"
module SFIFO (
input reset,// system reset
input wire init_done,
clk_write_logic,
clk_read_logic,
read_request,
write_request,
halffull_fifo_status,//it is input or output?
write_ack,
input wire [`FIFO_MEM_DATA_WIDTH-1:0] w_data,
input wire scan_in0, // test scan mode data input
input wire scan_en, // test scan mode enable
input wire test_mode, // test mode select
output wire [`FIFO_MEM_DATA_WIDTH-1:0] r_data,
output wire read_ack,
output w_enable,
output r_enable,
output empty_fifo_status,
output wire halfempty_fifo_status,
output full_fifo_status,
output wire scan_out0, // test scan mode data output,
output wire [`FIFO_MEM_ADDR_WIDTH:0] rd_ff1, // 读地址同步寄存器1
output wire [`FIFO_MEM_ADDR_WIDTH:0] rd_ff2, // 读地址同步寄存器2
output wire [`FIFO_MEM_ADDR_WIDTH:0] wr_ff1, // 写地址同步寄存器1
output wire [`FIFO_MEM_ADDR_WIDTH:0] wr_ff2 // 写地址同步寄存器2
);
wire [`FIFO_MEM_ADDR_WIDTH:0] read_pointer;
wire [`FIFO_MEM_ADDR_WIDTH:0] write_pointer;
wire [`FIFO_MEM_ADDR_WIDTH-1:0] w_addr;
wire [`FIFO_MEM_ADDR_WIDTH-1:0] r_addr;
assign w_addr = write_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
assign r_addr = read_pointer [`FIFO_MEM_ADDR_WIDTH-1:0];
read_control READ_CONTROL_MOD (
.reset (reset),
.clk_read_logic (clk_read_logic),
.write_pointer (write_pointer),
.read_request (read_request),
.r_enable (r_enable),
.read_ack (read_ack),
.read_pointer (read_pointer),
.empty_fifo_status (empty_fifo_status),
.halfempty_fifo_status (halfempty_fifo_status),
.rc_scan_in0 (scan_in0),
.rc_scan_en (scan_en),
.rc_test_mode (test_mode),
.rc_scan_out0 (scan_out0),
.wr_ff1 (wr_ff1),
.wr_ff2 (wr_ff2),
.init_done (init_done)
);
write_control WRITE_CONTROL_MOD (
.reset (reset),
.clk_write_logic (clk_write_logic),
.read_pointer (read_pointer),
.write_request (write_request),
.w_enable (w_enable),
.write_ack (write_ack),
.write_pointer (write_pointer),
.full_fifo_status (full_fifo_status),
.halffull_fifo_status (halffull_fifo_status),
.wc_scan_in0 (scan_in0),
// .wc_scan_en (scan_en),
// .wc_test_mode (test_mode),
// .wc_scan_out0 (scan_out0),
.rd_ff1 (rd_ff1),
.rd_ff2 (rd_ff2),
.init_done (init_done)
);
memory_array MEM_ARRAY_MOD (
.reset (reset),
.clk_write_logic (clk_write_logic),
.clk_read_logic (clk_read_logic),
.w_addr (w_addr),
.r_addr (r_addr),
.w_enable (w_enable),
.r_enable (r_enable),
.w_data (w_data),
.r_data (r_data),
.mem_scan_in0 (scan_in0),
.mem_scan_en (scan_en),
.mem_test_mode (test_mode),
.mem_scan_out0 (scan_out0)
);
endmodule
5.test_tb.v
// `timescale 1ns / 1ns
`timescale 1ns/100ps
`include "SFIFO.v"
module SFIFO_tb;
parameter DATA_WIDTH = 16 ;
reg reset ;
// 写时钟域tb信号定义
reg clk_write_logic ;
reg write_request ;
reg[DATA_WIDTH-1:0] w_data ;
wire full_fifo_status ;
// 读时钟域tb信号定义
reg clk_read_logic ;
reg read_request ;
wire[DATA_WIDTH-1:0]r_data ;
wire empty_fifo_status ;
// testbench自定义信号
reg init_done ; // testbench初始化结束
// FIFO初始化
initial begin
// 输入信号初始化
reset = 1 ;
clk_write_logic = 0 ;
clk_read_logic = 0 ;
write_request = 0 ;
read_request = 0 ;
w_data = 'b0 ;
init_done = 0 ;
// FIFO复位
#30 reset = 0 ;
write_request = 0 ;
read_request = 0 ;
#30 reset = 1 ;
write_request = 1 ;
read_request = 1 ;
// 初始化完毕
#30 init_done = 1 ;
if (init_done)begin
repeat(1000)begin
#4 read_request = 1;
write_request = 1;
#8 read_request = 0;//读请求 高两周期 低一周期 循环1000次
write_request = 0;//写请求 高两周期 低一周期 循环1000次
end
repeat(1000)begin
#8 write_request = 1;
read_request = 1;//写读请求 一直高两周期 循环1000次
end
repeat(1000)begin
#4 read_request = 0;
write_request = 0;
#8 read_request = 1;//读请求 低两周期 高一周期 循环1000次
write_request = 1;//写请求 低两周期 高一周期 循环1000次
end
repeat(1000)begin
#8 write_request = 1;
#4 write_request = 0;//写请求 高一周期 低两周期 读请求一直高电平 循环1000次
end
repeat(1000)begin
write_request = 1;//写请求80次,一次4ns
read_request = 0;
#320 read_request = 1;//读请求35次
write_request = 0;
#140 write_request = 1;//写请求10次
read_request = 0;
#40 read_request = 1;//读请求10次
write_request = 0;
#40 write_request = 1;//写请求35次
read_request = 0;
#140 read_request = 1;//读请求160次
write_request = 0;
#640;
end
repeat(1000)begin
write_request = 1;//写请求一直高电平
#4 read_request = 1;
#8 read_request = 0;//读请求 高两周期 低一周期 循环1000次
end
end
end
// 写时钟 写快读慢
always
#2 clk_write_logic = ~clk_write_logic;
// 读时钟
always
#4 clk_read_logic = ~clk_read_logic;
//再开一个initial write_request和read_request会乱
// repeat(10)begin
// if (init_done)
// if(clk_write_logic)
// write_request = 1;
// read_request = 1;
// #8;
// write_request = 0;//写请求 高两周期 低两周期 循环1000次
// read_request = 0;//读请求 高两周期 低两周期 循环1000次
// #4;
// end
// read_request = 1;
// end
// 写入数据自增
always @(posedge clk_write_logic) begin
if(init_done) begin
if( write_request==1'b1 && full_fifo_status == 1'b0 )begin
w_data <= w_data + 1;
// $display("w_data = 'h%h", w_data);
end
else
w_data <= w_data;
end
else begin
w_data <= 'b0;
end
end
SFIFO SFIFO_inst1
(
.reset (reset ),
.clk_write_logic (clk_write_logic ),
.clk_read_logic (clk_read_logic ),
.read_request (read_request ),
.w_data (w_data ),
.write_request (write_request ),
.r_data (r_data ),
.read_ack (read_ack ),
.w_enable (w_enable ),
.r_enable (r_enable ),
.empty_fifo_status (empty_fifo_status ),
.halfempty_fifo_status (halfempty_fifo_status ),
.full_fifo_status (full_fifo_status ),
.halffull_fifo_status (halffull_fifo_status ),
.write_ack (write_ack ),
.scan_in0 (scan_in0 ),
.scan_en (scan_en ),
.test_mode (test_mode ),
.scan_out0 (scan_out0 ),
.init_done (init_done )
);
endmodule