参考手册ug570。
一、ICAPE3原语
1.1 ICAPE3原语的介绍

ICAPE3 #(
.DEVICE_ID(32'h03628093), // Specifies the pre-programmed Device ID value to be used for simulation purposes.
.ICAP_AUTO_SWITCH("DISABLE"), // Enable switch ICAP using sync word.
.SIM_CFG_FILE_NAME("NONE") // Specifies the Raw Bitstream (RBT) file to be parsed by the simulation model.
)
ICAPE3_inst (
.AVAIL(AVAIL), // 1-bit output: Availability status of ICAP.
.O(O), // 32-bit output: Configuration data output bus.
.PRDONE(PRDONE), // 1-bit output: Indicates completion of Partial Reconfiguration.
.PRERROR(PRERROR), // 1-bit output: Indicates error during Partial Reconfiguration.
.CLK(CLK), // 1-bit input: Clock input.
.CSIB(CSIB), // 1-bit input: Active-Low ICAP enable.
.I(I), // 32-bit input: Configuration data input bus.
.RDWRB(RDWRB) // 1-bit input: Read/Write Select input.
);
各信号解释如下:
| 信号名称 | 解释 |
|---|---|
| AVAIL | ICAP可用标志 |
| O | 配置数据输出总线 |
| PRDONE | partial reconfiguration重配置完成标志 |
| PRERROR | partial reconfiguration重配置错误标志 |
| CLK | 时钟输入 |
| CSIB | 片选信号,低电平有效 |
| I | 配置数据输入总线 |
| RDWRB | 读写标志,高电平读,低电平写 |
1.2 ICAPE3完成FPGA重加载IPROG
使用ICAPE3完成IPROG功能,UG570中提供了示例的流程,如下图:

只需要按照上述流程依次配置即可。只需要注意确定好固件加载的起始地址,即Warm boot start address (Load the desired address)。
另外需要注意的是,ICAPE3的端口是32位端口,使用程序写入到其输入总线是,需要进行bit位置互换,互换方式参考下图:

本文中控制ICAPE3的示例代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2026/01/09 17:13:44
// Design Name:
// Module Name: FPGA_firmware_reload
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module FPGA_firmware_reload(
input clk,
input rst_n,
input firmware_reload_req,
output reg firmware_reload_error = 1'b0, //高电平有效,若失败则通知CXP板卡;否则直接重新启动相机固件,此时CXP链路断开,CXP板卡重新发现设备
input flash_busy //flash忙,此时不响应reload命令
);
reg firmware_reload_req_d1 = 1'b0;
reg firmware_reload_req_d2 = 1'b0;
//ICAPE3信号
wire ICAP_AVAIL;
wire [31:0] ICAP_O;
wire ICAP_PRDONE;
wire ICAP_PRERROR;
reg ICAP_CSIB = 1'b1;
reg [31:0] ICAP_I = 0;
reg ICAP_RDWRB = 1'b0;
//-----------相关命令字-------------------------------------
localparam DUMMY_WORD = 32'hFFFF_FFFF; // 填充字
localparam SYNC_WORD = 32'hAA99_5566; // 同步字
localparam TYPE1_NOOP = 32'h2000_0000; // TYPE1 空操作
localparam TYPE1_WBSTAR = 32'h3002_0001; //写一个字到WBSTAR
localparam WBSTAR = 32'h0; //指定WBSTAR地址,即程序加载起始地址
localparam TYPE1_CMD = 32'h3000_8001; //写一个字到CMD
localparam IPROG_CMD = 32'h0000_000F; //IPROG命令
localparam S_IDLE = 4'd0;
localparam S_JUDGE = 4'd1;
localparam S_CONFIG = 4'd2;
localparam S_END = 4'd3;
reg [3:0] state = S_IDLE; //按顺序进行相关操作,共计8条指令
reg [3:0] config_cnt = 0; //配置命令计数
// 功能:将输入数据按ICAPE3要求的格式进行转换
// 步骤:1. 每个字节内比特颠倒 2. 整体字节顺序交换
function [31:0] icap_data_swap;
input [31:0] data_in;
icap_data_swap = {data_in[24], data_in[25], data_in[26], data_in[27], data_in[28], data_in[29], data_in[30], data_in[31],
data_in[16], data_in[17], data_in[18], data_in[19], data_in[20], data_in[21], data_in[22], data_in[23],
data_in[8], data_in[9], data_in[10], data_in[11], data_in[12], data_in[13], data_in[14], data_in[15],
data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6], data_in[7]};
endfunction
// 状态机
always @(posedge clk)
begin
firmware_reload_req_d1 <= firmware_reload_req;
firmware_reload_req_d2 <= firmware_reload_req_d1;
ICAP_CSIB <= 1'b1; //片选信号,默认高电平
ICAP_RDWRB <= 1'b1;//读写信号,默认高电平读
case(state)
S_IDLE: begin
config_cnt <= 0;
if(!firmware_reload_req_d2 & firmware_reload_req_d1) begin //firmware_reload_req上升沿
state <= S_JUDGE;
firmware_reload_error <= 1'b0;
end
end
S_JUDGE : begin
if(ICAP_AVAIL) begin//ICAPE3准备好
state <= S_CONFIG;
end
else begin
state <= S_IDLE;
firmware_reload_error <= 1'b1;
end
end
S_CONFIG: begin
if(config_cnt <= 7 ) begin
config_cnt <= config_cnt + 1'b1;
ICAP_CSIB <= 1'b0;
ICAP_RDWRB <= 1'b0;
end
else begin
state <= S_END;
end
case(config_cnt)
4'd0 : ICAP_I <= icap_data_swap(DUMMY_WORD); //发送填充字
4'd1 : ICAP_I <= icap_data_swap(SYNC_WORD); //发送同步字
4'd2 : ICAP_I <= icap_data_swap(TYPE1_NOOP); //发送TYPE1 空操作
4'd3 : ICAP_I <= icap_data_swap(TYPE1_WBSTAR); //写一个字到WBSTAR
4'd4 : ICAP_I <= icap_data_swap(WBSTAR); //指定WBSTAR地址,即程序加载起始地址
4'd5 : ICAP_I <= icap_data_swap(TYPE1_CMD); //写一个字到CMD
4'd6 : ICAP_I <= icap_data_swap(IPROG_CMD); //IPROG命令
4'd7 : ICAP_I <= icap_data_swap(TYPE1_NOOP); //发送TYPE1 空操作
endcase
end
S_END: begin
if(ICAP_PRDONE) begin
state <= S_IDLE;
if(ICAP_PRERROR)
firmware_reload_error <= 1'b1;
end
end
endcase
end
ICAPE3 #(
.DEVICE_ID(32'h03628093), // Specifies the pre-programmed Device ID value to be used for simulation purposes.
.ICAP_AUTO_SWITCH("DISABLE"), // Enable switch ICAP using sync word.
.SIM_CFG_FILE_NAME("NONE") // Specifies the Raw Bitstream (RBT) file to be parsed by the simulation model.
)
ICAPE3_inst (
.AVAIL(ICAP_AVAIL), // 1-bit output: Availability status of ICAP.
.O(ICAP_O), // 32-bit output: Configuration data output bus.
.PRDONE(ICAP_PRDONE), // 1-bit output: Indicates completion of Partial Reconfiguration.
.PRERROR(ICAP_PRERROR), // 1-bit output: Indicates error during Partial Reconfiguration.
.CLK(clk), // 1-bit input: Clock input.
.CSIB(ICAP_CSIB), // 1-bit input: Active-Low ICAP enable.
.I(ICAP_I), // 32-bit input: Configuration data input bus.
.RDWRB(ICAP_RDWRB) // 1-bit input: Read/Write Select input.
);
endmodule
本文中验证IPROG是否成功,采用的方法是:
第一步,烧录到FLASH中一个程序,控制LED闪烁。
第二步,通过JTAG烧录一个新的程序,控制LED常亮。新的程序中含有IPROG功能。
第三步,通过VIO控制程序重新加载FLASH中的固件,此时LED闪烁,证明IPROG成功。
具备ICAPE3原语并实现FPGA重新加载的工程文件如下:
二、MULTIBOOT功能的实现
根据上一章节,可以自行设计不同的逻辑,使用ICAPE3原语,实现在FLASH中不同地址的固件的加载,实现多种多样的MULTIBOOT的功能。本章节中就不在赘述。
本章节讲述将IPROG命令嵌入到bit文件中的具体操作方式,使用golden程序和update程序的切换。
2.1 MULTIBOOT工程介绍
上电后的操作流程基本如下:


如何能实现这种嵌入式的IPROG命令?将IPROG命令嵌入到bit文件中,自动实现上电后启动update程序?实现方式比较简单,只需要添加约束文件即可,在golden工程和update工程中均添加约束文件。
golden工程中添加如下的约束文件:
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
##################golden镜像约束#############
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property BITSTREAM.CONFIG.NEXT_CONFIG_ADDR 32'h01000000 [current_design]
#由于fallback使用SPIx1,因此请确定一定有此配置
set_property CONFIG_MODE SPIX1 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
# 如果 FLASH 大小大于等于 256Mb,要增加如下约束,否则高于24位的地址会被忽略,导致无法启动对应的 update 镜像
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
#超时时间=设置值* 20ns 此处的20ns为配置速率的时钟周期。此处设置超时时间为5s
set_property BITSTREAM.CONFIG.TIMER_CFG 32'hEE6B280 [current_design]
##################golden镜像约束#############
注意,在golden工程中,约束的update的配置地址是32'h01000000,那么update程序的起始地址即为32'h01000000。
update工程中添加的约束文件如下:
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
##################MultiBoot镜像约束#############
set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
set_property CONFIG_MODE SPIX1 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
#由于fallback使用SPIx1,因此请确定一定有此配置
# 如果 FLASH 大小大于等于 256Mb,要增加如下约束,否则高于24位的地址会被忽略,导致无法启动对应的 update 镜像
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
#超时时间=设置值* 20ns 此处的20ns为配置速率的时钟周期。此处设置超时时间为5s
set_property BITSTREAM.CONFIG.TIMER_CFG 32'hEE6B280 [current_design]
##################MultiBoot镜像约束#############
需要注意的是,golden工程的约束和update工程的约束,需要基本保持一致。
2.2 MULTIBOOT的工程示例
第一步,编译golden工程和update工程,编译完成后,得到两个bit文件。
第二步,生成MULTIBOOT配置文件。在vivado中,点击 "Tool -> generate memory configuration File",按照如下图方式填写:

第三步,将得到的MCS文件烧录到FLASH中,重新上电启动,可以看到运行的是update的程序;
第四步,若想验证update程序异常时,是否能回到golden程序,则手动将mcs文件中update的文件修改几个字符,然后重新将错误的mcs文件烧录到FLASH中。此时可以看到运行的是golden程序。
编写的golden程序工程和update程序工程如下: