赞
踩
是一种更新程序的方法。在实际情况下,我们做好一个产品,进行更新的时候,不可能把产品拆开,对里面的固件进行升级。所以会涉及到一种远程升级的方法,就是上位机通过串口,或者PCIe等网络协议,将需要更新的程序放入FPGA的fifo,FPGA通过spi控制与flash进行交互,将从上位机收到的数据传入flash。
这里的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:
a.golden工程:点亮led0
b.update工程:点亮led1
设备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博客
- #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要用。
生成之后看下对应的功能是否正常。具体请看步骤1
对上图进行一下讲解:
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
这个就不具体说了,不清楚的搜下看看。
固化之后,断电,拔掉Jtag,然后上电。正常的话,会看到led1亮,也就是Update工程被加载了。
正常我们是加载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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。