异步fifo设计及验证verilog代码

论文参考: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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值