zynq 裸机调试串口在线升级升级
- 查看网上资料,一般都是利用网口给开发板升级,自己调试一个利用调试串口给开发板升级,升级过程中各种突发情况引起升级失败,防止变成板砖如何处理。
- 开发板:zynq7020
- spi flash型号:MT25QL128ABA(16MB)镁光)的flash、W25Q128J (华邦的)flash(这个flash正常烧录在vivado2018.3的环境可能程序起来不了,具体如何处理可以私信我)
- 串口:波特率115200 一位停止位,没有校验位和起始位
- 开发环境:vivado 2018.3 以及对应的SDK
- 裸机升级文件:fsbl.elf, hello.bit,hello.elf一起在SDK中构建成一个BOOT.BIN
1.预防变成板砖的办法
-
multiboot机制
2个boot bin文件放在flash的不同地址。
可以修改multiboot register然后在软件里触发软复位,这样zynq-7000会重新从multiboot register指定的flash偏移地址去boot,multiboot register的值每加一,偏移地址增加32KB。multiboot register只能被POR reset清除。
multiboot register: 0x0000C000
Reboot status register: 0x60782000 -
判断升级失败成功的办法
在flash的末尾留一个四个字节的位置当着升级标志位,每次升级收到数据将标志位擦除,如果升级文件写到flash的数据经过校验成功后,给标志位写一个110的,每次上电启动的时候,在fsbl阶段就去读取flash中的标志位,如果读取的值等于110,代表上次升级成功,就重启跳转到multiboot register寄存器保存的地址启动升级后的BOOT.bin. -
如何保证接收数据的正确性
对整个接收数的数据做CRC16校验,将CRC校验值放在最后,先将数据全部放在开发板的DDR中,对接收到的数据进行CRC校验,如果CRC校验成功,然后将数据写入flash中,再将数据读出来,和DDR中数据进行一个对比,保证数据百分百的正确,如果校验都成功了,再去改变flash中的标志位。
2.升级代码示例
- 1、串口初始化
修改xuartps_polled_example 模版代码
int Uart_Init(void){
int Status;
Status = UartPsInterExample(&Intc ,&UartPs,
UART_DEVICE_ID, UART_INT_IRQ_ID);
if(Status != XST_SUCCESS){
xil_printf("uart Interrupt Example Test Failed\r\n);
return XST_FAILURE;
}
printf("Successfully ran UART Interrupt Example Test\r\n);
return XST_SUCCESS;
}
- 2、串口中断处理
if(Event == XUARTPS_EVENT_RECV_DATA || Event == XUARTPS_EVENT_RECV_TOUT){
TotalReceivedCount = EventData;
if(EventData == 2 && msg_recv_stage == RECVING_MSG_HEAD){
if(RecvBuffer[0] == 0x7B && RecvBuffer[1] == 0x7B){//准备升级
printf("start recv file\r\n);
msg_recv_state = RECV_UPDATE;
memset((u8 *)DDR_UPDATE_PROGRESS, 0, RECV_MAX_LEN);
XUartPs_Recv(&UartPs, (u8 *)DDR_UPDATE_PROGESS, RECV_MAX_LEN);//持续接收文件
}
}else{//其他错误数据
memset(RecvBuffer, 0, sizeof(RecvBuffer);
msg_recv_stage = NEED_RECV_MSG_HEAD;
}
}else(EventData >2 && msg_recv_stage == RECV_UPDATE){//这个位置其实可以修改成其他串口接收方式
if(*((u8 *)DDR_UPDATE_PROGESS + EventDate - 1) == 0x7D && *((u8 *)DDR_UPDATE_PROGESS + EventDate - 2) == 0x7D ){//如果接收到的数据长度和实际下发的长度相等,下发7D7D代表数据结束
len = EventData - 2;
msg_recv_stage = RECV_UPDATE_COMPLETE;
}else{
xil_printf("recv file len = %d\n", EventData);
xil_printf("====> please send end flag \" 0x7D 0x7D \" \n);
}
}
- 3、写入flash和校验
int Write_To_Flash(void){
1.先crc16校验数据,成功擦除,失败返回接收失败的应答
CRC = CRC_16(DATA, len);
2.校验成功,擦除
FlashErase(&Qspi, QSIFLASH_ADDRESS, page_count*PAGE_SIZE);
3.写入flash
FlashWrite(&Qspi, QSPIFLASH_ADDRESS, QSPIFLASH_ADDRSSS, PAGE_SIZE,4WRITE_CMD);
4.读flash的值与存到ddr中的数据进行对,升级一定要多次校验,保证数据正确
Flash_TO_DDR(&Qspi, QSPIFLASH_ADDRESS, len, READ_CMD, DDR_UPDATE_READ_ADDRESS);
5.比较数据,改变标志位数据
if(!strncmp((u8 *)DDR_UPDATE_PROGESS, (u8 *)DDR_UPDATE_READ_ADDRESS + 4, len){
WriteBuffer[DATA_OFFSET] = 110;
FlashWrite(&Qspi, QSPIFLASH_FLAG_ADDRESS, 1, READ_CMD);
xil_printf("update success !!!\n");
}else{
xil_printf("update fail !!!\n");
return -1;
}
return 0;
}
-
4、开机校验
开机校验的BOOT.bin程序也需要有能够升级的应用程序,升级失败后,可以通过校验程序升级。
开机校验有两种方式一种是在fsbl阶段读取校验,第二种是应用程序中校验,具体的可以参考
https://blog.youkuaiyun.com/weixin_41922484/article/details/104037513?spm=1001.2014.3001.5506 -
5.网口升级
网口升级和串口升级类似,以前实现过linux系统下通过上位机分别给同一个产品的Z7 和 K7两片flash升级,和串口的思想类似,在这里主要提供思路。如果需要源码可以私信我。