Zynq 7000系列下讀寫(xiě)Quad SPI及FLASH唯一的ID
掃描二維碼
隨時(shí)隨地手機(jī)看文章
QSPI是Quad SPI的簡(jiǎn)寫(xiě),表示4線spi,是Motorola公司推出的SPI接口的擴(kuò)展,比SPI應(yīng)用更加廣泛,本文主要演示zynq 7000下對(duì)QSPI 的讀寫(xiě)操作,以及讀取8字節(jié)唯一ID, 可以用于簡(jiǎn)單加密。
本文分為5部分:
-
硬件設(shè)計(jì)
-
軟件設(shè)計(jì)
-
測(cè)試驗(yàn)證
-
總結(jié)
-
擴(kuò)展
本文的前提條件是讀者會(huì)helloworld 實(shí)驗(yàn),也就是會(huì)zynq 7000的SDK開(kāi)發(fā)過(guò)程,如果不會(huì),那先去看我的zynq 7000下的helloworld 實(shí)驗(yàn),或者開(kāi)發(fā)板提供的helloworld 實(shí)驗(yàn)教程。
我實(shí)驗(yàn)平臺(tái)是黑金的核心板AC7010, 其板上的QSPI芯片是W25Q256FV
1 硬件設(shè)計(jì)
硬件設(shè)計(jì)基本和helloworld 一樣,不同的是要選擇QSPI, 否則怎么做實(shí)驗(yàn)?zāi)?。還有工程名字取為qspi。
在zynq配置的peripheral I/O Pins里選擇Quad SPI Flash, 如圖:
在zynq配置的MIO configuration里選擇Quad SPI Flash的每個(gè)腳為 fast, 如圖:
除此以外,都和hello world 實(shí)驗(yàn)的硬件設(shè)計(jì)一樣。
2:軟件設(shè)計(jì)
在SDK里 新建工程,取名為qspi_t , 在Next 里可以選擇hello world 或者empty project。
下載我提供的xqspips_flash_polled_example.c 文件, 放入.src ,就是hellowrld.c 的地方,刪除helloworld.c 。
為什么取這樣一個(gè)名字呢?
這是Xilinx提供的樣例程序,你可以在你的xilinx 目錄下搜索到這個(gè)文件。我提供的程序是在其上修改得到的。
FlashWrite,FlashRead,FlashErase,FlashReadID都是樣例程序里包含的。
我做的工作只是修改了main函數(shù),還有仿照FlashReadID, 編寫(xiě)了一個(gè)FlashReadUID函數(shù)。
FlashWrite, 寫(xiě)Flash, 其參數(shù)是QSPI驅(qū)動(dòng)指針, FLash寫(xiě)的起始地址, 寫(xiě)入字節(jié)數(shù), 寫(xiě)命令:
1void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) 2{ 3 u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; 4 u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ 5 u8 FlashStatus[2]; 6 7 /* 8 * Send the write enable command to the FLASH so that it can be 9 * written to, this needs to be sent as a seperate transfer before 10 * the write 11 */ 12 XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, 13 sizeof(WriteEnableCmd)); 14 15 16 /* 17 * Setup the write command with the specified address and data for the 18 * FLASH 19 */ 20 WriteBuffer[COMMAND_OFFSET] = Command; 21 WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); 22 WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); 23 WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); 24 25 /* 26 * Send the write command, address, and data to the FLASH to be 27 * written, no receive buffer is specified since there is nothing to 28 * receive 29 */ 30 XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, 31 ByteCount + OVERHEAD_SIZE); 32 33 /* 34 * Wait for the write command to the FLASH to be completed, it takes 35 * some time for the data to be written 36 */ 37 while (1) { 38 /* 39 * Poll the status register of the FLASH to determine when it 40 * completes, by sending a read status command and receiving the 41 * status byte 42 */ 43 XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, 44 sizeof(ReadStatusCmd)); 45 46 /* 47 * If the status indicates the write is done, then stop waiting, 48 * if a value of 0xFF in the status byte is read from the 49 * device and this loop never exits, the device slave select is 50 * possibly incorrect such that the device status is not being 51 * read 52 */ 53 if ((FlashStatus[1] & 0x01) == 0) { 54 break; 55 } 56 } 57}
FlashRead, 讀Flash, 其參數(shù)是QSPI驅(qū)動(dòng)指針, FLash讀的起始地址, 讀取字節(jié)數(shù), 讀命令
1void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) 2{ 3 /* 4 * Setup the write command with the specified address and data for the 5 * FLASH 6 */ 7 WriteBuffer[COMMAND_OFFSET] = Command; 8 WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); 9 WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); 10 WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); 11 12 if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || 13 (Command == QUAD_READ_CMD)) { 14 ByteCount += DUMMY_SIZE; 15 } 16 /* 17 * Send the read command to the FLASH to read the specified number 18 * of bytes from the FLASH, send the read command and address and 19 * receive the specified number of bytes of data in the data buffer 20 */ 21 XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer, 22 ByteCount + OVERHEAD_SIZE); 23}
FlashErase, 擦出Flash, 其參數(shù)是QSPI驅(qū)動(dòng)指針, FLash擦除起始地址, 擦除字節(jié)數(shù)。
1void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) 2{ 3 u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; 4 u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ 5 u8 FlashStatus[2]; 6 int Sector; 7 8 /* 9 * If erase size is same as the total size of the flash, use bulk erase 10 * command 11 */ 12 if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { 13 /* 14 * Send the write enable command to the FLASH so that it can be 15 * written to, this needs to be sent as a seperate transfer 16 * before the erase 17 */ 18 XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, 19 sizeof(WriteEnableCmd)); 20 21 /* 22 * Setup the bulk erase command 23 */ 24 WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; 25 26 /* 27 * Send the bulk erase command; no receive buffer is specified 28 * since there is nothing to receive 29 */ 30 XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, 31 BULK_ERASE_SIZE); 32 33 /* 34 * Wait for the erase command to the FLASH to be completed 35 */ 36 while (1) { 37 /* 38 * Poll the status register of the device to determine 39 * when it completes, by sending a read status command 40 * and receiving the status byte 41 */ 42 XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, 43 FlashStatus, 44 sizeof(ReadStatusCmd)); 45 46 /* 47 * If the status indicates the write is done, then stop 48 * waiting; if a value of 0xFF in the status byte is 49 * read from the device and this loop never exits, the 50 * device slave select is possibly incorrect such that 51 * the device status is not being read 52 */ 53 if ((FlashStatus[1] & 0x01) == 0) { 54 break; 55 } 56 } 57 58 return; 59 } 60 61 /* 62 * If the erase size is less than the total size of the flash, use 63 * sector erase command 64 */ 65 for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) { 66 /* 67 * Send the write enable command to the SEEPOM so that it can be 68 * written to, this needs to be sent as a seperate transfer 69 * before the write 70 */ 71 XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, 72 sizeof(WriteEnableCmd)); 73 74 /* 75 * Setup the write command with the specified address and data 76 * for the FLASH 77 */ 78 WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; 79 WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16); 80 WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8); 81 WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); 82 83 /* 84 * Send the sector erase command and address; no receive buffer 85 * is specified since there is nothing to receive 86 */ 87 XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, 88 SEC_ERASE_SIZE); 89 90 /* 91 * Wait for the sector erse command to the FLASH to be completed 92 */ 93 while (1) { 94 /* 95 * Poll the status register of the device to determine 96 * when it completes, by sending a read status command 97 * and receiving the status byte 98 */ 99 XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, 100 FlashStatus, 101 sizeof(ReadStatusCmd)); 102 103 /* 104 * If the status indicates the write is done, then stop 105 * waiting, if a value of 0xFF in the status byte is 106 * read from the device and this loop never exits, the 107 * device slave select is possibly incorrect such that 108 * the device status is not being read 109 */ 110 if ((FlashStatus[1] & 0x01) == 0) { 111 break; 112 } 113 } 114 115 Address += SECTOR_SIZE; 116 } 117}
寫(xiě)命令其實(shí)只有一個(gè)WRITE_CMD, 但讀命令有4個(gè)選擇:
-
READ_CMD
-
FAST_READ_CMD
-
DUAL_READ_CMD
-
QUAD_READ_CMD
FlashReadID就是讀取JEDEC ID
1int FlashReadID(void) 2{ 3 int Status; 4 5 /* 6 * Read ID in Auto mode. 7 */ 8 WriteBuffer[COMMAND_OFFSET] = READ_ID; 9 WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ 10 WriteBuffer[ADDRESS_2_OFFSET] = 0x08; 11 WriteBuffer[ADDRESS_3_OFFSET] = 0x09; 12 13 Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer, 14 RD_ID_SIZE); 15 if (Status != XST_SUCCESS) { 16 return XST_FAILURE; 17 } 18 19 xil_printf("Flash JEDEC ID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2], 20 ReadBuffer[3]); 21 22 return XST_SUCCESS; 23}
FlashReadUID是我仿照上面函數(shù)寫(xiě)的, 我查看了W25Q256FV的手冊(cè),讀取唯一ID的cmd 是0x4B, 我不確定其他芯片是否一樣,你需要自己查看。
1int FlashReadUID(void) 2{ 3 int Status; 4 5 /* 6 * Read ID in Auto mode. 7 */ 8 WriteBuffer[COMMAND_OFFSET] = 0x4B; 9 WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ 10 WriteBuffer[ADDRESS_2_OFFSET] = 0x08; 11 WriteBuffer[ADDRESS_3_OFFSET] = 0x09; 12 13 Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer, 14 16); 15 if (Status != XST_SUCCESS) { 16 return XST_FAILURE; 17 } 18 19 xil_printf("Flash Unique ID=0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n\r",ReadBuffer[5], ReadBuffer[6], ReadBuffer[7], ReadBuffer[8], ReadBuffer[9], ReadBuffer[10], ReadBuffer[11], ReadBuffer[12]); 20 21 return XST_SUCCESS; 22}
主程序的流程是:
基本初始化,獲取Flash 的驅(qū)動(dòng)指針,給WriteBuffer數(shù)組賦值,就是打算 寫(xiě)什么東西進(jìn)去,調(diào)用2個(gè)id 讀取, 擦除,讀取,顯示內(nèi)容,寫(xiě)入, 讀取,顯示內(nèi)容,調(diào)用2個(gè)id 讀取并顯示。
3:程序運(yùn)行
以下是程序運(yùn)行在串口監(jiān)視終端上的顯示內(nèi)容:
4:總結(jié)
按資料介紹,唯一id 每個(gè)芯片都是不一樣的,可不可以把你 的id 告訴我,看看是不是都不一樣。
如果這樣的話,我們就可以用這個(gè)id 加密,就是用一個(gè)算法,比如des,des3,得到一個(gè)結(jié)果,而這個(gè)結(jié)果只有知道算法的人才知道。
把這個(gè)結(jié)果填入一個(gè)地方,那就是程序員認(rèn)可了。這樣如果簡(jiǎn)單復(fù)制程序,那就不行的,防止盜版。
5 : 擴(kuò)展
其實(shí)xilinx 也有一套加密的算法,xilinx 的加密方法相關(guān)文檔是:
https://www.xilinx.com/support/documentation/user_guides/ug1025-zynq-secure-boot-gsg.pdf
xapp1175_zynq_secure_boot.pdf





