本文将演示如何对板载的QSPI FLASH进行数据的读写操作
之前在 工程八 中我们介绍过如何将芯片的程序固化进QSPI FLASH 中,以让系统每次开机从FLASH 芯片加载程序启动。 除了存储程序外,QSPI FLASH 还可以用来存储需要断电保存的数据。本文将对中间的过程进行演示。
写在前面:注意FLASH 是有读写寿命的,所以程序上要避免在死循环中对flash进行一直的反复写入。
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
(备注 此章节内容适用于 Smart ZYNQ SP和SL 版的板子,如果是Smart ZYNQ 标准版请看对应板子目录
一、Vivado 部分设计
Vivado 部分工程的创建和之前已经介绍过很多次了,所以这里仅仅介绍重点:
创建一个BLOCK设计
1)IP INTEGRATOR→Create Block Design,在弹出的对话框中输入设计名,最后点击“OK”,如下图所示
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-1.png)
2)在右侧的窗口里 ,点击加号,在选择框里搜索ZYNQ,并找到ZYNQ7 PROCESSING SYSTEM ,双击并打开
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-2.png)
3)软件自动生成了一个 zynq的block 如下图所示,接下来要做一些相应的设置,双击下图中的ZYNQ核
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-3.png)
4)依次在弹窗里找到DDR Configuration→DDR Controller Configuration→DDR3,在Memory Part下拉菜单中根据自己板子上的DDR来选择相应的DDR3,本实验所用到型号:MT41K256M16RE-125,数据位宽选择16bit 最后点击“OK”,如下图所示。
![](http://www.hellofpga.com/wp-content/uploads/2023/01/image-70.png)
5)在PS的MIO配置选项增加UART部分,使能UART 0 并在IO选项里选择EMIO方式
![](http://www.hellofpga.com/wp-content/uploads/2022/10/image-118.png)
6)在block design 的设置界面 使能QSPI的功能 如下图所示(当QSPI 时钟大于40MHZ的时候 就需要勾选Feedback clk)
![](http://www.hellofpga.com/wp-content/uploads/2022/10/image-56.png)
7)因为工程暂时用不到 AXI功能,所以可以先禁用AXI功能
![](http://www.hellofpga.com/wp-content/uploads/2022/10/image-131.png)
8)随后点OK 保存修改, 点击“Run Block Automation”如下图所示。在弹出的选项中保持默认,点击“OK”,即可完成对ZYNQ7 Processing System的配置
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-39.png)
9)引出UART 信号,因为我们增加了EMIO 的UART,所以这里需要将UART的引脚引出 ,右键选择ZYNQ模块的UART_0,然后选择Make External
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-40.png)
10) 在source→Design Source ,右键我们创建的BLOCK工程,点击create HDL wrapper,打包BLOCK文件并生成.v代码
![](http://www.hellofpga.com/wp-content/uploads/2022/11/image-179.png)
在弹出的菜单里直接点选OK
![](http://www.hellofpga.com/wp-content/uploads/2023/02/image-22.png)
11) 点击绿色箭头RUN 对代码进行编译
![](http://www.hellofpga.com/wp-content/uploads/2022/11/image-180.png)
12) 增加UART管脚定义,点击RTL 中的SCHEMATIC , 并选择右边出现的 IO Ports 来增加UART 0 EMIO部分的管脚定义(这一步也可以在约束文件中定义, 可看之前的例子)
![](http://www.hellofpga.com/wp-content/uploads/2022/07/image-7-1024x482.png)
将 UART 部分的 TX RX 分别设置成 L17,M17 电压属性设置成 LVCMOS33, 之后保存
![](http://www.hellofpga.com/wp-content/uploads/2023/04/image-41.png)
13) 生成bit文件 :按下Generate Bitstream 完成综合以及生成bit文件,等待弹出综合完成的窗口
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-18.png)
二、SDK部分设计
1)File→Export→Export hardware…在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-41.png)
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-24.png)
2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-21.png)
系统将自动打开SDK开发环境
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-42-1024x810.png)
3)新建一个工程 file→new→Application Project,来新建一个“Application Project”,如下图所示。
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-23.png)
4)在新建工程名中输入自己的工程名称,点击NEXT
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-43.png)
5)选择空工程,点击完成FINISH
![](http://www.hellofpga.com/wp-content/uploads/2021/08/image-26.png)
6) QSPI 的调用需要很多底层的函数,对于新手甚至有多年经验的程序员来说都有很大的难度,好在Xilinux 已经帮我们做好可以参考的demo,我们只需要打开demo进行研究即可。
展开工程中的BSP目录,并且双击其中的mss文件, 在右侧 qspips 中点选import Examples
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-47.png)
选中qspi示例程序中flash相关的程序: xqspips_flash_polled_example
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-48.png)
7) 之后qspi_flash的官方例程被打开,我们打开其中的xqspips_flash_polled_example.c文件查看代码
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-49.png)
可以看到,例程本身已经包含了 FlashWrite, FlashRead, FlashErase, FlashReadID(擦除,读,写,读取FLASH ID)等各种功能的函数了
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-50.png)
程序的框架也非常简单,主程序进来后,就调用了QspiFlashPolledExample函数
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-51.png)
QspiFlashPolledExample 函数的工作流程如下
- 对QSPI 功能进行初始化 (XQspiPs_CfgInitialize)
- 对读写 Buffer分别进行初始化(对WriteBuffer数组赋予顺序的初值, 对ReadBuffer进行清零操作)
- 设置 QSPI的工作模式 (XQspiPs_SetOptions),及分频系数 (XQspiPs_SetClkPrescaler)
- 使能 FLASH 芯片 (XQspiPs_SetSlaveSelect)
- 读取 Flash ID (FlashReadID)
- 使能Flash Quad模式 (FlashQuadEnable) ,及擦除FLASH (FlashErase)
- 将 write buffer 中的内容逐一通过 (FlashWrite) 函数写入 FLASH
- 通过(FlashRead)函数(分别用READ_CMD和FAST_READ_CMD两种方式)读出FLASH 中的内容,并存入ReadBuffer中
- 将ReadBuffer和WriteBuffer进行对比,验证读写数据是否一致
如果对比的内容一致,将在主函数中通过串口输出“Successfully ran QSPI FLASH Polled Example Test\r\n” ,否则将输出“QSPI FLASH Polled Example Test Failed\r\n”
三、下载程序到板子上验证
1) 先提前打开串口助手(这里用vivado SDK 自带的)
![](http://www.hellofpga.com/wp-content/uploads/2023/09/image-40-1024x482.png)
2) 打开Run as -> Run Configurations
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-52-1024x716.png)
如果是第一次打开此页面,之前没有debug过,则双击system debugger选项
![](http://www.hellofpga.com/wp-content/uploads/2023/09/image-37.png)
3 ) 在右侧 的窗口勾选 Reset entire system ,以及Program FPGA, 这样每次debug 的时候都会预先加载并配置FPGA。
![](http://www.hellofpga.com/wp-content/uploads/2023/09/image-38-1024x720.png)
4 ) 之后系统就会自动运行了(fpga也会被自动加载) 。 如果修改程序后 再次要debug,就可以直接点绿色箭头,或者选 Run As –>Launch on Hardware (system debugger即可)
![](http://www.hellofpga.com/wp-content/uploads/2023/09/image-39-1024x655.png)
5) 程序工作后, 结果如下图所示,可以看到程序已经读出Flash的ID为 0xEF 0x40 0x18
并且返回了 Successfully ran QSPI FLASH Polled Example Test ,证明写入flash 和从flash 读出的数据对比是一致的,实验成功。
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-53.png)
6) 再来查看 READ ID的程序, 我们读取的READ_ID在FLASH上是地址0x9F
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-54.png)
根据FLASH的数据手册上描述来看 0X9F 对应的地址 应该是值 EFh 4018h 和我们读出的FlashID=0xEF 0x40 0x18 一致
![](http://www.hellofpga.com/wp-content/uploads/2023/11/image-55-1024x766.png)
以上仅仅是调用官方的demo来对读写qspi flash 功能进行讲解和测试, 其他功能大家请自行尝试。
如果项目中 flash 同时需要拿来当作固化程序的存储空间,又要拿来存储其他的数据,请注意不要让存储的区域有交集,否则可能会导致程序运行的过程中,固件被程意外修改,导致下次启动程序无法正常工作。
另外请注意,flash 是有读写寿命的,所以程序上要避免在死循环中对flash进行一直的反复写入。
以下是本次的实验的原始工程,请自行测试
补充 如果要在vitis 里找官方demo 和sdk 中的位置是不同的 详细方法见下图
![](http://www.hellofpga.com/wp-content/uploads/2023/12/image-60-1024x557.png)