加载方法
ZYNQ的启动镜像是由FSBL程序(bootloader),PL配置文件(硬件比特流文件),应用层软件三个部分组成,其通过SDK的软件生成工具把三个部分按规定的格式拼凑成一个.bin文件,最终将这个文件写入到QSPIFLASH中,整个ZYNQ在配置好启动方式为FLASH启动后,便可做到上电自启动运行下载到FLASH中的用户程序。
为了实现远程加载,抛开仿真器,通过UART的方式将固定的.bin文件发送给ZYNQ,然后将文件写入到FLASH。其分三个步骤完成整个程序的更新:第一,搭建UART的传输,通过规定的握手协议,PC机将.bin文件以串口的形式发送给ZYNQ;第二,ZYNQ将收到的文件存储在指定的DDR3内存当中;第三,ZYNQ将DDR3中的文件数据写入到FLASH,写完以后在读出来与接收的数据作对比,做一个校验。
串口交互形式
开启PC机加载程序,等待任务机发送加载信号;
ZYNQ上电后第一时间给UART发送加载信号0xee,随后监测UART上的回应信号;
PC机监测UART上的加载信号,若是0xee,则发送回应信号0xab,表示PC机做好了进入加载的准备,若不是0xee则不会发送回应信号;
ZYNQ收到回应信息0xab以后,进入加载流程,发送获取长度指令信号0xcc给PC机;
PC机监测UART上的获取长度指令信号,若是0xcc,则发送数据总包数n给PC机(长度=128*n);
ZQNY收到数据长度数据后,发送应答信号0xaa给PC机,表示接收到数据长度了;
PC机监测UART上的应答信号,接收到应答信号0xaa后,PC机进入发送数据流程,监测ZYNQ的准备好接收数据信号0xdd;
ZYNQ接收完数据长度以后,发送准备好接收数据信号0xdd给PC机;
PC机监测UART上的准备好接收数据信号0xdd,收到准备好信号0xdd信号以后,发送一包数据(128byte)给ZYNQ,发送完了以后又监测准备好信号0xdd,然后又发送一包数据(128byte)给ZYNQ,往复循环,直到数据包发送完,发送完数据后监测数据接收完信号0xbb;
ZYNQ接收完数据后,发送接收完信号0xbb给PC机;
PC机监测数据接收完信号0xbb;收到接收完信号0xbb后,数据交互流程完成,ZYNQ监测FLASH写入操作完成信号;
ZYNQ将接收到的数据写入到FLASH,写入完了以后发送FLASH写入操作完成信号0xaf给PC机;
PC机监测到接收完成信号0xaf后,提示用户写flash操作完成;进入到监测FLASH数据校验阶段;
ZYNQ写完flash后,将对应数据从flash中读出来做校验,校验成功则发送校验完成信号0xcf给PC机,若校验失败则发送失败信号0xef给PC机;
PC机监测到校验完成信号或者校验失败信号后提示用户校验完成,整个加载程序完成。
交互流程如图1所示。
UART与QSPI读写函数的设计
Uart初始化流程
通过uart设备ID找到对应的外设信息;
填充uart外设寄存器基地址和一些相关信息;
uart外设自检;
配置uart的fifo的触发等级;
使能uart外设;
源码如下
void Init_Uart(void)
{
XUartPs_Config *UartConifgPtr;
s32 temp;
//find device
UartConifgPtr = XUartPs_LookupConfig (XPAR_PS7_UART_1_DEVICE_ID);
//config xuartps data struct
XUartPs_CfgInitialize(&Uart_1,UartConifgPtr, UartConifgPtr->BaseAddress);
//self test
temp = XUartPs_SelfTest(&Uart_1);
if(temp != XST_SUCCESS)
{
return;
}
// XUartPs_SetDataFormat(&Uart_1,XUARTPS_FORMAT_EVEN_PARITY);
//set uart fifo level
XUartPs_SetFifoThreshold(&Uart_1,8);
//uart enable
XUartPs_EnableUart(&Uart_1);
}
Uart接收字节函数
判断RX的FIFO是否为空,如果处于空的状态,一直等待;如果不为空,读取RX的FIFO里面的数据,源码如下:
u8 Uart_RecvByte(void)
{
u8 byte = 0;
while((((XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET)) & 0x02) == 0x02));
byte = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET);
return byte;
}
Uart发送字节函数
判断TX的FIFO是否满,如果处于满的状态,一直等待;如果未满,则把需要发送的数据写入到TX的FIFO中,源码如下:
void Uart_SendByte(u8 byte)
{
while((((XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET)) & 0x10) == 0x10));
XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET,byte);
}
初始化QSPI流程
通过qspi设备ID找到对应的外设信息;
填充qspi外设寄存器基地址和一些相关信息;
Qspi外设自检;
配置qspi的工作模式;
配置qspi的工作频率;
配置qspi的为从设备选择;
源码如下:
void Init_Qspi(void)
{
XQspiPs_Config *QspiConfig;
//find device
QspiConfig = XQspiPs_LookupConfig(XPAR_XQSPIPS_0_DEVICE_ID);
//config XQspiPs data struct QspiInstance
XQspiPs_CfgInitialize(&QspiInstance, QspiConfig,QspiConfig->BaseAddress);
//self test
XQspiPs_SelfTest(&QspiInstance);
//set qspi option
XQspiPs_SetOptions(&QspiInstance,XQSPIPS_MANUAL_START_OPTION |XQSPIPS_FORCE_SSELECT_OPTION |XQSPIPS_HOLD_B_DRIVE_OPTION);
//set qspi clk
XQspiPs_SetClkPrescaler(&QspiInstance, XQSPIPS_CLK_PRESCALE_8);
//set slave select of qspi
XQspiPs_SetSlaveSelect(&QspiInstance);
}
Flash擦除函数流程
根据需要写入数据的大小判断需要整片擦除还是扇区擦除;
通过qspi接口将写使能命令写入到flash;
通过qspi接口将写命令和需要擦除的flash地址以及数据发送到flash;
等待数据的传输完成;
函数源码:
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;
/** If erase size is same as the total size of the flash, use bulk erase command */
if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) {
/*
* Send the write enable command to the FLASH so that it can be
* written to, this needs to be sent as a seperate transfer
* before the erase
*/
XQspiPs_PolledTransfer (QspiPtr, &WriteEnableCmd, NULL,
sizeof(WriteEnableCmd));
/*
* Setup the bulk erase command
*/
WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD;
/*
* Send the bulk erase command; no receive buffer is specified
* since there is nothing to receive
*/
XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
BULK_ERASE_SIZE);
/*
* Wait for the erase command to the FLASH to be completed
*/
while (1) {
/*
* Poll the status register of the device to determine
* when it completes, by sending a read status command
* and receiving the status byte
*/
XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
FlashStatus,
sizeof(ReadStatusCmd));
/*
* If the status indicates the