FPGA开发技巧–BRAM资源优化

FPGA开发技巧--BRAM资源优化

前言

最近在优化之前一个项目的资源,总体来说是BRAM资源占用比较多,导致时序不太好收敛,那么我们就萌生了去优化BRAM资源的想法。

优化思路

在我们的项目里面有着大量位宽为128,深度为640的单端口BRAM。并且我们知道xilinx的BRAM可以配置为两个18kbit或一个36kbit。

那么对于我们项目中这种规格的BRAM理论上需要占用的BRAM个数为

128∗64036∗1024=2.2 \frac{128*640}{36*1024}=2.2 36∗1024128∗640=2.2

但是实际占用是4个36k的BRAM。

所以我们可以想办法去优化这种规格的BRAM。

我们可以想到一个xilinx的BRAM位宽是36bit,深度是1024,那么就可以使用这个BRAM的双端口模式来拼接完成一个128*640的单端口BRAM。

比如使用0号BRAM的0-639深度,完成一个36*640规格的RAM,使用1号BRAM的0-639深度,完成第二个36*640规格的RAM,这样我们就有了一个72*640规格的RAM。此时注意到0号和1号BRAM的640-1023深度其实并没有被使用到,那么我们就能利用这部分地址来完成剩余的拼接,但是这个时候剩余的深度只有384不足以完成640深度的RAM,所以我们引入2号BRAM。这样在0号BRAM的640-1024,2号BRAM的0-255,这么多地址空间就能完成一个36*640的RAM,这样我们就完成了108*640的RAM拼接,同理我们可以利用1号RAM的剩余地址和2号RAM的剩余地址完成剩下的20*640的RAM。

这样我们就使用3个RAM完成了128*640的BRAM,相比于之前的方案减少了一个BRAM的占用。虽然这里个数比较少,但是我整个工程中用到这种规格BRAM的地方非常多,所以整体下来BRAM减少了很多。

整体的结构图如下,就是三个BRAM来进行拼接

实际代码

verilog 复制代码
module bram_w128_d640 (
    input                  clk  ,
    input        [127 : 0] wdata,
    input                  wen  ,
    input        [9 : 0]   addr ,
    input                  en   ,
    output       [127 : 0] rdata
);

wire [9:0] ram_addr0;
wire [9:0] ram_addr1;

assign ram_addr0 = addr + 10'd640;
assign ram_addr1 = addr;

wire ram_en0;
wire ram_en1;

reg ram_en0_ff;

assign ram_en0 = en & (addr[9:8] == 2'b00);
assign ram_en1 = en & (addr[9:8] != 2'b00);

always @(posedge clk ) begin
    ram_en0_ff <= ram_en0;
end

wire [35:0] rdata0;
wire [35:0] rdata2;
wire [35:0] rdata1;
wire [35:0] rdata3;

assign rdata[107:72]  = ram_en0_ff ? rdata0 : rdata2; 
assign rdata[127:108] = ram_en0_ff ? rdata1[19:0] : rdata3[19:0]; 

tpram #(
    .DP   (1024),
    .DW   (36),
    .PIPE (1)
) tpram_inst_0 (    
    .clk      (clk        ),
    .wdata_0  (wdata[35:0]),
    .wen_0    (wen        ),
    .addr_0   (addr       ),
    .en_0     (en         ),
    .rdata_0  (rdata[35:0]),
    .wdata_1  (wdata[107:72]),
    .wen_1    (wen        ),
    .addr_1   (ram_addr0  ),
    .en_1     (ram_en0    ),
    .rdata_1  (rdata0     )
);

tpram #(
    .DP   (1024),
    .DW   (36),
    .PIPE (1)
) tpram_inst_1 (    
    .clk      (clk         ),
    .wdata_0  (wdata[71:36]),
    .wen_0    (wen         ),
    .addr_0   (addr        ),
    .en_0     (en          ),
    .rdata_0  (rdata[71:36]),
    .wdata_1  ({16'd0, wdata[127:108]}),
    .wen_1    (wen        ),
    .addr_1   (ram_addr0  ),
    .en_1     (ram_en0    ),
    .rdata_1  (rdata1     )
);

tpram #(
    .DP   (1024),
    .DW   (36),
    .PIPE (1)
) tpram_inst_2 (    
    .clk      (clk          ),
    .wdata_0  (wdata[107:72]),
    .wen_0    (wen          ),
    .addr_0   (ram_addr1    ),
    .en_0     (ram_en1      ),
    .rdata_0  (rdata2       ),
    .wdata_1  ({16'd0, wdata[127:108]}),
    .wen_1    (wen           ),
    .addr_1   (ram_addr0     ),
    .en_1     (ram_en1       ),
    .rdata_1  (rdata3        )
);
    
endmodule

注意到这里,我并不是在0号和1号BRAM里面利用了384深度,而是利用了256深度。在2号RAM里面利用了更多的地址空间。

测试代码如下

verilog 复制代码
module tb_top (
    
);

reg clk;

initial begin
    clk = 1'b0;
    forever begin
        clk = #5 !clk;
    end
end

reg [127:0] wdata;  
reg         wen  ;  
reg [9:0]   addr ;  
reg         en   ;  

reg rd_valid;

wire [127:0] rdata_0; 
wire [127:0] rdata_1; 

initial begin
    wdata <= 128'd0;
    wen   <=   1'd0;
    addr  <=  10'd0;
    en    <=   1'd0;
    repeat(20) @(posedge clk);
    wdata <= {$random, $random, $random, $random};
    wen   <=   1'd1;
    addr  <=  10'd0;
    en    <=   1'd1;   
    repeat(639)begin
        @(posedge clk);
        wdata <= {$random, $random, $random, $random};
        wen   <=   1'd1;
        addr  <=  addr + 10'd1;
        en    <=   1'd1;   
    end
    @(posedge clk);
    wdata <= 128'd0;
    wen   <=   1'd0;
    addr  <=  10'd0;
    en    <=   1'd0;
    @(posedge clk);
    addr  <=  10'd0;
    en    <=   1'd1;
    repeat(639)begin
        @(posedge clk);
        addr  <=  addr + 10'd1;
        en    <=   1'd1;       
    end
    @(posedge clk);
    addr  <=  10'd0;
    en    <=   1'd0;
    repeat(20) @(posedge clk);
    $display("sim success");
    $finish;
end

always @(posedge clk ) begin
    rd_valid <= en & (!wen);
end

initial begin
    forever begin
        @(posedge clk)begin
            if(rd_valid)begin
                if(rdata_0 != rdata_1)begin
                    $display("sim error rdata_0 : %h, rdata_1 : %h\n", rdata_0, rdata_1);
                    $finish;
                end
                else begin
                    $display("sim success rdata_0 : %h, rdata_1 : %h\n", rdata_0, rdata_1);
                end              
            end
        end
    end
end

top top_inst(
    .clk     (clk    ),
    .wdata   (wdata  ),
    .wen     (wen    ),
    .addr    (addr   ),
    .en      (en     ),
    .rdata_0 (rdata_0),
    .rdata_1 (rdata_1)
);
endmodule

仿真结果如下

资源占用如下

可以看到第一种方案占用了32个LUT,一个FF,3个BRAM

第二种直接使用Xilinx IP核的方案占用了4个BRAM

相关推荐
szxinmai主板定制专家2 天前
工业视觉新思路:FPGA图像预处理+RK NPU推理,实现高速缺陷检测
人工智能·嵌入式硬件·fpga开发·rk3576+codesys
FPGA小徐2 天前
【硬件实战】立创 EDA 绘制 TF/SD 卡模块电路|SPI 模式原理图 + 阻抗匹配 + PCB 布局布线全教程
单片机·嵌入式硬件·fpga开发
狂奔蜗牛(bradley)2 天前
EtherCAT DC 分布式时钟同步的 ModelSim 仿真:从帧模板到从站行为建模
人工智能·嵌入式硬件·fpga开发·架构
ALINX技术博客2 天前
8 通道同步采样 ADC!AN706 波形显示实验 【FPGA黑金云课堂】
fpga开发
szxinmai主板定制专家3 天前
RK3568+FPGA+CODESYS异构控制方案:赋能半导体设备与工业自动化升级
人工智能·fpga开发·自动化·rk3576·半导体设备
辰丁电子3 天前
allegro入门教程-菜单栏Logic
fpga开发·pcb设计·pcb工艺·layout·pcb layout
FPGA小徐3 天前
数字IC/FPGA设计基础_ILA原理与使用
fpga开发
FPGA小徐4 天前
FPGA 部署 YOLO 完整指南:从模型量化到 Zynq PS+PL 硬件加速
yolo·fpga开发
dadaobusi4 天前
学习:chiesel,scala
学习·fpga开发·scala
爱吃汽的小橘4 天前
异步FIFO核心揭秘:格雷码与指针同步
fpga开发