使用单口RAM实现FIFO,其实很简单,其中的重点就是区分出读写,
读写如果同时启动,你肯定会思考单口RAM肯定会出问题,毕竟单口RAM只有一个口,肯定不能实现同时读写,那么怎么解决这个问题呢。
有两种办法:
第一种办法就是采用两个单口RAM,这样就可以了,两个单口RAM分开奇偶,相当于乒乓的意思,然后再加一个REG,这就相当于把读写分开了
那么就可能分为以下几种情况:
①同时读写:读写同时为奇,这种情况就是在当前一拍,将写数据存入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的情况。
代码如下
`timescale 1ns / 1ps
//
// Company:
// Engineer: Brad
//
// Create Date: 2022/05/11 19:38:41
// Design Name:
// Module Name: FIFO_single_ram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
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
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 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));
assign rd_en_odd = rd_ram & (~rd_flag);
assign rd_en_even = rd_ram & (rd_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
我这个代码没有做仔细的仿真,经过各位网友的提醒,改了几处,首先针对读写的时候对奇偶不同时操作,通过加了一个直通路解决,以防止下一拍写的时候漏掉数据。
并对这个代码进行了详细仿真,仿真代码如下:文章来源:https://www.toymoban.com/news/detail-430969.html
第二种情况就是针对单口RAM的位宽进行变化,比如FIFO的读写数据位宽为8Bit,那么就针对RAM的位宽设置为FIFO位宽的两倍,外部加两个同等位宽的读写寄存器,通过将读写寄存器与外界读写和读写寄存器与RAM的交互实现FIFO的功能,比如对于同时读写的情况,在时钟上按2拍完成一次寄存器与RAM数据的交互,比如第一拍完成读,第二拍完成写,对于外界的接口而言数据是源源不断的从寄存器出来的,从而实现了单口RAM实现FIFO的功能,这种情况我仔细想了想只适用于 对FIFO连续读写的情况下才能使用,不连续读写总有一次会把其置成同时读写的情况。文章来源地址https://www.toymoban.com/news/detail-430969.html
到了这里,关于FIFO专题之单口RAM实现FIFO(同步)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!