单端口RAM实现FIFO

发布于:2022-12-21 ⋅ 阅读:(548) ⋅ 点赞:(0)

RAM分类
单口ram:
在这里插入图片描述
单端口RAM只有一组数据线和一组地址线,只有一个时钟,读写共用地址线。输出只有一个端口。所以单端口RAM的读写操作不能同时进行。当wea拉高时,会将数据写入对应的地址,同时douta输出的数据与此时写入的数据是一致的,因此在读的时候需要重新生成对应的读地址给addra,并且disable掉wea。

module single_ram
(	
	input clk,
	input rstn,
	input enable_wr,
	input [7:0] addr,
	input [7:0]data,
	output reg[7:0]data_out
);
reg [7:0] mem [255:0];
always @(posedge clk, negedge rstn) begin
	if(!rst) begin
		data_out<='b0;
	end
	else if(enable_wr)begin
		data_out<=mem[addr];
	end
end

always @(posedge clk, negedge rstn) begin
	if(!enable_wr)begin
		mem[addr]<=data;
	end
end
endmodule

伪双口RAM
在这里插入图片描述
输入只有一组数据线,两组地址线,两个时钟。
所以一个端口只读,一个端口只写,但是写入和读取的时钟可以不同,且位宽比可以不是1:1。即允许写A的同时读B,且速率可以不相同。

module single_ram
(	
	input wrclk,
	input rstn,
	input wr,
	input [7:0] wr_addr,
	input [7:0]data,
	input rd_clk,
	input [7:0] rd_addr,
	input rd_en,
	output reg[7:0]data_out
);
reg [7:0] mem [255:0];
reg [7:0] i;
always @(posedge wr_clk, negedge rstn) begin
	if(!rstn) begin
		for(i='b0;i<256;i=i+1'b1)
			mem[i]<='b0;
	end
	else if(enable_wr)begin
		mem[wr_addr]<=data;
	end
end

always @(posedge rd_clk, negedge rstn) begin
	if(!rstn) begin
		data_out<='b0;
	end
	else if(rd_en)begin
		data_out<=mem[rd_addr];
	end
	else 
		data_out<=data_out;
end
endmodule

**真双口RAM:**有两组地址线和数据线,两个时钟。
输出有两个分别的数据线。
所以双口RAM两个端口都分别带有读写端口,可以在没有干扰的情况下进行读写,彼此互不干扰。
在这里插入图片描述

module double_ram(
	rst_n,
	cs_n,//片选信号,低有效
	//A port write signals 
	clk_a,
	wrdata_a,
	wraddr_a,
	wrena_n,
	//B port read signals 
	clk_b,
	rdaddr_b,
	rdenb_n,
	rddata_b);

parameter ADDR_WIDTH=7;
parameter DATA_WIDTH=16;
parameter DATA_DEPTH=128;
input clk_a;     // input signal  default type wire 
input clk_b;
input rst_n;
input cs_n;

input [DATA_WIDTH-1:0]wrdata_a;
input [ADDR_WIDTH-1:0]wraddr_a;
input wrena_n;

input [ADDR_WIDTH-1:0]rdaddr_b;
input rdenb_n;
output [DATA_WIDTH-1:0]rddata_b;

reg    [DATA_WIDTH-1:0]rddata;
integer i;
reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];// 定义一个位宽为DATA_WIDTH,深度为DATA_DEPTH的存储器
always@(posedge clk_a or negedge rst_n) begin 
	if(~rst_n) begin 
		for(i=0;i<DATA_DEPTH;i=i+1)
		mem[i]<=0;
	end 
	else if(cs_n==1'b0 & wrena_n==1'b0 )//片选和写信号均低有效
	mem[wraddr_a]<=wrdata_a; //写入
end 
always@(posedge clk_b or negedge rst_n)begin 
	if(~rst_n)begin 
	rddata<=0;
	end 
	else if(cs_n==1'b0 & rdenb_n==1'b0)begin 
	rddata<=mem[rdaddr_b];
	end
	else 
	rddata<=rddata;
end 
assign rddata_b=rddata;
endmodule

单口RAM实现异步FIFO:
重点区分出同时读写。
第一种办法:采用两个单口RAM,两个单口RAM分开奇偶,相当于乒乓意思,然后再加一个REG,这样就把REG分开了。我的思路是遇到同时读写的时候就对写打一拍这个时候是对奇ram写入,然后下一拍在遇到同时读写那么同样打一拍后对偶ram写入
那么可能分为以下几种情况:
①同时读写:读写同时为奇,这种情况就是在当前一拍,将写数据存入REG中,并将REG_VALID拉高告诉FIFO我下一拍要写数据,并在当前拍从奇数的FIFO中读取数据,那么下一拍如果再此发生同时读写,那么此时的同时读写就为偶,这一拍发生的情况就是将前一拍REG中的数据写入FIFO,然后将REG中数据更新新数据,然后将REG_VALID再拉高,告诉偶数FIFO下一拍要写数据了,并同时从偶数FIFO中取出要读的数据。

其实核心观点就是用两个单口RAM一个REG,用来区分最难的读写同时发生的情况,通过将RAM分为奇偶再加一个REG寄存器用来缓存,这就使不能同时读写的情况给解决了。

如果同时读写,且奇偶不同,那这种情况就更容易解决了,当前拍写数据的模块将数据写入REG,读模块的读出数据,然后下一拍将REG再写入单口RAM,和之前同时读写同时为奇偶的情况很相似。

②不同时读写:这种情况就是你只要写就先将数据写入REG,然后拉高VALID下一拍将REG中的数据写入单口RAM,如果读就直接读出数据。

通过这种方式就完美的解决了单口RAM没办法同时操作RAM的情况。
————————————————
版权声明:本文为CSDN博主「Brad.Ji」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Bradji/article/details/124717180

module FIFO_single_ram
#( parameter WIDTH = 8,
   parameter DEEPTH= 256
)
(
    input clk ,
    input rstn ,
    input [WIDTH-1:0] data,
    input wr,
    input rd,
    output reg [WIDTH-1:0] data_out,
    output full,
    output empty
    );
    //-------------下面是FIFO的操作------------------//
    reg [8:0] rd_cnt;  //多一位用来回卷判断空满
    reg [8:0] wr_cnt;
    reg [8:0] wr_cnt_r;
    wire [7:0] rd_addr;
    wire [7:0] wr_addr;
    wire [7:0] addr_odd;
    wire [7:0] addr_even;
    reg [WIDTH-1:0] data_reg;
    reg       reg_valid;
    reg       wr_flag; //用来判断读写的奇偶 0代表奇 1代表偶数
    reg       rd_flag;
    wire      wr_ram;  //用来判断是否可以写
    wire      rd_ram;  //用来判断是否可以读
    wire      wr_en_odd; //奇数写
    wire      wr_en_even;//偶数写
    wire      rd_en_odd; //奇数写
    wire      rd_en_even;//偶数写
    reg      rd_en_odd_r; //奇数写
    reg      rd_en_even_r;//偶数写
    wire [7:0] datao_odd;
    wire [7:0] datao_even;
    assign rd_addr = rd_cnt [7:0];
    assign wr_addr = wr_cnt_r [7:0];
    assign full = (wr_cnt[8]^rd_cnt[8])&&(wr_cnt[7:0]==rd_cnt[7:0]); //最高位不同 低位相同;
    assign empty= (wr_cnt == rd_cnt); //全等
    assign wr_ram = (~full)&(wr);
    assign rd_ram = (~empty)&(rd);
	
	
	
	
//    assign data_out = (rd_en_odd_r)? datao_odd:datao_even;
    always@(*) begin
    if(rd_en_odd_r)
        data_out = datao_odd;
    else if(rd_en_even_r)
        data_out = datao_even;
    else
        data_out = data_out;
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn) begin
        wr_cnt <= 9'b0;
        data_reg <= {WIDTH{1'b0}};
        reg_valid <= 1'b0;
        wr_cnt_r <= 9'b0;
        end
    else if(wr_ram)
        begin
        wr_cnt <= wr_cnt + 1'b1;
        data_reg <= data;
        reg_valid <= 1'b1;
        wr_cnt_r <= wr_cnt;
        end
    else if(!rd_en_odd & reg_valid & (~wr_flag))
        begin
        reg_valid <= 1'b0;
        end
	else if(!rd_en_even & reg_valid & (~wr_flag))
        begin
        reg_valid <= 1'b0;
        end
	
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn)
        rd_cnt <= 9'b0;
    else if(rd_ram)
        rd_cnt <= rd_cnt + 1'b1;
    end
 
    //------------------------读写奇偶判断----------------------
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        wr_flag <= 1'b0;
    else if(reg_valid)
        wr_flag <=wr_flag + 1'b1; //往里面写数据的同时奇偶改变
    else 
        wr_flag <= wr_flag ; //往里面读数据的同时奇偶改变
    end
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        rd_flag <= 1'b0;
    else if(rd_ram)
        rd_flag <= rd_flag + 1'b1; //往里面读数据的同时奇偶改变
    else
        rd_flag <= rd_flag;
    end
    //--------------------RAM读写使能控制 分为奇偶两种类型
    always@(posedge clk or negedge rstn) begin
    if(~rstn)begin
        rd_en_odd_r <= 1'b0;
        rd_en_even_r <= 1'b0;
        end
    else
    begin
        rd_en_odd_r <= rd_en_odd;
        rd_en_even_r<=rd_en_even;
    end
    end
    
	assign rd_en_odd = rd_ram & (~rd_flag);
    assign rd_en_even = rd_ram & (rd_flag);
    assign wr_en_odd =(rd_en_odd)? 1'b0:( reg_valid & (~wr_flag));
    assign wr_en_even =(rd_en_even)? 1'b0:( reg_valid & (wr_flag));

    wire ena_odd = wr_en_odd | rd_en_odd;
    wire ena_even = wr_en_even | rd_en_even;
    assign addr_odd= (wr_en_odd)? wr_addr:rd_addr;
    assign addr_even= (wr_en_even)?wr_addr:rd_addr;
    SINGLE_RAM odd_ram (
      .clka(clk),    // input wire clka
      .ena(ena_odd),      // input wire ena
      .wea(wr_en_odd),      // input wire [0 : 0] wea
      .addra(addr_odd),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_odd)  // output wire [7 : 0] douta
    );
    SINGLE_RAM even_ram (
      .clka(clk),    // input wire clka
      .ena(ena_even),      // input wire ena
      .wea(wr_en_even),      // input wire [0 : 0] wea
      .addra(addr_even),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_even)  // output wire [7 : 0] douta
    );
endmodule

网站公告

今日签到

点亮在社区的每一天
去签到