同步FIFO设计

  • FIFO有一个读口和一个写口,读写时钟一致是同步FIFO,时钟不一致就是异步FIFO
  • IP设计中通常使用的是同步FIFO
  • 异步FIFO通常使用在跨时钟域设计中

RAM(Random Access Memory)的设计

FIFO中的数据可以存储在寄存器中或者SRAM中,FIFO的容量比较小的时候,使用register,FIFO容量比较大的时候使用的是SRAM

  • SRAM通常是Memory Compiler进行产生的
  • SRAM不需要手动写仿真模型
  • RAM在时钟的驱动下,可以从两个端口同时进行读写操作
  • 写操作:写数据、写地址、写使能
  • 读操作:读地址、读使能,读取完成之后,会返回读数据

双端口RAM的Verilog代码(仿真模型)


  • 将RAM的位宽、深度进行参数化设计,方便后续进行修改
  • 首先要定义一个存储空间,用于存储数据
  • SRAM是已经综合好的block,其中布局布线已经完成
  • 在进行DC综合的时候,会将定义的存储体综合为寄存器或者sram(如果有相应的库文件),使用sram,面效比会比较好;register作为存储体的时候,容量小的时候,面积比较小,并且在下一个周期读出其中的值
  • 使用register作为存储体,使用组合逻辑在当拍就可以得到数据
//1.
always@ (posedge clk) begin
  if(read_en)
    read_data <= memory[read_addr];
end

//2.
// 当拍就可以得到memory的值
wire [DATA_WIDTH-1:0] read_data_next;
assign read_data_next = memory[read_addr];

always @(posedge clk) begin
  if(read_en)
    read_data <= read_data_next;
end

同步FIFO设计

  • FIFO包含两部分,一部分是存储体,另一部分是fifo ctrl
  • fifo是不会存在根据地址取值的,fifo - 先入先出,所以端口没有地址,地址通过其中的fifo ctrl进行控制
  • 没有读地址和写地址
  • fifo满了之后,不能写入数据,fifo为空的时候,不能向外读取数据,关键是full和empty信号的产生
  • fifo上下游模块看到fifo的状态之后,满不写,空不读;在fifo内部也需要进行控制

方法1

  • 需要维护读指针和写指针

  • 产生一个写使能,需要给一个地址给到写使能,写指针加1;产生一个读使能,读指针加1
  • 产生空满信号:用一个计数器,表示当前fifo中一共存有多少数据,写使能来的时候,计数器加1,读使能来的时候,计数器减1,写使能和读使能同时来的时候,计数器不变
  • 当counter为容量减一的时候,来了一个写使能,会变为满
  • 当counter为1的时候,来了一个读使能,会变为空



// 满信号产生
if((!write_allow) && (Fcounter == 9'b0))
  empty <= 1;
else if((!write_allow) && (Fcounter == 9'b1 && (read_allow))
  empty <=1;
else 
  empty <= 1'b0;


  • counter的值要覆盖所有的RAM地址范围
parameter COUNTER_WIDTH = 9;
reg Fcounter [COUNTER_WDITH-1:0]; // 只能表示0-511,表示不了512这个数
reg Fcounter [COUNTER_WDITH:0]; // 9bit可以表示512这个数
module syncfifo(
	fifo_rst,
	fifo_clk,
	
	read_enable,
	write_enable,
	
	read_data,
	write_data,
	
	full,
	empty,
	
	fcounter
);

	parameter DATA_WIDTH = 8;
	parameter ADDR_WIDTH = 9;
	
	input fifo_clk;
	input fifo_rst;
	
	input read_enable;
	input write_enable;
	
	input [DATA_WIDTH-1:0] write_data;
	input [DATA_WIDTH-1:0] read_data;
	
	output full;
	output empty;
	
	reg [ADDR_WIDTH:0] fcounter;
	
	reg [DATA_WIDTH-1:0] read_addr; 
	reg [DATA_WIDTH-1:0] write_addr;
	
	DUALRAM_RAM(
		read_clk (fifo_clk),
		write_clk (fifo_clk),
	
		read_allow (read_allow),
		write_allow (write_allow),
		
		read_addr (read_addr),
		write_addr (write_addr),
		
		read_data (read_data),
		write_data (write_data)
	);
	
	wire read_allow = (read_enable && !empty);
	wire write_allow = (write_enable && !full);
	
	
	always @(posedge fifo_clk or negedge fifo_rst) begin
		if(fifo_rst)
			empty <= 1'b1;
		else
			empty <= (!write_allow) && (fcounter[8:0] == 8'h0) && ((fcounter[0]==0)||read_allow);	
	end
	
	always @(posedge fifo_clk or negedge fifo_rst) begin
		if(fifo_rst)
			full <= 1'b0;
		else
			full <= (!read_allow) && (fcounter[8:1] == 8'hff) && (fcounter[0]==1 || write_allow);
	end
	
	
	always @(posedge fifo_clk or negedge fifo_rst) begin
		if(fifo_rst)
			read_addr <= 'h0
		esle if(read_allow)
			read_addr <= read_addr + 1'b1;
	end
	
	always @(posedge fifo_clk or negedge fifo_rst) begin
		if(fifo_rst)
			write_addr <= 'h0
		esle if(read_allow)
			write_addr <= read_addr + 1'b1;
	end
	
	always @(posedge fifo_clk or negedge fifo_rst) begin
		if(fifo_rst)
			fcounter <= 'h0;
		else if((!read_allow && write_allow) || (read_allow && !write_allow))
			if(write_allow) 
				fcounter <= fcounter + 1'b1;
			else
				fcounter <= fcounter - 1'b1;
	end
	
endmodule

方法2

方法1多例化了一个计数器,计数器需要占据一定的资源和面积


  • 读写指针的空间扩大一倍,使用除最高位之外的其他位寻址sram
module sync_fifo
#(
    parameter ADDR_WIDTH = 4,
    parameter RAM_WIDTH = 8,
    parameter RAM_DEPTH = 16
)
(
    input   wire                    fifo_clk,
    input   wire                    fifo_rst_n,
    input   wire                    read_en,
    input   wire                    write_en,
    input   wire [RAM_WIDTH-1:0]    w_data,
    output  reg [RAM_WIDTH-1:0]    r_data,
    output  wire                     empty,
    output  wire                     full
);

    // 指针
    reg [ADDR_WIDTH:0]    write_addr;
    reg [ADDR_WIDTH:0]    read_addr;
    wire    [ADDR_WIDTH-1:0] w_addr;
    wire    [ADDR_WIDTH-1:0] r_addr;

    wire write_allow = write_en && (!empty);
    wire read_allow = read_en && (!full);

    reg [RAM_WIDTH-1:0] mem [RAM_DEPTH-1:0];

    always @(posedge fifo_clk or negedge fifo_rst_n) begin
        if(!fifo_rst_n)
            read_addr <= {ADDR_WIDTH{1'b0}};
        else if(read_allow) begin 
            r_data <= mem[read_addr];
            read_addr <= read_addr + 1;
        end
    end

    always @(posedge fifo_clk or negedge fifo_rst_n) begin
        if(!fifo_rst_n)
            write_addr <= {ADDR_WIDTH{1'b0}};
        else if(write_allow) begin 
            mem[write_addr] <= w_data;
            write_addr <= write_addr + 1;
        end
    end

    assign empty = read_addr == write_addr ? 1 : 0;
    assign full = (read_addr[ADDR_WIDTH]!=write_addr[ADDR_WIDTH]) &&
                (read_addr[ADDR_WIDTH-1:0] == write_addr[ADDR_WIDTH-1:0]);

    assign r_addr = read_addr[ADDR_WIDTH-1:0];
    assign w_addr = write_addr[ADDR_WIDTH-1:0];
endmodule
posted @ 2023-12-18 17:24  Icer_Newer  阅读(83)  评论(0编辑  收藏  举报