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,如果读就直接读出数据。文章来源:https://www.toymoban.com/news/detail-631598.html
通过这种方式就完美的解决了单口RAM没办法同时操作RAM的情况。
————————————————
版权声明:本文为CSDN博主「Brad.Ji」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Bradji/article/details/124717180文章来源地址https://www.toymoban.com/news/detail-631598.html
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
到了这里,关于单端口RAM实现FIFO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!