赞
踩
Xilinx_RAM_IP核的使用
说明:单口RAM、伪双口RAM、双口RAM的读写,以及RAM资源占用的分析。
环境:Vivado2018.3。
IP核:Block Memory Generator。
参考手册:
UG473: 7 Series FPGAs Memory Resources
PG058 :Block Memory Generator v8.4。
随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。RAM工作时可以随时从任何一个指定的地址写入(存入)或读出(取出)信息。它与ROM的最大区别是数据的易失性,即一旦断电所存储的数据将随之丢失 。RAM在计算机和数字系统中用来暂时存储程序、数据和中间结果。
单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。
读写全在A口:
伪双口RAM,一个端口只读,另一个端口只写。
A口写、B口读:
双口RAM两个端口都可以读写。
A口可读写、B口可读写:
块RAM 可被配置为 ROM、RAM 以及 FIFO 等常用的存储模块。区别于分布式 RAM(Distributed RAM)(主要由 LUT 组成的,不占用 BRAM 的资源)。分布式 RAM 也可以被配置为 ROM、RAM 以及 FIFO 等常用的存储模块,但是性能不如 BRAM,毕竟 BRAM 才是专用的,一般是 BRAM 资源不够用的情况下才使用分布式 RAM。反之,BRAM 由一定数量固定大小的存储块构成的,使用 BRAM 资源不占用额外的逻辑资源,并且速度快,不过使用的时候消耗的 BRAM 资源只能是其块大小的整数倍,就算你只存了 1 bit 也要占用一个 BRAM。 一个 BRAM 的大小为 36K Bits,并且分成两个小的 BRAM 各自为 18K Bits,排列成又分为上下两块。内部视图如下:
IP配置:
端口介绍:
Xilinx_RAM IP提供的读写模式有Write First Mode、Read First Mode、No Change Mode。
Write First Mode :数据先写入RAM中,然后再下一个时钟输出该数据。
Read First Mode :数据先写入RAM中,在下一周期输出内存中该地址的上一次数据。
No Change Mode :输出在写入操作器件保持不变。
单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。
以下代码实现单口RAM 8bit数据的读写(下面波形为同一代码三种读写模式ILA图)。
单口读写代码:
module top ( input clk_25m ); /********************************************************************** *--*时钟 **********************************************************************/ wire clk_100m; wire clk_locked; clk_wiz_0 clk_0 ( .clk_out1(clk_100m), .locked(clk_locked), .clk_in1(clk_25m) ); /********************************************************************** *--*复位 **********************************************************************/ wire RSTn; assign RSTn = clk_locked; /********************************************************************** *--*BRAM **********************************************************************/ wire ram_ena; wire ram_wea; wire[2:0] ram_addra; wire[15:0] ram_dina; wire[15:0] ram_douta; reg ram_ena_r; reg ram_wea_r; reg[2:0] ram_addra_r; reg[15:0] ram_dina_r; reg[15:0] ram_douta_r; assign ram_ena = ram_ena_r; assign ram_wea = ram_wea_r; assign ram_addra = ram_addra_r; assign ram_dina = ram_dina_r; reg[31:0] del_cnt;//用于开机延时 reg[7:0] ram_cnt;//用于产生写入RAM的数据 //RAM状态机 reg[3:0] RAM_STATE; parameter RAM_IDLE = 4'd0, RAM_WRITR = 4'd1, RAM_READ = 4'd2, RAM_DONE = 4'd3; always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cnt <= 8'd0; RAM_STATE <= RAM_IDLE; ram_dina_r <= 16'h0000; ram_addra_r <= 3'h0; end else begin case(RAM_STATE) RAM_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; RAM_STATE <= (del_cnt == 32'd100_000_000)?RAM_WRITR:RAM_IDLE; end RAM_WRITR:begin ram_cnt <= (ram_cnt==4'd7)?4'd0:ram_cnt+4'd1; //数据产生 ram_addra_r <= (ram_cnt==4'd7)?3'h0:ram_addra_r + 3'h1; ram_dina_r <= (ram_cnt==4'd7)?16'h0000:ram_cnt+4'd1; ram_wea_r <= (ram_cnt==4'd7)?1'b0:1'b1; RAM_STATE <= (ram_cnt==4'd7)?RAM_READ:RAM_WRITR; end RAM_READ:begin ram_cnt <= (ram_cnt==4'd7)?4'd0:ram_cnt+4'd1; //读取计数 ram_ena_r <= 1'b1; ram_wea_r <= 1'b0; ram_addra_r <= (ram_cnt==4'd7)?3'h0:ram_addra_r + 3'h1; RAM_STATE <= (ram_cnt==4'd7)?RAM_DONE:RAM_READ; end RAM_DONE:begin RAM_STATE <= RAM_DONE; end default:begin ram_dina_r <= 16'h0000; end endcase end end blk_mem_gen_0 blkram_0( .clka(clk_25m), // input wire clka .ena(ram_ena), // input wire ena .wea(ram_wea), // input wire [0 : 0] wea .addra(ram_addra), // input wire [2 : 0] addra .dina(ram_dina), // input wire [15 : 0] dina .douta(ram_douta) // output wire [15 : 0] douta ); /********************************************************************** *--*ILA **********************************************************************/ ila_0 ila ( .clk(clk_100m), // input wire clk .probe0(clk_25m), // input wire [0:0] probe0 .probe1(ram_ena), // input wire [0:0] probe1 .probe2(ram_wea), // input wire [0:0] probe2 .probe3(ram_addra), // input wire [2:0] probe3 .probe4(ram_dina), // input wire [15:0] probe4 .probe5(ram_douta) // input wire [15:0] probe5 ); endmodule
观察波形时注意:读取数据时,在读取地址的下一周期出数据。
单口RAM读写波形(Write First模式):
单口RAM读写波形(Read First模式):
单口RAM读写波形(No change模式):
伪双口RAM,一个端口只读,另一个端口只写。A口写、B口读
A、B口均以25M时钟分别写与读8bit数据。
module top ( input clk_25m ); /********************************************************************** *--*时钟 **********************************************************************/ wire clk_50m; wire clk_100m; wire clk_locked; clk_wiz_0 clk_0 ( .clk_out1(clk_100m), .clk_out2(clk_50m), .locked(clk_locked), .clk_in1(clk_25m) ); /********************************************************************** *--*复位 **********************************************************************/ wire RSTn; assign RSTn = clk_locked; /********************************************************************** *--*BRAM **********************************************************************/ wire ram_ena; wire ram_wea; wire[2:0] ram_addra; wire[15:0] ram_dina; wire ram_enb; wire[2:0] ram_addrb; wire[15:0] ram_doutb; reg ram_ena_r; reg ram_wea_r; reg[2:0] ram_addra_r; reg[15:0] ram_dina_r; reg ram_enb_r; reg[2:0] ram_addrb_r; assign ram_ena = ram_ena_r; assign ram_wea = ram_wea_r; assign ram_addra = ram_addra_r; assign ram_dina = ram_dina_r; assign ram_enb = ram_enb_r; assign ram_addrb = ram_addrb_r; reg[31:0] del_cnt;//用于开机延时 reg[7:0] ram_cntw;//用于产生写入RAM的数据 reg[7:0] ram_cntr;//用于读RAM //RAM状态机 reg[3:0] RAM_STATE; parameter RAM_IDLE = 4'd0, RAM_WRD = 4'd1, RAM_DONE = 4'd2; always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cntw <= 8'd0; ram_cntr <= 8'd0; RAM_STATE <= RAM_IDLE; ram_dina_r <= 16'h0000; ram_addra_r <= 3'h0; ram_enb_r <= 1'b0; ram_addrb_r <= 3'h0; end else begin case(RAM_STATE) RAM_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写使能 ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写命令 RAM_STATE <= (del_cnt == 32'd100_000_000)?RAM_WRD:RAM_IDLE; end RAM_WRD:begin ram_cntw <= (ram_cntw==4'd7)?ram_cntw:ram_cntw+4'd1; //数据产生 ram_addra_r <= (ram_cntw==4'd7)?3'h0:ram_addra_r + 3'h1; //写入地址 ram_dina_r <= (ram_cntw==4'd7)?16'h0000:ram_cntw+4'd1; //写入数据 ram_wea_r <= (ram_cntw==4'd7)?1'b0:1'b1; //写入命令 ram_cntr <= (ram_cntw>=4'd1)?((ram_cntr==4'd7)?4'd0:ram_cntr+4'd1):4'd0; //读取控制(在开始写后最少延迟一个周期开始读) ram_addrb_r <= (ram_cntw>=4'd1)?((ram_cntr==4'd7)?3'h0:ram_addrb_r + 3'h1):3'h0; //读取地址 ram_enb_r <= 1'b1; //读取使能 RAM_STATE <= (ram_cntr==4'd7)?RAM_DONE:RAM_WRD; end RAM_DONE:begin ram_enb_r <= 1'b0; RAM_STATE <= RAM_DONE; end default:begin ram_dina_r <= 16'h0000; end endcase end end blk_mem_gen_0 blkram_0 ( .clka(clk_25m), // input wire clka .ena(ram_ena), // input wire ena .wea(ram_wea), // input wire [0 : 0] wea .addra(ram_addra), // input wire [2 : 0] addra .dina(ram_dina), // input wire [15 : 0] dina .clkb(clk_25m), // input wire clkb .enb(ram_enb), // input wire enb .addrb(ram_addrb), // input wire [2 : 0] addrb .doutb(ram_doutb) // output wire [15 : 0] doutb ); /********************************************************************** *--*ILA **********************************************************************/ ila_0 ila ( .clk(clk_100m), // input wire clk .probe0(clk_25m), // input wire [0:0] probe0 .probe1(ram_ena), // input wire [0:0] probe1 .probe2(ram_wea), // input wire [0:0] probe2 .probe3(ram_addra), // input wire [2:0] probe3 .probe4(ram_dina), // input wire [15:0] probe4 .probe5(clk_50m), // input wire [0:0] probe5 .probe6(ram_enb), // input wire [0:0] probe6 .probe7(ram_addrb), // input wire [2:0] probe7 .probe8(ram_doutb) // input wire [15:0] probe8 );
波形
★注意A口写、B口读的起始不能在同一时间,这样读写会出错。应该A口先写至少一个周期后B口再读,一下就是A、B口同时执行写与读,最终读出的数据出错:
always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cntw <= 8'd0; ram_cntr <= 8'd0; RAM_STATE <= RAM_IDLE; ram_dina_r <= 16'h0000; ram_addra_r <= 3'h0; ram_enb_r <= 1'b0; ram_addrb_r <= 3'h0; end else begin case(RAM_STATE) RAM_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写使能 ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写命令 ram_enb_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //读命令 RAM_STATE <= (del_cnt == 32'd100_000_000)?RAM_WRD:RAM_IDLE; end RAM_WRD:begin ram_cntw <= (ram_cntw==4'd7)?ram_cntw:ram_cntw+4'd1; //数据产生 ram_addra_r <= (ram_cntw==4'd7)?3'h0:ram_addra_r + 3'h1; //写入地址 ram_dina_r <= (ram_cntw==4'd7)?16'h0000:ram_cntw+4'd1; //写入数据 ram_wea_r <= (ram_cntw==4'd7)?1'b0:1'b1; //写入命令 ram_cntr <= (ram_cntr==4'd7)?ram_cntr:ram_cntr+4'd1; //读取控制 ram_addrb_r <= (ram_cntr==4'd7)?3'h0:ram_addrb_r + 3'h1; //读取地址 //读取使能 RAM_STATE <= (ram_cntr==4'd7)?RAM_DONE:RAM_WRD; end RAM_DONE:begin ram_enb_r <= 1'b0; RAM_STATE <= RAM_DONE; end default:begin ram_dina_r <= 16'h0000; end endcase end end
A口25M时钟写8bit,B口12.5M时钟读8bit:
module top ( input clk_25m ); /********************************************************************** *--*时钟 **********************************************************************/ wire clk_12_5m; wire clk_100m; wire clk_locked; clk_wiz_0 clk_0 ( .clk_out1(clk_100m), .clk_out2(clk_12_5m), .locked(clk_locked), .clk_in1(clk_25m) ); /********************************************************************** *--*复位 **********************************************************************/ wire RSTn; assign RSTn = clk_locked; /********************************************************************** *--*BRAM **********************************************************************/ wire ram_ena; wire ram_wea; wire[2:0] ram_addra; wire[15:0] ram_dina; wire ram_enb; wire[2:0] ram_addrb; wire[15:0] ram_doutb; reg ram_ena_r; reg ram_wea_r; reg[2:0] ram_addra_r; reg[15:0] ram_dina_r; reg ram_enb_r; reg[2:0] ram_addrb_r; assign ram_ena = ram_ena_r; assign ram_wea = ram_wea_r; assign ram_addra = ram_addra_r; assign ram_dina = ram_dina_r; assign ram_enb = ram_enb_r; assign ram_addrb = ram_addrb_r; reg[31:0] del_cnt; //用于开机延时 reg[7:0] ram_cntw;//用于产生写入RAM的数据 reg[7:0] ram_cntr;//用于读RAM reg[0:0] ram_rflag;//RAM读取标志 //RAM写状态机 reg[3:0] RAM_STATE; parameter RAM_IDLE = 4'd0, RAM_WD = 4'd1, RAM_DONE = 4'd2; always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cntw <= 8'd0; RAM_STATE <= RAM_IDLE; ram_dina_r <= 16'h0000; ram_addra_r <= 3'h0; ram_rflag <= 1'b0; end else begin case(RAM_STATE) RAM_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写使能 ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写命令 RAM_STATE <= (del_cnt == 32'd100_000_000)?RAM_WD:RAM_IDLE; end RAM_WD:begin ram_cntw <= (ram_cntw==4'd7)?ram_cntw:ram_cntw+4'd1; //数据产生 ram_addra_r <= (ram_cntw==4'd7)?3'h0:ram_addra_r + 3'h1; //写入地址 ram_dina_r <= (ram_cntw==4'd7)?16'h0000:ram_cntw+4'd1; //写入数据 ram_wea_r <= (ram_cntw==4'd7)?1'b0:1'b1; //写入命令 ram_rflag <= 1'b1; //读标志 RAM_STATE <= (ram_cntw==4'd7)?RAM_DONE:RAM_WD; end RAM_DONE:begin RAM_STATE <= RAM_DONE; end default:begin ram_dina_r <= 16'h0000; end endcase end end //RAM读状态机 reg[3:0] RAMB_STATE; parameter RAMB_IDLE = 4'd0, RAMB_RD = 4'd1, RAMB_DONE = 4'd2; always@(negedge clk_12_5m or negedge RSTn) begin if(!RSTn) begin ram_enb_r <= 1'b0; ram_addrb_r <= 3'h0; ram_cntr <= 8'd0; RAMB_STATE <= RAMB_IDLE; end else begin case(RAMB_STATE) RAMB_IDLE:begin ram_enb_r <= (ram_rflag==1'b1)?1'b1:1'b0; RAMB_STATE <= (ram_rflag==1'b1)?RAMB_RD:RAMB_IDLE; end RAMB_RD:begin ram_cntr <= (ram_cntr==8'd7)?ram_cntr:ram_cntr+8'd1; ram_addrb_r <= (ram_cntr==8'd7)?3'h0:ram_addrb_r+3'h1; RAMB_STATE <= (ram_cntr==8'd7)?RAMB_DONE:RAMB_RD; end RAMB_DONE:begin RAMB_STATE <= RAMB_DONE; end default:ram_enb_r <= 1'b0; endcase end end blk_mem_gen_0 blkram_0 ( .clka(clk_25m), // input wire clka .ena(ram_ena), // input wire ena .wea(ram_wea), // input wire [0 : 0] wea .addra(ram_addra), // input wire [2 : 0] addra .dina(ram_dina), // input wire [15 : 0] dina .clkb(clk_12_5m), // input wire clkb .enb(ram_enb), // input wire enb .addrb(ram_addrb), // input wire [2 : 0] addrb .doutb(ram_doutb) // output wire [15 : 0] doutb ); /********************************************************************** *--*ILA **********************************************************************/ ila_0 ila ( .clk(clk_100m), // input wire clk .probe0(clk_25m), // input wire [0:0] probe0 .probe1(ram_ena), // input wire [0:0] probe1 .probe2(ram_wea), // input wire [0:0] probe2 .probe3(ram_addra), // input wire [2:0] probe3 .probe4(ram_dina), // input wire [15:0] probe4 .probe5(clk_12_5m), // input wire [0:0] probe5 .probe6(ram_enb), // input wire [0:0] probe6 .probe7(ram_addrb), // input wire [2:0] probe7 .probe8(ram_doutb) // input wire [15:0] probe8 ); endmodule
波形:
A口先写4bit,B口读出4bit,B口写入4bit,A口再读出4bit。A口时钟为25M,B口时钟为12.5M。
module top ( input clk_25m ); /********************************************************************** *--*时钟 **********************************************************************/ wire clk_12_5m; wire clk_100m; wire clk_locked; clk_wiz_0 clk_0 ( .clk_out1(clk_100m), .clk_out2(clk_12_5m), .locked(clk_locked), .clk_in1(clk_25m) ); /********************************************************************** *--*复位 **********************************************************************/ wire RSTn; assign RSTn = clk_locked; /********************************************************************** *--*BRAM **********************************************************************/ wire ram_ena; wire ram_wea; wire[2:0] ram_addra; wire[15:0] ram_dina; wire[15:0] ram_douta; wire ram_enb; wire ram_web; wire[2:0] ram_addrb; wire[15:0] ram_dinb; wire[15:0] ram_doutb; reg ram_ena_r; reg ram_wea_r; reg[2:0] ram_addra_r; reg[15:0] ram_dina_r; reg ram_enb_r; reg ram_web_r; reg[2:0] ram_addrb_r; reg[15:0] ram_dinb_r; assign ram_ena = ram_ena_r; assign ram_wea = ram_wea_r; assign ram_addra = ram_addra_r; assign ram_dina = ram_dina_r; assign ram_enb = ram_enb_r; assign ram_web = ram_web_r; assign ram_addrb = ram_addrb_r; assign ram_dinb = ram_dinb_r; reg[31:0] del_cnt; //用于开机延时 reg[7:0] ram_cntw;//用于RAMA reg[7:0] ram_cntr;//用于RAMB reg[0:0] ramb_rflag;//RAMB读取标志 reg[0:0] rama_rflag;//RAMA读取标志 //RAM写状态机 reg[3:0] RAMA_STATE; parameter RAMA_IDLE = 4'd0, RAMA_WD = 4'd1, RAMA_WDONE = 4'd2, RAMA_RD = 4'd3, RAMA_RDONE = 4'd4; always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cntw <= 8'd0; RAMA_STATE <= RAMA_IDLE; ram_dina_r <= 16'h0000; ram_addra_r <= 3'h0; ramb_rflag <= 1'b0; end else begin case(RAMA_STATE) RAMA_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写使能 ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; //写命令 RAMA_STATE <= (del_cnt == 32'd100_000_000)?RAMA_WD:RAMA_IDLE; end RAMA_WD:begin ram_cntw <= (ram_cntw==4'd3)?ram_cntw:ram_cntw+4'd1; //数据产生 ram_addra_r <= (ram_cntw==4'd3)?3'h0:ram_addra_r + 3'h1; //写入地址 ram_dina_r <= (ram_cntw==4'd3)?16'h0000:ram_cntw+4'd1; //写入数据 ram_wea_r <= (ram_cntw==4'd3)?1'b0:1'b1; //写入命令 ram_ena_r <= (ram_cntw==4'd3)?1'b0:1'b1;; //写使能 ramb_rflag <= 1'b1; //读标志 RAMA_STATE <= (ram_cntw==4'd3)?RAMA_WDONE:RAMA_WD; end RAMA_WDONE:begin ram_cntw <= (rama_rflag==1'b1)?3'd4:3'd0; ram_addra_r <= (rama_rflag==1'b1)?3'h4:3'h0; ram_ena_r <= (rama_rflag==1'b1)?1'b1:1'b0; RAMA_STATE <= (rama_rflag==1'b1)?RAMA_RD:RAMA_WDONE; end RAMA_RD:begin ram_cntw <= (ram_cntw==8'd7)?ram_cntw:ram_cntw+8'd1; ram_addra_r <= (ram_cntw==8'd7)?3'h0:ram_addra_r+3'h1; RAMA_STATE <= (ram_cntw==8'd7)?RAMA_RDONE:RAMA_RD; end RAMA_RDONE:begin ram_ena_r <= 1'b0; RAMA_STATE <= RAMA_RDONE; end default:begin ram_dina_r <= 16'h0000; end endcase end end //RAM读状态机 reg[3:0] RAMB_STATE; parameter RAMB_IDLE = 4'd0, RAMB_RD = 4'd1, RAMB_RDONE = 4'd2, RAMB_WD = 4'd3, RAMB_WDONE = 4'd4; always@(negedge clk_12_5m or negedge RSTn) begin if(!RSTn) begin ram_enb_r <= 1'b0; ram_addrb_r <= 3'h0; ram_cntr <= 8'd0; ram_web_r <= 1'b0; ram_dinb_r <= 16'h0000; rama_rflag <= 1'b0; RAMB_STATE <= RAMB_IDLE; end else begin case(RAMB_STATE) RAMB_IDLE:begin ram_enb_r <= (ramb_rflag==1'b1)?1'b1:1'b0; RAMB_STATE <= (ramb_rflag==1'b1)?RAMB_RD:RAMB_IDLE; end RAMB_RD:begin ram_cntr <= (ram_cntr==8'd3)?ram_cntr:ram_cntr+8'd1; ram_addrb_r <= (ram_cntr==8'd3)?3'h0:ram_addrb_r+3'h1; RAMB_STATE <= (ram_cntr==8'd3)?RAMB_RDONE:RAMB_RD; end RAMB_RDONE:begin ram_cntr <= 8'd4; ram_enb_r <= 1'b1; ram_web_r <= 1'b1; ram_dinb_r <= 8'd4; ram_addrb_r <= 3'h4; RAMB_STATE <= RAMB_WD; end RAMB_WD:begin ram_cntr <= (ram_cntr==4'd7)?ram_cntr:ram_cntr+4'd1; //数据产生 ram_addrb_r <= (ram_cntr==4'd7)?3'h0:ram_addrb_r + 3'h1; //写入地址 ram_dinb_r <= (ram_cntr==4'd7)?16'h0000:ram_cntr+4'd1; //写入数据 ram_web_r <= (ram_cntr==4'd7)?1'b0:1'b1; //写入命令 RAMB_STATE <= (ram_cntr==4'd7)?RAMB_WDONE:RAMB_WD; end RAMB_WDONE:begin rama_rflag <= 1'b1; //读标志 ram_enb_r <= 1'b0; RAMB_STATE <= RAMB_WDONE; end default:ram_enb_r <= 1'b0; endcase end end blk_mem_gen_0 blkram_0 ( .clka(clk_25m), // input wire clka .ena(ram_ena), // input wire ena .wea(ram_wea), // input wire [0 : 0] wea .addra(ram_addra), // input wire [2 : 0] addra .dina(ram_dina), // input wire [15 : 0] dina .douta(ram_douta), // output wire [15 : 0] douta .clkb(clk_12_5m), // input wire clkb .enb(ram_enb), // input wire enb .web(ram_web), // input wire [0 : 0] web .addrb(ram_addrb), // input wire [2 : 0] addrb .dinb(ram_dinb), // input wire [15 : 0] dinb .doutb(ram_doutb) // output wire [15 : 0] doutb ); /********************************************************************** *--*ILA **********************************************************************/ ila_0 ila ( .clk(clk_100m), // input wire clk .probe0(clk_25m), // input wire [0:0] probe0 .probe1(ram_ena), // input wire [0:0] probe1 .probe2(ram_wea), // input wire [0:0] probe2 .probe3(ram_addra), // input wire [2:0] probe3 .probe4(ram_dina), // input wire [15:0] probe4 .probe5(ram_douta), // input wire [15:0] probe5 .probe6(clk_12_5m), // input wire [0:0] probe6 .probe7(ram_enb), // input wire [0:0] probe7 .probe8(ram_web), // input wire [0:0] probe8 .probe9(ram_addrb), // input wire [2:0] probe9 .probe10(ram_dinb), // input wire [15:0] probe10 .probe11(ram_doutb) // input wire [15:0] probe11 ); endmodule
7系列FPGA一个 BRAM 的大小为 36K Bits,并且分成两个小的 BRAM 各自为 18K Bits,排列成又分为上下两块,不过使用的时候消耗的 BRAM 资源只能是其块大小的整数倍,就算你只存了 1 bit 也要占用一个 BRAM。也就是说如果BRAM使用不到18Kbit,则需要0.5个BRAM,Vivado显示BRAM使用量则为0.5,如果18Kbit<使用量<=36Kbit,则需要1个BRAM,Vivado显示使用量则为1。
18Kbits=18×1024=18432 bit
36Kbits=36×1024=36864 bit
也就是说一个BRAM可以存储的容量为36864 bit。
配置RAM时会有宽度和深度的选择:
BRAM的使用量=Width×Depth
比如数据宽度为18,数据深度为1024,则需要BRAM的容量为18*1024=18Kbit。
以下为Xilinx官方列举的RAMB36E1的数据宽度对应最大深度表:
分别以不同的数据宽度与深度观察BRAM的资源占用量。
首先要明白:
0.5个BRAM=18Kbit=18432 bit
1 个BRAM =32Kbit=36864bit
按照以下代码,改变代码中RAM深度和IP核的配置,进行实践。
改变1:代码读写深度
`define RAM_NUM 16'd2048
改变2:IP数据深度配置
测试代码:
module top ( input clk_25m ); /********************************************************************** *--*时钟 **********************************************************************/ wire clk_100m; wire clk_locked; clk_wiz_0 clk_0 ( .clk_out1(clk_100m), .locked(clk_locked), .clk_in1(clk_25m) ); /********************************************************************** *--*复位 **********************************************************************/ wire RSTn; assign RSTn = clk_locked; /********************************************************************** *--*BRAM **********************************************************************/ wire ram_ena; wire ram_wea; wire[16:0] ram_addra; wire[17:0] ram_dina; wire[17:0] ram_douta; reg ram_ena_r; reg ram_wea_r; reg[17:0] ram_addra_r; reg[17:0] ram_dina_r; reg[15:0] ram_douta_r; assign ram_ena = ram_ena_r; assign ram_wea = ram_wea_r; assign ram_addra = ram_addra_r; assign ram_dina = ram_dina_r; `define RAM_NUM 16'd20480 reg[31:0] del_cnt;//用于开机延时 reg[15:0] ram_cnt;//用于产生写入RAM的数据 //RAM状态机 reg[3:0] RAM_STATE; parameter RAM_IDLE = 4'd0, RAM_WRITR = 4'd1, RAM_READ = 4'd2, RAM_DONE = 4'd3; always@(negedge clk_25m or negedge RSTn) begin if(!RSTn) begin del_cnt <= 32'd0; ram_cnt <= 16'd0; RAM_STATE <= RAM_IDLE; ram_dina_r <= 18'h0000; ram_addra_r <= 16'h0; end else begin case(RAM_STATE) RAM_IDLE:begin del_cnt <= (del_cnt == 32'd100_000_000)?del_cnt:del_cnt+32'd1;//开机延时 ram_ena_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; ram_wea_r <= (del_cnt == 32'd100_000_000)?1'b1:1'b0; RAM_STATE <= (del_cnt == 32'd100_000_000)?RAM_WRITR:RAM_IDLE; end RAM_WRITR:begin ram_cnt <= (ram_cnt==(`RAM_NUM-1))?16'd0:ram_cnt+16'd1; //数据产生 ram_addra_r <= (ram_cnt==(`RAM_NUM-1))?16'h0:ram_addra_r + 16'h1; ram_dina_r <= (ram_cnt==(`RAM_NUM-1))?18'h0000:ram_cnt+18'd1; ram_wea_r <= (ram_cnt==(`RAM_NUM-1))?1'b0:1'b1; RAM_STATE <= (ram_cnt==(`RAM_NUM-1))?RAM_READ:RAM_WRITR; end RAM_READ:begin ram_cnt <= (ram_cnt==(`RAM_NUM-1))?16'd0:ram_cnt+16'd1; //读取计数 ram_ena_r <= 1'b1; ram_wea_r <= 1'b0; ram_addra_r <= (ram_cnt==(`RAM_NUM-1))?16'h0:ram_addra_r + 16'h1; RAM_STATE <= (ram_cnt==(`RAM_NUM-1))?RAM_DONE:RAM_READ; end RAM_DONE:begin RAM_STATE <= RAM_DONE; end default:begin ram_dina_r <= 18'h0000; end endcase end end blk_mem_gen_0 blkram_0( .clka(clk_25m), // input wire clka .ena(ram_ena), // input wire ena .wea(ram_wea), // input wire [0 : 0] wea .addra(ram_addra), // input wire [15 : 0] addra .dina(ram_dina), // input wire [17 : 0] dina .douta(ram_douta) // output wire [17 : 0] douta ); /********************************************************************** *--*ILA **********************************************************************/ ila_0 ila ( .clk(clk_100m), // input wire clk .probe0(clk_25m), // input wire [0:0] probe0 .probe1(ram_ena), // input wire [0:0] probe1 .probe2(ram_wea), // input wire [0:0] probe2 .probe3(ram_addra), // input wire [15:0] probe3 .probe4(ram_dina), // input wire [17:0] probe4 .probe5(ram_douta) // input wire [17:0] probe5 ); endmodule
实验一、
数据深度:8
宽度:16
占用BRAM内存:8*16=128 bit
通过以上的分析不足18Kbit,应该占用一个18Kb的BRAM即0.5个BRAM。
实验二、
数据深度:1024
宽度:18
占用BRAM内存:1024×18=18 Kbit
通过以上的分析18Kbit正好为0.5个BRAM为,应该占用一个18Kb的BRAM即0.5个BRAM。
实验三、
数据深度:1025
宽度:18
占用BRAM内存:1025×18=18 Kbit +18bit
通过以上的分析超过18Kbit,应该占用2个18Kb的BRAM即1个BRAM。
实验四、
数据深度:2048
宽度:18
占用BRAM内存:2048×18=2×18 Kbit
通过以上的分析超过18Kbit但小于36Kbit,应该占用2个18Kb的BRAM即1个BRAM。
实验五、
数据深度:2049
宽度:18
占用BRAM内存:2048×18=2×18 Kbit+18bit
通过以上的分析超过36Kbit,应该占用3个18Kb的BRAM即1.5个BRAM。
实验五、
数据深度:20480
宽度:18
占用BRAM内存:20480×18=20×18 Kbit=10×36 Kbit
通过以上的分析正好为10个36Kbit,应该占用10个36Kb的BRAM即10个BRAM。
ila IP配置如下
ila数据总宽度:55
ila数据深度:1024
55*1024=56320=55Kbits
需要BRAM 至少55/36=1.52→2个(往上取整)。
★★★如有错误欢迎指导!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。