FPGA在线升级 -- Multiboot

news2024/12/26 23:41:41

简介

        本章节主要描述关于如何从Golden  Image转换到Multiboot Image程序。

升级方案

        Golden  Image转换到Multiboot Image的方法主要又两种

        1、使用ICAPE2 原语;

        2、在XDC文件中加入升级约束命令;

        以上两种方案都可以实现在线升级,第一种升级可控,第二种不可控下面主要针对第一种方案详细描述。

  原语描述

        原语顶层代码如下:

/* 
 * file			: multiboot_ctrl.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2023-11-30
 * version		: v1.0
 * description	: ICAP 原语实现程控 multiboot(多重启动),K7需要使用 ICAPE2 原语
 * Copyright ? 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */
//`default_nettype none
module multiboot_ctrl(
input	wire			clk,
input	wire			rst_n,

input	wire			multiboot_start,	//触发Multiboot, 上升沿有效
input	wire	[31:0]	multiboot_addr,		//要启动的Muliboot Image的起始地址

output	reg				busy
);

//-------------------ICAPE2原语-----------------------------
wire			ICAPE2_CLK;
wire	[31:0]	ICAPE2_O;
reg				ICAPE2_CSIB;
wire	[31:0]	ICAPE2_I;
reg				ICAPE2_RDWRB;

assign	ICAPE2_CLK	= clk;

ICAPE2 #(
     .DEVICE_ID			(32'h3636093),	// Specifies the pre-programmed Device ID value to be used for simulation purposes. K7-325T的为32'h3651093
     .ICAP_WIDTH		("X32"),		// Specifies the input and output data width.
     .SIM_CFG_FILE_NAME	("NONE")		// Specifies the Raw Bitstream (RBT) file to be parsed by the simulation model.
)
ICAPE2_inst(
     .O					(ICAPE2_O),		// 32-bit output: Configuration data output bus
     .CLK				(ICAPE2_CLK),	// 1-bit input: Clock Input
     .CSIB				(ICAPE2_CSIB),	// 1-bit input: Active-Low ICAP Enable
     .I					(ICAPE2_I),		// 32-bit input: Configuration data input bus
     .RDWRB				(ICAPE2_RDWRB)	// 1-bit input: Read/Write Select input    1对应rd,0对应wr
);

wire	[31:0]	Dummy		= 32'hFFFFFFFF;
wire	[31:0]	Sync_Word	= 32'hAA995566;
wire	[31:0]	NOOP		= 32'h20000000;
wire	[31:0]	WR_WBSTAR	= 32'h30020001;

/*When using ICAPE2 to set the WBSTAR address, the 24 most significant address bits should be written
  to WBSTAR[23:0]. For SPI 32-bit addressing mode, WBSTAR[23:0] are sent as address bits [31:8]. The
  lower 8 bits of the address are undefined and the value could be as high as 0xFF. Any bitstream
  at the WBSTAR address should contain 256 dummy bytes before the start of the bitstream.*/
wire	[31:0]	WBSTAR		= {3'b000, 5'h0, multiboot_addr[31:8]};

wire	[31:0]	WR_CMD		= 32'h30008001;
wire	[31:0]	IPROG		= 32'h0000000F;

//ICAPE2位翻转
reg		[31:0]	wrdat;
assign	ICAPE2_I	= {wrdat[24], wrdat[25], wrdat[26], wrdat[27], wrdat[28], wrdat[29], wrdat[30], wrdat[31], 
					   wrdat[16], wrdat[17], wrdat[18], wrdat[19], wrdat[20], wrdat[21], wrdat[22], wrdat[23], 
					   wrdat[8], wrdat[9], wrdat[10], wrdat[11], wrdat[12], wrdat[13], wrdat[14], wrdat[15], 
					   wrdat[0], wrdat[1], wrdat[2], wrdat[3], wrdat[4], wrdat[5], wrdat[6], wrdat[7]};

//------------------------FSM----------------------------------
localparam	S_IDLE			= 16'h0001;
localparam	S_DUMMY			= 16'h0002;
localparam	S_SYN_WORD		= 16'h0004;
localparam	S_NOOP1			= 16'h0008;
localparam	S_WR_WBSTAR		= 16'h0010;
localparam	S_WBSTAR		= 16'h0020;
localparam	S_WR_CMD		= 16'h0040;
localparam	S_IPROG			= 16'h0080;
localparam	S_NOOP2			= 16'h0100;
localparam	S_STOP			= 16'h0200;

wire	multiboot_start_pe;
reg		multiboot_start_d0;
reg		multiboot_start_d1;

assign	multiboot_start_pe	= multiboot_start_d0 & (~multiboot_start_d1);

always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        multiboot_start_d0 <= 'd0;
        multiboot_start_d1 <= 'd0;
    end
    else begin
	   multiboot_start_d0		<= multiboot_start;
	   multiboot_start_d1		<= multiboot_start_d0;
	end
	
end

reg		[15:0]	state		= S_IDLE;
reg		[15:0]	next_state;

always @(posedge clk) begin
	if(~rst_n) begin
		state	<= S_IDLE;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin
	case(state)
	S_IDLE: begin
		if(multiboot_start_pe) begin
			next_state	<= S_DUMMY;
		end
		else begin
			next_state	<= S_IDLE;
		end
	end
	S_DUMMY:		next_state	<= S_SYN_WORD;
	S_SYN_WORD:		next_state	<= S_NOOP1;
	S_NOOP1:		next_state	<= S_WR_WBSTAR;
	S_WR_WBSTAR:	next_state	<= S_WBSTAR;
	S_WBSTAR:		next_state	<= S_WR_CMD;
	S_WR_CMD:		next_state	<= S_IPROG;
	S_IPROG:		next_state	<= S_NOOP2;
	S_NOOP2:		next_state	<= S_STOP;
	S_STOP:			next_state	<= S_IDLE;
	default:		next_state	<= S_IDLE;
	endcase
end

always @(posedge clk) begin
	case(state)
	S_IDLE: begin
		wrdat			<= 32'd0;
		ICAPE2_CSIB		<= 1'b1;
		ICAPE2_RDWRB	<= 1'b1;
	end
	S_DUMMY: begin
		wrdat			<= Dummy;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_SYN_WORD: begin
		wrdat			<= Sync_Word;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_NOOP1: begin
		wrdat			<= NOOP;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_WR_WBSTAR: begin
		wrdat			<= WR_WBSTAR;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_WBSTAR: begin
		wrdat			<= WBSTAR;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_WR_CMD: begin
		wrdat			<= WR_CMD;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_IPROG: begin
		wrdat			<= IPROG;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_NOOP2: begin
		wrdat			<= NOOP;
		ICAPE2_CSIB		<= 1'b0;
		ICAPE2_RDWRB	<= 1'b0;
	end
	S_STOP: begin
		wrdat			<= 32'd0;
		ICAPE2_CSIB		<= 1'b1;
		ICAPE2_RDWRB	<= 1'b1;
	end
	default: begin
		wrdat			<= 32'd0;
		ICAPE2_CSIB		<= 1'b1;
		ICAPE2_RDWRB	<= 1'b1;
	end
	endcase
end

always @(*) begin
	case(state)
	S_IDLE:		busy	<= 1'b0;
	default:	busy	<= 1'b1;
	endcase
end

endmodule

        以上代码参考网上编写,经过自己代码仿真信号输出如下:

        在start的上升沿输出如上红色命令到ICAPE2原语内部,输入的内容介绍如下:

       以上WBSTAR为翻转升级程序的起始地址,本文设置Multiboot Image的起始地址为0x0080_0000。

        原语接口如下:

ICAPE2 #(
     .DEVICE_ID			(32'h3636093),	// Specifies the pre-programmed Device ID value to be used for simulation purposes. K7-325T的为32'h3651093
     .ICAP_WIDTH		("X32"),		// Specifies the input and output data width.
     .SIM_CFG_FILE_NAME	("NONE")		// Specifies the Raw Bitstream (RBT) file to be parsed by the simulation model.
)
ICAPE2_inst(
     .O					(ICAPE2_O),		// 32-bit output: Configuration data output bus
     .CLK				(ICAPE2_CLK),	// 1-bit input: Clock Input
     .CSIB				(ICAPE2_CSIB),	// 1-bit input: Active-Low ICAP Enable
     .I					(ICAPE2_I),		// 32-bit input: Configuration data input bus
     .RDWRB				(ICAPE2_RDWRB)	// 1-bit input: Read/Write Select input    1对应rd,0对应wr
);

        上文最终输入原语的数据是经过大小端重组的,官方原文解释如下:

        本次设计使用的是A7 200T系列FPGA,根据官方提供的手册《ug470_7Series_Config》查询DEVICE_ID如下:

   代码    

         以上就是原语设置内容,现使用两个LED作为工程,Golden  Image工程如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/11/20 16:33:43
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module updata_top(
	input SYS_CLK_clk_p,
	input SYS_CLK_clk_n,

    input updata_en,
    
	output fpga_led
    
    
);

wire test_data;
wire pll_locked;
wire sys_clk;
wire updata_clk_25m;
wire sys_rst_n;
wire updata_clk;
wire updata_clk_in;

assign sys_rst_n = pll_locked;
//wire updata_en;

//   IBUFDS #(
//      .DIFF_TERM("FALSE"),       // Differential Termination
//      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
//      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
//   ) IBUFDS_inst (
//      .O(updata_clk_in),  // Buffer output
//      .I(SYS_CLK_clk_p),  // Diff_p buffer input (connect directly to top-level port)
//      .IB(SYS_CLK_clk_n) // Diff_n buffer input (connect directly to top-level port)
//   );

//   BUFG BUFG_inst (
//      .O(updata_clk), // 1-bit output: Clock output.
//      .I(updata_clk_in)  // 1-bit input: Clock input.
//   );



sys_pll	sys_pll_inst(
  // Clock out ports
    .clk_out1(updata_clk),
    .clk_out2(updata_clk_25m),
  // Status and control signals
   . locked(pll_locked),
 // Clock in ports
    .clk_in1_p(SYS_CLK_clk_p),
    .clk_in1_n(SYS_CLK_clk_n)

);


fpga_led	fpga_led_inst(

    .sys_clk(updata_clk),
    .sys_rst_n('d1),
	.led_top(fpga_led)
    //.updata_en(updata_en)
);

//vio_test	vio_test_inst(
//	.clk(updata_clk),
//	.probe_out0(updata_en)

//);

multiboot_ctrl  multiboot_ctrl_inst(
	.clk				(updata_clk_25m),
	.rst_n				(sys_rst_n),

	.multiboot_start	(updata_en),
	.multiboot_addr		(32'h80000000),		//加载0x01000000处的Multiboot Image

	.busy				()
);


endmodule

        multiboot_addr的地址这里写的是0x8000_0000,实际上在multiboot_ctrl内部做了8位偏移,而我使用的FLASH型号的S25FL128,采用的是24位地址。实际上跳转地址是0x0080_0000,经过仿真输出的是0x0080_0000;

        约束文件如下:

set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]

        第一条约束代表FPGA启动速度为33MHz;

        第二条约束代表生成的程序经过压缩处理,本设计使用的FLASH是16MByte,不经过压缩无法放两个程序在FLASH里面。这里需要注意的是无需再放NEXT_CONFIG_ADDR、CONFIGFALLBACK等命令。

        Multiboot Image的代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/11/20 16:33:43
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module updata_top(
	input SYS_CLK_clk_p,
	input SYS_CLK_clk_n,

    //input updata_clk_10m,
    
	output fpga_led
    
    
);

wire test_data;
wire pll_locked;
wire sys_clk;
//wire updata_clk_10m;
wire sys_rst_n;
wire updata_clk_25m;

wire updata_en;

assign sys_rst_n = pll_locked;


sys_pll	sys_pll_inst(
  // Clock out ports
    .clk_out1(sys_clk),
    .clk_out2(updata_clk_25m),
  // Status and control signals
   . locked(pll_locked),
 // Clock in ports
    .clk_in1_p(SYS_CLK_clk_p),
    .clk_in1_n(SYS_CLK_clk_n)

);


fpga_led	fpga_led_inst(

    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
	.led_top(fpga_led),
    .updata_en(updata_en)
);



//vio_test	vio_test_inst(
//	.clk(sys_clk),
//	.probe_out0(fpga_led)

//);	


multiboot_ctrl  multiboot_ctrl_inst(
	.clk				(updata_clk_25m),
	.rst_n				(1'b1),

	.multiboot_start	(updata_en),
	.multiboot_addr		(32'h00000000),		//加载0x00000000处的Golden Image

	.busy				()
);




endmodule


        与Golden  Image不同的是这里翻转地址multiboot_addr是0地址。

        另外fpga_led模块主要是LED灯模块,Golden  Image和Multiboot Image的灯显示频率不同以作区分。

        Golden  Image的LED灯代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/10/29 15:47:28
// Design Name: 
// Module Name: fpga_led
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fpga_led(
	input sys_clk,
	input sys_rst_n,
	
	output reg updata_en,
	output reg led_top
    );

parameter led_time_num = 'd100_000_000;
parameter updata_delay_num = 'd400_000_000;
	
	
reg[31:0] led_cnt = 'd0;	
reg[31:0] updata_delay_cnt ='d0;	
	
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n == 'd0)begin
		led_top<= 'd0;
	end		
	else if(led_cnt >= led_time_num - 'd1)begin
		led_top <= ~led_top;
		led_cnt <= 'd0;
	end
	else begin
		led_cnt <= led_cnt + 'd1;
	end
end	
	

always @(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n == 'd0)begin
		updata_en<= 'd0;
		updata_delay_cnt<= 'd0;
	end	
    else if(updata_delay_cnt >= updata_delay_num)begin
        updata_en <= 'd1;
    end
    else begin
        updata_delay_cnt <= updata_delay_cnt + 1'b1;
    end
 end  

	
	
	
endmodule



        Multiboot Image的LED灯代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/10/29 15:47:28
// Design Name: 
// Module Name: fpga_led
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fpga_led(
	input sys_clk,
	input sys_rst_n,
	
	output reg updata_en,
	output reg led_top
    );

parameter led_time_num = 'd10_000_000;
parameter updata_delay_num = 'd400_000_000;
	
	
reg[31:0] led_cnt;	
reg[31:0] updata_delay_cnt;	
	
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n == 'd0)begin
		led_top<= 'd0;
	end		
	else if(led_cnt >= led_time_num - 'd1)begin
		led_top <= ~led_top;
		led_cnt <= 'd0;
	end
	else begin
		led_cnt <= led_cnt + 'd1;
	end
end	
	

always @(posedge sys_clk or negedge sys_rst_n)begin
	if(sys_rst_n == 'd0)begin
		updata_en<= 'd0;
		updata_delay_cnt<= 'd0;
	end	
    else if(updata_delay_cnt >= updata_delay_num)begin
        updata_en <= 'd1;
    end
    else begin
        updata_delay_cnt <= updata_delay_cnt + 1'b1;
    end
 end  

	
	
	
endmodule



         对比可看出led_time_num参数的值不同,也就是闪烁频率不一样,两个程序各自持续4秒钟时间做跳转。

文件合成

        两个工程生成的bit文件需要合成一个MCS或者BIN文件烧录到程序中,流程如下:

        1、合并生成的文件类型;

        2、选择FLASH型号;

        3、生成的文件存储路径和名称;

        4、烧录模式,选择SPIx1;

        5、Golden  Image程序起始地址;

        6、Multiboot Image程序起始地址;

        7、校验和覆盖功能,第三个根据自己习惯选择;

        8、生成的脚本,该脚本可以再VIVADO直接执行。

        生成的MCS文件烧录到板子里,可以看到Golden  Image和Multiboot Image程序每执行4秒钟切换,两个程序通过LED灯的闪烁频率区分。

        以上就是程序跳转功能,这个是远程升级的基础,后面需要FPGA通过以太网/串口等方式接收升级程序并写入到FLASH后,启动升级功能。

        下一篇文章将描述如何实现在线控制FLASH擦除、写入等,完成远程升级功能。

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

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

相关文章

守护进程化

目录 一、进程组 二、会话 &#xff08;1&#xff09;什么是会话 &#xff08;2&#xff09;如何创建一个会话 三、守护进程 一、进程组 之前我们学习过进程&#xff0c;其实每一个进程除了有一个进程 ID(PID)之外 还属于一个进程组。进程组是一个或者多个进程的集合&…

QML插件扩展

https://note.youdao.com/ynoteshare/index.html?id294f86c78fb006f1b1b78cc430a20d74&typenote&_time1706510764806

RabbitMQ七种工作模式之 RPC通信模式, 发布确认模式

文章目录 六. RPC(RPC通信模式)客户端服务端 七. Publisher Confirms(发布确认模式)1. Publishing Messages Individually(单独确认)2. Publishing Messages in Batches(批量确认)3. Handling Publisher Confirms Asynchronously(异步确认) 六. RPC(RPC通信模式) 客⼾端发送消息…

ArcGIS字符串补零与去零

我们有时候需要 对属性表中字符串的补零与去零操作 我们下面直接视频教学 下面看视频教学 ArcGIS字符串去零与补零 推荐学习 ArcGIS全系列实战视频教程——9个单一课程组合 ArcGIS10.X入门实战视频教程&#xff08;GIS思维&#xff09; ArcGIS之模型构建器&#xff08;Mod…

前端面试如何出彩

1、原型链和作用域链说不太清&#xff0c;主要表现在寄生组合继承和extends继承的区别和new做了什么。2、推荐我的两篇文章&#xff1a;若川&#xff1a;面试官问&#xff1a;能否模拟实现JS的new操作符、若川&#xff1a;面试官问&#xff1a;JS的继承 3、数组构造函数上有哪些…

大模型应用编排工具Dify之构建专属FQA应用

1.前言 ​ 通过 dify可以基于开源大模型的能力&#xff0c;并结合业务知识库、工具API和自定义代码等构建特定场景、行业的专属大模型应用。本文通过 dify工作室的聊天助手-工作流编排构建了一个基于历史工作日志回答问题的助手&#xff0c;相比原始的大模型答复&#xff0c;通…

前端node环境安装:nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)

需求&#xff1a;在做前端开发的时候&#xff0c;有的时候 这个项目需要 node 14 那个项目需要 node 16&#xff0c;我们也不能卸载 安装 。这岂不是很麻烦。这个时候 就需要 一个工具 来管理我们的 node 版本和 npm 版本。 下面就分享一个 nvm 工具 用来管理 node 版本。 这个…

c基础加堆练习题

1】思维导图&#xff1a; 2】在堆区空间连续申请5个int类型大小空间&#xff0c;用来存放从终端输入的5个学生成绩&#xff0c;然后显示5个学生成绩&#xff0c;再将学生成绩升序排序&#xff0c;排序后&#xff0c;再次显示学生成绩。显示和排序分别用函数完成 要求&#xff…

嵌入式Linux 设备树 GPIO详解 示例分析 三星 NXP RK

GPIO设备树用于在Linux内核中定义与GPIO相关的硬件资源&#xff0c;它使操作系统可以识别、配置和使用GPIO引脚。设备树中通常会指定GPIO控制器的基地址、GPIO引脚的中断配置、时钟和其他相关信息。 目录 RK相关案例代码 NXP相关案例代码 三星相关案例代码 在设备树中&…

【日记】不想随礼欸(926 字)

正文 今天忙了一天。感觉从早上就开始在救火。客户经理迎接检查&#xff0c;要补资料&#xff0c;找我们问这样要那样&#xff0c;我自己的事情几乎完全开展不了。虽说也没什么大事就是了。 晚上行长还让我重装系统…… 难绷。看来这个爹味新行长懂得还挺多。 中午趁着不多的休…

Spring 源码学习(七)——注解后处理器

通过之前对注解式配置的解析&#xff08;Spring 源码学习&#xff08;三&#xff09;—— 注解式配置解析_spring源码学习-CSDN博客&#xff09;可以发现其使用 AnnotationConfigUtils 类的 registerAnnotationConfigProcessors 静态方法对象注解后处理器对象进行注册&#xff…

如何避免缓存击穿?超融合常驻缓存和多存储池方案对比

作者&#xff1a;SmartX 解决方案专家 钟锦锌 很多运维人员都知道&#xff0c;混合存储介质配置可能会带来“缓存击穿”的问题&#xff0c;尤其是大数据分析、数据仓库等需要频繁访问“冷数据”的应用场景&#xff0c;缓存击穿可能会更频繁地出现&#xff0c;影响业务运行。除…

Scala的正则表达式二

验证用户名是否合法 规则 1.长度在6-12之间 2.不能数字开头 3.只能包含数字&#xff0c;大小写字母&#xff0c;下划线def main(args: Array[String]): Unit {val name1 "1admin"//不合法&#xff0c;是数字开头val name2 "admin123"//合法val name3 &quo…

【CKA】Kubernetes(k8s)认证之CKA考题讲解

CKA考题讲解 0.考试101 0.1 kubectl命令⾃动补全 在 bash 中设置当前 shell 的⾃动补全&#xff0c;要先安装 bash-completion 包。 echo "source <(kubectl completion bash)" >> ~/.bashrc还可以在补全时为 kubectl 使⽤⼀个速记别名&#xff1a; al…

导入kotlin

android studio 导入kotlin项目 android studio kotlin教程 或者直接拿一个kt文件进来&#xff0c;在顶部会显示一个config&#xff0c;然后设置version&#xff0c;点击OK就可以了自动导了

《CSS 知识点》大屏卡片布局思路:弹性布局 flex-grow

思路 大屏左右两侧高宽一致&#xff0c;内部卡片可按比例设置&#xff01; 使用弹性布局和属性 flex-grow 设置比例&#xff1b;间隔使用 margin-bottom 设置&#xff0c;最后一个卡片不设置&#xff1b; 效果如图 代码说明 CSS代码 26 - 30&#xff0c;左右两侧设置弹性布…

责任链模式的理解和实践

责任链模式&#xff08;Chain of Responsibility&#xff09;是行为型设计模式之一&#xff0c;它通过将多个对象连成一条链&#xff0c;并沿着这条链传递请求&#xff0c;直到有对象处理它为止。这个模式的主要目的是将请求的发送者和接收者解耦&#xff0c;使请求沿着处理链传…

如何在 Ubuntu 上安装开源监控工具 Uptime Kuma

简介 Uptime Kuma&#xff08;或简称 Kuma&#xff09;是一个开源监控工具&#xff0c;用于监控 HTTP、HTTPS、DNS 等协议的服务。Uptime Kuma 提供多种功能&#xff0c;如多语言支持、多个状态页面、代理支持等。 接下来&#xff0c;我将一步一步教大家如何进行安装和部署&am…

go语言zero框架对接阿里云消息队列MQ的rabbit的配置与调用

在 Go 语言中对接阿里云消息队列&#xff08;MQ&#xff09;的 RabbitMQ 配置与调用&#xff0c;首先需要安装和配置相关的 Go 库&#xff0c;并了解如何通过 RabbitMQ 与阿里云消息队列进行交互。 ### 步骤一&#xff1a;安装 RabbitMQ Go 客户端库 阿里云的消息队列&#x…

AttributeError: module ‘cv2.dnn‘ has no attribute ‘DictValue‘如何解决?

AttributeError: module cv2.dnn has no attribute DictValue如何解决&#xff1f; 出现场景出错原因解决方案 出现场景 当在代码中导入opencv的时候&#xff1a;import cv2&#xff0c;出现&#xff1a; 出错原因 查看大家出现的错误&#xff0c;发现是因为opencv版本问题…