Xilinx_RAM_IP核的使用

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内存: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。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 ila占用BRAM资源分析

ila IP配置如下
在这里插入图片描述在这里插入图片描述
ila数据总宽度:55
ila数据深度:1024
55*1024=56320=55Kbits
需要BRAM 至少55/36=1.52→2个(往上取整)。
在这里插入图片描述
在这里插入图片描述

★★★如有错误欢迎指导!!!

  • 9
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字硬鉴

你的鼓励就是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值