1,这个模块能使用的前提是在vivado工程中勾选FLASH配置和eth0或eth1支持(不然硬件不支持后面文件会报错)。无论是裸机还是带操作系统升级程序都需要勾选。
2,SDK裸机实现程序升级,进入需要使能 lwip 141库,并进行设置
3,将use_axieth_on_zynq 和 use_emaclite_on_zynq 设为 0;修改 lwip_memory_options 设置,将 mem_size,memp_n_pbuf 这 2 个参数值设大,这样会提高 UDP 传输效率。修改 pbuf_options 设置,将 pbuf_pool_size 设大,增加可用的 pbuf 数量,这样同样会提高 UDP 传输效率。修改 tcp_options 设置,将 lwip_tcp 设置为 false,tcp_queue_ooseq 设为 0,关闭 tcp功能;配置如图:
4,此项不是必须设置的,如果为了提高升级效率可以进行配置。修改 temac_adapter_options 设置,将 n_rx_descriptors 和 n_tx_descriptors 参数设大。这样可以提高 zynq 内部 emac dma 的数据搬移效率:
5,使能 DHCP 功能,将 memory 空间尽可能设置大一些,增大缓存空间,提高效率,,前面已经设置过了
6,将qspi_update模块添加到SDK工程里面,里面包含了qspi.h和qspi.c头文件
qspi.h
#ifndef SRC_QSPI_H_
#define SRC_QSPI_H_
#include "xparameters.h" /* SDK generated parameters */
#include "xqspips.h" /* QSPI device driver */
#include "xil_printf.h"
#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID
#define WRITE_STATUS_CMD 0x01
#define WRITE_CMD 0x02
#define READ_CMD 0x03
#define WRITE_DISABLE_CMD 0x04
#define READ_STATUS_CMD 0x05
#define WRITE_ENABLE_CMD 0x06
#define FAST_READ_CMD 0x0B
#define DUAL_READ_CMD 0x3B
#define QUAD_READ_CMD 0x6B
#define BULK_ERASE_CMD 0xC7
#define SEC_ERASE_CMD 0xD8
#define READ_ID 0x9F
#define COMMAND_OFFSET 0 /* FLASH instruction */
#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */
#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */
#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */
#define DATA_OFFSET 4 /* Start of Data for Read/Write */
#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad
reads */
#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and
quad reads */
#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */
#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */
#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */
#define OVERHEAD_SIZE 4
#define SECTOR_SIZE 0x10000
#define NUM_SECTORS 0x100
#define NUM_PAGES 0x10000
#define PAGE_SIZE 256
#define PAGE_COUNT 16
#define TEST_ADDRESS 0x00055000
#define UNIQUE_VALUE 0x05
#define MAX_DATA PAGE_COUNT * PAGE_SIZE
int update_qspi(XQspiPs *QspiInstancePtr, u16 QspiDeviceId, unsigned int TotoalLen, char *FlashDataToSend) ;
#endif /* SRC_QSPI_H_ */
/***************************** Include Files *********************************/
#include "qspi.h"
#include "xtime_l.h"
#include "stdio.h"
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
void FlashWriteEnable(XQspiPs *QspiPtr);
void FlashWriteDisable(XQspiPs *QspiPtr) ;
void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount);
void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command);
int FlashReadID(void);
void print_percent(int percent) ;
/************************** Variable Definitions *****************************/
XQspiPs QspiInstance;
/*
* The following variables are used to read and write to the flash and they
* are global to avoid having large buffers on the stack
*/
u8 ReadBuffer[PAGE_SIZE + DATA_OFFSET + DUMMY_SIZE];
u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];
int update_qspi(XQspiPs *QspiInstancePtr, u16 QspiDeviceId, unsigned int TotoalLen, char *FlashDataToSend)
{
int Status;
int i ;
unsigned int HasSendNum = 0 ;
unsigned int WriteAddr = 0 ;
unsigned int HasRecvNum = 0 ;
unsigned int ReadAddr = 0 ;
XTime TimerStart, TimerEnd;
float elapsed_time ;
int PercentCurr = -1 ;
int PercentLast = -1 ;
XQspiPs_Config *QspiConfig;
/*
* Initialize the QSPI driver so that it's ready to use
*/
QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
if (NULL == QspiConfig) {
return XST_FAILURE;
}
Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,
QspiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
XQSPIPS_FORCE_SSELECT_OPTION |
XQSPIPS_HOLD_B_DRIVE_OPTION);
XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
XQspiPs_SetSlaveSelect(QspiInstancePtr);
FlashReadID();
printf("Performing Erase Operation...\r\n") ;
XTime_SetTime(0) ;
XTime_GetTime(&TimerStart) ;
FlashErase(QspiInstancePtr, 0, TotoalLen);
XTime_GetTime(&TimerEnd) ;
printf("100%%\r\n") ;
elapsed_time = ((float)(TimerEnd-TimerStart))/((float)COUNTS_PER_SECOND) ;
printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
printf("Erase Operation Successful.\r\n") ;
printf("Performing Program Operation...\r\n") ;
XTime_SetTime(0) ;
XTime_GetTime(&TimerStart) ;
do
{
PercentCurr = (int)(((float)HasSendNum/(float)TotoalLen)*10) ;
if (PercentCurr != PercentLast)
print_percent(PercentCurr) ;
PercentLast = PercentCurr ;
if ((HasSendNum+PAGE_SIZE) > TotoalLen)
{
for (i = 0 ; i < PAGE_SIZE ; i++)
{
if (i >= TotoalLen-HasSendNum)
{
WriteBuffer[DATA_OFFSET + i] = 0 ;
}
else
{
WriteBuffer[DATA_OFFSET + i] = (u8)(FlashDataToSend[HasSendNum+i]);
}
}
FlashWrite(QspiInstancePtr, WriteAddr, PAGE_SIZE, WRITE_CMD);
printf("100%%\r\n") ;
XTime_GetTime(&TimerEnd) ;
elapsed_time = (float)(TimerEnd-TimerStart)/(COUNTS_PER_SECOND) ;
printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
printf("Program Operation Successful.\r\n") ;
HasSendNum+= PAGE_SIZE ;
}
else
{
for (i = 0 ; i < PAGE_SIZE ; i++)
{
WriteBuffer[DATA_OFFSET + i] = (u8)(FlashDataToSend[HasSendNum+i]);
}
FlashWrite(QspiInstancePtr, WriteAddr, PAGE_SIZE, WRITE_CMD);
HasSendNum+= PAGE_SIZE ;
WriteAddr+= PAGE_SIZE ;
}
}while(HasSendNum < TotoalLen) ;
HasSendNum = 0 ;
WriteAddr = 0 ;
printf("Performing Verify Operation...\r\n") ;
memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
XTime_SetTime(0) ;
XTime_GetTime(&TimerStart) ;
do{
PercentCurr = (int)(((float)HasRecvNum/(float)TotoalLen)*10) ;
if (PercentCurr != PercentLast)
print_percent(PercentCurr) ;
PercentLast = PercentCurr ;
if ((HasRecvNum+PAGE_SIZE) > TotoalLen)
{
FlashRead(QspiInstancePtr, ReadAddr, PAGE_SIZE, FAST_READ_CMD);
for (i = 0 ; i < TotoalLen-HasRecvNum; i++)
{
if (ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i] != (u8)(FlashDataToSend[HasRecvNum+i]))
{
printf("Verify data error, address is 0x%x\tSend Data is 0x%x\tRead Data is 0x%x\r\n", ReadAddr+i+1,FlashDataToSend[HasRecvNum+i], ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i]) ;
break ;
}
}
HasRecvNum+= PAGE_SIZE ;
printf("100%%\r\n") ;
XTime_GetTime(&TimerEnd) ;
elapsed_time = (float)(TimerEnd-TimerStart)/(COUNTS_PER_SECOND) ;
printf("INFO:Elapsed time = %.2f sec\r\n", elapsed_time) ;
printf("Verify Operation Successful.\r\n") ;
}
else
{
FlashRead(QspiInstancePtr, ReadAddr, PAGE_SIZE, FAST_READ_CMD);
for (i = 0 ; i < PAGE_SIZE ; i++)
{
if (ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i] != (u8)(FlashDataToSend[HasRecvNum+i]))
{
printf("Verify data error, address is 0x%x\tSend Data is 0x%x\tRead Data is 0x%x\r\n",
ReadAddr+i+1,FlashDataToSend[HasRecvNum+i], ReadBuffer[DATA_OFFSET + DUMMY_SIZE+i]) ;
break ;
}
}
HasRecvNum+= PAGE_SIZE ;
ReadAddr+= PAGE_SIZE ;
}
}while(HasRecvNum < TotoalLen) ;
HasRecvNum = 0 ;
ReadAddr = 0 ;
return XST_SUCCESS;
}
void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{
u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */
u8 FlashStatus[2];
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));
WriteBuffer[COMMAND_OFFSET] = Command;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
ByteCount + OVERHEAD_SIZE);
while (1)
{
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,
sizeof(ReadStatusCmd));
if ((FlashStatus[1] & 0x01) == 0)
{
break;
}
}
}
void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command)
{
WriteBuffer[COMMAND_OFFSET] = Command;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||
(Command == QUAD_READ_CMD))
{
ByteCount += DUMMY_SIZE;
}
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer,ByteCount + OVERHEAD_SIZE);
}
void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount)
{
u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */
u8 FlashStatus[2];
int Sector;
unsigned int EraseSecNum ;
int PercentCurr = -1 ;
int PercentLast = -1 ;
if (ByteCount == (NUM_SECTORS * SECTOR_SIZE))
{
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD;
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
BULK_ERASE_SIZE);
while (1)
{
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
if ((FlashStatus[1] & 0x01) == 0)
{
xil_printf("Bulk Erase Done!\r\n") ;
break;
}
}
return;
}
EraseSecNum = ((ByteCount / SECTOR_SIZE) + 1) ;
xil_printf("Erase Size is %u Bytes\r\n", EraseSecNum*SECTOR_SIZE) ;
for (Sector = 0; Sector < EraseSecNum ; Sector++)
{
PercentCurr = (int)(((float)Sector/(float)EraseSecNum)*10) ;
if (PercentCurr != PercentLast)
print_percent(PercentCurr) ;
PercentLast = PercentCurr ;
XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,sizeof(WriteEnableCmd));
WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD;
WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16);
WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8);
WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,SEC_ERASE_SIZE);
while (1)
{
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
if ((FlashStatus[1] & 0x01) == 0)
{
break;
}
}
Address += SECTOR_SIZE;
}
}
int FlashReadID(void)
{
int Status;
WriteBuffer[COMMAND_OFFSET] = READ_ID;
WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */
WriteBuffer[ADDRESS_2_OFFSET] = 0x08;
WriteBuffer[ADDRESS_3_OFFSET] = 0x09;
Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer,RD_ID_SIZE);
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2],ReadBuffer[3]);
return XST_SUCCESS;
}
void print_percent(int percent)
{
switch(percent)
{
case 0 : xil_printf("0%%..") ; break ;
case 1 : xil_printf("10%%..") ; break ;
case 2 : xil_printf("20%%..") ; break ;
case 3 : xil_printf("30%%..") ; break ;
case 4 : xil_printf("40%%..") ; break ;
case 5 : xil_printf("50%%..") ; break ;
case 6 : xil_printf("60%%..") ; break ;
case 7 : xil_printf("70%%..") ; break ;
case 8 : xil_printf("80%%..") ; break ;
case 9 : xil_printf("90%%..") ; break ;
case 10 : xil_printf("100%..") ; break ;
default : break ;
}
}
7,主函数中加入文件
(1)在主函数main.ct中添加头文件#include "src/qspi_update/qspi.h"
然后再主函数外面添加下列定义:
static struct udp_pcb *udp8080_pcb = NULL;//PS端软件端口号
unsigned int ReceivedCount = 0 ;
unsigned int StartUpdate = 0;//程序升级标志
struct ip_addr target_addr;//接收来自电脑的目标IP地址
#define MAX_FLASH_LEN 32*1024*1024
char FlashRxBuffer[MAX_FLASH_LEN] ;
XQspiPs QspiInstance;
#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any)//绑定IP地址
(2)在主函数添加udp判断函数:udp_recive(void *arg, struct udp_pcb *pcb, struct pbuf *p_rx,struct ip_addr *addr, u16_t port)
用来判断上位机或网口助手发过来得是否是升级程序BOOT.BIN文件,如果是,就将升级程序标志位置1,准备升级
void udp_recive(void *arg, struct udp_pcb *pcb, struct pbuf *p_rx,struct ip_addr *addr, u16_t port)
{
char *pData;
if (p_rx != NULL)
{
pData = (char *) p_rx->payload;
int udp_len = p_rx->len ;
if (udp_len == 6 && !(memcmp("update", p_rx->payload, 6)))
{
xil_printf("Received Size is %u Bytes\r\n", ReceivedCount) ;
xil_printf("Initialization done, programming the memory\r\n") ;
StartUpdate = 1 ;
}
else
{
memcpy(&FlashRxBuffer[ReceivedCount], pData, udp_len);
ReceivedCount += udp_len ;
}
pbuf_free(p_rx);
}
}
(3)主函数main.c中加入:
udp8080_pcb = udp_new();
udp_bind(udp8080_pcb, IP_ADDR_ANY, 8080);
udp_recv(udp8080_pcb, udp_recive, 0);
IP4_ADDR(&target_addr, 192,168,0,223);//上位机IP
while(1)
{
xemacif_input(echo_netif);
if (StartUpdate)
{
int Status = update_qspi(&QspiInstance, QSPI_DEVICE_ID, ReceivedCount, FlashRxBuffer) ;
if (Status != XST_SUCCESS)
xil_printf("Write Flash Error!\r\n") ;
else
{
StartUpdate = 0 ;
ReceivedCount = 0;
}
}
…………………………………………….
}
8,run as或debug 加载该程序程序(zynq ps ip设为192.168.0.16):
IP4_ADDR(&(echo_netif->ip_addr), 192, 168, 0, 16);
IP4_ADDR(&(echo_netif->netmask), 255, 255, 255, 0);
IP4_ADDR(&(echo_netif->gw), 192, 168, 0, 1);
此时从cmd中能ping通192.168.0.16就说明加载的程序已经运行起来了可以开始升级
9,SDK程序升级,使用网口助手发送:
(1)选择UDP协议类型,填入电脑本地的IP地址和端口号(自己定一个),填入ZYNQ PS端的IP地址192.168.0.16和端口号8080;点击启用文件数据源,从文件夹中选择要升级的文件BOOT.bin并打开,最后点击发送直到发送完成;
(2)去掉启用文件数据源,然后从网口助手输入“update”字符串,最后点击发送ZYNQ主控收到字符串就会拉高标志位,然后执行程序升级,然后等一段时间等待升级完成。
(3)重启板子电源,然后从cmd ping 192.168.0.10(新程序BOOT.bin的IP),能ping通,说明新程序裸机升级到ZYNQ成功;
附录:ZYNQ带系统实现程序升级:
使用TFTP从PC机将需要升级的文件传输到系统替换原来的新程序,在系统中使用脚本自动运行程序,就算升级完成。