2026 / 4 / 20
感谢X圣开源,前面忘了,后面忘了
Multiboot是什么?简单来说,正常FPGA从FLASH启动只会读取一份bit固件,当flash数据错误或者你需要刷新的bit启动时,就需要重新烧录——而Multiboot提供了一个接口,允许你在同一个Flash中划分地址区域存入多份bit文件,你可以手写一份代码用于指定FPGA从Flash哪个地址开始重新加载,也可以通过预制菜XDC命令,指定当前bit出错时跳转到哪个地址。这样就避免了二次烧录的麻烦工作
准备工作
1-测试工程(可以根据下面给的代码自己生成)
2-测试开发板 XI050CD
3-能正常开发bibabo的电脑
工程说明
测试分为两种应用场景:一种是手写最开始的引导程序,根据条件指定FPGA从哪个地址开始加载(也就是Golden,常见于固件切换);另一种是直接使用预制菜XDC命令,指定FPGA跳转到哪儿(常见于AB备份切换)
测试工程有两个,一个是Golden,用于切换指定FLASH地址;另一个是LED_Test,用于生成不同点灯bit。通过观察LED的闪烁状态判断当前FPGA工作在哪个bit,有没有发生加载
其中 LED0表示LED0.bit,LED1表示LED1.bit,LED2表示LED2.bit,LED3表示Golden.bit
Golden的代码和xdc如下(根据测试流程有细微变动)
module top ( input i_sys_clk, // 板上稳定时钟 output [3:0] o_led ); //===================================================== // Golden LED指示 //===================================================== reg [22:0] led_cnt; always@(posedge i_sys_clk) led_cnt <= led_cnt + 1; assign o_led[3] = led_cnt[22]; assign o_led[2] = 1'b1; assign o_led[1] = 1'b1; assign o_led[0] = 1'b1; //===================================================== // 延时启动(避免刚上电时序不稳定) //===================================================== reg [24:0] delay_cnt = 0; reg delay_ok = 0; always @(posedge i_sys_clk) begin if (delay_cnt < 25'h1FF_FFFF) begin delay_cnt <= delay_cnt + 1; end else begin delay_ok <= 1; end end //===================================================== // ICAP 控制信号 //===================================================== reg [ 4:0] state = 0; reg [31:0] icap_data; reg icap_csib = 1; reg icap_rdwrb = 0; reg [28:0] target_addr; wire [31:0] icap_data_rev; wire [31:0] WBSTAR; localparam DUMMY = 32'hFFFFFFFF; localparam SYNC_WORD = 32'hAA995566; localparam NOOP = 32'h20000000; localparam WR_WBSTAR = 32'h30020001; localparam WR_CMD = 32'h30008001; localparam IPROG = 32'h0000000F; assign WBSTAR = {3'b000, target_addr};//3位RS配置,29位Flash地址 // bit reverse(ICAP要求) assign icap_data_rev = { icap_data[24], icap_data[25], icap_data[26], icap_data[27], icap_data[28], icap_data[29], icap_data[30], icap_data[31], icap_data[16], icap_data[17], icap_data[18], icap_data[19], icap_data[20], icap_data[21], icap_data[22], icap_data[23], icap_data[ 8], icap_data[ 9], icap_data[10], icap_data[11], icap_data[12], icap_data[13], icap_data[14], icap_data[15], icap_data[ 0], icap_data[ 1], icap_data[ 2], icap_data[ 3], icap_data[ 4], icap_data[ 5], icap_data[ 6], icap_data[ 7]}; //===================================================== // 状态机:控制镜像A B C的加载 //===================================================== localparam ADDR_BIN_GOLDEN = 29'h0000_0000; // Golden地址 localparam ADDR_BIN_A = 29'h0040_0000; // 镜像A地址 localparam ADDR_BIN_B = 29'h0080_0000; // 镜像B地址 localparam ADDR_BIN_C = 29'h00C0_0000; // 镜像C地址 always @(posedge i_sys_clk) begin case (state) 0: begin target_addr <= ADDR_BIN_B; icap_csib <= 1; state <= delay_ok ? state + 1 : state; end // 开始 1: begin icap_csib <= 0; state <= state + 1; end 2: begin icap_data <= DUMMY; state <= state + 1; end 3: begin icap_data <= SYNC_WORD; state <= state + 1; end 4: begin icap_data <= NOOP; state <= state + 1; end 5: begin icap_data <= WR_WBSTAR; state <= state + 1; end 6: begin icap_data <= WBSTAR; state <= state + 1; end 7: begin icap_data <= WR_CMD; state <= state + 1; end 8: begin icap_data <= IPROG; state <= state + 1; end 9: begin icap_data <= NOOP; state <= state + 1; end // 结束 10: begin icap_csib <= 1; state <= state; end endcase end //===================================================== // ICAPE2 原语 //===================================================== ICAPE2 #( .DEVICE_ID (32'h0362C093 ), .ICAP_WIDTH ("X32" ) ) icap_inst ( .CLK (i_sys_clk ), .CSIB (icap_csib ), .RDWRB (icap_rdwrb ), .I (icap_data_rev ), .O ( ) ); endmodule
set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design] set_property CONFIG_MODE SPIx1 [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 22 [current_design] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN W19 } [get_ports i_sys_clk] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN P17 } [get_ports {o_led[0]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN V20 } [get_ports {o_led[1]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN V22 } [get_ports {o_led[2]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN U21 } [get_ports {o_led[3]}]
LED_Test的代码和xdc如下(根据测试流程有细微变动)
module top ( input i_sys_clk, output [3:0] o_led ); reg [24:0] cnt; always@(posedge i_sys_clk) cnt <= cnt + 1; assign o_led[2] = cnt[23]; assign o_led[0] = 1'b1; assign o_led[1] = 1'b1; assign o_led[3] = 1'b1; endmodule
set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design] set_property CONFIG_MODE SPIx1 [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 22 [current_design] # 启动失败自动回退,回退地址为Golden Image set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN W19 } [get_ports i_sys_clk] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN P17 } [get_ports {o_led[0]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN V20 } [get_ports {o_led[1]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN V22 } [get_ports {o_led[2]}] set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN U21 } [get_ports {o_led[3]}]
测试流程
先说Golden方式的测试流程
1-按上面给出的LED_Test工程,修改Verilog工程的assign o_led,xdc保持不变,根据点亮不同LED生成LED0.bit、LED1.bit和LED2.bit备用
2-按上面给出的Golden工程,修改Target addr为你想要的跳转的地址宏定义,默认为B镜像(也就是地址0x00800000开始重加载),生成Golden.bit
3-在Bibabo——>Tools——>Generate Memory Configuration File, 配置如下
Memory Size你知道选啥型号可以选,不知道就直接写大小
FileName自己改路径放mcs文件
固化选SPIx1(这个要和xdc一致)
四个bit依次放入,地址划分按Golden代码里来
勾上Overwrite,不然二次生成的时候会报错已存在文件
4-将Multiboot.mcs烧录进去
5-重新上电(插充电宝或者关Bibabo的Hardware Manager,不然会重加载不了),正常情况就是LED3闪烁一会(Golden工作),然后熄灭,然后LED1开始闪烁(B镜像地址对应LED1.bit)
然后是破坏bit,模拟数据损坏,Fallback功能的测试
1-将上面生成的Multiboot.mcs复制一份命名位Multiboot_Err.mcs
2-使用编辑器(我这边是Vscode+Hex Editer)打开,里面应该有四份bit流,直接搜索bit头跳转到第三个位置(对应LED1.bit),在下面的数据随便改改几处
3-重新烧录,测试现象为,LED3闪烁,熄灭,LED3重新闪烁并重复。说明FPGA从Golden跳出去后没有启动成功,通过LED2.bit使能的Fallback重新回到了Golden,如此循环。
然后是预制菜XDC自动跳转的测试
1-在LED_Test工程中,点亮LED1,并在XDC中增加约束,生成新的LED1.bit
set_property BITSTREAM.CONFIG.NEXT_CONFIG_ADDR 32'h00400000 [current_design]
如果不加该约束,默认Fallback会回到0地址,也就是传统意义上的Golden;现在将其指向0040 0000也就是LED0.bit的地址
2-重新生成Multiboot.mcs
3-跟刚刚一样,修改Multiboot.mcs第三处对应LED1.bit的数据随便改改
4-重新烧录。测试现象为LED3闪烁后,切到LED0闪烁。即Golden运行后,本应切到LED1.bit,但是由于启动失败,Fallback到了指定地址也就是LED0的bit开始工作
注意事项
1-Golden的Flash数据也会坏,那么怎么起到保护作用的呢?答:Golden的代码量可以做到几十行内,对应Flash的数据量可以做的非常小,只能尽可能降低数据损坏的概率。
2-真的随便坏数据都能自动Fallback吗?MCS文件也有帧头帧尾,只能说坏在用户数据都还行,要是AA995566坏了就直接执行不了后面的了;要是30020001 00400000坏了,前半截是Next Addr指令,后半截是具体的跳转地址,可能就跳到别的地方了,然后开始找新的AA995566开始加载。这个我们可以做个测试:
将Golden指向0040 000也就是LED0.bit的地址
将LED0工程加入NEXT ADDR的约束指向下一个地址0080 0000
生成对应MCS文件,并破坏第一个AA995566,破坏第二个AA995566后面用户数据部分
重新烧录,观察测试现象为,LED3直接不闪烁,长时间后LED1开始闪烁。原因是LED3对应的Golden的同步头没了,直接没加载,一路到0040 0000找到新的文件头,但是这个LED0.bit又被破坏了,进一步Fallback到了LED1.bit开始运行
版本更新
260420:初始版本
260421:完善剩余部分