添加数据清零逻辑后BRAM转为分布式RAM的原因及实现可行性问询
初始Verilog代码可成功综合为块BRAM(Block RAM):
(* ram_style = "block" *) reg [7:0] mem [511:0]; reg [7:0] data_read = 8'b0; always@(posedge clk) begin if (write_i) begin mem[addr_i] <= write_data_i; end else begin data_read <= mem[addr_i]; end end assign read_data_o = data_read;
但添加如下复位清零逻辑后,综合工具将其转为分布式RAM(Distributed RAM):
if (rst) begin for(i=0; i<512 ; i= i+1) begin mem[i] <= 8'b0; end end
请问该现象的原因是什么?能否实现带数据清零逻辑的BRAM?
回答
现象原因
FPGA里的块BRAM硬件本身就不支持全局并行复位清零——块BRAM的物理结构只能通过端口逐地址写入修改内容,根本做不到在一个时钟周期内把所有存储单元同时置0。
你写的for循环复位逻辑,要求复位触发时一次性给512个地址的存储单元并行赋值,这种需求完全违背块BRAM的硬件特性。综合工具识别到这个矛盾后,只能放弃用块BRAM,转而用LUT(查找表)搭建分布式RAM——因为LUT可以通过逻辑实现并行复位的功能,代价就是占更多逻辑资源,性能也不如块BRAM。
另外,就算你加了(* ram_style = "block" *)属性,当代码逻辑和块BRAM硬件特性冲突时,综合工具会直接忽略这个属性,优先满足代码的功能要求。
带清零功能的块BRAM实现方案
能实现带清零逻辑的块BRAM,但不能用全局并行复位的方式,常用的有两种方案:
上电初始化清零
借助FPGA工具的初始化文件(比如.coe或.mif),在BRAM配置阶段就把所有存储单元初始化为0。这种方式是芯片上电时完成清零,不需要额外复位逻辑,适合只需要上电清零的场景。逐地址写清零
设计一个状态机,当复位信号触发时,依次遍历BRAM的所有地址,逐个写入0值。这种方式需要多个时钟周期完成清零,但能保留块BRAM的硬件结构,适合需要运行时动态清零的场景。示例逻辑如下:(* ram_style = "block" *) reg [7:0] mem [511:0]; reg [7:0] data_read = 8'b0; reg [8:0] clear_addr; // 512个地址需要9位宽 reg clear_state; always@(posedge clk) begin if (rst) begin clear_addr <= 9'd0; clear_state <= 1'b1; end else if (clear_state) begin mem[clear_addr] <= 8'b0; clear_addr <= clear_addr + 1'b1; if (clear_addr == 9'd511) begin clear_state <= 1'b0; end end else if (write_i) begin mem[addr_i] <= write_data_i; end else begin data_read <= mem[addr_i]; end end assign read_data_o = data_read;注意这种方案在清零过程中,BRAM无法对外提供正常读写服务,需要根据业务需求处理好状态切换。
内容的提问来源于stack exchange,提问作者Abdulkadir Arslan

