我是靠谱客的博主 悦耳黑夜,这篇文章主要介绍【正点原子FPGA连载】第十三章QSPI Flash读写测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南第十三章QSPI Flash读写测试实验,现在分享给大家,希望可以做个参考。

1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第十三章QSPI Flash读写测试实验

PS的输入/输出外设(IOP)有两个具有不同功能特性和IO接口性能的QSPI控制器。它们共享相同的APB从接口和MIO引脚。一次只能使用控制器中的一个。QSPI控制器可以访问多比特位宽的Flash设备,以实现较少的引脚数达到高吞吐量的应用。
本章我们将通过QSPI Flash控制器,来完成对QSPI Flash的读写操作。本章包括以下几个部分:
1313.1简介
13.2实验任务
13.3硬件设计
13.4软件设计
13.5下载验证

13.1简介

MPSOC中的QSPI Flash控制器分为传统QSPI控制器(LQSPI)和通用QSPI控制器(GQSPI)两个。传统QSPI控制器通过AXI从接口提供了线性可寻址的内存空间。支持引导配置(BOOT)和应用软件配置的就地执行(execute-in-place)。通用QSPI控制器提供I/O,DMA和SPI三种接口模式,不支持引导(BOOT)和就地执行(execute-in-place)。I/O接口配置如下图所示:
在这里插入图片描述

图13.1.1 QSPI I/O接口配置
传统QSPI控制器(LQSPI)只在线性寻址模式下工作。在这种模式下,可以连接一个或两个Flash器件,为了最小化引脚数量,两块Flash可以通过8bit并行模式或者4bit堆叠排列模式连接到传统QSPI控制器。线性地址模式下,控制器通过使用部分器件操作来消除读Flash时的软件开销。QSPI控制器给Flash发送命令,控制Flash总线到AXI接口的数据流。控制器响应AXI接口上的Flash存储器请求,把Flash存储器当作ROM存储器。
通用QSPI控制器(GQSPI)满足软件对通用低级访问的要求。由于QSPI控制器的通用性,软件可以在任何模式下生成任何命令序列。同时,QSPI控制器支持SPI、Dual SPI和Quad SPI模式下的功能。QSPI控制器运行在I/O、DMA和SPI三种模式下。通用QSPI控制器也支持连接一块或两块Flash设备,为了最小化引脚数量,两块Flash可以通过8bit并行模式或者4bit堆叠排列模式连接到通用QSPI控制器。
在通用I/O模式下,软件和存储设备密切交互。软件将Flash命令写到通用FIFO中,并将数据写到TXFIFO。软件读取RXD寄存器,获取从Flash设备接收到的数据。在I/O模式下,通用QSPI控制器消除了写TXFIFO时产生的软件开销。
在通用DMA模式下,内部DMA模块将Flash设备中的数据传输到系统内存中。这种模式避免了用处理器从Flash中读数据,并且消除了TXFIFO中写满来自Flash数据时产生的软件开销。
在SPI模式下,通用QSPI控制器可以作为标准SPI控制器使用。
双QSPI结构框图如下图所示,控制器由一个传统线性QSPI控制器和一个通用QSPI控制器组成。当控制寄存器设置为1时,选择通用QSPI控制器。传统QSPI控制器和通用QSPI控制器共享带延迟线的接收捕获逻辑。
在这里插入图片描述

图13.1.2 双QSPI控制器

13.2实验任务

本章的实验任务是使用QSPI Flash控制器,先后对开发板上的QSPI Flash进行写、读操作。通过对比读出的数据是否等于写入的数据,从而验证读写操作是否正确。
13.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
在这里插入图片描述

图 13.3.1 系统框图
从图 13.3.1中可以看出,本次实验是在“Hello Wold”实验的基础上增加了一个QSPI Flash控制器。我们将通过该控制器对QSPI Flash进行读写操作,并通过串口打印读写数据对比之后的结果。
首先创建Vivado工程,工程名为“qspi_flash_test”,然后创建Block Design设计(system.bd)并添加Zynq Ultrascale+ MPSOC模块。接下来按照《“Hello World”实验》中的步骤对Zynq Ultrascale+ MPSOC模块进行配置,配置完成后我们要添加本次实验所使用的QSPI Flash控制器模块。如下图所示:
在这里插入图片描述

图 13.3.2 QSPI配置界面
如图 13.3.2所示,在左侧导航栏中选择“I/O Configuraton”,然后在右侧勾选“QSPI”,并选择“Single”模式,QSPI Data Mode选择“x4”,QSPI使用默认的“MIO0…5”。“Single”指的是单个Flash器件。看以看出,该模式下控制器使用了MIO0至MIO5共6个引脚。
最后点击右下角的“OK”,本次实验Zynq Ultrascale+ MPSOC处理系统就配置完成了。配置完成后的模块如下图所示:
在这里插入图片描述

图 13.3.3 Zynq Ultrascale+ MPSOC模块
到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
然后在菜单栏中选择 File > Export > Export hardware,将硬件文件导出到新建的vitis文件夹下,最后在菜单栏选择Tools > Launch Vitis,将路径设置到本工程的vitis文件夹下,启动Vitis软件。

13.4软件设计

在Vitis软件中新建名为“qspi_flash_test”的应用工程。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码的主体部分如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
1 #include "xparameters.h" /* Vitis generated parameters */ 2 #include "xqspipsu.h" /* QSPIPSU device driver */ 3 #include "xil_printf.h" 4 #include "xil_cache.h" 5 6 //定义flash读写命令 7 #define WRITE_STATUS_CMD 0x01 8 #define WRITE_CMD 0x02 9 #define READ_CMD 0x03 10 #define WRITE_DISABLE_CMD 0x04 11 #define READ_STATUS_CMD 0x05 12 #define WRITE_ENABLE_CMD 0x06 13 #define VOLATILE_WRITE_ENABLE_CMD 0x50 14 #define QUAD_MODE_ENABLE_BIT 0x06 15 #define FAST_READ_CMD 0x0B 16 #define DUAL_READ_CMD 0x3B 17 #define QUAD_READ_CMD 0x6B 18 #define BULK_ERASE_CMD 0xC7 19 #define SEC_ERASE_CMD 0xD8 20 #define READ_ID 0x9F 21 #define READ_CONFIG_CMD 0x35 22 #define WRITE_CONFIG_CMD 0x01 23 24 #define READ_CMD_4B 0x13 25 #define FAST_READ_CMD_4B 0x0C 26 #define DUAL_READ_CMD_4B 0x3C 27 #define QUAD_READ_CMD_4B 0x6C 28 29 #define READ_FLAG_STATUS_CMD 0x70 30 31 #define COMMAND_OFFSET 0 //Flash instruction 32 #define ADDRESS_1_OFFSET 1 //数据偏移地址的最高位 33 #define ADDRESS_2_OFFSET 2 //数据偏移地址的中间位 34 #define ADDRESS_3_OFFSET 3 //数据偏移地址的最低位 35 #define ADDRESS_4_OFFSET 4 //数据偏移地址为四字节时 最低位 36 37 #define DATA_OFFSET 5 //Start of Data for Read/Write 38 #define DUMMY_OFFSET 4 //Dummy byte offset for fast, dual and quad reads 39 40 #define DUMMY_SIZE 1 //Number of dummy bytes for fast, dual and quad reads 41 42 #define DUMMY_CLOCKS 8 //Number of dummy bytes for fast, dual and quad reads 43 44 #define RD_ID_SIZE 4 //Read ID command + 3 bytes ID response 45 #define BULK_ERASE_SIZE 1 //Bulk Erase command size 46 #define SEC_ERASE_SIZE 4 //Sector Erase command + Sector address 47 #define BANK_SEL_SIZE 2 //BRWR or EARWR command + 1 byte bank value 48 49 #define RD_CFG_SIZE 2 //1 byte Configuration register + RD CFG command 50 51 #define WR_CFG_SIZE 3 //WRR command + 1 byte each Status and Config Reg 52 53 #define DIE_ERASE_SIZE 4 //Die Erase command + Die address 54 55 #define OVERHEAD_SIZE 4 56 57 //flash基地址 58 #define FLASH1BASE 0x0000000 59 60 //16MB 61 #define SIXTEENMB 0x1000000 62 63 //quad enable mask bit 64 #define FLASH_QUAD_EN_MASK 0x02 65 66 #define FLASH_SRWD_MASK 0x80 67 68 // Bank mask 69 #define BANKMASK 0xF000000 70 71 72 // Identification of Flash 73 // Micron: 74 // Byte 0 is Manufacturer ID; 75 // Byte 1 is first byte of Device ID - 0xBB or 0xBA 76 // Byte 2 is second byte of Device ID describes flash size: 77 // 128Mbit : 0x18; 256Mbit : 0x19; 512Mbit : 0x20 78 #define MICRON_ID_BYTE0 0x20 79 #define MICRON_ID_BYTE2_128 0x18 80 #define MICRON_ID_BYTE2_256 0x19 81 #define MICRON_ID_BYTE2_512 0x20 82 #define MICRON_ID_BYTE2_1G 0x21 83 #define MICRON_ID_BYTE2_2G 0x22 84 85 // Spansion: 86 // Byte 0 is Manufacturer ID; 87 // Byte 1 is Device ID - Memory Interface type - 0x20 or 0x02 88 // Byte 2 is second byte of Device ID describes flash size: 89 // 128Mbit : 0x18; 256Mbit : 0x19; 512Mbit : 0x20 90 #define SPANSION_ID_BYTE0 0x01 91 #define SPANSION_ID_BYTE2_64 0x17 92 #define SPANSION_ID_BYTE2_128 0x18 93 #define SPANSION_ID_BYTE2_256 0x19 94 #define SPANSION_ID_BYTE2_512 0x20 95 96 #define WINBOND_ID_BYTE0 0xEF 97 #define WINBOND_ID_BYTE2_128 0x18 98 99 #define ISSI_ID_BYTE0 0x9D 100 #define ISSI_ID_BYTE2_08 0x14 101 #define ISSI_ID_BYTE2_16 0x15 102 #define ISSI_ID_BYTE2_32 0x16 103 #define ISSI_ID_BYTE2_64 0x17 104 #define ISSI_ID_BYTE2_128 0x18 105 #define ISSI_ID_BYTE2_256 0x19 106 #define ISSI_ID_BYTE2_512 0x1a 107 108 #define QSPIPSU_DEVICE_ID XPAR_XQSPIPSU_0_DEVICE_ID 109 110 //flash页的数量 111 #define PAGE_COUNT 32 112 113 //页大小的最大值 114 #define MAX_PAGE_SIZE 1024 115 116 #define TEST_ADDRESS 0x000000 117 118 #define UNIQUE_VALUE 0x06 119 120 /**************************** Type Definitions *******************************/ 121 typedef struct{ 122 u32 SectSize; //扇区大小 123 u32 NumSect; //扇区总个数 124 u32 PageSize; //页大小 125 u32 NumPage; //总页数 126 u32 FlashDeviceSize; //一个存储器件的大小 127 u8 ManufacturerID; //制造商ID 128 u8 DeviceIDMemSize; //指出存储容量的器件ID 129 u32 SectMask; //扇区开始地址掩码 130 u8 NumDie; // No. of die forming a single flash 131 } FlashInfo; 132 133 u8 ReadCmd; 134 u8 WriteCmd; 135 u8 StatusCmd; 136 u8 SectorEraseCmd; 137 u8 FSRFlag; 138 139 /************************** Function Prototypes ******************************/ 140 141 int QspiPsuPolledFlashExample(XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuDeviceId); 142 int FlashReadID(XQspiPsu *QspiPsuPtr); 143 int FlashErase(XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr); 144 int FlashWrite(XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 Command, 145 u8 *WriteBfrPtr); 146 int FlashRead(XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 Command, 147 u8 *WriteBfrPtr, u8 *ReadBfrPtr); 148 u32 GetRealAddr(XQspiPsu *QspiPsuPtr, u32 Address); 149 int BulkErase(XQspiPsu *QspiPsuPtr, u8 *WriteBfrPtr); 150 int FlashEnableQuadMode(XQspiPsu *QspiPsuPtr); 151 /************************** Variable Definitions *****************************/ 152 u8 TxBfrPtr; 153 u8 ReadBfrPtr[3]; 154 FlashInfo Flash_Config_Table[] = { 155 {SECTOR_SIZE_64K, NUM_OF_SECTORS128, BYTES256_PER_PAGE, 156 0x8000, 0x800000, SPANSION_ID_BYTE0, 157 SPANSION_ID_BYTE2_64, 0xFFFF0000, 1} 158 }; 159 160 u32 FlashMake; 161 u32 FCTIndex; //闪存配置表的索引 162 163 //QSPI实例 164 static XQspiPsu QspiPsuInstance; 165 166 static XQspiPsu_Msg FlashMsg[5]; 167 168 //测试变量 用于产生发送数据 169 int Test = 1; 170 171 //用于存储读写数据的变量 172 u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8] __attribute__ ((aligned(64))); 173 u8 WriteBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + DATA_OFFSET]; 174 u8 CmdBfr[8]; 175 176 u32 MaxData = PAGE_COUNT*256; 177 178 //主函数 179 int main(void) 180 { 181 int Status; 182 183 xil_printf("QSPIPSU Generic Flash Polled Example Test rn"); 184 185 //调用QspiPsu Polled example 186 Status = QspiPsuPolledFlashExample(&QspiPsuInstance, QSPIPSU_DEVICE_ID); 187 if (Status != XST_SUCCESS) { 188 xil_printf("QSPIPSU Generic Flash Polled Example Failedrn"); 189 return XST_FAILURE; 190 } 191 192 xil_printf("Successfully ran QSPIPSU Generic Flash Polled Examplern"); 193 return XST_SUCCESS; 194 }

首先,本次实验的C程序是在官方提供的示例程序“xqspipsu_generic_flash_polled_example.c”的基础上修改得到的,该示例程序演示了如何使用轮询模式对QSPI Flash进行读写操作。
在程序的开头,我们定义了一系列的参数,包括Flash器件的指令、Flash BUFFER中各数据段的偏移量、Flash器件PAGE、SECTOR的数目和大小等信息。这些信息针对不同型号的Flash器件有所不同,需要通过查看器件的数据手册得到。
接下来在程序第141至150行声明了八个函数,这些函数是前面我们提到的示例程序中所提供的。我们对其中的函数QspiPsuPolledFlashExample(XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuDeviceId)、FlashReadID(XQspiPsu *QspiPsuPtr)进行修改,从而简化读写测试过程。而其他的函数如擦除FlashErase( )、写操作FlashWrite( )、读操作FlashRead( )等,改动不大。
程序的主函数特别简单,就是通过调用修改之后的示例函数QspiPsuPolledFlashExample ( )来对Flash进行读写测试,并打印最终的测试结果。下面是该示例函数的代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
198 int QspiPsuPolledFlashExample(XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuDeviceId) 199 { 200 u8 UniqueValue; 201 int Count; 202 int Page; 203 XQspiPsu_Config *QspiPsuConfig; 204 int ReadBfrSize; 205 206 ReadBfrSize = (PAGE_COUNT * MAX_PAGE_SIZE) + 207 (DATA_OFFSET + DUMMY_SIZE)*8; 208 209 //根据ID查找Qspi配置信息 210 QspiPsuConfig = XQspiPsu_LookupConfig(QspiPsuDeviceId); 211 if (QspiPsuConfig == NULL) { 212 return XST_FAILURE; 213 } 214 215 //初始化Qspi 216 XQspiPsu_CfgInitialize(QspiPsuInstancePtr, QspiPsuConfig, 217 QspiPsuConfig->BaseAddress); 218 219 //设置Options 220 XQspiPsu_SetOptions(QspiPsuInstancePtr, XQSPIPSU_MANUAL_START_OPTION); 221 222 //为qspi时钟设置分频系数 223 XQspiPsu_SetClkPrescaler(QspiPsuInstancePtr, XQSPIPSU_CLK_PRESCALE_8); 224 225 XQspiPsu_SelectFlash(QspiPsuInstancePtr, 226 XQSPIPSU_SELECT_FLASH_CS_LOWER, 227 XQSPIPSU_SELECT_FLASH_BUS_LOWER); 228 229 //读Flash ID 230 FlashReadID(QspiPsuInstancePtr); 231 232 233 //Initialize MaxData according to page size. 234 235 MaxData = PAGE_COUNT * (Flash_Config_Table[FCTIndex].PageSize); 236 237 //使能flash quad模式 238 FlashEnableQuadMode(QspiPsuInstancePtr); 239 242 //Address size and read command selection 243 ReadCmd = QUAD_READ_CMD; 244 WriteCmd = WRITE_CMD; 245 SectorEraseCmd = SEC_ERASE_CMD; 246 247 /* Status cmd - SR or FSR selection */ 248 if ((Flash_Config_Table[FCTIndex].NumDie > 1) && 249 (FlashMake == MICRON_ID_BYTE0)) { 250 StatusCmd = READ_FLAG_STATUS_CMD; 251 FSRFlag = 1; 252 } else { 253 StatusCmd = READ_STATUS_CMD; 254 FSRFlag = 0; 255 } 256 257 for (UniqueValue = UNIQUE_VALUE, Count = 0; 258 Count < Flash_Config_Table[FCTIndex].PageSize; 259 Count++, UniqueValue++) { 260 WriteBuffer[Count] = (u8)(UniqueValue + Test); 261 } 262 263 for (Count = 0; Count < ReadBfrSize; Count++) { 264 ReadBuffer[Count] = 0; 265 } 266 267 //擦除flash 268 FlashErase(QspiPsuInstancePtr, TEST_ADDRESS, MaxData, CmdBfr); 269 270 for (Page = 0; Page < PAGE_COUNT; Page++) { 271 FlashWrite(QspiPsuInstancePtr, 272 (Page * Flash_Config_Table[FCTIndex].PageSize) + TEST_ADDRESS, 273 Flash_Config_Table[FCTIndex].PageSize, 274 WriteCmd, WriteBuffer); 275 } 276 277 //从flash中读出数据 278 FlashRead(QspiPsuInstancePtr, TEST_ADDRESS, MaxData, ReadCmd, 279 CmdBfr, ReadBuffer); 280 281 //读出的数据和写入的数据对比 282 for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MaxData; 283 Count++, UniqueValue++) { 284 if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { 285 return XST_FAILURE; 286 } 287 } 288 289 return XST_SUCCESS; 290 }

在示例函数中,首先查找QSPI Flash控制器配置信息,接着对控制器驱动进行初始化,如代码的第210行至217行所示。向WriteBuffer中写入数据,然后将WriteBuffer中的数据写到Flash中,如代码第270行至第275行所示。注意在写Flash之前,调用FlashErase( )函数对Flash进行擦除,这是因为Flash写操作只能将1写成0,不能将0写成1,而擦除操作才能将0写成1。
最后,在程序的第282至287行,通过对比写入BUFFER的数据与读BUFFER中的数据是否一致,从而判断Flash读写测试实验是否成功。
程序的剩余部分是前面所声明的一系列操作Flash的函数的实现,因为我们将其当作库函数直接调用,因此代码就不再贴出来了。大家有兴趣的话也可以研究一下,这些函数是如何将读写指令和数据转换成QSPI Flash所要求的命令格式的。实际上,这些函数的功能也都是通过调用xqspipsu.h头文件中的库函数XQspiPsu_PolledTransfer( )来实现的。
13.5下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将开发板的USB_UART接口(PS PORT)与电脑连接,用于串口通信。最后连接开发板的电源,给开发板供电。
打开Vitis Terminal终端,设置并连接串口。然后下载本次实验的程序,下载完成后,在下方的Terminal中可以看到应用程序打印的信息“Successfully ran QSPIPSU Generic Flash Polled Test”,如下图所示:
在这里插入图片描述

图 13.5.1 串口打印结果
从图 13.5.1中可以看出,本次实验所实现的QSPI Flash读写测试功能,在MPSOC开发板上面下载验证成功。

最后

以上就是悦耳黑夜最近收集整理的关于【正点原子FPGA连载】第十三章QSPI Flash读写测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南第十三章QSPI Flash读写测试实验的全部内容,更多相关【正点原子FPGA连载】第十三章QSPI内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部