Zynq 7000系列下读写Quad SPI及FLASH唯一的ID
扫描二维码
随时随地手机看文章
QSPI是Quad SPI的简写,表示4线spi,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛,本文主要演示zynq 7000下对QSPI 的读写操作,以及读取8字节唯一ID, 可以用于简单加密。
本文分为5部分:
-
硬件设计
-
软件设计
-
测试验证
-
总结
-
扩展
本文的前提条件是读者会helloworld 实验,也就是会zynq 7000的SDK开发过程,如果不会,那先去看我的zynq 7000下的helloworld 实验,或者开发板提供的helloworld 实验教程。
我实验平台是黑金的核心板AC7010, 其板上的QSPI芯片是W25Q256FV
1 硬件设计
硬件设计基本和helloworld 一样,不同的是要选择QSPI, 否则怎么做实验呢。还有工程名字取为qspi。
在zynq配置的peripheral I/O Pins里选择Quad SPI Flash, 如图:
在zynq配置的MIO configuration里选择Quad SPI Flash的每个脚为 fast, 如图:
除此以外,都和hello world 实验的硬件设计一样。
2:软件设计
在SDK里 新建工程,取名为qspi_t , 在Next 里可以选择hello world 或者empty project。
下载我提供的xqspips_flash_polled_example.c 文件, 放入.src ,就是hellowrld.c 的地方,删除helloworld.c 。
为什么取这样一个名字呢?
这是Xilinx提供的样例程序,你可以在你的xilinx 目录下搜索到这个文件。我提供的程序是在其上修改得到的。
FlashWrite,FlashRead,FlashErase,FlashReadID都是样例程序里包含的。
我做的工作只是修改了main函数,还有仿照FlashReadID, 编写了一个FlashReadUID函数。
FlashWrite, 写Flash, 其参数是QSPI驱动指针, FLash写的起始地址, 写入字节数, 写命令:
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, 其参数是QSPI驱动指针, FLash读的起始地址, 读取字节数, 读命令
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, 其参数是QSPI驱动指针, FLash擦除起始地址, 擦除字节数。
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}
写命令其实只有一个WRITE_CMD, 但读命令有4个选择:
-
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是我仿照上面函数写的, 我查看了W25Q256FV的手册,读取唯一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 的驱动指针,给WriteBuffer数组赋值,就是打算 写什么东西进去,调用2个id 读取, 擦除,读取,显示内容,写入, 读取,显示内容,调用2个id 读取并显示。
3:程序运行
以下是程序运行在串口监视终端上的显示内容:
4:总结
按资料介绍,唯一id 每个芯片都是不一样的,可不可以把你 的id 告诉我,看看是不是都不一样。
如果这样的话,我们就可以用这个id 加密,就是用一个算法,比如des,des3,得到一个结果,而这个结果只有知道算法的人才知道。
把这个结果填入一个地方,那就是程序员认可了。这样如果简单复制程序,那就不行的,防止盗版。
5 : 扩展
其实xilinx 也有一套加密的算法,xilinx 的加密方法相关文档是:
https://www.xilinx.com/support/documentation/user_guides/ug1025-zynq-secure-boot-gsg.pdf
xapp1175_zynq_secure_boot.pdf





