9.19-同步fifo

109 阅读2分钟

1 双端口ram设计

设计一个512x8的双端口,ram宽度为8位,深度为512,地址位宽为9位。

双端口ram: 独立的读写时钟,独立读写地址和数据端口,并具有读和写的使能信号。

read_clk write_clk

read_addr write_addr

read_data write_data

read_allow write_allow

parameter ram_width = 8

parameter ram_depth = 512

parameter addr_width = 9

module DUALRAM(read_clk,write_clk,read_addr,write_addr,read_data,write_data,read_allow,write_allow);

parameter DLY = 1;
parameter RAM_WIDTH = 8;
parameter RAM_DEPTH = 512;
parameter ADDR_WIDTH = 9;

input read_clk,write_clk,read_allow,write_allow;

input [RAM_WIDTH-1:0] write_data;
output [RAM_WIDTH-1:0] read_data

input [RAM_ADDR-1:0] read_addr;
input [RAM_ADDR-1:0] write_addr;


reg [RAM_WIDTH-1:0] read_data;
reg [RAM_WIDTH-1:0]  MEM [RAM_DEPTH-1:0];

always@(posedge write_clk) begin
    if(write_allow)
        MEM[write_addr] <= #DLY write_data;
end


always@(posedge read_clk) begin
    if(read_allow)
        read_data <= #DLY MEM[read_addr];
end
    
 
endmodule

2 verilog代码中使用#1延迟

为了修正非阻塞赋值的问题,要求加上#1 delay。因此在一般的非阻塞赋值中经常会看到#1的延迟,工程师的解释是为了防止非阻塞赋值崩溃。实际上,加不加#1都不会导致非阻塞赋值崩溃。

在非阻塞赋值的RHS加#1延迟,既有好的原因,也有很多坏的原因。

在非阻塞赋值上加#1,输出变化会有一个时间单位的延迟,便于查看波形。
许多高性能触发器的hold时间是在0~800ps之间。在那些驱动门级模型的RTL模型上加上#1延迟后,通常就可以修正很多与RTL和门级混合仿真相关的问题。
非阻塞赋值加#1的延迟能够提供一些仿真上的便利,但是这个延迟对仿真性能有很大的影响,虽然VCS的+nbaopt编译选项可以对#1延迟的非阻塞赋值提高仿真性能,但是还是没有达到与不使用延迟的非阻塞赋值一样的性能。

3 同步fifo

full 和 empty 1 fifo 对双口ram,通过read_allow,write_allow 实现数据的存储和读取

2 read_allow 和 write_allow 通过full和empty,read_enable和write_enable来实现。

3 空满信号通过Fcount计数器来控制

4 Fcount的更新

输入 复位信号和时钟信号,以及写入数据,读写使能信号。

输出 读到的数据 空满信号,计数器计数来间接控制空满信号。

输入: fifo_rst,clk,read_enable,write_enable,write_data

输出: read_data,full,empty,Fcounter

// Fcounter 计数数据深度。

module SYNCFIFO(Fifo_rst,Clock,Read_enable,Write_enable,Write_data,Read_data,Full,Empty,Fcounter);
    
    parameter DATA_WIDTH = 8;
    parameter ADDR_WIDTH = 9;
    
    input Fifo_rst;
    input Clock;
    input Read_enable;
    input Write_enable;
    input [DATA_WIDTH-1:0] Write_data;
    output [DATA_WIDTH-1:0] Read_data;
    
    output Full;
    output Empty;
    output [ADDR_WIDTH-1:0] Fcounter;
    
    reg [DATA_WIDTH-1:0] Read_data;
    reg Full;
    reg Empty;
    reg [ADDR_WIDTH-1:0] Fcounter;
    reg [ADDR_WIDTH-1:0] Read_addr;
    reg [ADDR_WIDTH-1:0] Write_addr;
    
    wire Read_allow = (Read_enable && !empty);  // 空不能读
    wire Write_allow = (Write_enable && !Full); //满不能写
    
    DUALRAM U_RAM(
    .Read_clock(Clock),
    .Write_clock(Clock),
    .Read_allow(Read_allow),
    .Write_allow(Write_allow),
    .Read_addr(Read_addr),
    .Write_addr(Write_addr),
    .Write_data(Write_data),
    .Read_data(Read_data)
    );
 //  空满信号的产生  
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Empty <= 1'b1;
      else
          Empty <= (!Write_enable && (Fcount[8:1] == 8'b0) && ((Fcount[0] == 1'b0) || Read_enable));
          
always@(posedge Clock or posedge Fifo_rst)
    if(Fifo_rst)
        Full <= 1'b1;
    else
        Full <= (!Read_enable && (Fcount[8:1] == 8'hFF) && ((Fcount[0] == 1'b1) || Write_enable));
// 计数器

always@(posedge Clock or posedge Fifo_rst)
     if(Fifo_rst)
        Fcount <= 1'b0;
     else if(read_enable && write_enable)
         Fcount <= Fcount;
     else if(Read_allow)
         Fcount <= Fcount - 1'b1;
     else if(Write_allow)
         Fcount <= Fcount + 1'b1;
     else
         Fcount <= Fcount;
         
// 写数据  读数据控制。 通过Read_allow和Write_allow和双口ram联系起来
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Read_addr <= 1'b0;
      else if(Read_allow)
          Read_addr <= Read_addr + 1'b1;
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Write_addr <= 1'b0;
      else if(Write_allow)
          Write_addr <= Write_addr + 1'b1;


endmodule