打印
[Verilog HDL]

VERILOG实现异步FIFO

[复制链接]
3784|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaochy1126|  楼主 | 2023-5-29 14:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

首先要理解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



使用特权

评论回复

相关帖子

沙发
gaochy1126|  楼主 | 2023-5-29 14:55 | 只看该作者


 
读数据:

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


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1025

主题

11271

帖子

25

粉丝