学习内容
本文首先介绍Flash和QSPI Flash控制器的相关内容,然后使用 QSPI Flash 控制器,开发板上的 QSPI Flash 进行写、 读操作。通过对比读出的数据是否等于写入的数据, 从而验证读写操作是否正确。
开发环境
vivado 18.3&SDK,PYNQ-Z2开发板。
Flash简介
Flash存储器,又叫做闪存,是一种非易失性存储器。具有操作方便读写速度快等优点。一般用于存储操作系统和程序代码,或者用于数据存储。Flash的存储单元组织为块阵列,块是擦除操作的最小单位,擦除操作将块内的所有为置位为1,页是读写操作的基本单位。
在对页进行写操作前,要判断该页内所有位是否为1。如果全部为1可以写操作,否则要对整块进行擦除操作(Flash只能从1反转到0)。Flash在内部结构(接口)上主要分为Nor flash 和NAND flash。
Nor Flash :写入和擦除的速度低;结构复杂,成本高;存储容量较小;一般用于存储Bootloader以及操作系统或者程序代码,可以在芯片内部直接运行代码。NAND Flash :写入和擦除的速度较快;结构简单,成本低;存储容量较大;一般用于存储材料和数据。
Flash在外部接口上主要分为CFI flash 和SPI(STD/Dual/Quad) flash。CFI flash 读写速度快,需要的硬件引脚多且不同容量的硬件不兼容;而SPI(STD/Dual/Quad) flash读写速度慢,需要的硬件引脚少且不同容量的硬件兼容。
QSPI Flash简介
介绍
Quad-SPI闪存控制器是位于PS内的输入/输出外设(IOP)的一部分。它用于访问多位串行闪存设备,以实现高吞吐量和低引脚数应用。
控制器可以以三种模式运行:I / O模式,线性寻址模式和传统SPI模式。
在I / O模式下,软件与闪存设备协议紧密交互,所以在该模式下需要对协议进行详细了解。软件通过使用四个TXD寄存器将闪存命令和数据写入控制器。软件通过读取RXD寄存器。
线性寻址模式使用设备操作的子集来消除I / O模式读取闪存所需的软件开销。也就是说线性寻址模式比IO模式要快的多。线性模式采用硬件来向闪存发出命令,并控制从flash到AXI接口的数据。控制器响应AXI接口上的存储器请求,就好像闪存是ROM存储器。
在传统模式下,QSPI控制器充当普通的SPI控制器。
控制器可以与一或两个闪存设备接口。可以并行连接两个设备以实现8位性能,也可以以堆叠的4位布局连接以最大程度地减少引脚数。
系统框图
QSPI Flash 控制器的系统框图如下所示:
从上图中可知, QSPI Flash 控制器通过 MIO 与外部 Flash 器件连接,支持三种模式:单个从器件模式、双从器件并行模式和双从器件堆叠模式。双从器件并行模式把每个flash的IO进行单独连接,扩展成8位用于控制对不同flash的访问。而双从器件堆叠模式,使用SS片选信号进行区分flash的使能,所以想扩展 QSPI Flash 的存储容量,可以使用双从器件并行模式。
当使用单个设备时,直接存储器读取的地址映射从FC00_0000开始,最大为FCFF_FFFF(16 MB)。两台设备系统的地址映射取决于存储设备和I / O配置。
QSPI Flash 控制器内部框图
由上图可知,在线性地址模式下,使用的是AXI的接口进行数据交互的,首先由AXI总线读取到响应的指令,并存到Command FIFO,然后使用AXI to SPI的命令转化器传输到选择器。在IO模式下,使用的是APB接口直接把接收到的信号传输给选择器进行选择。
然后选择器进行功能选择后传输到TxFIFO,接着把Tx FIFO的数据进行串行化,然后通过MIO引脚发送。接收部分原理相反,而且可以看出在线性地址模式下,接收信号的转换使用的是数据转换器。所以在线性地址模式下,只支持读取数据,不支持发送数据。
流程控制
在数据传输期间,I / O模式具有不同的流控制模式。用户可以在自动和手动模式之间进行选择,该模式由config_reg.MANSTARTEN(Man_start_com)控制。在自动模式下,包括芯片选择控制在内的整个传输过程都在硬件中完成。
无需软件干预。在手动模式下,用户控制数据传输的开始。在这种情况下,软件要么将整个传输序列写入TxFIFO,要么直到TxFIFO已满。在手动模式下,用户除了控制传输开始之外,还可以选择控制芯片选择。从命令开始,软件再次将传输序列写入TxFIFO,直到TxFIFO已满。然后,软件拉高CS,然后手动启动进行硬件管理。
编程指南
在xilinx给出的UG585指导手册中,对QSPI Flash 控制器编程设计进行了指导开发,用户可以根据自己的设计需要参考指导手册中的编程顺序实现自己的应用功能。
启动顺序
配置时钟。
配置发送/接收信号。(以上两步在vivado底层搭建中已经完成设计)
配置控制器
该步骤适用于线性寻址和I / O模式。它配置了控制器的波特率,FIFO,flash模式,时钟相位/极性和对环回延迟进行编程。
配置控制器。写入qspi.Config_reg寄存器。a. 设置波特率[BAUD_RATE_DIV]。b. 选择主模式,[MODE_SEL] = 1。C. 选择flash模式(不是传统SPI),[LEG_FLSH] = 1。d. 选择Little Endian,[endian] = 0。e. 将FIFO宽度设置为32位[FIFO_WIDTH]。F. 设置时钟相位[CLK_PH]和极性[CLK_POL]。
启用回送时钟。如果使用回送时钟,确保将qspi.Config_reg [BAUD_RATE_DIV]设置为0b00,并使用以下设置配置qspi.LPBK_DLY_ADJ(回送延迟调整)寄存器:a. 设置为选择内部时钟。qspi.LPBK_DLY_ADJ [USE_LPBK] = 1。b. 将时钟延迟设置为0。qspi.LPBK_DLY_ADJ [DLY0] = 0b00。c. 设置时钟延迟1. qspi.LPBK_DLY_ADJ [DLY1] = 0b00。
线性寻址模式
线性寻址模式下数据读取(存储器读取)的操作顺序如下:
将手动启动启用设置为自动模式。设置qspi.Config_reg [Man_start_en] = 0。
使能片选信号。设置qspi.Config_reg [PCS] = 0。
将配置寄存器编程为线性寻址模式。
启用控制器。设置qspi.En_REG [SPI_EN] = 1。
从线性地址存储区读取数据。内存范围取决于大小和设备数量。范围是从0xFC00_0000到0xFDFF_FFFF。
禁用控制器。设置qspi.En_REG [SPI_EN] = 0。
取消使能片选信号。设置qspi.Config_reg [PCS] = 1。
配置I / O模式
使用I / O模式进行读取和写入编程步骤如下:
启用手动模式。qspi.Config_reg [Man_start_en,Manual_CS] = 1。
配置flash设备。对单个闪存设备使用qspi.LQSPI_CFG寄存器的重置值。如果是并行双闪存设备,则将1写入TWO_MEM,SEP_BUS位字段。
使能片选。设置qspi.Config_reg [PCS] = 0。
启用控制器。设置qspi.En_REG [SPI_EN] = 1。
将字节序列写入闪存。使用TXD从1到4字节写入TxFIFO寄存器。
避免TxFIFO溢出。当TxFIFO为空时,可以写入252个字节。此后,软件可以通过读取qspi.Intr_status_REG [TX_FIFO_full]并等待直到它等于0之后再写入TXD寄存器,来避免TxFIFO溢出。
启用中断。写入qspi.Intrpt_en_REG。
开始数据传输。设置qspi.Config_reg [Man_start_com] = 1。
中断处理程序:在编程/读取操作期间,将所有需要的数据传输到QSPI flash,并传输到Quad-SPI flash。
如果执行了读取操作:重新排列READ数据以消除由于空循环而读取的数据。
取消使能片选信号。设置QSPI.Config_reg [PCS] = 1。
禁用控制器。设置qspi.En_REG [SPI_EN] = 0。
I/O模式中断服务程序
配置ISR以根据Quad-SPI器件类型处理中断条件。要从Quad-SPI器件读取数据,最简单的ISR从RxFIFO读取数据并将内容写入TxFIFO。控制器生成系统外设中断(SPI),IRQ ID#51。对以下两种情况进行触发中断。a. 读取传输中断。RxFIFO不为空中断b. 写传输中断。TxFIFO未完全中断
I/O模式中断
中断仅在I / O模式下使用。只要满足任何中断条件,控制器中断就被置为有效。Quad-SPI中断处理程序检查中断原因。单个中断服务程序可以管理所有中断条件。
Rx和Tx的中断处理程序
中断处理程序由IRQ ID#51触发。示例读取RxFIFO直到其为空,然后填充TxFIFO。RxFIFO非空中断状态用于确定是否可以从RxFIFO读取内容。TxFIFO未满中断指示TxFIFO中是否有空间容纳更多内容。
禁用控制器中的所有中断。将qspi.Intrpt_dis_REG [TX_FIFO_not_full,RX_FIFO_full]都设置为1。
清除中断。将1s写入中断状态寄存器qspi.Intr_status_REG。
清空RxFIFO。检查是否声明了RxFIFO非空中断。如果qspi.Intr_status_REG [RX_FIFO_not_empty] = 1,则RxFIFO中有数据。a. 如果状态为有效,则从RxFIFO读取数据。使用qspi.RX_data_REG寄存器读取数据。b. 从RxFIFO读取数据并轮询中断状态,直到RxFIFO为空。当qspi.Intr_status_REG [RX_FIFO_not_empty] = 0时,RxFIFO为空。
填充TxFIFO。检查TxFIFO未满状态是否有效。如果 qspi.Intr_status_REG [TX_FIFO_not_Full] = 1,则存在要发送到闪存设备的数据(编程和/或读取操作):a. 将数据写入qspi.TXD0寄存器。b. 轮询qspi.Intr_status_REG [TX_FIFO_full] = 1,这表明TX FIFO已满。c. 遵循步骤a和b,直到将所有数据写入TxFIFO或直到qspi.Intr_status_REG[TX_FIFO_full] = 1。
启用中断。将qspi.Intrpt_en_REG [TX_FIFO_not_full,RX_FIFO_full]都设置为1。
开始数据传输。设置qspi.Config_reg [MANSTRTEN] = 1。
在本次工程设计中,使用QSPI Flash控制器对ZYNQ的QSPI Flash进行读写操作。通过对比读取数据和写入数据是否相等验证读写功能是否正常。
硬件平台搭建
新建工程,创建 Block Design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。完成对所用资源的勾选,在clock configuration这里可以查看对QSPI的时钟配置。这里不需要对其他资源进行配置,所以可以使用默认配置。然后我们进行generate output product 然后生成HDL封装。这里用到了仅仅用到了PS部分的资源,所以不需要进行管脚分配。点击导出硬件资源(不用包含bit流文件),接着launch SDK,进入软件部分编写。
SDK软件部分
打开SDK后,新建application project。在system.mss中可以打开相关参考文档辅助设计。可以选择qspi的例程进行参考设计,导入例程模板,根据提供的模板,主要使用的函数如下,这里对他的读写测试进行了简化,
int QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId)
{
int Status;
u8 *BufferPtr;
u8 UniqueValue;
int Count;
int Page;
XQspiPs_Config *QspiConfig;
//初始化QSPI
QspiConfig = XQspiPs_LookupConfig(QspiDeviceId);
XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,QspiConfig-》BaseAddress);
//自测
Status = XQspiPs_SelfTest(QspiInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//初始化读写BUFFERfor (UniqueValue = UNIQUE_VALUE, Count = 0; Count 《 PAGE_SIZE;
Count++, UniqueValue++) {
WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test);
}
memset(ReadBuffer, 0x00, sizeof(ReadBuffer));
//设置手动启动和手动片选模式
XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION |
XQSPIPS_FORCE_SSELECT_OPTION |
XQSPIPS_HOLD_B_DRIVE_OPTION);
//设置QSPI时钟的分频系数
XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);
//片选信号置为有效
XQspiPs_SetSlaveSelect(QspiInstancePtr);
//读FLASH ID
FlashReadID();
//使能FLASH Quad模式
FlashQuadEnable(QspiInstancePtr);
//擦除FLASH
FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA);
//向FLASH中写入数据for (Page = 0; Page 《 PAGE_COUNT; Page++) {
FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS,
PAGE_SIZE, WRITE_CMD);
}
//使用QUAD模式从FLASH中读出数据
FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, READ_CMD);
//对比写入FLASH与从FLASH中读出的数据
BufferPtr = &ReadBuffer[DATA_OFFSET];
for (UniqueValue = UNIQUE_VALUE, Count = 0; Count 《 MAX_DATA;
Count++, UniqueValue++) {
if (BufferPtr[Count] != (u8)(UniqueValue + Test)) {
return XST_FAILURE;
}
}
return XST_SUCCESS;
}
运行效果
References
ZYNQ正点原子开发视频
UG585
编辑:jq
全部0条评论
快来发表一下你的评论吧 !