最近做的了一个无线通信的项目,需要在同一套设备上实现两套不同的波形软件,因为FPGA的逻辑资源不够同时放下两套代码,因此采用了镜像切换的方式来实现,xilinx的专业术语叫multi boot功能 。意思是在一片Flash中的不同地址放两个代码镜像,通过FPGA的任意一个IO切换镜像。详细概念可以参考UG470,PG134等文档,本文仅讲具体的实现代码。
既然是多镜像,意思就是同一套硬件,有多套软件。类似于同一台电脑,可以装了一个linux系统,又装了一个win7系统,甚至多套系统。开机时由用户选择启动哪个系统。
本示例包含2个工程镜像,使用512Mbit的QSPI flash。工程1的镜像放在0地址,工程2的镜像放在地址0x02000000。当外部输入引脚SW为低电平时加载第一个镜像,否则加载第二个镜像。每当SW电平变化,都会启动镜像切换。
第一个工程的顶层如下:
module project1(
input CLK,//输入时钟,不要超过100M
input rst_n,//复位输入
output SW, //输入为低电平启动镜像1,输入为高电平启动镜像2
output LED1,//镜像1亮,镜像2灭
output LED2 //镜像1灭,镜像2亮
);
assign LED1 = 1;
assign LED2 = 0;
mult_boot mult_boot_inst(
.clk (CLK),
.rst_n (rst_n),
.img_index (0),
.boot_sel (SW));
endmodule
第二个工程顶层如下:
module project2(
input CLK,//输入时钟,不要超过100M
input rst_n,//复位输入
output SW, //输入为低电平启动镜像1,输入为高电平启动镜像2
output LED1,//镜像1亮,镜像2灭
output LED2 //镜像1灭,镜像2亮
);
assign LED1 = 0;
assign LED2 = 1;
mult_boot mult_boot_inst(
.clk (CLK),
.rst_n (rst_n),
.img_index (1),
.boot_sel (SW));
endmodule
注意两个工程都需要调用mult_boot模块,不同的工程img_index输入不一样。工程1设置为0,工程2设置为1。
如果你的SPI flash大于128Mb,必须加上如下约束:
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
mult_boot.v代码如下:
module mult_boot(
input clk,
input img_index, //constant to 0 for image0,1 for image1
input rst_n,
input boot_sel //0=boot image0;1=boot image1;
);
parameter [31:0] IMAGE0_ADDR = 32'h0;
parameter [31:0] IMAGE1_ADDR = 32'h02000000;
/*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..*/
localparam [31:0] WBSTAR_IMAGE0_ADDR = {8'h0,IMAGE0_ADDR[31:8]};
localparam [31:0] WBSTAR_IMAGE1_ADDR = {8'h0,IMAGE1_ADDR[31:8]};
reg [1:0] state;
reg boot_sel_lock;
wire [31:0] next_image_addr = (boot_sel_lock==0)? WBSTAR_IMAGE0_ADDR:WBSTAR_IMAGE1_ADDR;
reg [2:0] index;
reg [31:0] icap2_din;
reg icap2_csn;
ICAPE2 #(
.DEVICE_ID(0'h3691093), // Specifies the pre-programmed Device ID value to be used for simulation
// purposes.
.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(), // 32-bit output: Configuration data output bus
.CLK(clk), // 1-bit input: Clock Input
.CSIB(icap2_csn), // 1-bit input: Active-Low ICAP Enable
.I(icap2_din), // 32-bit input: Configuration data input bus
.RDWRB(1'b0) // 1-bit input: Read_n/Write Select input
);
function [31:0] icap_lut;
input [7:0] index;
begin
case(index)
0 :icap_lut={32'hFFFFFFFF };//Dummy Word
1 :icap_lut={32'hAA995566 };//Sync Word
2 :icap_lut={32'h20000000 };//Type 1 NO OP
3 :icap_lut={32'h30020001 };//Type 1 Write 1 Words to WBSTAR
4 :icap_lut={next_image_addr };//Warm Boot Start Address (Load the Desired Address)
5 :icap_lut={32'h30008001 };//Type 1 Write 1 Words to CMD
6 :icap_lut={32'h0000000F };//IPROG Command
7 :icap_lut={32'h20000000 };//Type 1 NO OP
default:icap_lut=0;
endcase
end
endfunction
reg [31:0] command;
always @ (posedge clk) command <= icap_lut(index);
wire [31:0] command_swapped = {
command[24],command[25],command[26],command[27],command[28],command[29],command[30],command[31],
command[16],command[17],command[18],command[19],command[20],command[21],command[22],command[23],
command[ 8],command[ 9],command[10],command[11],command[12],command[13],command[14],command[15],
command[ 0],command[ 1],command[ 2],command[ 3],command[ 4],command[ 5],command[ 6],command[ 7]};
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n) begin
state<=0;icap2_csn<=1;boot_sel_lock<=img_index;index<=0;
end
else case(state)
0:begin
boot_sel_lock<=boot_sel;
if(boot_sel_lock!=img_index)
state<=1;
end
1:begin
if(index==8) begin
state<=0;index<=0;
end
else begin
state<=2;index<=index+1;icap2_csn<=0;icap2_din<=command_swapped;
end
end
2:begin state<=3;icap2_csn<=1;end
3:state<=1;
default:state<=0;
endcase
end
endmodule
注意DEVICE_ID的值要根据具体FPGA的型号填入不同的值,V7系列请参考UG470的第14页。
最终实现的效果:SW输入0则加载第一个工程的镜像,LED1亮。SW输入1则加载第二个工程的镜像,LED2亮。
附上两个工程的bit文件合成一个mcs文件的脚本:
write_cfgmem -format mcs -size 64 -interface SPIx4 -loadbit {up 0x00000000 "project1.bit" up 0x02000000 "project2.bit" } -force -file "merged.mcs"
有相关问题请留言