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