前言 这是一种无需重新烧录比特就能切换FPGA正在运行的比特的方法。 分为两种,一种是全局可重构,也可以叫做启动镜像切换,本质上是将两份或多份比特流同时固化到FLASH中,在运行过程实时中切换使用哪一份比特,这是基于NOR FLASH的片上执行特性(暂且这么称呼,本质上是因为NOR只需要给地址就能出数据,没有译码转址这一过程) 。所以对于配置了NOR FLASH的FPGA板卡,我们可以通过ICAP原语来给出比特流加载的同步字,然后送入加载基地址,并给出加载命令,来进行当前执行比特流的切换。
另一种是部分可重构,这种要更复杂一点,但是本质上只不过是把重加载全局比特变成了重加载部分模块的比特,所以会有个画pblock的过程,相比于全局可重构,流程会复杂许多,而且如果是中间模块的替换,由于输入输出端口的变化,会造成一些时序约束的问题,且无法直接使用OOC IP核,只能把IP核做成网表再调用,在我自己的工程上对于资源利用率的降低作用不显著。本文就只着眼全局可重构。
全局可重构我目前探索出来的路径有两条,一条已经实现,就是后文的合并bit到一个mcs文件中,然后使用icape原语启动。另外的一条路是上位机通过pcie的dma通道,流式传输要切换的比特到板卡上,完成比特的切换,这条路还在摸索,如果能够完成的话,那么对于机箱上的FPGA板卡来说,就可以让上位机保存多份比特,在运行之中随便切换,完成FPGA板卡功能的更新或者切换。
下面仅从怎么做的描述如何将两个工程的比特流合并到同一个mcs文件下 ,并使用这个mcs来对fpga进行烧录固化,并编写简单的代码来调用icape原语来对启动地址进行切换,以达到不重新烧写比特就能进行工程的切换的方法。
有关FLASH的几个工程xdc约束 需要添加到工程的xdc中
title 1 2 3 4 5 6 7 set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design] set_property CONFIG_MODE SPIx1 [current_design] set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
压缩比特,这是一个蛮有用的选项,如果不使用压缩比特的话,那么任何一个工程,不论代码有多简单或者多复杂,都会生成一个同样大小的,未经过压缩的比特,对于7k325t会占用大概11MB的大小,更复杂的片子会更大。如果启用了,就会根据工程代码的复杂程度对比特进行压缩,一般来说都挺有效果的,能把最简单的LED流水灯工程压缩到几百KB的水平。能把我的工程压缩到5.5MB左右大小
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
设置烧录速度,50M是SPI时钟线上的时钟频率,一般的SPI存储芯片都能支持这个速率。
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1
SPI宽度,这是用于控制烧录程序的接口的,因为我们要使用Fallback,所以这里必须是x1
set_property CONFIG_MODE SPIx1
配置模式宽度,和上面一样
set_property BITSTREAM.CONFIG.SPI_32BIT_ADDR YES [current_design]
启用32bit地址线模式,如果FLASH大小大于等于256Mb,也就是32MB,需要启用这个约束,并且写入镜像的地址线的方法需要变动,见后文。我的板子上的s25fl256大小正好是256Mb,所以得开。
multiboot模块的调用方法示例 title 1 2 3 4 5 6 7 8 multiboot_ctrl multiboot_ctrl_inst ( .clk (clk_out_100), .rst_n (sys_rst_n), .multiboot_start (~reconfig_key_n), .multiboot_addr (32'h00500000 ), .busy_o () );
这里要注意的是给地址的要求,如果前面使能了32bit地址线,则要根据下文把multiboot_addr[31:8]写到WBSTAR[23:0]
title 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 module multiboot_ctrl ( input wire clk, input wire rst_n, input wire multiboot_start, input wire [31 :0 ] multiboot_addr, output reg busy_o ); wire ICAPE2_CLK; wire [31 :0 ] ICAPE2_O; reg ICAPE2_CSIB; wire [31 :0 ] ICAPE2_I; reg ICAPE2_RDWRB; assign ICAPE2_CLK = clk; ICAPE2 #( .DEVICE_ID (32'h3651093 ), .ICAP_WIDTH ("X32" ), .SIM_CFG_FILE_NAME ("NONE" ) ) ICAPE2_inst ( .O (ICAPE2_O), .CLK (ICAPE2_CLK), .CSIB (ICAPE2_CSIB), .I (ICAPE2_I), .RDWRB (ICAPE2_RDWRB) ); 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 ; 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 ; reg [31 :0 ] wrdat; assign ICAPE2_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 ] }; localparam S_IDLE = 16'h0001 ; localparam S_DUMMY = 16'h0002 ; localparam S_SYN_WORD = 16'h0004 ; localparam S_NOOP1 = 16'h0008 ; localparam S_WR_WBSTAR = 16'h0010 ; localparam S_WBSTAR = 16'h0020 ; localparam S_WR_CMD = 16'h0040 ; localparam S_IPROG = 16'h0080 ; localparam S_NOOP2 = 16'h0100 ; localparam S_STOP = 16'h0200 ; wire multiboot_start_pe; reg multiboot_start_d0; reg multiboot_start_d1; assign multiboot_start_pe = multiboot_start_d0 & (~multiboot_start_d1); always @(posedge clk) begin multiboot_start_d0 <= multiboot_start; multiboot_start_d1 <= multiboot_start_d0; 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_o <= 1'b0 ; default : busy_o <= 1'b1 ; endcase end endmodule
生成两个.bit文件后合并到MCS文件 分别运行两个经过了FLASH约束和添加了multiboot模块的工程,获得它们的比特
然后点击VIVADO左上角工具栏的”Tools-Generate Memory Configuration File”
然后进行FLASH的选择,这里要根据FPGA板卡的原理图进行选择,我这块板卡的型号是这样的。
这里的Interface选择X1,因为之前的约束里表明了是x1的接口。
比特大小和地址的确定 第一个工程在应该被放置在地址零处,假设.bit文件占用4MB,也就是0x400000
那么我们可以吧把第二个地址设置在0x500000处,只要不占用第一个工程的地址最后且不超过FLASH的物理地址就可以。
成功生成mcs文件后就可以使用这个文件去进行烧录了。
注意事项 :烧写固化msc后,需要板子下电后,拔掉JTAG再上电才会从FLASH中的程序从启动,否则在JTAG插着 且VIVADO的硬件管理器页面在激活的情况 下,是不会使用FLASH中的比特流 启动的。
实例演示 我这里使用了两个工程,一个工程实现的物理效果是板子上4个LED的闪烁,另一个工程实现的是板子上8个LED的闪烁。使用板载的一个按键,来进行工程之间的互相切换。
将合并的MultiBoot_LEDs.mcs烧录进板卡,并下电后拔掉jtag再上电 ,然后按动板子上的按键,就能看到原本是4个LED闪烁,变为了8个LED闪烁,再按一下,变回了4个LED闪烁。这只是一个简单的例子,实现的效果虽然简单,但是本质确实是整个工程运行代码的切换。
图我就不放了,就是个LED闪烁工程。
工程打包 链接: https://pan.baidu.com/s/1QTcCIaY0xiVLThJ79uWMFA 提取码: mcqz
参考链接 https://blog.csdn.net/dare_xds/article/details/139151733
https://blog.csdn.net/qq_43557686/article/details/134750377