在线升级指的是程序不通过JTAG,仅仅是上位机发数据给fpga,fpga在将数据写到flash, 断电上电后,程序自动加载到fpga中,相当于仅仅通过一个接口(网口,usb接口或者串口),更新了fpga中的程序,对于一款成熟的产品,在线升级功能是必须的。下面将通过验证和实现两个方面分别介绍。
1, 在线升级的验证。
数据的大致流向是: 串口调试助手通过串口发数据到fpga,fpga用spi接口发数据到flash.
首先需要了解的是MCS文件的数据结构。
每个记录包含5个域,它们按以下格式排列: :llaaaatt[dd...]cc
每一组字母对应一个不同的域,每一个字母对应一个十六进制编码的数字。每一个域由至少两个十六进制编码数字组成,它们构成一个字节,就像以下描述的那样:
: 每个Intel HEX记录都由冒号开头.
ll 是数据长度域,它代表记录当中数据字节(dd)的数量。
aaaa 是地址域,它代表记录当中数据的起始地址。
tt 是代表HEX记录类型的域,它可能是以下数据当中的一个: 00 – 数据记录 01 – 文件结束记录 02 – 扩展段地址记录 04 – 扩展线性地址记录
dd 是数据域,它代表一个字节的数据。一个记录可以有许多数据字节.记录当中数据字节的数量必须和数据长度域(ll)中指定的数字相符。
cc 是校验和域,它表示这个记录的校验和。校验和的计算是通过将记录当中所有十六进制编码数字对的值相加,以256为模进行以下补足。
:10246200464C5549442050524F46494C4500464C33
其中: 10 是这个记录当中数据字节的数量。
2462 是数据将被下载到存储器当中的地址。
00 是记录类型(数据记录)
464C…464C是数据。
33 是这个记录的校验和。
:02000004FFFFFC
其中: 02 是这个记录当中数据字节的数量。
0000 是地址域,对于扩展线性地址记录,这个域总是0000。
04 是记录类型 04(扩展线性地址记录)
FFFF 是地址的高16位。
FC 是这个记录的校验和。
具体可参考https://wenku.baidu.com/view/5feed5d6195f312b3169a5f7.html?sxts=1539592612656
我们只需要将记录类型为00中的数据取出来即可,组成能够被串口调试助手识别的TXT文件,然后以16进制的方式往下发。
通过串口进入到fpga中数据送到fifo中,作为缓存和跨时钟域,接收到数据后,执行flash擦除操作,当数据存到256个时,一次将这些数据写入到flash中,持续这样,当延时1s后,fifo中有数据但是还是不足256个,判断该数据是最后的数据,将这些数据写完后,升级过程完成。
回读了flash中的数据与写进的数据作对比,完全一样;并且断电上电后,更新到flash中的数据能够加载到FPGA中,并能正常运行。依此判断:该方案可行。
2,在线升级的实现。
验证过程将调试助手替代上位机软件,并不科学,不能用于实际的产品中,所以需要上位机根据相应的协议发相应的指令到fpga,并执行相应的操作。
上位机发数据的波特率是9600bps,一bit起始位位,8bit数据位,1bit停止位。spi时钟是25M,上位机将mcs文件拆分为一包一包的数据,每包数据256个字节,在写命令之前需要发出擦除指令,每包数据后面跟着相应的CRC校验位,校验结果通过串口返回给上位机,,正确则继续发下一包数据,不正确则重发该包数据。3个字节的地址和256个字节的数据缓存到ram中,之后开始启动flash操作,完成相应操作,操作完成够,返回 信息给上位机,上位机下发下一包数据。
CRC16用的是CRC16--XMODEM模型,代码来自https://www.cnblogs.com/youngforever/archive/2013/05/26/3104648.html
充分利用crc信号,即可算出相应的crc结果。
module CRC_GEN(
input rst, /*async reset,active low*/
input clk, /*clock input*/
input [7:0] data_in, /*parallel data input pins */
input d_valid, /* data valid,start to generate CRC, active high*/
output reg[15:0] crc
);
integer i;
reg feedback;
reg [15:0] crc_tmp; /* * sequential process */
always @(posedge clk or negedge rst)
begin
if(!rst)
crc <= 16'b0; /*触发器中的初始值十分重要 */
else if(d_valid==1'b0)
crc <= crc;
else
crc <= crc_tmp;
end /* * combination process */
always@( data_in or crc)
begin
crc_tmp = crc;
for(i=7; i>=0; i=i-1)
begin
feedback = crc_tmp[15] ^ data_in[i];
crc_tmp[15] = crc_tmp[14];
crc_tmp[14] = crc_tmp[13];
crc_tmp[13] = crc_tmp[12];
crc_tmp[12] = crc_tmp[11] ^ feedback;
crc_tmp[11] = crc_tmp[10] ;
crc_tmp[10] = crc_tmp[9];
crc_tmp[9] = crc_tmp[8];
crc_tmp[8] = crc_tmp[7];
crc_tmp[7] = crc_tmp[6];
crc_tmp[6] = crc_tmp[5];
crc_tmp[5] = crc_tmp[4] ^ feedback;
crc_tmp[4] = crc_tmp[3];
crc_tmp[3] = crc_tmp[2];
crc_tmp[2] = crc_tmp[1];
crc_tmp[1] = crc_tmp[0];
crc_tmp[0] = feedback;
end
end
endmodule
经过仿真后,结果也没问题。
经过反复调试,将节拍对上后。本以为工作告一段落,却出现了两个意料之外的问题。
a, 上位机写数据写到几十包几百包的时候,fpga没有返回校验结果。操作无法继续执行。 后来发现是我用分频时钟驱动好几个 模块,(从黑金借鉴来的代码,坑爹啊。)大概是驱动能力不足导致数据接收错误。换个底层的串口模块后,问题解决。
b. 当几万包的数据写完后,第一包数据总是有问题,后面的正确。反复确认发现,当数据能凑成是256的整数时,数据正常,当
最后的数据不足256时,第一包数据异常。发现是上位机写错了位置,它重写了第一包数 据。