原创 【数字IC】高级数字 IC 设计(9)单口 RAM、同步 FIFO、异步 FIFO 的设计

2023-3-18 21:52 573 8 8 分类: FPGA/CPLD 文集: 数字IC

使用单口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的情况。

代码如下

单口 RAM

 

Plaintext
module BRAM_PORTA(
input clka,
input ena,
input wea,
input [3:0] addra,
input [15:0] dina,
output reg [15:0] douta
);


reg [15:0] mem [15:0];


always @(posedge clka)begin
if(ena)begin
if(wea)begin//写数据
mem[addra] <= dina;
douta <= 16'bzzzzzzzzzzzzzzzz;
end
else begin //读数据
douta <= mem[addra];
mem[addra] <= mem[addra];
end
end
else
douta <= 16'bzzzzzzzzzzzzzzzz;
end


endmodule

 

 

同步 FIFO

 

 

Plaintext
`define INDEX 6
`define WIDTH 16
`define DEPTH 64
module Sync_FIFO(
input clk,
input rst_n,
input re,
input we,
input [`WIDTH-1:0] din,
output empty,
output full,
output reg [`INDEX-1:0] fifo_cnt,
output reg [`WIDTH-1:0] dout
);


reg [`WIDTH-1:0] mem [`DEPTH-1:0];
reg [`INDEX-1:0] raddr;
reg [`INDEX-1:0] waddr;


// 1、空满标志;
assign empty = (fifo_cnt == 0) ? 1:0;
assign full = (fifo_cnt == `DEPTH-1) ? 1:0;


// 2、读写计数;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
fifo_cnt <= 0;
else begin
if((!empty && re)&&(!full && we))// 同时读写,计数不变;
fifo_cnt <= fifo_cnt;
else if(!empty && re)// 读数据,计数减一;
fifo_cnt <= fifo_cnt - 1'b1;
else if(!full && we)// 写数据,计数加一;
fifo_cnt <= fifo_cnt + 1'b1;
else
fifo_cnt <= fifo_cnt;
end
end


// 3、读写数据;
always @(posedge clk or negedge rst_n)begin// 读数据;
if(!rst_n)
dout <= 0;
else begin
if(!empty && re)
dout <= mem[raddr];
else
dout <= dout;
end
end
always @(posedge clk)begin// 写数据;
if(!full && we)
mem[raddr] <= din;
else
mem[raddr] <= mem[waddr];
end


// 4、读写地址;
always @(posedge clk or negedge rst_n)begin// 写地址;
if(!rst_n)
waddr <= 0;
else begin
if(!full && we)
waddr <= waddr + 1'b1;
else
waddr <= waddr;
end
end
always @(posedge clk or negedge rst_n)begin// 读地址;
if(!rst_n)
raddr <= 0;
else begin
if(!empty && re)
raddr <= raddr + 1'b1;
else
raddr <= raddr;
end
end


endmodule

 

 

异步 FIFO

 

 

Plaintext
`define INDEX 6 // FIFO 索引;
`define WIDTH 16 // FIFO 宽度;
`define DEPTH 64 // FIFO 深度;
module Async_FIFO(
input rd_clk,
input wr_clk,
input rst_n,
input re,
input we,
input [`WIDTH-1:0] din,
output empty,
output full,
output [`WIDTH-1:0] dout
);


reg [`WIDTH-1:0] mem [`DEPTH-1:0];
wire [`INDEX-1:0] raddr;
wire [`INDEX-1:0] waddr;
reg [`INDEX:0] wbin,rbin;
wire [`INDEX:0] wbin_next,rbin_next,wgray_next,rgray_next;
reg [`INDEX:0] rp,wr1_rp,wr2_rp,wp,rd1_wp,rd2_wp;


// 1、空满标志;
// 可能需要延时;
assign empty = (rd2_wp == rgray_next) ? 1:0;
assign full = ({~wr2_rp[`INDEX:`INDEX-1],wr2_rp[`INDEX-2:0]} == wgray_next) ? 1:0;// 高两位不同,取反;


// 2、读计数;
// 产生读地址 raddr + 读地址自增 + 将普通二进制码转化为格雷码,并赋给读指针 rp;
// 将读指针 rp 同步到写时钟域;
always @(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
{rbin,rp} <= 0;
else
{rbin,rp} <= {rbin_next,rgray_next};
end


assign raddr = rbin[`INDEX-1:0];// 抛弃高位;
assign rbin_next = rbin + (!empty && re);// 地址加一;
assign rgray_next = rbin_next ^ (rbin_next >> 1);// 转为格雷码,右移 + 异或;


always @(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
{wr2_rp,wr1_rp} <= 0;
else
{wr2_rp,wr1_rp} <= {wr1_rp,rp};
end


// 3、写计数;
// 产生写地址 waddr + 写地址自增 + 将普通二进制码转化为格雷码,并赋给写指针 wp;
// 将写指针 wp 同步到读时钟域;
always @(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
{wbin,wp} <= 0;
else
{wbin,wp} <= {wbin_next,wgray_next};
end


assign waddr = wbin[`INDEX-1:0];// 抛弃高位;
assign wbin_next = wbin + (!full && we);// 地址加一;
assign wgray_next = wbin_next ^ (wbin_next >> 1);// 转为格雷码,右移 + 异或;


always @(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
{rd2_wp,rd1_wp} <= 0;
else
{rd2_wp,rd1_wp} <= {rd1_wp,wp}; // 多比特的,但是只有单比特发生变化,属于单比特处理领域;
end


// 4、异步读数据;
assign dout = mem[raddr];


// 5、同步写数据;
always @(posedge wr_clk)begin
if(!full && we)
mem[waddr] <= din;
else
mem[waddr] <= mem[waddr];
end


endmodule

 

 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条