关于FPGA中的异步FIFO始终无法用bram而只能用lut ram的问题

关于FPGA中的异步FIFO始终无法用bram而只能用lut ram的问题

······最近在写异步FIFO,参考别人的代码后,在FPGA中复现时发现总是无法生成期望的bram,而是花费大量的lut来形成lut ram,造成lut的浪费。

······首先提一下,我们知道对于Xilinx 的FPGA在定义的memory之前,可以使用(参考ug901):

(*  ram_style="block" *) reg [data_size-1:0] myram [2**addr_size-1:0];
(*  ram_style="distributed" *) reg [data_size-1:0] myram [2**addr_size-1:0];

来分别提示编译器综合成bram或者dram(lut ram)。但实际上这紧密取决于你对reg数组的行为描述规范,也即你的reg数组行为本身既符合lut ram的规范,又符合bram的规范时上述 ram_style才有用。一旦出现无论如何都是综合成lut ram时,就有必要检查对reg数组的读写操作规范了。

······我这里先分享一下写异步fifo时,复位操作不当引起的无法生成bram问题。首先,我们知道afifo中的ram模型可以对应为一个读、一个写的simple dual-port ram,所谓伪双口ram。也即afifo中,对ram操作仅出现在两个always块内(一个读、一个写)。

parameter   data_width = 8;
parameter   addr_width = 12;
//储存ram,实际行为表现为简单双口ram
(* RAM_STYLE="block" *) reg [data_width-1:0] fifo_ram [2**addr_width-1:0];

//========================================================= write fifo 
always@(posedge wr_clk or negedge rstn)
    begin
       if(!rstn)
			 fifo_ram[wr_addr] <= 'h0;    //复位后
       else if(wr_en && (~full))
          fifo_ram[wr_addr] <= din;
       else
          fifo_ram[wr_addr] <= fifo_ram[wr_addr];
    end   
//======================================================== read_fifo
always@(posedge rd_clk or negedge rstn)
   begin
      if(!rstn)
         begin
            dout <= 'h0;         //fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为0;
         end
      else if(rd_en && (~empty))
         begin
            dout <= fifo_ram[rd_addr];
         end
      else
         begin
            dout <=   'h0;
         end
   end

assign wr_addr = wr_addr_ptr[addr_width-1:0];
assign rd_addr = rd_addr_ptr[addr_width-1:0];

······整个afifo文件中只有上述块的描述对fifo_ram进行读写操作,理论上只并行进行了一个读、一个写,完全能用simple dual-port ram来综合,但实际上无论加不加 RAM_STYLE=“block” ,最终都会只会用lut ram而不会用bram来生成,造成大量lut被占用,进而影响其他电路的时序综合。
······经过不断尝试以及参考xilinx的关于ram生成的官方标准代码(ug901-vivado-synthesis-examples.rar),发现只要去掉第二个always块中的异步复位复位敏感信号,vivado2019就能正常按照 RAM_STYLE 来使用bram或者lut ram生成所需的ram。也即:

always@(posedge rd_clk )
   begin
      if(!rstn)
         begin
            dout <= 'h0;         //fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为0;
         end
      else if(rd_en && (~empty))
         begin
            dout <= fifo_ram[rd_addr];
         end
      else
         begin
            dout <=   'h0;
         end
   end

······其实我不是很理解,你说要是对ram进行写操作时使用异步复位对所有的reg进行了一次性的复位操作不能用bram来实现倒是说得过去,但是我一个异步复位仅仅用来约束与ram无直接关联的dout却无法实现。
难道是综合器认为 它非要把dout放进bram中,形成寄存一拍的完整bram,但是发现该dout寄存是异步复位的而无法实现?
·····至于综合器采取什么策略我不得而知,也不去猜了,但是我们可以参考xilinx官方的ram生成例程,来尽量满足其写法的规范。

参考ug901第四章RAM HDL Coding Guidelines,我这里仅仅列出 Simple Dual-Port Block RAM with Dual Clocks (Verilog)

//Simple Dual-Port Block RAM with Dual Clocks (Verilog)
//Filename: simple_dual_two_clocks.v
// Simple Dual-Port Block RAM with Two Clocks
// File: simple_dual_two_clocks.v
module simple_dual_two_clocks (clka,clkb,ena,enb,wea,addra,addrb,dia,dob);
input clka,clkb,ena,enb,wea;
input [9:0] addra,addrb;
input [15:0] dia;
output [15:0] dob;
reg [15:0] ram [1023:0];
reg [15:0] dob;
always @(posedge clka) 
begin 
	if (ena)
 	begin
 		if (wea)
 		ram[addra] <= dia;
 	end
end
always @(posedge clkb) 
begin
 	if (enb)
	 begin
 		dob <= ram[addrb];
 	end
end
endmodule

可以看到,其描述中读写均未出现异步的复位或者使能信号。

最后,回到异步fifo,我认为对fifo内部的ram进行清零的复位操作实在没必要,因为真正决定fifo是否能进行读写数据操作的是其读写指针以及空满标志,至于ram内部的数据就像硬盘删除数据一样并不是真的去擦除硬盘,而只是做一个标志,待到下次写到同一个地址时从新写一遍覆盖了即可。

  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值