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

对于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 >==============================//inputinput 		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)beginif(!i_rst_n)begin r_wram_en	<= 0;r_wram_addr	<= 0;r_wram_data	<= 0;WR_S     	<= 2'd0;endelse begincase(WR_S)0:beginif(i_data_valid)begin		//数据有效r_wram_en    <= 1'b1;	//设置写使能r_wram_addr  <= 0; 		//设置地址从0开始r_wram_data  <= i_data;	//写数据		WR_S   <= 2'd1; 		//下一个状态endelse beginr_wram_en    <= 1'b0;	r_wram_addr  <= r_wram_addr;r_wram_data  <= r_wram_data;			endend1:beginif(i_data_valid & (r_wram_addr == (WRAM_ADDR_MAX-1)))beginr_wram_en    <= 1'b1;	r_wram_addr  <= 0;r_wram_data  <= i_data;	endelse if(i_data_valid)begin r_wram_en    <= 1'b1;	//设置写使能r_wram_addr <= r_wram_addr + 1'b1;//通道A地址,地址增加1r_wram_data  <= i_data;endelse beginr_wram_en    <= 1'b0;	r_wram_addr  <= r_wram_addr;r_wram_data  <= r_wram_data;				endenddefault:WR_S   <= 2'd0;endcaseendend//==========================================================================//==    read RAM//==========================================================================always @(posedge i_sys_clk_rd)begin//o_data_valid根据配置的输出寄存器有关,至少滞后地址一个时钟周期if(!i_rst_n)o_data_valid <= 1'b0;elseo_data_valid <= i_rram_en;endsdp_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 >==============================//inputreg 					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 beginclk_w 	= 	1'b1	;clk_r 	= 	1'b1	;rst_n  <= 	1'b0	;#100rst_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)beginif(!rst_n)cnt_flag <= 0;elsecnt_flag <= cnt_flag + 1'b1;end//==========================================================================//==    cnt_data 用于构造RAM写入数据//==========================================================================always @(posedge clk_w)beginif(!rst_n)cnt_data <= 0;else if(&cnt_flag)cnt_data <= cnt_data + 4'd4;elsecnt_data <= cnt_data;end //==========================================================================//==    write data//==========================================================================always @(posedge clk_w)beginif(!rst_n)begini_data <= 0;i_data_valid <= 1'b0;endelse if(&cnt_flag)begin//计数到全1i_data <= {cnt_data+4'd3,cnt_data+4'd2,cnt_data+4'd1,cnt_data};i_data_valid <= 1'b1;endelse begini_data <= i_data;i_data_valid <= 1'b0;endend//==========================================================================//==    cnt_rd 用于读使能判断//==========================================================================always @(posedge clk_r)beginif(!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)beginif(!rst_n)begini_rram_en <= 1'b0;	i_rram_addr <= 0;endelse if( (cnt_rd == 2'd2) && (o_wram_addr == 1) )begini_rram_en <= 1'b1;	i_rram_addr <= 0;endelse if(cnt_rd == 2'd2)begini_rram_en <= 1'b1;	i_rram_addr <= i_rram_addr + 1'b1;endelse begini_rram_en <= 1'b0;	i_rram_addr <= i_rram_addr;endend
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)beginif(!I_rstn)begin //复位重置相关寄存器wr_frame <= 8'd0;addra    <= 10'd0;ena      <= 1'b1;wea      <= 1'b0;WR_S     <= 2'd0;endelse begincase(WR_S)0:beginaddra  <= 10'd0; //设置地址从0开始ena    <= 1'd1;  //设置通道A使能wea    <= 1'b1;  //设置写使能WR_S   <= 2'd1;  //下一个状态end1:beginif(addra != 10'd1023)begin //如果写地址不等于1023,wea   <= 1'b1; //设置写使能ena   <= 1'b1; //设置通道A使能addra <= addra + 1'b1;//那么通道A地址,每个时钟地址增加1endelse begin //否则代表完成了1帧数据写入到BRAMwea   <= 1'b0; //设置写使能为0,停止写ena   <= 1'b0; //设置通道A使能为0wr_frame <= wr_frame +1'b1;//帧计数器WR_S   <= 2'd2;//下一个状态endend2:beginif(RD_S == 2'd2) //如果读操作完成WR_S   <= 2'd0; //回到状态0重新开始enddefault:WR_S   <= 2'd0;endcaseend
endalways @(posedge I_sysclk)beginif(!I_rstn)begin //复位重置相关寄存器addrb    <= 10'd0;enb      <= 1'b0;RD_S     <= 2'd0;endelse begincase(RD_S)0:beginenb     <= 1'b0; //设置读使能0addrb   <= 10'd0; //设置读地址从0开始if(addra == 10'd1023)begin//读数据在写数据的第1023个地址开始enb   <= 1'b1; //使能读通道RD_S  <= 2'd1; //下一状态end  end1:beginenb    <= 1'b1;//设置读使能1if(addrb != 10'd1023)如果读地址不等于1023,addrb  <= addrb + 1'b1;//那么通道B地址,每个时钟地址增加1elseRD_S   <= 2'd2;//下一状态end2:beginRD_S   <= 2'd0;//下一状态enddefault:RD_S   <= 2'd0;endcaseend
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 ;//定义时钟周期 单位nsreg   I_sysclk; //系统时钟
reg   I_rstn;   //系统复位//例化bram_test
bram_test bram_test_inst
(.I_sysclk	(I_sysclk	),.I_rstn		(I_rstn		)
);//初始化
initial beginI_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
beginsys_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;
elsewr_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;
elsewea <= 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;
elseaddra <= 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;
elsecnt <= 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.rhkb.cn/news/328805.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

商品服务:SPUSKU规格参数销售属性

1.Object划分 1.PO&#xff08;Persistant Object&#xff09;持久对象 PO就是对应数据库中某个表中的一条记录&#xff0c;多个记录可以用PO的集合。PO中应该不报含任何对数据库的操作 2.DO(Domain Object) 领域对象 就是从现实世界中抽象出来的有形或无形的业务实体。 3…

线程池的简单实现与应用

1.什么是线程池 线程池其实就是一种多线程处理形式&#xff0c;处理过程中可以将任务添加到队列中&#xff0c;然后在创建线程后自动启动这些任务。 线程池最大的好处就是减少每次启动、销毁线程的损耗。 2.线程池参数介绍 参数名称说明corePoolSize正式员工的数量.(正式员…

代码随想录——填充每个节点的下一个右侧节点指针 II(Leetcode117)

题目链接 层序遍历 /* // Definition for a Node. class Node {public int val;public Node left;public Node right;public Node next;public Node() {}public Node(int _val) {val _val;}public Node(int _val, Node _left, Node _right, Node _next) {val _val;left _l…

21【Aseprite 作图】画白菜

1 对着参考图画轮廓 2 缩小尺寸 变成这样 3 本来是红色的描边&#xff0c;可以通过油漆桶工具&#xff08;取消 “连续”&#xff09;&#xff0c;就把红色的轮廓线&#xff0c;变成黑色的 同时用吸管工具&#xff0c;吸取绿色和白色&#xff0c;用油漆桶填充颜色 4 加上阴影…

svn批量解锁

问题 svn对文件进行checkout之后&#xff0c;先进行lock&#xff0c;之后再去更改&#xff0c;最后进行Commit操作&#xff1b; 上述为我们通过svn管理代码的正常方式&#xff0c;但总会有其他现象发生&#xff1b; 如果我们非正常操作&#xff0c;批量锁所有的svn文件&#x…

Django Celery 的配置及使用---最详细教程

Django Celery 的配置及使用 Redis提供队列消息功能 一、安装redis 系统版本&#xff1a;Ubuntu 20.041、获取最新软件包 sudo apt update sudo apt install redis-server2、安装完成后&#xff0c;Redis服务器会自动启动。查看redis是否启动成功 sudo systemctl status …

uniapp + vue3 设置 axios proxy 代理,并重写路径

uniapp vue2 设置代理如下&#xff1a; 已生成的项目架构里面找到manifest.json文件&#xff0c;通过源码视图的方式打开文件&#xff0c;在文件中添加一下代码即可完成代理&#xff1a; "h5": {"devServer": {"disableHostCheck": true, //禁…

Milvus的系统架构

简介 Milvus的构建在许多知名的向量搜索库比如Faiss, HNSW, DiskANN, SCANN等之上的&#xff0c;它针对稠密向量数据集的相似搜索而设计&#xff0c;能支持百万、十亿甚至万亿级别的向量搜索。 Milvus支持数据分片&#xff0c;流式数据插入&#xff0c;动态schema&#xff0c…

离散型制造行业智能工厂标准解决方案(49页 PPT)

方案介绍&#xff1a; 该解决方案的核心功能模块包括生产计划与排程、质量控制与追溯、设备管理与维护、物料管理与仓储以及供应链协同等。通过智能算法和数据分析&#xff0c;实现对生产过程的精准控制和优化。同时&#xff0c;智能工厂还支持远程监控和预测性维护&#xff0…

15.计算机网络

1.物理层的互联设备 中继器 和 集线器 2.集线器可以看做特殊的多路中继器 集线器 不可以做到自动寻址的功能 3.数据链路层 网桥 和 交换机 4.交换机是多端口网桥 5.网络层 路由器 6.应用层 网关 7.广播域 网络层 可以形成多个广播域 冲突域 网络层数据链路层 可以形成多个冲突域…

三大平台直播视频下载保存方法

终于解决了视频号下载的问题&#xff0c;2024年5月15日亲测可用。 而且免费。 教程第二部分&#xff0c;有本地电脑无法下载的解决方案。 第一部分&#xff1a;使用教程&#xff08;正常&#xff09; 第1步&#xff1a;下载安装包 下载迅雷网盘搜索&#xff1a;大海福利合集…

STM32时钟系统和时钟树

目录 STM32时钟系统 认识时钟树 什么是时钟 认识时钟树(F407) 配置系统时钟 系统时钟配置步骤 外设时钟使能和失能 sys_stm32_clock_init函数(F4) 时钟配置一般步骤 System文件夹_时钟系统工作原理 System文件夹介绍 Sys文件介绍 Delay文件介绍 Systic工作原理 S…

vue2人力资源项目9权限管理

页面搭建 <template><div class"container"><div class"app-container"><el-button size"mini" type"primary">添加权限</el-button><el-table-column label"名称" /><el-table-co…

基于Pytorch深度学习神经网络MNIST手写数字识别系统源码(带界面和手写画板)

第一步&#xff1a;准备数据 mnist开源数据集 第二步&#xff1a;搭建模型 我们这里搭建了一个LeNet5网络 参考代码如下&#xff1a; import torch from torch import nnclass Reshape(nn.Module):def forward(self, x):return x.view(-1, 1, 28, 28)class LeNet5(nn.Modul…

二.使用PgAdmin连接Postgresql

二.使用PgAdmin连接Postgresql PostgreSQL是一种开源的对象关系型数据库管理系统(ORDBMS),它支持大部分SQL标准并提供了许多高级功能,例如事务、外键、视图、触发器等。PostgreSQL由PostgreSQL全球开发组维护和开发,它是一种高度可扩展的数据库系统,可以在各种操作系统…

finallyshell激活-支持所有版本(老版 + 最新版) + 所有平台(mac + windows)

一&#xff1a;打开finally shell的激活页面 二&#xff1a;点击离线激活 三&#xff1a;复制机器码&#xff0c;然后执行一下代码 原文&#xff1a;大哥原文&#xff0c;但是这个大佬是用java实现的&#xff0c;执行因为依赖的问题一直报错 基于以上问题&#xff0c;所以使…

linux Docker在线/离线服务安装并支持centos7和centos8系统

注&#xff1a;以下内容都是经过测试;能在生产环境使用. 一、centos7版本的docker在线安装 1&#xff1a;运行以下命令&#xff0c;下载docker-ce的yum源。 sudo wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo…

Electron学习笔记(五)

文章目录 相关笔记笔记说明 七、系统1、系统对话框2、自定义窗口菜单3、系统右键菜单4、快捷键(1)、监听网页按键事件 &#xff08;窗口需处于激活状态&#xff09;(2)、监听全局按键事件 &#xff08;窗口无需处于激活状态&#xff09;(3)、补充&#xff1a;自定义窗口菜单快捷…

ModuleNotFoundError: No module named ‘sklearn‘

ModuleNotFoundError: No module named sklearn 解决办法&#xff1a; pip install scikit-learn

Qt---信号和槽

一、信号和槽机制 所谓信号槽&#xff0c;实际就是观察者模式。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号&#xff08;signal&#xff09;。这种发出是没有目的的&#xff0c;类似广播。如果有对象对这个信号…