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。
文章目录
1.RAM分类
随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。RAM工作时可以随时从任何一个指定的地址写入(存入)或读出(取出)信息。它与ROM的最大区别是数据的易失性,即一旦断电所存储的数据将随之丢失 。RAM在计算机和数字系统中用来暂时存储程序、数据和中间结果。
1.1 单口RAM
单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。
读写全在A口:
1.2 伪双口RAM
伪双口RAM,一个端口只读,另一个端口只写。
A口写、B口读:
1.3 双口RAM
双口RAM两个端口都可以读写。
A口可读写、B口可读写:
2.FPGA内部BRAM介绍
块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,排列成又分为上下两块。内部视图如下:
3.RAM读写
IP配置:
端口介绍:
3.1 读写模式
Xilinx_RAM IP提供的读写模式有Write First Mode、Read First Mode、No Change Mode。
Write First Mode :数据先写入RAM中,然后再下一个时钟输出该数据。
Read First Mode :数据先写入RAM中,在下一周期输出内存中该地址的上一次数据。
No Change Mode :输出在写入操作器件保持不变。
3.2 单口RAM读写
单口与双口的区别在于,单口只有一组数据线与地址线,因此读写不能同时进行。
以下代码实现单口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模式):
3.3伪双口RAM读写
伪双口RAM,一个端口只读,另一个端口只写。A口写、B口读
3.3.1 伪双口RAM读写(AB口同时钟)
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
3.3.2 伪双口RAM读写(AB口以不同时钟写读)
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
波形:
3.4 双口RAM读写(不同时钟)
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
4.BRAM容量
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的数据宽度对应最大深度表:
4.1 BRAM资源占用分析
分别以不同的数据宽度与深度观察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内存:816=128 bit
通过以上的分析不足18Kbit,应该占用一个18Kb的BRAM即0.5个BRAM。
实验二、
数据深度:1024
宽度:18
占用BRAM内存:102418=18 Kbit
通过以上的分析18Kbit正好为0.5个BRAM为,应该占用一个18Kb的BRAM即0.5个BRAM。
实验三、
数据深度:1025
宽度:18
占用BRAM内存:102518=18 Kbit +18bit
通过以上的分析超过18Kbit,应该占用2个18Kb的BRAM即1个BRAM。
实验四、
数据深度:2048
宽度:18
占用BRAM内存:204818=2×18 Kbit
通过以上的分析超过18Kbit但小于36Kbit,应该占用2个18Kb的BRAM即1个BRAM。
实验五、
数据深度:2049
宽度:18
占用BRAM内存:204818=2×18 Kbit+18bit
通过以上的分析超过36Kbit,应该占用3个18Kb的BRAM即1.5个BRAM。
实验五、
数据深度:20480
宽度:18
占用BRAM内存:2048018=20×18 Kbit=10×36 Kbit
通过以上的分析正好为10个36Kbit,应该占用10个36Kb的BRAM即10个BRAM。
4.2 ila占用BRAM资源分析
ila IP配置如下
ila数据总宽度:55
ila数据深度:1024
55*1024=56320=55Kbits
需要BRAM 至少55/36=1.52→2个(往上取整)。
★★★如有错误欢迎指导!!!