Xilinx RAM IP核的使用及注意事项

news2024/11/23 0:34:51

对于RAM IP核(Block Memory Generator核)的使用以及界面的配置介绍,文章RAM的使用介绍进行了较详细的说明,本文对RAM IP核使用中一些注意的地方加以说明。

文章目录

  • 三种RAM的区别
    • 单端口RAM(Single-port RAM)
    • 简单双端口RAM(Simple Dual-port RAM)
    • 真双端口RAM(True Dual-port RAM)
  • 典型应用
  • 使能端口
  • 位宽与深度
  • 位宽转换是的数据地址对应关系
  • BLOCK RAM的读写模式
    • 先写模式(Write First Mode)
    • 先读模式(Read First Mode)
    • 不变模式(No Change Mode)
  • 读数据延迟
  • 冲突(Collision Behavior)
  • 附录
    • 附录1
    • 附录2
    • 附录3
  • 参考

三种RAM的区别

单端口RAM(Single-port RAM)

单端口RAM只有一个地址接口addra,对应有一对读写的数据接口dina和douta,ena和wea为1时,向addra地址中写数据;wea为0时,通过addra地址中读数据。
在这里插入图片描述

简单双端口RAM(Simple Dual-port RAM)

简单双端口RAM相当于读写分开,addra、dina和wea完成写,addrb和doutb完成读,你读你的,我写我的,互不干扰。
简单双端口RAM有两个地址接口addra和addrb,但却只有一对读写的数据接口dina和doutb,所以也叫他伪双端口;
A端口用来写RAM,B端口用来读RAM,可以同时进行读写数据
在这里插入图片描述

真双端口RAM(True Dual-port RAM)

真双端口RAM是两组地址对同一块Memory进行读写(如下图官方手册上有说明),对于AB两端口中的一个端口A或B其实就是单端口RAM的控制逻辑。我们在外部控制的时候如果只把A端口用作写,B端口用作读,那其实就和伪双端口RAM一样了。
在这里插入图片描述
真双端口RAM拥有两个地址接口,并且每个地址接口都有对应的读写数据接口,所以叫真双端口RAM,真双端口RAM支持两个端口同时对Memory进行访问,有效的提高了访问速度。同样的,对于双端口的ROM,我们可以通过两个端口同时读取ROM中的数据,所以如果两个processor是访问同样的一组数据的话,就不需要例化两个相同的ROM了。

在这里插入图片描述

总结:无论是单端口RAM、简单双端口RAM还是真双端口RAM,他们都只有一块Memory,并且他们都是通过寻址的方式访问这块Memory,在配置相同的位宽和存储深度时,所使用的BRAM资源是一样的,区别在于对应Memory的接口数量不同,也即是所谓的端口不同。

典型应用

Block Memory Generator(BMG)核用于创建定制存储器,以满足任何应用的需要。典型的应用包括:

存储器类型典型应用
Single-port RAMProcessor scratch RAM, look-up tables
Simple Dual-port RAMContent addressable memories, FIFOs
True Dual-port RAMMulti-processor storage
Single-port ROMProgram code storage, initialization ROM
Dual-port ROMSingle ROM shared between two processors/systems

使能端口

ENA、ENB置0时,写或读数据无效,输出的DOUT数据保存不变。

如果使用使能端口(Use ENA Pin),那么会多一个ena/enb。A端口在wea和ena同时为1时写入数据有效。B端口在enb为1时写入数据有效。选择始终使能(Always Enabled)时,在wea为1时写入数据有效。
在这里插入图片描述

位宽与深度

读写位宽:读写时的数据位宽,如写一个数8‘hcd即为8位宽。
读写深度:需要存储的数据的个数。例如需要存储4个数据此处填写4,而不要填写地址的位宽2。
在这里插入图片描述

位宽转换是的数据地址对应关系

在AB两端口进行位宽转换时无论A端口低位宽数据转换为B端口高位宽数据,或是A端口高位宽数据转换为B端口低位宽数据。
低位宽的起始地址从高位宽起始地址中数据的低位开始取数据。

如下:16bit转32bit数据

在这里插入图片描述

16bit转4bit数据

在这里插入图片描述

验证代码见附录1。

BLOCK RAM的读写模式

读写支持3种模式,分别是Write First Mode, Read First Mode, No Change Mode。

DOUTA和WEA相关联,DOUTB和WEB相关联,在Single-port RAM和True Dual-port RAM的两种RAM格式中会体现,也就是说Single-port RAM或True Dual-port RAM中,在WEA/WEB写使能置1时DOUTA(WEB写使能置1时DOUTB)也会有输出,并且此时的输出和读写模式有关。

通过下面的介绍可以看出:

单端口和真双端口:该读写模式的设置只会影响在写数据(wea=1)时DOUTA的输出,在wea=0时实际上输出的数据就是上一次写在地址中的数据。模式设置的不同在写数据时的DOUT数据作为一些条件判断等可能会有一定的用途。

简单双端口:读端口B的数据doutb由于有读地址线的控制,所以读出的数据和读地址addrb对应;在读写冲突时,读出的数据为此时写入的数据,所以读写模式对于简单双端口不起作用,无需关心。(验证代码见附录2)
在这里插入图片描述

接下来我们向单端口RAM里写入数据,写满后读出RAM中的数据,对比三种效果。

先写模式(Write First Mode)

这种模式下:
1)写操作:设置WEA为1写入当前地址的数据,在下一个时钟DOUTA会输出这个地址新写入的数据
2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟DOUTA会输出这个地址的数据。

从 Write First 模式仿真图可以看到,当我们往 RAM 里写数据是,就有数据读出了,并且读出的是新写入的数据。
在这里插入图片描述

先读模式(Read First Mode)

这种模式下:
1)写操作:设置WEA为1写入当前地址的数据,而且在下一个时钟DOUTA会输出这个地址的原先的数据
2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟DOUTA会输出这个地址的数据。

由于我们配置的单端口RAM是读优先模式,所以当我们往RAM里写入数据时,RAM读出的数据为写入前RAM中存储的数据,故当我们第一次往RAM写数据时,RAM读出的数据为初始的0,在第二次写入不同数据到地址时,由于读优先,所以DOUTA读出了第一次存入的数据。
在这里插入图片描述

不变模式(No Change Mode)

这种模式下:
1)写操作:设置WEA为1写入当前地址的数据,和前面两种方式不一样,DOUT保存不变。
2)读操作:设置WEA为0读出当前地址的数据,在下一个时钟DOUTA会输出这个地址的数据。

由于我们配置的单端口RAM是不变模式,所以当我们往RAM里写入数据时,RAM读出的数据为上一次读出的数据并保持不变,故当我们第一次往RAM写数据时,RAM读出的数据为初始的0,在第二次写入不同数据到地址时,DOUTA保持读0地址的数据1不变。
在这里插入图片描述
验证代码见附录3。

读数据延迟

IP核的输出给定读地址后,读数据会滞后至少一个时钟周期输出,具体输出延迟的周期由Port A/B Optional Output Registers决定。
在这里插入图片描述

有寄存器和无寄存器输出,可以达到的最高时钟频率不同,所以增加寄存器输出可以提高速度。
在这里插入图片描述

最终的延时可在总结页面中查看到。
在这里插入图片描述

冲突(Collision Behavior)

冲突一般在真双端口RAM读写会考虑。由于其支持Port同时写入,那么就会存在这个冲突的问题,在冲突发生的时候,存储内容可预知;
冲突主要出现在一下场景 :

  • 两个Port的异步时钟,一个port正在往一个地址写数据,另一个port在指定时间内,必须不能够去读写这个地方;
  • 两个Port同步时钟的情况下,两个Port同时企图写同一个memory的情况下;
  • 使用Byte-Writes的时候,正好同时写到了同一个地方的情况下;

遇到此类问题可参考官方文档与参考链接中的介绍。

附录

附录1

简单双端口RAM、读位宽16、读深度8、写位宽32、写深度4、无输出寄存器、使用使能端口、从0地址开始写。
RTL部分:

module ram_w_r
#(
	//=========================< Parameter >==============================
	parameter 				WA_WIDTH		=	3	,	//写地址位宽
	parameter 				WD_WIDTH		=	16	,	//写数据位宽
	parameter 				RA_WIDTH		=	2	,	//读地址位宽
	parameter 				RD_WIDTH		=	32	,	//读数据位宽
	parameter				WRAM_ADDR_MAX	=	8		//写地址最大深度
)
(
	//=========================< Port Name >==============================
	//input
	input 		wire 					i_sys_clk_wr   		,
	input 		wire 					i_sys_clk_rd   		,
	input 		wire					i_rst_n				,
	
	input 	  	wire    [WD_WIDTH-1:0]	i_data				,//准备写入RAM中的数据
	input 	  	wire    				i_data_valid		,//数据有效标志
	
	input 	  	wire    				i_rram_en			,//读取的RAM数据使能	
	input 	  	wire    [RA_WIDTH-1:0]	i_rram_addr			,//读取的RAM地址	                                                    
	//output	                                            
	output 		wire	[RD_WIDTH-1:0]	o_data	  			,//读取的RAM数据
	output		reg						o_data_valid		,//读取的RAM数据有效
	output		wire	[WA_WIDTH-1:0]	o_wram_addr			 //写RAM地址
	
);
	//=========================< Variable >==============================
	reg		[WA_WIDTH-1:0]	r_wram_addr	;
	reg 	[WD_WIDTH-1:0]	r_wram_data	;	
	reg						r_wram_en	;
	
	reg 	[1:0]			WR_S		;//写状态机
	//==========================================================================
	//==    wr_ram_address
	//==========================================================================
	assign	o_wram_addr = r_wram_addr	;
	//==========================================================================
	//==    write RAM
	//==========================================================================
	always @(posedge i_sys_clk_wr)begin
		if(!i_rst_n)begin 
			r_wram_en	<= 0;
			r_wram_addr	<= 0;
			r_wram_data	<= 0;
			WR_S     	<= 2'd0;
		end
		else begin
			case(WR_S)
			0:begin
				if(i_data_valid)begin		//数据有效
					r_wram_en    <= 1'b1;	//设置写使能
					r_wram_addr  <= 0; 		//设置地址从0开始
					r_wram_data  <= i_data;	//写数据		
					WR_S   <= 2'd1; 		//下一个状态
				end
				else begin
					r_wram_en    <= 1'b0;	
					r_wram_addr  <= r_wram_addr;
					r_wram_data  <= r_wram_data;			
				end
			end
	
			1:begin
				if(i_data_valid & (r_wram_addr == (WRAM_ADDR_MAX-1)))begin
					r_wram_en    <= 1'b1;	
					r_wram_addr  <= 0;
					r_wram_data  <= i_data;	
				end
				else if(i_data_valid)begin 
					r_wram_en    <= 1'b1;	//设置写使能
					r_wram_addr <= r_wram_addr + 1'b1;//通道A地址,地址增加1
					r_wram_data  <= i_data;
				end
				else begin
					r_wram_en    <= 1'b0;	
					r_wram_addr  <= r_wram_addr;
					r_wram_data  <= r_wram_data;				
				end
			end
			default:WR_S   <= 2'd0;
			endcase
		end
	end
	//==========================================================================
	//==    read RAM
	//==========================================================================
	always @(posedge i_sys_clk_rd)begin//o_data_valid根据配置的输出寄存器有关,至少滞后地址一个时钟周期
		if(!i_rst_n)
			o_data_valid <= 1'b0;
		else
			o_data_valid <= i_rram_en;
	end
	
	sdp_ram_16_8_32_4 u_sdp_ram_16_8_32_4 (
		.clka		(i_sys_clk_wr	),  // input wire clka
		.ena		(i_rst_n		),  // input wire ena
		.wea		(r_wram_en		),  // input wire [0 : 0] wea
		.addra		(r_wram_addr	),  // input wire [2 : 0] addra
		.dina		(r_wram_data	),  // input wire [15 : 0] dina
		.clkb		(i_sys_clk_rd	),  // input wire clkb
		.enb		(i_rram_en		),  // input wire enb
		.addrb		(i_rram_addr	),  // input wire [1 : 0] addrb
		.doutb		(o_data			)  	// output wire [31 : 0] doutb
	);
	
endmodule

仿真部分(5us),每写两个地址读一次:

`timescale 	1ns/1ps 	//时间精度
//========================================================================
// 	module_name.v	:tb_ram_w_r.v
//========================================================================
module tb_ram_w_r();

	//=========================< Parameter >==============================
	parameter 				CLK_PERIOD0		=	20		    	 ;//设置时钟信号周期
	parameter 				HALF_CLK_PERIOD0	=	CLK_PERIOD0/2;//生成时钟信号半周期
	
	parameter 				CLK_PERIOD1		=	10		   		 ;//设置时钟信号周期
	parameter 				HALF_CLK_PERIOD1	=	CLK_PERIOD1/2;//生成时钟信号半周期
	
	parameter 				WA_WIDTH		=	3	;	//写地址位宽
	parameter 				WD_WIDTH		=	16	;	//写数据位宽
	parameter 				RA_WIDTH		=	2	;	//读地址位宽
	parameter 				RD_WIDTH		=	32	;	//读数据位宽
	parameter				WRAM_ADDR_MAX	=	8	;	//写地址最大深度
	//=========================< Port Name >==============================
	//input
	reg 					clk_w							;
	reg 					clk_r							;
	reg 					rst_n							;
	
	reg		[3:0]			cnt_flag						;
	reg		[1:0]			cnt_rd							;
	reg		[3:0]			cnt_data						;
	
	reg    	[WD_WIDTH-1:0]	i_data							;
	reg    					i_data_valid					;
	                                            
	reg	    				i_rram_en						;
	reg	    [RA_WIDTH-1:0]	i_rram_addr						;
															
	wire	[RD_WIDTH-1:0]	o_data	  						;
	wire					o_data_valid					;
	wire	[WA_WIDTH-1:0]	o_wram_addr						;
	//==========================< Clock block >============================
	always 	  	#HALF_CLK_PERIOD0		clk_w = ~clk_w;
	always 	  	#HALF_CLK_PERIOD1		clk_r = ~clk_r;
	//==========================< Reset block >============================
	 initial begin
		clk_w 	= 	1'b1	;
		clk_r 	= 	1'b1	;
		rst_n  <= 	1'b0	;
		#100
		rst_n  <= 	1'b1	;
	end

	//==========================< Module Instance >============================
	 ram_w_r
	#(
		.WA_WIDTH		(WA_WIDTH		),
        .WD_WIDTH	    (WD_WIDTH		),
        .RA_WIDTH	    (RA_WIDTH		),
        .RD_WIDTH	    (RD_WIDTH		),
        .WRAM_ADDR_MAX  (WRAM_ADDR_MAX	)						
	)	
	u_ram_w_r(	
		.i_sys_clk_wr 					(clk_w 				), //input clk_w
		.i_sys_clk_rd					(clk_r				),
		.i_rst_n		 				(rst_n 				), //input rst_n
		
		.i_data							(i_data				),
		.i_data_valid                   (i_data_valid   	),
									     				
		.i_rram_en					    (i_rram_en			),
		.i_rram_addr	                (i_rram_addr		),
									     				
		.o_data	  	                    (o_data	  	    	),
		.o_data_valid                   (o_data_valid   	),
		.o_wram_addr	                (o_wram_addr		)
	);
	
	//==========================================================================
	//==    cnt_flag 用于控制读有效和数据
	//==========================================================================
	always @(posedge clk_w)begin
		if(!rst_n)
			cnt_flag <= 0;
		else
			cnt_flag <= cnt_flag + 1'b1;
	end
	//==========================================================================
	//==    cnt_data 用于构造RAM写入数据
	//==========================================================================
 	always @(posedge clk_w)begin
		if(!rst_n)
			cnt_data <= 0;
		else if(&cnt_flag)
			cnt_data <= cnt_data + 4'd4;
		else
			cnt_data <= cnt_data;
	end 
	//==========================================================================
	//==    write data
	//==========================================================================
	always @(posedge clk_w)begin
		if(!rst_n)begin
			i_data <= 0;
			i_data_valid <= 1'b0;
		end
		else if(&cnt_flag)begin//计数到全1
			i_data <= {cnt_data+4'd3,cnt_data+4'd2,cnt_data+4'd1,cnt_data};
			i_data_valid <= 1'b1;
		end
		else begin
			i_data <= i_data;
			i_data_valid <= 1'b0;
		end
	end
	//==========================================================================
	//==    cnt_rd 用于读使能判断
	//==========================================================================
		always @(posedge clk_r)begin
		if(!rst_n)
			cnt_rd <= 0;
		else if((cnt_rd == 2'd3) &&  ((o_wram_addr == 1) || (o_wram_addr == 3)  || (o_wram_addr == 5)  || (o_wram_addr == 7)) )
			cnt_rd <= cnt_rd;
		else if((o_wram_addr == 0) || (o_wram_addr == 2)  || (o_wram_addr == 4)  || (o_wram_addr == 6))
			cnt_rd <= 0;
		else if((o_wram_addr == 1) || (o_wram_addr == 3)  || (o_wram_addr == 5)  || (o_wram_addr == 7))
			cnt_rd <= cnt_rd + 1'b1;
	end
	//==========================================================================
	//==    读ram控制
	//==========================================================================
	always @(posedge clk_r)begin
		if(!rst_n)begin
			i_rram_en <= 1'b0;	
			i_rram_addr <= 0;
		end
		else if( (cnt_rd == 2'd2) && (o_wram_addr == 1) )begin
			i_rram_en <= 1'b1;	
			i_rram_addr <= 0;
		end
		else if(cnt_rd == 2'd2)begin
			i_rram_en <= 1'b1;	
			i_rram_addr <= i_rram_addr + 1'b1;
		end
		else begin
			i_rram_en <= 1'b0;	
			i_rram_addr <= i_rram_addr;
		end
	end
endmodule

仿真结果与数据:
在这里插入图片描述

附录2

简单双端口RAM、读写位宽32、读写深度1024、无输出寄存器、使用使能端口、不同读写模式修改IP核即可。
RTL部分:

module bram_test(
	input I_rstn, //系统复位输入
	input I_sysclk //系统时钟输入
);

reg [9:0]addra;    //通道A 地址
reg [7:0]wr_frame; //帧计数器
reg [1:0]WR_S;     //写状态机
reg ena;           //通道A使能
reg wea;           //通道A写使能

reg [9:0]addrb;   //读通道B地址
reg [1:0]RD_S;    //读状态机
reg enb;          //通道B使能

wire [31:0] dina;    //bram 数据输入
wire [31:0] doutb;   //bram 数据输出

assign dina = wr_frame+addra;  //输入的数据包:帧信号+通道A地址

always @(posedge I_sysclk)begin

    if(!I_rstn)begin //复位重置相关寄存器
       wr_frame <= 8'd0;
       addra    <= 10'd0;
       ena      <= 1'b1;
       wea      <= 1'b0;
       WR_S     <= 2'd0;
    end

    else begin
        case(WR_S)
        0:begin
             addra  <= 10'd0; //设置地址从0开始
             ena    <= 1'd1;  //设置通道A使能
             wea    <= 1'b1;  //设置写使能
             WR_S   <= 2'd1;  //下一个状态
        end

        1:begin
            if(addra != 10'd1023)begin //如果写地址不等于1023,
               wea   <= 1'b1; //设置写使能
               ena   <= 1'b1; //设置通道A使能
               addra <= addra + 1'b1;//那么通道A地址,每个时钟地址增加1
            end
            else begin //否则代表完成了1帧数据写入到BRAM
               wea   <= 1'b0; //设置写使能为0,停止写
               ena   <= 1'b0; //设置通道A使能为0
               wr_frame <= wr_frame +1'b1;//帧计数器
               WR_S   <= 2'd2;//下一个状态
            end
        end

        2:begin
            if(RD_S == 2'd2) //如果读操作完成
               WR_S   <= 2'd0; //回到状态0重新开始
        end
        default:WR_S   <= 2'd0;
        endcase
     end
end

always @(posedge I_sysclk)begin
    if(!I_rstn)begin //复位重置相关寄存器
       addrb    <= 10'd0;
       enb      <= 1'b0;
       RD_S     <= 2'd0;
    end
    else begin
        case(RD_S)
        0:begin
            enb     <= 1'b0; //设置读使能0
            addrb   <= 10'd0; //设置读地址从0开始
             if(addra == 10'd1023)begin//读数据在写数据的第1023个地址开始
                enb   <= 1'b1; //使能读通道
                RD_S  <= 2'd1; //下一状态
             end  
        end

        1:begin
            enb    <= 1'b1;//设置读使能1
            if(addrb != 10'd1023)如果读地址不等于1023,
               addrb  <= addrb + 1'b1;//那么通道B地址,每个时钟地址增加1
            else
               RD_S   <= 2'd2;//下一状态
        end

        2:begin
            RD_S   <= 2'd0;//下一状态
        end
        default:RD_S   <= 2'd0;
        endcase
     end
end  

//例化BRAM IP,简单双口RAM
blk_mem_gen_0 bram_inst (
      .clka	(I_sysclk	),    //通道A时钟输入
      .ena	(ena	 	),    //通道A使能
      .wea	(wea	 	),    //写使能
      .addra(addra	 	),     //通道A地址
      .dina	(dina	 	),     //通道A数据输入
      .clkb	(I_sysclk	),    //通道B时钟输入
      .enb	(enb	 	),    //通道B使能
      .addrb(addrb	 	),    //通道B地址
      .doutb(doutb	 	)     //通道B数据输出
    );
endmodule

仿真部分(100us):

`timescale 1ns / 1ns//仿真时间刻度/精度

module tb_bram_test;
localparam  SYS_TIME = 20 ;//定义时钟周期 单位ns

reg   I_sysclk; //系统时钟
reg   I_rstn;   //系统复位

//例化bram_test
bram_test bram_test_inst
(
	.I_sysclk	(I_sysclk	),
	.I_rstn		(I_rstn		)
);

//初始化
initial begin
    I_sysclk  = 1'b0;
    I_rstn = 1'b0;
    #100;//产生100ns的系统复位
    I_rstn = 1'b1;//复位完成
end

//产生仿真时钟
always #(SYS_TIME/2) I_sysclk= ~I_sysclk;                                                 
endmodule

附录3

单端口RAM、读写位宽2、读写深度4、无输出寄存器、始终使能、不同读写模式修改IP核即可
RTL代码:

 module bram_test
(
input wire sys_clk , //系统时钟,频率 50MHz
input wire [2:0] addra , //输入 ram 读写地址
input wire [2:0] dina , //输入 ram 写入数据
input wire wea , //输入 ram 写使能

output wire [2:0] douta //输出读 ram 数据

 );
 
 //---------------ram_4x2bit_inst--------------
 blk_mem_gen_0 u_blk_mem_gen_0 
 (
 .clka (sys_clk ), //使用系统时钟作为读写时钟
 .addra (addra ), //读写地址线
 .dina (dina ), //输入写入 RAM 的数据
 .wea (wea ), //写 RAM 使能
 .douta (douta ) //输出读 RAM 数据
 );
 
endmodule

仿真(1us):

`timescale 1ns/1ns
module tb_bram_test();
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg [1:0] addra ;
reg wea ;
reg wr_flag ;
reg [1:0] cnt;
//wire define
wire [1:0] dina ;
wire [1:0] douta ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

initial
begin
	sys_clk = 1'b1 ;
	sys_rst_n <= 1'b0 ;
	#200 sys_rst_n <= 1'b1 ;
end

//sys_clk:模拟系统时钟,每 10ns 电平取反一次,周期为 20ns,频率为 50Mhz
always #10 sys_clk = ~sys_clk;

//写完成标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
	wr_flag <= 1'b0;
else if(addra == 2'd3)
	wr_flag <= wr_flag + 1'b1;
else
	wr_flag <= wr_flag;
//wea:产生写 RAM 使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
	wea <= 1'b0;
else if(wr_flag == 1'b1)
	wea <= 1'b0;
else
	wea <= 1'b1;

//addra:读写地址(0~3 循环)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
	addra <= 2'd0;
else if(addra == 2'd3)
	addra <= 2'd0;
else
	addra <= addra + 1'b1;

//写使能为高时产生写数据 0~3
assign dina = (wea == 1'b1) ? (addra+cnt) : 2'd0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
	cnt <= 2'd0;
else if(addra == 2'd3 && wea == 1'b1)
	cnt <= cnt+2'd1;
else
	cnt <= cnt;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//---------------ram_inst--------------
bram_test bram_test_inst
(
.sys_clk (sys_clk ), //系统时钟,频率 50MHz
.addra (addra ), //输入 ram 读写地址
.dina (dina ), //输入 ram 写入数据
.wea (wea ), //输入 ram 写使能

.douta (douta ) //输出读 ram 数据

);

endmodule

参考

PG058-Block Memory Generator.pdf
RAM(ip 核与原语的使用)介绍
浅谈XILINX BRAM的基本使用 - 米联客(milianke)
不详细的讲一下Xilinx的BMG:单端口和双端口RAM的区别
XILINX BMG (Block Memory Generator)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1679429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Security入门教程:实现自定义用户配置

在上一篇文章中&#xff1a;Spring Security入门教程&#xff1a;利用Spring Security实现安全控制 我们学会了使用Spring Security实现安全控制&#xff0c;学会了他的基础使用&#xff0c;这节课我们来学习一下它的自定义的功能&#xff0c;更深入的了解和使用Spring Securit…

计算机组成结构—寻址方法

目录 一、指令寻址 二、数据寻址 1.立即寻址 2.直接寻址 3.间接寻址 4.隐含寻址 5.寄存器寻址 6.寄存器间接寻址 7.基址寻址 8.变址寻址 9.相对寻址 10. 堆栈寻址 寻址方式是寻找指令或操作数有效地址的方式&#xff0c;也就是指确定本条指令的数据地址&#xff0c;…

C++错题集(持续更新ing)

Day 1 一、选择题 解析&#xff1a; 在数字不会溢出的前提下&#xff0c;对于正数和负数&#xff0c;有&#xff1a; 1&#xff09;左移n位&#xff0c;相当于操作数乘以2的n次方&#xff1b; 2&#xff09;右移n位&#xff0c;相当于操作数除以2的n次方。 解析&#xff1a…

深度盘点在当今经济形势下资深项目经理或PMO的或去或从

在当今经济形势下&#xff0c;资深项目经理&#xff08;Project Manager&#xff09;或项目管理办公室&#xff08;PMO&#xff09;的去向和选择受到多种因素的影响。以下是对他们可能面临的或去或从的深度盘点&#xff1a; 1、发展去向 1. 深化专业领域&#xff1a;在经济形势…

2024年【制冷与空调设备运行操作】考试内容及制冷与空调设备运行操作考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作考试内容是安全生产模拟考试一点通生成的&#xff0c;制冷与空调设备运行操作证模拟考试题库是根据制冷与空调设备运行操作最新版教材汇编出制冷与空调设备运行操作仿真模拟考试。2024年【制冷…

如何实现Linux双网卡同时连接内网和外网的配置?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

快速查看字符对应的ASCII码

1、借助gdb查看 打印字符串用双引号括起来打印单个字符用单引号括起来x 表示十六机制d 表示十进制t 表示二进制 2、借助二进制查看软件 第一步&#xff1a;把要查看的字符保存到文本文件中第二步&#xff1a;借助二进制查看工具&#xff08;比如&#xff1a;Hex Editor Neo&am…

Windows 10无法远程桌面连接:原因及解决方案

在信息技术日益发展的今天&#xff0c;远程桌面连接已成为企业日常运维、技术支持乃至个人用户远程办公的必备工具。然而&#xff0c;有时我们可能会遇到Windows 10无法远程桌面连接的问题&#xff0c;这无疑会给我们的工作和生活带来诸多不便。 原因分析 1、远程访问未启用&a…

对比测评3款BI分析工具

前不久&#xff0c;一位准备入职阿里的学弟问我&#xff0c;他要做电商数据分析&#xff0c;电商有庞杂的标签、模型、数据和业务逻辑&#xff0c;菜鸟应该要具备什么样的分析能力啊&#xff1f; 我看了他的岗位职责&#xff0c;主要是负责经营决策支持、专题分析和数据看板搭…

智能决策引擎架构设计

智能决策引擎概述 智能决策引擎系统是在大数据支撑下,根据行业专家经验制定规则策略、以及机器学习/深度学习/AI领域建立的模型运算,对当前的业务进行全面的评估,并给出决策结果的一套系统。 一套商业决策引擎系统动辄百万而且需要不断加钱定制,大多数企业最终仍会走上自研…

纯血鸿蒙APP实战开发——Worker子线程中解压文件

介绍 本示例介绍在Worker 子线程使用ohos.zlib 提供的zlib.decompressfile接口对沙箱目录中的压缩文件进行解压操作&#xff0c;解压成功后将解压路径返回主线程&#xff0c;获取解压文件列表。 效果图预览 使用说明 点击解压按钮&#xff0c;解压test.zip文件&#xff0c;显…

ASP.NET在线二手交易系统的设计与实现

摘 要 随着当今社会信息技术的进步&#xff0c;基于互联网的各种应用日益受到了人们的重视&#xff0c;二手商品的重新利用也逐渐被人们关注&#xff0c;二手交易系统就在这种形势下产生了&#xff0c;它利用网络&#xff0c;改变了人们的购物方式。 本文是基于现代二手交易…

网络安全护网行动:形式主义还是真有价值?

中国每年都投入大量人力物力进行护网行动&#xff0c;如网络攻防演练、黑客技术研究等。有人质疑这些行动是否只是形式主义&#xff0c;缺乏真正的价值。然而&#xff0c;本文将深入解释这些护网行动的原因&#xff0c;并阐明其对信息安全发展的真实价值。 网络信息安全问题的…

Google Ads被暂停的原因,如何防范?

跨境出海业务少不了需要做Google Ads推广业务&#xff1b;其中让投手们闻风丧胆的消息就是帐户被暂停。当 Google 检测到任何违反其政策且可能损害用户在线体验的行为时&#xff0c;就会发生这种情况。那么如何在做广告推广的同时&#xff0c;保证账号不被封禁呢&#xff1f;看…

学前端网络安全这块还不懂?细说CSRF

什么是CSRF&#xff1f; 举个栗子&#xff0c;比如我们需要在某个博客上删除一个文章&#xff0c;攻击者首先在自己的域构造一个页面&#xff0c;使用了一个img标签&#xff0c;其地址指向了删除博客的链接。攻击者诱使目标用户&#xff0c;也就是博客主访问这个页面&#xff…

Linux系统 的持续学习

昨天学习了目录结构、补充命令和配置网络&#xff0c;其中配置网络用了nat方法&#xff0c;今天学习用桥接方法&#xff0c;通配符、正则表达式的一部分内容。 桥接模式 如果重网卡失败&#xff1a; 1.检查配置文件是否正确 2.检查虚拟器编辑器有没有选对&#xff08;网卡类…

二进制搭建 k8s 单 Master 集群架构

一、单机 matser 部署环境 mater节点mater01192.168.80.7kube-apiserver、kube-controller-manager、kube-scheduler、 etcdnode节点node01192.168.80.11kubelet kube-proxy docker &#xff08;容器引擎&#xff09;node02192.168.80.12kubelet kube-proxy docker &#xff0…

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 删除并获得点数(难度⭐⭐)(76)

1. 题目解析 题目链接&#xff1a;LCR 091. 粉刷房子 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 1. 状态定义 在解决这类问题时&#xff0c;我们首先需要根据题目的具体要求来定义状态。针对房屋粉刷问题&#…

《系统架构设计师教程(第2版)》第4章-信息安全技术基础知识-02-信息加密技术

文章目录 1. 信息加密技术1.1 数据加密1.2 对称密钥加密算法1&#xff09;数据加密标准&#xff08;DES)2&#xff09;三重DES&#xff08;Triple-DES&#xff09;3&#xff09;国际数据加密算法&#xff08;IDEA&#xff09;4&#xff09;高级加密标准&#xff08;AES&#xf…

如何管理测试用例?测试用例有什么管理工具?YesDev

3.1 测试用例 测试用例(Test Case) 是指对一项特定的软件产品进行测试任务的描述&#xff0c;体现测试方案、方法、技术和策略。其内容包括测试目标、测试环境、输入数据、测试步骤、预期结果等。简单地认为&#xff0c;测试用例是为某个特殊目标而编制的一组测试输入、执行条…