FPGA在线升级 -- Multiboot
简介
本章节主要描述关于如何从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(
inputwireclk,
inputwirerst_n,
inputwiremultiboot_start,//触发Multiboot, 上升沿有效
inputwire[31:0]multiboot_addr,//要启动的Muliboot Image的起始地址
outputregbusy
);
//-------------------ICAPE2原语-----------------------------
wireICAPE2_CLK;
wire[31:0]ICAPE2_O;
regICAPE2_CSIB;
wire[31:0]ICAPE2_I;
regICAPE2_RDWRB;
assignICAPE2_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;
assignICAPE2_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----------------------------------
localparamS_IDLE= 16'h0001;
localparamS_DUMMY= 16'h0002;
localparamS_SYN_WORD= 16'h0004;
localparamS_NOOP1= 16'h0008;
localparamS_WR_WBSTAR= 16'h0010;
localparamS_WBSTAR= 16'h0020;
localparamS_WR_CMD= 16'h0040;
localparamS_IPROG= 16'h0080;
localparamS_NOOP2= 16'h0100;
localparamS_STOP= 16'h0200;
wiremultiboot_start_pe;
regmultiboot_start_d0;
regmultiboot_start_d1;
assignmultiboot_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_pllsys_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_ledfpga_led_inst(
.sys_clk(updata_clk),
.sys_rst_n('d1),
.led_top(fpga_led)
//.updata_en(updata_en)
);
//vio_testvio_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_pllsys_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_ledfpga_led_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.led_top(fpga_led),
.updata_en(updata_en)
);
//vio_testvio_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擦除、写入等,完成远程升级功能。
原文地址:https://blog.csdn.net/weixin_51418325/article/details/144370219
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!