当前位置:   article > 正文

Xilinx FPGA Multiboot-使用ICAPE2原语

Xilinx FPGA Multiboot-使用ICAPE2原语

什么是远程升级

是一种更新程序的方法。在实际情况下,我们做好一个产品,进行更新的时候,不可能把产品拆开,对里面的固件进行升级。所以会涉及到一种远程升级的方法,就是上位机通过串口,或者PCIe等网络协议,将需要更新的程序放入FPGA的fifo,FPGA通过spi控制与flash进行交互,将从上位机收到的数据传入flash。

这里的flash就是一个存储产品配置的一个存储器。

什么是Multiboot?

将flash分区,放入不同的镜像工程,可以根据地址寻找不同的镜像工程进行加载。

flash存储的形式?

在flash中,我们会进行分区。

Golden Image区:从地址0开始,存储产品的一个稳定版本。当update区的程序启动失败时,让产品加载这个稳定版本,不至于让产品不能启动。

Update Image区:在golden区的地址之上开辟。用来存储我们的升级版本。

这是一个加载的顺序图,从图上我们可以看出:

step1:FPGA首先从地址0开始加载,这时候我们处于Golden镜像。

step2:由于我们在golden镜像的工程里面设置了跳转。所以这个时候我们会跳过Golden区。跳到我们设置的flash地址。

step3:这时候我们开始开始加载Update Image

step4:这里会进行一个判断,如果Update Image没有问题。我们就直接加载Update镜像,也就是我们需要升级的程序。如果有问题,那我们会进入step5;

step5:如果Update Image有问题,会进行一个fallback,也就是回滚。加载稳定程序 Golden Image.

想要做到多镜像启动,有两种方法。

1.是在bit流中加入指令,让FPGA加载的时候根据指令跳入到需要加载的flash镜像地址。

2.Xlinx提供了一个原语,可以在代码中进行操作,本质还是在bit流文件中加入指令。不过在代码中操作更方便。

小测试:

代码工程放在最下面,需要的自取

说一下测试的功能点以及怎么体现Multiboot:

1.两个工程:

a.golden工程:点亮led0

b.update工程:点亮led1

2.使用ICAPE2原语进行分区控制。

设备ID查找表:

怎么使用?我先贴出一个代码参考:

  1. `default_nettype none
  2. module multiboot_ctrl(
  3. input wire clk,
  4. input wire rst_n,
  5. input wire multiboot_start, //触发Multiboot, 上升沿有效
  6. input wire [31:0] multiboot_addr, //要启动的Muliboot Image的起始地址
  7. output reg busy
  8. );
  9. wire multiboot_start_pe;
  10. reg multiboot_start_d0;
  11. reg multiboot_start_d1;
  12. assign multiboot_start_pe = multiboot_start_d0 & (~multiboot_start_d1); // 采集控制使能的下降沿
  13. always @(posedge clk) begin
  14. multiboot_start_d0 <= multiboot_start;
  15. multiboot_start_d1 <= multiboot_start_d0;
  16. end
  17. //-------------------ICAPE2原语-----------------------------
  18. wire ICAPE2_CLK;
  19. wire [31:0] ICAPE2_O;
  20. reg ICAPE2_CSIB;
  21. wire [31:0] ICAPE2_I;
  22. reg ICAPE2_RDWRB;
  23. assign ICAPE2_CLK = clk;
  24. ICAPE2 #(
  25. .DEVICE_ID (32'h362d093), // 需要看自己芯片对应的设备ID A7-35T的为32'h362d093
  26. .ICAP_WIDTH ("X32"), // 输入,输出位宽配置 "X32", "X8", "X16" 默认 "X32"
  27. .SIM_CFG_FILE_NAME ("NONE") // 配置仿真模型要解析的原始比特流文件,这里没有 所以填"NONE"
  28. )
  29. ICAPE2_inst(
  30. .O (ICAPE2_O), // 32-bit output: Configuration data output bus
  31. .CLK (ICAPE2_CLK), // 1-bit input: Clock Input
  32. .CSIB (ICAPE2_CSIB), // 1-bit input: ICAP的使能信号,低有效。如果不发指令或地址的时候,拉高
  33. .I (ICAPE2_I), // 32-bit input: 数据总线,输入对应的指令或flash跳转地址
  34. .RDWRB (ICAPE2_RDWRB) // 1-bit input: 1对应rd0对应wr。发指令或地址的时候就是0,否则1
  35. );
  36. wire [31:0] Dummy = 32'hFFFFFFFF; //伪指令,不进行操作,实际上为等待状态
  37. wire [31:0] Sync_Word = 32'hAA995566; //跳转指令同步字
  38. wire [31:0] NOOP = 32'h20000000; //中断指令
  39. wire [31:0] WR_WBSTAR = 32'h30020001; //type1 写入跳转空间的字节
  40. wire [31:0] WBSTAR = {3'b000, 5'h0, multiboot_addr[31:8]}; //跳转空间地址,可以根据bit大小修改
  41. wire [31:0] WR_CMD = 32'h30008001; //type1 写入的控制指令字
  42. wire [31:0] IPROG = 32'h0000000F; //跳转到下一个程序的控制指令
  43. //ICAPE2位翻转
  44. reg [31:0] wrdat;
  45. assign ICAPE2_I = {wrdat[24], wrdat[25], wrdat[26], wrdat[27], wrdat[28], wrdat[29], wrdat[30], wrdat[31],
  46. wrdat[16], wrdat[17], wrdat[18], wrdat[19], wrdat[20], wrdat[21], wrdat[22], wrdat[23],
  47. wrdat[8], wrdat[9], wrdat[10], wrdat[11], wrdat[12], wrdat[13], wrdat[14], wrdat[15],
  48. wrdat[0], wrdat[1], wrdat[2], wrdat[3], wrdat[4], wrdat[5], wrdat[6], wrdat[7]};
  49. //------------------------FSM----------------------------------
  50. localparam S_IDLE = 16'h0001;
  51. localparam S_DUMMY = 16'h0002;
  52. localparam S_SYN_WORD = 16'h0004;
  53. localparam S_NOOP1 = 16'h0008;
  54. localparam S_WR_WBSTAR = 16'h0010;
  55. localparam S_WBSTAR = 16'h0020;
  56. localparam S_WR_CMD = 16'h0040;
  57. localparam S_IPROG = 16'h0080;
  58. localparam S_NOOP2 = 16'h0100;
  59. localparam S_STOP = 16'h0200;
  60. reg [15:0] state = S_IDLE;
  61. reg [15:0] next_state;
  62. always @(posedge clk) begin
  63. if(~rst_n) begin
  64. state <= S_IDLE;
  65. end
  66. else begin
  67. state <= next_state;
  68. end
  69. end
  70. always @(*) begin
  71. case(state)
  72. S_IDLE: begin
  73. if(multiboot_start_pe) begin
  74. next_state <= S_DUMMY;
  75. end
  76. else begin
  77. next_state <= S_IDLE;
  78. end
  79. end
  80. S_DUMMY: next_state <= S_SYN_WORD;
  81. S_SYN_WORD: next_state <= S_NOOP1;
  82. S_NOOP1: next_state <= S_WR_WBSTAR;
  83. S_WR_WBSTAR: next_state <= S_WBSTAR;
  84. S_WBSTAR: next_state <= S_WR_CMD;
  85. S_WR_CMD: next_state <= S_IPROG;
  86. S_IPROG: next_state <= S_NOOP2;
  87. S_NOOP2: next_state <= S_STOP;
  88. S_STOP: next_state <= S_IDLE;
  89. default: next_state <= S_IDLE;
  90. endcase
  91. end
  92. always @(posedge clk) begin
  93. case(state)
  94. S_IDLE: begin
  95. wrdat <= 32'd0;
  96. ICAPE2_CSIB <= 1'b1;
  97. ICAPE2_RDWRB <= 1'b1;
  98. end
  99. S_DUMMY: begin
  100. wrdat <= Dummy;
  101. ICAPE2_CSIB <= 1'b0;
  102. ICAPE2_RDWRB <= 1'b0;
  103. end
  104. S_SYN_WORD: begin
  105. wrdat <= Sync_Word;
  106. ICAPE2_CSIB <= 1'b0;
  107. ICAPE2_RDWRB <= 1'b0;
  108. end
  109. S_NOOP1: begin
  110. wrdat <= NOOP;
  111. ICAPE2_CSIB <= 1'b0;
  112. ICAPE2_RDWRB <= 1'b0;
  113. end
  114. S_WR_WBSTAR: begin
  115. wrdat <= WR_WBSTAR;
  116. ICAPE2_CSIB <= 1'b0;
  117. ICAPE2_RDWRB <= 1'b0;
  118. end
  119. S_WBSTAR: begin
  120. wrdat <= WBSTAR;
  121. ICAPE2_CSIB <= 1'b0;
  122. ICAPE2_RDWRB <= 1'b0;
  123. end
  124. S_WR_CMD: begin
  125. wrdat <= WR_CMD;
  126. ICAPE2_CSIB <= 1'b0;
  127. ICAPE2_RDWRB <= 1'b0;
  128. end
  129. S_IPROG: begin
  130. wrdat <= IPROG;
  131. ICAPE2_CSIB <= 1'b0;
  132. ICAPE2_RDWRB <= 1'b0;
  133. end
  134. S_NOOP2: begin
  135. wrdat <= NOOP;
  136. ICAPE2_CSIB <= 1'b0;
  137. ICAPE2_RDWRB <= 1'b0;
  138. end
  139. S_STOP: begin
  140. wrdat <= 32'd0;
  141. ICAPE2_CSIB <= 1'b1;
  142. ICAPE2_RDWRB <= 1'b1;
  143. end
  144. default: begin
  145. wrdat <= 32'd0;
  146. ICAPE2_CSIB <= 1'b1;
  147. ICAPE2_RDWRB <= 1'b1;
  148. end
  149. endcase
  150. end
  151. always @(*) begin
  152. case(state)
  153. S_IDLE: busy <= 1'b0;
  154. default: busy <= 1'b1;
  155. endcase
  156. end
  157. endmodule

就是根据一个使能信号,然后一步一步往ICAPE2里面发指令,具体看上面代码。

代码参考这位大佬的:K7系列FPGA多重启动(Multiboot)_multiboot多重配置-CSDN博客

3.XDC文件的配置
  1. #Updata Image工程中添加
  2. set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
  3. set_property CONFIG_MODE SPIX1 [current_design]
  4. set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]
  1. #Golden Image工程中添加
  2. set_property BITSTREAM.CONFIG.CONFIGFALLBACK ENABLE [current_design]
  3. set_property BITSTREAM.CONFIG.NEXT_CONFIG_ADDR 0x00400000 [current_design]
  4. set_property CONFIG_MODE SPIX1 [current_design]
  5. set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]


这一条是让golden可以进行跳转,以及跳转到哪个地址。

这一条是配置回滚使能。让Update Image加载失败的时候能够回滚到Golden Image。

这两个是配置SPI的模式,后面生成mcs要用。

4.bit流生成

生成之后看下对应的功能是否正常。具体请看步骤1

5.生成mcs文件。

对上图进行一下讲解:

1.生成文件类型,毫无疑问选MCS(因为我们就是要生成这个)

2.根据你的flash的大小填写,我这边是16MB,这个是以字节为单位哈。

3.填写你的生成文件路径

4.程序下载的方式,这里和上面XDC对应上,XDC写的SPI 1,所以这里选的SPIx1

5.你的Golden Image的bit文件路径,记住这里的开始地址是0;

6.你的Update Image的bit文件路径,记住这里的开始地址是你自己设置的update Image的起始地址。如果你不知道应该设置多大,你可以先填写大一点。然后在prm文件里面可以查看。

prm文件:是mcs的伴生文件,里面是一些mcs文件的信息。

例如下图:

可以看到bit流文件的大小。

点击OK,然后在你填写的文件生成路径下面可以看到两个文件,一个mcs一个prm

6.将mcs文件固化到板子上。

这个就不具体说了,不清楚的搜下看看。

7.测试正常功能

固化之后,断电,拔掉Jtag,然后上电。正常的话,会看到led1亮,也就是Update工程被加载了。

8.测试fallback

正常我们是加载Update Image,现在我们测试一下能不能回滚:

1.修改update的bit流文件。打开之后随便在中间改几个字节。

我用的vscode,下载一个插件就能打开了

2.改好了之后,我们生成一个新的mcs文件,就是战损版。

3.使用战损版mcs进行固化

4.这个时候应该看到led0亮,因为update的bit流被我们人为破坏了,所以回滚到了Gloden 工程。

重点:

1.理解多镜像分区,以及为什么要分区。

2.ICAPE2原语的使用。

3.具体代码可以在我的gitee上自取,有现成的mcs文件。如果有帮助,麻烦gitee上点个心。

Xilinx-Multiboot-ICAPE2-test: 使用Xilinx的ICAPE2原语,实现一个Multiboot的小测试

如果有说错的地方,欢迎评论区或私聊指出,大家可以一起讨论哈~~~

更多嵌入式,FPGA资料可参考:天津大学四川院FPGA培训中心 -- www.sxfpga.cn

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/701851
推荐阅读
相关标签
  

闽ICP备14008679号