最近做的了一个无线通信的项目,需要在同一套设备上实现两套不同的波形软件,因为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"

有相关问题请留言

11-25 09:16