RAM/FIFO 学习回顾
参考正点原子FPGA逻辑设计指南
&&github上某位大佬的代码(async_fifo)
sp_ram(单端口)
存储用ram数组模型即可,根据深度和宽度选择。
reg [7:0] ram [31:0] ; //ram 数据
根据使能信号赋值
always @(posedge clock ) begin
if(wen && en)
ram[address] <= data;
end
always @(posedge clock ) begin
if(( wen == 1'b0) && en)
q <= ram[address];
else
q <= 8'hx ;
end
需要一个读写模块操作读写地址和读写使能信号,并在其中例化Sp_ram模块。
仿真结果如图所示
tp_ram(伪双端口)
基本思路差不多,与单端口的差别是可以进行同时读写,也会遇到一种极端情况,读过快,这时就需要用到一种技巧:读穿通到写,直接把要写的值赋给读。
sync_fifo(同步fifo)
同步fifo的设计关键是读写指针判断空满标志
fifo数据量计算
always @ (posedge clk or negedge rst) begin
if (rst == 1'b0)
count <= 0;
else begin
case({wr_en,rd_en})
2'b00:count<= count;
2'b01:
if(count!==5'b00000)
count<=count-1;
2'b10:
if(count!== max1_count)
count<=count+1;
2'b11:count<=count;
endcase
end
end
空满标志判断
always @(count) begin
if(count==5'b00000)
empty = 1;
else
empty = 0;
end
always @(count) begin
if (count== max_count)
full = 1;
else
full = 0;
end
同步fifo一般用于数据缓存
下图为仿真图
async_fifo(异步fifo)
要点:二进制转格雷码,格雷码域的计数,如何用格雷码判断空满标志
读地址同步到写时钟域,写地址要同步到读时钟域
always@(posedge fifo_wr_clk or negedge rst_n)
if(!rst_n)begin
sync_r2w_r1 <= 11'd0;
sync_r2w_r2 <= 11'd0;
end else begin
sync_r2w_r1 <= gray_rdaddress;
sync_r2w_r2 <= sync_r2w_r1;
end
always@(posedge fifo_rd_clk or negedge rst_n)
if(!rst_n)begin
sync_w2r_r1 <= 11'd0;
sync_w2r_r2 <= 11'd0;
end else begin
sync_w2r_r1 <= gray_wraddress ;
sync_w2r_r2 <= sync_w2r_r1;
end
同步过程中要用格雷码,避免二进制的多个位同时变化,产生竞争现象
assign gray_rdaddress = (rdaddress >>1) ^ rdaddress;//(({1'b0,rdaddress[9:1]}) ^ rdaddress);
assign gray_wraddress = (({1'b0,wraddress[10:1]}) ^ wraddress);
格雷码如何判断空满。
空:读比写快,读指针追上了写指针
满:写比读快,写超越了写指针一圈
assign fifo_empty = (gray_rdaddress == sync_w2r_r2);
assign fifo_full = (gray_wraddress == {~sync_r2w_r2[10:9],sync_r2w_r2[8:0]});
性能测试方法:分别测试读快写慢和读慢写快的两种情况fifo的反应
fifo是有性能限制的,实际读写时钟差距过多时需要通过改变深度来提高性能。深度的计算又是一门学问。
同时,异步fifo也可用于位宽转换,通过读写时钟比来完成。