Xilinx FPGA Multiboot-使用ICAPE2原语

什么是远程升级?

是一种更新程序的方法。在实际情况下,我们做好一个产品,进行更新的时候,不可能把产品拆开,对里面的固件进行升级。所以会涉及到一种远程升级的方法,就是上位机通过串口,或者PCIe等网络协议,将需要更新的程序放入FPGA的fifo,FPGA通过spi控制与flash进行交互,将从上位机收到的数据传入flash。

这里的flash就是一个存储产品配置的一个存储器。

什么是Multiboot?

将flash分区,放入不同的镜像工程,可以根据地址寻找不同的镜像工程进行加载。

flash存储的形式?

在flash中,我们会进行分区。

Golden Image区:从地址0开始,存储产品的一个稳定版本。当update区的程序启动失败时,让产品加载这个稳定版本,不至于让产品不能启动。

Update Image区:在golden区的地址之上开辟。用来存储我们的升级版本。

这是一个加载的顺序图,从图上我们可以看出:

step1:FPGA首先从地址0开始加载,这时候我们处于Golden镜像。

step2:由于我们在golden镜像的工程里面设置了跳转。所以这个时候我们会跳过Golden区。跳到我们设置的flash地址。

step3:这时候我们开始开始加载Update Image

step4:这里会进行一个判断,如果Update Image没有问题。我们就直接加载Update镜像,也就是我们需要升级的程序。如果有问题,那我们会进入step5;

step5:如果Update Image有问题,会进行一个fallback,也就是回滚。加载稳定程序 Golden Image.

想要做到多镜像启动,有两种方法。

1.是在bit流中加入指令,让FPGA加载的时候根据指令跳入到需要加载的flash镜像地址。

2.Xlinx提供了一个原语,可以在代码中进行操作,本质还是在bit流文件中加入指令。不过在代码中操作更方便。

小测试:

代码工程放在最下面,需要的自取

说一下测试的功能点以及怎么体现Multiboot:

1.两个工程:

a.golden工程:点亮led0

b.update工程:点亮led1

2.使用ICAPE2原语进行分区控制。

设备ID查找表:

怎么使用?我先贴出一个代码参考:

`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
);


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) begin
	multiboot_start_d0		<= multiboot_start;
	multiboot_start_d1		<= multiboot_start_d0;
end

//-------------------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'h362d093),	// 需要看自己芯片对应的设备ID A7-35T的为32'h362d093
     .ICAP_WIDTH		("X32"),		// 输入,输出位宽配置 "X32", "X8", "X16" 默认 "X32"
     .SIM_CFG_FILE_NAME	("NONE")		// 配置仿真模型要解析的原始比特流文件,这里没有 所以填"NONE"
)
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: ICAP的使能信号,低有效。如果不发指令或地址的时候,拉高
     .I					(ICAPE2_I),		// 32-bit input: 数据总线,输入对应的指令或flash跳转地址
     .RDWRB				(ICAPE2_RDWRB)	// 1-bit input: 1对应rd,0对应wr。发指令或地址的时候就是0,否则1
);

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; //type1 写入跳转空间的字节
wire	[31:0]	WBSTAR		= {3'b000, 5'h0, multiboot_addr[31:8]}; //跳转空间地址,可以根据bit大小修改
wire	[31:0]	WR_CMD		= 32'h30008001; //type1 写入的控制指令字
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;



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

就是根据一个使能信号,然后一步一步往ICAPE2里面发指令,具体看上面代码。

代码参考这位大佬的:K7系列FPGA多重启动(Multiboot)_multiboot多重配置-CSDN博客

3.XDC文件的配置
#Updata Image工程中添加
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property CONFIG_MODE SPIX1 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
#Golden Image工程中添加
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property BITSTREAM.CONFIG.NEXT_CONFIG_ADDR 0x00400000 [current_design]
set_property CONFIG_MODE SPIX1 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]


这一条是让golden可以进行跳转,以及跳转到哪个地址。

这一条是配置回滚使能。让Update Image加载失败的时候能够回滚到Golden Image。

这两个是配置SPI的模式,后面生成mcs要用。

4.bit流生成

生成之后看下对应的功能是否正常。具体请看步骤1

5.生成mcs文件。

对上图进行一下讲解:

1.生成文件类型,毫无疑问选MCS(因为我们就是要生成这个)

2.根据你的flash的大小填写,我这边是16MB,这个是以字节为单位哈。

3.填写你的生成文件路径

4.程序下载的方式,这里和上面XDC对应上,XDC写的SPI 1,所以这里选的SPIx1

5.你的Golden Image的bit文件路径,记住这里的开始地址是0;

6.你的Update Image的bit文件路径,记住这里的开始地址是你自己设置的update Image的起始地址。如果你不知道应该设置多大,你可以先填写大一点。然后在prm文件里面可以查看。

prm文件:是mcs的伴生文件,里面是一些mcs文件的信息。

例如下图:

可以看到bit流文件的大小。

点击OK,然后在你填写的文件生成路径下面可以看到两个文件,一个mcs一个prm

6.将mcs文件固化到板子上。

这个就不具体说了,不清楚的搜下看看。

7.测试正常功能

固化之后,断电,拔掉Jtag,然后上电。正常的话,会看到led1亮,也就是Update工程被加载了。

8.测试fallback

正常我们是加载Update Image,现在我们测试一下能不能回滚:

1.修改update的bit流文件。打开之后随便在中间改几个字节。

我用的vscode,下载一个插件就能打开了

2.改好了之后,我们生成一个新的mcs文件,就是战损版。

3.使用战损版mcs进行固化

4.这个时候应该看到led0亮,因为update的bit流被我们人为破坏了,所以回滚到了Gloden 工程。

重点:

1.理解多镜像分区,以及为什么要分区。

2.ICAPE2原语的使用。

3.具体代码可以在我的gitee上自取,有现成的mcs文件。如果有帮助,麻烦gitee上点个心。

Xilinx-Multiboot-ICAPE2-test: 使用Xilinx的ICAPE2原语,实现一个Multiboot的小测试

如果有说错的地方,欢迎评论区或私聊指出,大家可以一起讨论哈~~~

更多嵌入式,FPGA资料可参考:天津大学四川院FPGA培训中心 -- www.sxfpga.cn

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值