赛灵思ZYNQ官方文档UG585自学翻译笔记与代码示例:Quad-SPl Flash 闪存控制器
XILINX 四通道 SPI 闪存控制器
12.1 概述
四通道 SPI(Quad-SPI)闪存控制器是位于处理系统(PS)内的输入 / 输出外设(IOP)的一部分,用于访问多比特串行闪存设备,适用于高吞吐量、低引脚数的应用场景。
控制器可工作在三种模式之一:I/O 模式、线性寻址模式和传统 SPI 模式。在 I/O 模式下,软件与闪存设备协议紧密交互,通过四个 TXD 寄存器向控制器写入闪存命令和数据,并从 RXD 寄存器读取从闪存设备接收的数据。
线性寻址模式使用设备操作的子集,消除了 I/O 模式下读取闪存所需的软件开销。该模式通过硬件发出闪存命令,并控制数据从闪存总线到 AXI 接口的流动,控制器响应 AXI 接口上的内存请求,如同闪存是 ROM 存储器。在传统模式下,QSPI 控制器作为普通 SPI 控制器工作。
控制器可连接 1 个或 2 个闪存设备。两个设备可并行连接以实现 8 位性能,或采用堆叠式 4 位结构以减少引脚数量。两种设备组合方式如图 12-1 所示。
12.1.1 特性
- 用于线性寻址模式传输的 32 位 AXI 接口
- 用于 I/O 模式传输的 32 位 APB 接口
- 支持 Micron 和 Spansion 闪存的可编程总线协议
- 传统 SPI 及可扩展性能:1x、2x、4x、8x I/O 宽度
- 灵活的 I/O 配置
- 单片选 4 位 I/O 闪存接口模式
- 双片选 8 位并行 I/O 闪存接口模式
- 双片选 4 位堆叠 I/O 闪存接口模式
- 单片选传统 SPI 接口
- 每个设备支持 16MB 寻址(两个设备共 32MB)
- I/O 模式和线性模式支持最大 128Mb 的设备密度,I/O 模式还支持大于 128Mb 的密度
- I/O 模式(闪存命令和数据)
- 软件发出指令并管理闪存操作
- 用于 FIFO 控制的中断
- 63 字的接收 FIFO(RxFIFO)和 63 字的发送 FIFO(TxFIFO)
- 线性寻址模式(可执行读访问)
- 控制器解析内存读写操作
- AXI 端口可缓冲最多 4 个读请求
- AXI 递增和 wrap 地址功能
12.1.2 系统视角
四通道 SPI 闪存控制器属于 IOP 的一部分,通过 MIO 连接到外部 SPI 闪存,如图 12-1 所示。控制器支持 1 个或 2 个存储器。
(图 12-1:四通道 SPI 控制器系统视角,此处省略图表)
线性地址模式的地址映射和设备匹配
使用单个设备时,直接内存读取的地址映射从 FC00_0000 开始,最大到 FCFF_FFFF(16MB)。双设备系统的地址映射取决于存储设备和 I/O 配置。在双设备系统中,四通道 SPI 设备需来自同一供应商,以确保协议一致。
8 位并行 I/O 配置还要求设备容量相同,其地址映射从 FC00_0000 开始,到合并内存容量的地址为止,最大为 FDFF_FFFF(32MB)。
对于 4 位堆叠 I/O 配置,设备可具有不同容量,但必须采用相同协议。若使用两个不同大小的设备,Xilinx 建议在低地址使用 128Mb 设备。在此模式下,QSPI 0 设备从 FC00_0000 开始,最大到 FCFF_FFFF(16MB);QSPI 1 设备从 FD00_0000 开始,最大到 FDFF_FFFF(另一个 16MB)。若第一个设备容量小于 16MB,两个设备之间会存在内存空间空洞。
12.1.3 框图
控制器的框图如图 12-2 所示。
(图 12-2:四通道 SPI 控制器框图,此处省略图表)
12.1.4 注意事项
操作限制
使用单个设备时,必须连接到 QSPI 0。使用两个设备时,两者必须完全相同(同一供应商且协议序列一致)。
四通道 SPI 控制器的 MIO 引脚与 SMC 控制器的 NOR 和 NAND 接口冲突,使用四通道 SPI 时,不能使用 NOR/SRAM 和 NAND 接口。有关 MIO 引脚的更多信息,请参见 2.5 节 “PS-PL MIO-EMIO 信号和接口”。
12.2 功能描述
四通道 SPI 闪存控制器可工作在 I/O 模式或线性寻址模式。对于读取操作,两种模式均支持单、双和四通道读模式;对于写入操作,I/O 模式支持单和四通道模式,线性寻址模式不支持写入。
12.2.1 操作模式
四通道 SPI 的操作模式转换如图 12-3 所示。
(图 12-3:四通道 SPI 操作模式转换,此处省略图表)
在 I/O 模式下,软件可通过设置相应的寄存器位,对读数据管理的不同方面进行不同程度的控制。在线性模式下,控制器执行所有必要的读数据管理,软件看来内存读取如同访问 ROM。
12.2.2 I/O 模式
在 I/O 模式下,软件负责根据四通道 SPI 协议准备和格式化命令及数据为指令,由 CMD 和数据组成的格式化指令序列通过重复写入 TXD 寄存器推入发送 FIFO。发送逻辑根据四通道 SPI 接口规范将 TxFIFO 的内容串行化,并发送到闪存。在发送逻辑输出 TxFIFO 内容的同时,它会同步采样原始串行数据,进行串并转换,并存储到 RxFIFO 中。
对于读命令,当命令和地址字节发送后由闪存驱动数据时,MIO 在发送逻辑的控制下适时从输出切换为输入。移入 RxFIFO 的数据反映了这种切换,使 RxFIFO 中对应条目包含有效数据。
软件需要从 RxFIFO 中过滤原始数据以获取相关内容,控制器不会修改软件写入的指令或存入 RxFIFO 的捕获数据。
控制器支持小端模式,一条 4 字节指令的最低有效字节的最高有效位首先发送。
流控制
I/O 模式在数据传输过程中有不同的流控制方式,用户可通过 config_reg.MANSTARTEN(Man_start_com)选择自动或手动模式。在手动模式下,用户可通过 Config_reg.SSFORCE(Manual_CS)进一步选择手动或自动片选。断言片选信号表示 MIO 上命令序列的开始,片选断言后,D0 上的串行数据被闪存解释为命令。
自动模式下,包括片选控制在内的整个传输序列由硬件完成,无需软件干预。向 TxFIFO 写入数据后传输立即开始,片选自动激活;TxFIFO 为空时数据传输结束,片选自动失效。在此模式下,为实现连续数据传输,软件必须以不低于 MIO 上数据移动的速率向 TxFIFO 提供数据,但由于从 RXD 读取和向 TXD 写入以 APB 时钟速率进行,这可能较难实现。
手动模式下,用户控制数据传输的开始。软件将整个传输序列写入 TxFIFO 或直到 TxFIFO 满,设置 Man_start_en 位后,控制器接管操作:断言片选,将数据从 TxFIFO 移出并移入 RxFIFO,适当控制 MIO 的输入 / 输出状态,当 TxFIFO 为空时通过撤销片选终止序列。此模式下,每个命令序列的最大字节数受限于 TxFIFO 的深度(252 字节)。
在手动模式下,用户除控制传输开始外,还可选择控制片选。软件先将传输序列(从命令开始)写入 TxFIFO 直到满,然后断言片选,接着手动启动,硬件接管操作。但 TxFIFO 为空时片选不会撤销,软件可再次填充 TxFIFO 以继续之前的命令。这种方法消除了每个命令序列的字节数限制,可有效用于大数据传输。命令序列完成后,软件通过写入 Manual_CS 位撤销片选。
12.2.3 I/O 模式发送寄存器(TXD)
软件写入特定闪存设备所需的字节序列(参考四通道 SPI 设备供应商规范)。控制器有四个只写 32 位 TXD 寄存器,供软件向闪存发送命令流以获取状态和读写数据。四通道 SPI TXD 寄存器写入格式如表 12-1 所示,访问 TXD0、TXD1、TXD2 或 TXD3 寄存器都会相应地写入 TxFIFO。
表 12-1:四通道 SPI TXD 寄存器写入格式
寄存器 | 写入数据格式(31:24、23:16、15:8、7:0) | 示例用途 |
---|---|---|
TXD 1 | 保留、保留、保留、数据或命令 | 设置写使能 |
TXD 2 | 保留、保留、数据 0、数据或命令 | 带数据写入状态 |
TXD 3 | 保留、数据 1、数据 0、数据或命令 | 带两个虚拟字节读取状态 |
TXD 0 | 数据 3、数据 2、数据 1、数据或命令 | 发送写入数据或读取用虚拟数据 |
连续访问以下寄存器之间时,用户必须清空 TxFIFO:
- TXD0 到 TXD1/TXD2/TXD3
- TXD1 到 TXD0/TXD1/TXD2/TXD3
- TXD2 到 TXD0/TXD1/TXD2/TXD3
- TXD3 到 TXD0/TXD1/TXD2/TXD3
TxD0 到 TXD0 的访问无需清空 FIFO。
FIFO 读写
TxFIFO 和 RxFIFO 共享同一门控时钟,因此从 TxFIFO 移出的每个字节(包括命令和地址字节),都会有一个对应字节移入 RxFIFO。
要从四通道 SPI 闪存读取数据,软件需向 TxFIFO 写入四通道 SPI 闪存所需的适当命令、地址、模式(四通道或双通道 I/O 模式时)和虚拟周期,此外还必须用额外的虚拟数据填充 TxFIFO,这些虚拟数据提供将数据移入 RxFIFO 所需的 CLK。更多编程细节参见 12.3.5 节 “Rx/Tx FIFO 对 I/O 命令序列的响应”。
12.2.4 I/O 模式注意事项
RxFIFO 中断状态位在数据实际可读取前就指示数据可用,这种延迟与时钟域交叉相关,通常会被软件处理中断的时间所覆盖。
在读取命令期间,软件必须向 TxFIFO 写入虚拟数据以接收来自设备的数据。自动模式下,若 TxFIFO 为空,四通道 SPI 控制器会撤销片选,要继续接收数据,软件必须向设备发送读命令和地址。
12.2.5 线性寻址模式
控制器具有 32 位 AXI 从接口,支持读操作的线性地址映射。当主设备通过该端口发出 AXI 读命令时,四通道 SPI 控制器生成 QSPI 命令以加载相应的内存数据,并通过 AXI 接口返回。
在线性模式下,闪存子系统如同具有 AXI 接口的典型只读存储器,支持 4 级命令流水线。与 I/O 模式相比,线性模式通过减少软件开销提高了用户友好性和整体读内存吞吐量。从软件角度看,访问线性四通道 SPI 内存子系统与访问其他 ROM 无明显差异,只是可能延迟更长。
当 qspi.LQSPI_CFG.[LQ_MODE] 位设置为 1 时,切换到 LQSPI 模式。进入线性寻址模式前,用户必须确保 TXFIFO 和 RXFIFO 均为空。设置 qspi.LQSPI_CFG.[LQ_MODE] 位后,FIFO 由 LQSPI 模块自动控制,对 TXD 和 RXD 的 I/O 访问无定义。
线性模式下,CS 引脚由 QSPI 控制器自动控制。切换到 LQSPI 模式前,用户必须确保 qspi.Config_reg [Man_start_en] 和 qspi.Config_reg [PCS] 均为 0。
控制器的简化框图(显示线性和 I/O 部分)如图 12-2 所示。
AXI 接口操作
线性寻址模式仅支持 AXI 读命令,所有有效的写地址和写数据会立即被确认但被忽略,即不会对闪存执行相应的编程(写入)操作,所有 AXI 写入会在写响应通道产生 SLVERR 错误。
支持递增或 wrap 地址突发读取,不支持固定地址突发,否则会导致 SLVERR 错误。因此,仅识别 arburst [1:0] 值为 2'b01 或 2'b10。所有读访问必须字对齐,数据宽度为 32 位(不允许窄突发传输)。
表 12-2 列出了接口忽略的主设备读地址通道信号。
表 12-2:被忽略的 AXI 读地址通道信号
信号 | 值 |
---|---|
araddr[1:0] | 忽略,假设为 0(即始终假设字对齐) |
arsize[2:0] | 忽略,始终为 32 位接口 |
arlock[1:0] | 忽略 |
arcache[3:0] | 忽略 |
arprot[2:0] | 忽略 |
AXI 从接口提供 4 的读接受能力,可接受最多 4 个未完成的 AXI 读命令。
AXI 读命令处理
AXI 读突发命令被转换为 SPI 闪存读指令,发送到四通道 SPI 控制器的 TxFIFO。控制器发送逻辑负责从 FIFO 检索读指令,并根据 SPI 协议传递给 SPI 闪存。
64 深度的 FIFO 用于提供读数据缓冲,可容纳最多 4 个 16 数据突发。由于 Rx FIFO 在片选信号激活后立即开始接收数据,线性地址模块会移除与指令码(如有)、地址、虚拟周期对应的输入数据,并以有效数据响应 AXI 读指令。
接口配置和读模式
AXI 读突发传输被转换为 SPI 闪存读指令,发送到控制器的 TxFIFO。发送逻辑从 TxFIFO 检索读指令,并根据 SPI 协议传递给 SPI 闪存设备。
软件通过写入 qspi.LQSPI_CFG [INST_CODE] 定义线性寻址模式中使用的 SPI 读命令。支持的读命令码和推荐的配置寄存器设置(qspi.LQSPI_CFG)如表 12-3 所示。使用 33MHz PS_CLK 时四通道 SPI 启动性能的最佳寄存器值如表 6-10 和表 12-3 所示。这些四通道 SPI 寄存器可在非安全模式下通过 BootROM 头中的寄存器初始化功能编程,以加快 FSBL / 用户代码的加载。若使用更快的 PS_CLK,需调整时钟分频器。
表 12-3:四通道 SPI 设备配置寄存器值
操作模式 | 指令码 | Winbond & Spansion | Micron | ||
---|---|---|---|---|---|
1 个设备 | 2 个设备 | 1 个设备 | 2 个设备 | ||
读(串行位) | 0x03 | 0x80000003 | 0xE0000003 | 0x80000003 | 0xE0000003 |
快速读(串行位) | 0x0B | 0x8000010B | 0xE000010B | 0x8000010B | 0xE000010B |
双输出快速读 | 0x3B | 0x8000013B | 0xE000013B | 0x8000013B | 0xE000013B |
四输出快速读 | 0x6B | 0x8000016B | 0xE000016B | 0x8000016B | 0xE000016B |
双 I/O 快速读 | 0xBB | 0x82FF00BB | 0xE2FF00BB | 0x82FF01BB | 0xE2FF01BB |
四 I/O 快速读 | 0xEB | 0x82FF02EB | 0xE2FF02EB | 0x82FF04EB | 0xE2FF06EB |
操作模式的选择取决于所连接设备的能力。I/O 快速读模式使用 4 位并行传输地址和数据,性能最快;输出快速读模式仅数据使用 4 位并行传输,仍比串行位模式快。
性能模式
要获得最高性能,用户应在四通道 I/O 模式下使用四通道 SPI 控制器,通过在连续读模式下使用四通道 SPI 设备可提高读性能,减少连续命令的读指令开销(详见附录 B “寄存器详情” 中的 LQSPI_CFG 寄存器)。
操作频率请参考适用的 Zynq-7000 AP SoC 数据手册。
读数据管理
63 深度的 RxFIFO 提供读数据缓冲,至少可容纳 3 个 16 字节的 AXI 突发传输长度。由于 RxFIFO 在片选信号激活后立即开始接收数据,线性地址适配器会移除与指令码(如有)、地址和虚拟周期对应的输入数据。
读数据必须与地址指定的相应字边界对齐,为实现数据对齐,控制器在将地址发送到闪存设备前可能会进行修改(如图 12-4 所示)。地址修改包括将地址减少最多 3 个字节位置,使返回数据自动按字对齐,地址变化量对 AXI 接口透明,且取决于指令。
(图 12-4:用于字对齐的自动地址偏移,此处省略图表)
例如,若 Cmd + 地址 + 模式 + 虚拟(QSPI 指令)未在 32 位边界结束,线性控制器会从地址中减去 1、2 或 3,以使数据在 32 位边界对齐。
读延迟
线性模式下,默认读模式为快速四通道 I/O。以下示例计算 100MHz 下四通道 I/O 模式(带 2 个虚拟字节)的内存延迟:对于单个设备,从 8 位指令码和 24 位地址可用到第一个 32 位数据可用的时钟周期数为:
总延迟 = 指令延迟 + 地址延迟 + 开销(模式 + 虚拟位 + 偏移)+8 个周期 = 30 周期 + 6 周期 + 8(2+4+2)周期 + 8 周期 = 30 周期
SPI 时钟为 100MHz 时,内存接口的延迟为 320ns。其他读模式的延迟更高,可按类似方式计算。
12.2.6 不支持的设备
一些设备采用自定义的 4 位宽类 SPI 接口用于闪存访问,如 SST 的 SQI 设备和 Atmel 的 Fast4 设备。其他一些四通道 SPI 设备(如某些 Micron/Numonyx 设备)通过非易失性配置位提供切换到此类自定义 4 位接口的选项。
这些接口与四通道 SPI 控制器支持的设备操作不同,它们在指令阶段以及地址和数据阶段均工作在 4 位模式,这要求四通道 SPI 闪存控制器上电后工作在 4 位模式并保持(或在有选项时可重新配置)。目前没有计划支持这些自定义接口。
12.2.7 支持的内存读写命令
支持在 SCK 上升沿每时钟传输 1 位地址,并在 SCK 上升沿每时钟返回 1、2 或 4 位数据的命令,包括 1 位数据的读或快速读、2 位数据的双输出读、4 位数据的四输出读。
支持在时钟上升沿每时钟传输 2 或 4 位地址和数据的命令,即 2 位的双 I/O 和 4 位的四 I/O。
表 12-4:内存读写命令
指令名称 | 描述 | 代码(十六进制) |
---|---|---|
READ | 读,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟返回 1 位数据 | 03 |
FAST_READ | 快速读,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟返回 1 位数据 | 0B |
DOR | 双输出读,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟返回 2 位数据 | 3B |
QOR | 四输出读,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟返回 4 位数据 | 6B |
DIOR | 双 I/O 读,时钟上升沿每时钟发送 2 位地址,SCLK 上升沿每时钟返回 4 位数据 | BB |
QIOR | 四 I/O 读,时钟上升沿每时钟发送 4 位地址,SCLK 上升沿每时钟返回 4 位数据 | EB |
PP | 页编程,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟发送 1 位数据 | 02 |
QPP | 四页编程,时钟上升沿每时钟发送 1 位地址,SCLK 上升沿每时钟发送 4 位数据 | Spansion 和 Micron 设备为 32,Macronix 设备为 38 |
12.3 编程指南
示例:启动序列
- 配置时钟(参见 12.4.1 节 “时钟”)
- 配置 Tx/Rx 信号(参见 12.5.2 节 “MIO 编程”)
- 复位控制器(参见 12.4.2 节 “复位”)
- 配置控制器(参见 12.3.1 节 “配置”)
之后,可将控制器配置为线性寻址模式(12.2.5 节 “线性寻址模式”)或 I/O 模式(12.3.3 节 “配置 I/O 模式” 和 12.3.4 节 “I/O 模式中断”)。
12.3.1 配置
示例:配置控制器
此示例适用于线性寻址和 I/O 模式,用于设置控制器的波特率、FIFO、闪存模式、时钟相位 / 极性,并编程回环延迟。
qspi.Config_reg 寄存器的编程值如表 12-3(344 页)所示。
- 配置控制器,写入 qspi.Config_reg 寄存器:
a. 设置波特率 [BAUD_RATE_DIV]
b. 选择主模式 [MCDE_SEL] = 1
c. 选择闪存模式(非传统 SPI)[LEG_FLSH] = 1
d. 选择小端模式 [endian] = 0
e. 设置 FIFO 宽度为 32 位 [FIFO_WIDTH]
f. 设置时钟相位 [CLK_PH] 和极性 [CLK_POL] - 若波特率分频器为 2,需修改默认设置。若 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
12.3.2 线性寻址模式
示例:线性寻址模式(内存读取)
线性寻址模式下数据读取的操作序列如下:
- 设置手动启动使能为自动模式 qspi.Config_reg [Man_start_en] = 0
- 断言片选 qspi.Config_reg [PCS] = 0
- 编程线性寻址模式的配置寄存器(示例值如表 12-3(344 页)所示)
- 使能控制器 qspi.En_REG [SPI_EN] = 1
- 从线性地址内存区域读取数据,内存范围取决于设备数量和大小,范围从 0xFC00_0000 到 0xFDFF_FFFF
- 禁用控制器 qspi.En_REG [SPI_EN] = 0
- 撤销片选 qspi.Config_reg [PCS] = 1
12.3.3 配置 I/O 模式
示例:I/O 模式(内存读写)
使用 I/O 模式进行读写的操作序列:
- 使能手动模式,写入 1 到 qspi.Config_reg [Man_start_en, Manual_CS] = 1
- 配置闪存设备(参见 355 页图 12-6),单个闪存设备使用 qspi.LQSPI_CFG 寄存器的复位值,并行双闪存设备时,向 TWO_MEM、SEP_BUS 位域写入 1
- 断言片选 qspi.Config_reg [PCS] = 0
- 使能控制器 qspi.En_REG [SPI_EN] = 1
- 向闪存写入字节序列,使用 TXD 寄存器向 TxFIFO 写入 1 到 4 字节(参见 12.2.3 节 “I/O 模式发送寄存器(TXD)”)
- 避免 TxFIFO 溢出,TxFIFO 为空时可写入 252 字节,之后软件可通过读取 qspi.Intr_status_REG [TX_FIFO_full],等待其为 0 后再写入 TXD 寄存器
- 使能中断,写入 qspi.Intrpt_en_REG(中断处理程序参见中断处理程序部分)
- 开始数据传输 qspi.Config_reg [Man_start_com] = 1
- 中断处理程序:在四通道 SPI 闪存的编程 / 读取操作期间,将所有所需数据传输到 QSPI 闪存(参见 349 页 “示例:I/O 模式中断服务程序”)
- 若执行读取操作:重新排列读取数据,消除虚拟周期导致的读取数据
- 禁用控制器 qspi.En_REG [SPI_EN] = 0
- 撤销片选 QSPI.Config_reg [PCS] = 1
注意,TxFIFO 宽度必须编程为 32 位:qspi.Config_reg [FIFO_WIDTH] = 0b11,软件需处理 “连续非字对齐” 传输。
示例:I/O 模式中断服务程序
- 根据四通道 SPI 设备类型配置 ISR 以处理中断条件。要从四通道 SPI 设备读取,最简单的 ISR 从 RxFIFO 读取数据并写入 TxFIFO。系统中断控制器(GIC)在第 7 章 “中断” 中描述,控制器生成系统外设中断(SPI),IRQ ID#51。四通道 SPI 控制器的中断机制参见 12.3.4 节 “I/O 模式中断”。
a. 读传输中断:RxFIFO 非空中断
b. 写传输中断:TxFIFO 非满中断
12.3.4 I/O 模式中断
中断仅用于 I/O 模式,任何中断条件满足时,控制器都会断言中断。四通道 SPI 中断处理程序检查中断原因,单个中断服务程序可管理所有中断条件。
示例:Rx 和 Tx 的中断处理程序
中断处理程序由 IRQ ID#51 触发,示例中读取 RxFIFO 直到为空,然后填充 TxFIFO。RxFIFO 非空中断状态用于判断是否可从 RxFIFO 读取内容,TxFIFO 非满中断指示 TxFIFO 是否有空间容纳更多内容。
- 禁用控制器中的所有中断,设置 qspi.Intrpt_dis_REG [TX_FIFO_not_full, RX_FIFO_full] 均 = 1
- 清除中断,读取中断状态寄存器 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) - 填充 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
12.3.5 Rx/Tx FIFO 对 I/O 命令序列的响应
示例命令和序列
- 写使能命令
- 读状态命令
- 读数据序列
示例中,YY 可为任意值,每个 YY 对可不同。
在串行传统模式下接收数据时,从 MISO/DQ1 线采样的值同步于时钟存入 RxFIFO,而命令和地址事务在 MOSI/DQ0 上进行。
示例:写使能命令(代码 0x06)
- 发送写使能命令(WREN),向 qspi.TXD1 寄存器写入 0xYYYY_YY06:
a. WREN 命令 = 0×06
b. YY=0
c. 控制器从 TxFIFO 向设备移出 1 个字节,并在 RxFIFO 接收 1 个字节 - 读状态,读取 qspi.RXD 寄存器接收 0xYYPP_PPPP:
a. 当 YY=0×0(状态)且 PP_PPPP=0×0(位的先前状态)时,值为 0x0000_0000
b. 软件记录写使能命令产生 1 个字节,并向调用函数返回 0xYY
发送 WREN 命令后 RxFIFO 的内容如下(“先前” 表示寄存器值未从之前的值改变):
RxFIFO 条目 | MSB | LSB | ||
---|---|---|---|---|
1 | 无效 | 无效 | 无效 | 无效 |
0 | 00 | 先前 | 先前 | 先前 |
示例:读状态命令(代码 0x05)
- 发送读状态命令(RDSR),向 qspi.TXD2 寄存器写入 0xYYYY_DD05:
a. 命令为 0x05,DD = 虚拟数据,YY=0
b. 控制器从 TxFIFO 向闪存移出 2 个字节,并在 RxFIFO 接收 2 个字节 - 读状态值,从 qspi.RXD 寄存器读取 0xZZYY_PPPP:
a. 当 ZZ=0×03、YY=0×0 且 PPPP=0x0 时,值为 0x0300_0000
b. 软件记录 2 个字节有效,并向调用函数返回 0x00、0x03
发送 RDSR 命令后 RxFIFO 的内容如下(“先前” 表示寄存器值未从之前的值改变):
TxFIFO 条目 | MSB | LSB | ||
---|---|---|---|---|
1 | 无效 | 无效 | 无效 | 无效 |
0 | 0x3 | 0x00 | 先前 | 先前 |
示例:读数据序列
此示例向调用函数返回地址 0 处的 4 个字节数据。
- 发送数据读取指令,向 qspi.TXD0 寄存器写入 0xA2A1_A003:
a. 指令包括命令(0x03)和地址(A0、A1 和 A2) - 发送虚拟数据,向 qspi.TXD0 寄存器(第二个 TxFIFO 条目)写入 0xD0D1_D2D3(虚拟数据):
a. 控制器从 TxFIFO 向闪存移出 8 个字节,并在 RxFIFO 接收 8 个字节
本示例的 TxFIFO 内容如下,从控制器到设备的字节序列为:0x03、Y0、Y1、Y2、D0、D1、D2 和 D3。
TxFIFO 条目 | MSB | LSB | ||
---|---|---|---|---|
1 | D3 | D2 | D1 | D0 |
0 | A2 | A1 | A0 | 0x03 |
- 读取指令字之后的内容,读取 qspi.RXD 寄存器接收 0xYYYY_YYYY:
a. YY=0 - 读取闪存数据,再次读取 RXD 寄存器接收 0xD3D2_D1D0:
a. 对于第二次读取,软件记录 4 个字节有效
b. 示例数据:0x2468ACEF
c. 软件总共读取这些字节:0x00、0x00、0x00、0x00、0x24、0x68、0xAC、0xEF,并向调用函数返回 4 个字节数据
本示例的 RxFIFO 内容如下,从设备到控制器的字节序列为:YY、YY、YY、YY、0xEF、0xAC、0x68 和 0x24。
RxFIFO 条目 | MSB | LSB | ||
---|---|---|---|---|
1 | 0x24 | 0x68 | 0xAC | 0xEF |
0 | YY | YY | YY | YY |
12.3.6 寄存器概述
寄存器概述如表 12-5 所示。
表 12-5:四通道 SPI 寄存器概述
地址偏移 | 软件助记符名称 | 描述 |
---|---|---|
0x00 | Config_reg | 配置 |
0x04 | Intr_status_REG | 中断状态 |
0x08 | Intrpt_en_REG | 中断使能 |
0x0C | Intrpt_dis_REG | 中断禁用 |
0x10 | Intrpt_mask_REG | 中断掩码 |
0x14 | En_REG | 控制器使能 |
0x18 | Delay_REG | 延迟 |
0x1C | TXD0 | 传输 1 字节命令和 3 字节数据或 4 字节数据 |
0x20 | Rx_data_REG | 接收数据(RxFIFO) |
0x24 | Slave_Idle_count_REG | 从机空闲计数 |
0x28 | TX_thres_REG | TxFIFO 阈值水平(4 字节字) |
0x2C | RX_thres_REG | RxFIFO 阈值水平(4 字节字) |
0x30 | GPIO | 通用输入输出 |
0x38 | LPBK_DLY_ADJ | 回环主时钟延迟调整 |
0x80 | TXD1 | 传输 1 字节命令 |
0x84 | TXD2 | 传输 1 字节命令和 1 字节数据 |
0x88 | TXD3 | 传输 1 字节命令和 2 字节数据 |
0xA0 | LQSPI_CFG | 线性模式配置 |
0xA4 | LQSPI_STS | 线性模式状态 |
0xFC | MOD_ID | 模块 ID |
12.4 系统功能
12.4.1 时钟
控制器和 I/O 接口由参考时钟(QSPI_REF_CLK)驱动,控制器的互连还需要 APB 接口 CPU_1x 时钟,这些时钟由 PS 时钟子系统生成。
CPU_1x 时钟
通用时钟编程信息参见 25.2 节 “CPU 时钟”,CPU_1x 时钟与四通道 SPI 参考时钟异步运行。
QSPI_REF_CLK 和四通道 SPI 接口时钟
QSPI_REF_CLK 是主控制器时钟,源自 PS 时钟子系统。时钟使能、PLL 选择和分频器设置通过 slcr.LQSPI_CLK_CTRL 寄存器编程,四通道 SPI 参考时钟频率的编程参见 25.6.3 节 “SDIO、SMC、SPI、四通道 SPI 和 UART 时钟”。
四通道 SPI 接口时钟由 QSPI_REF_CLK 通过 qspi.Config_reg [BAUD_RATE_DIV] 位域分频得到,分频系数为 2、4、8、16、32、64、128 或 256。
为进行电源管理,可使用 slcr 寄存器中的时钟使能关闭时钟,参考时钟的工作频率在数据手册中定义。
手动模式下的时钟比率限制
手动模式下,为保证控制器可靠运行,QSPI_REF_CLK 频率必须大于或等于 CPU_1x 时钟频率。
自动模式下无此限制,参考时钟通过 qspi.Config_reg [baud_rate_divisor] 分频生成闪存的 SCLK 时钟。
示例:设置参考时钟
假设所选 PLL(ARM、DDR 或 IO)工作在 1000MHz,期望的四通道 SPI 参考时钟频率为 200MHz。
- 选择 PLL 源、分频器并使能,向 slcr.QSPI_CLK_CTRL 寄存器写入 0x0000_0501:
a. 使能参考时钟
b. 将 I/O PLL 时钟分频 5:DIVISOR=0×05
c. 选择 I/O PLL 作为时钟源
四通道 SPI 反馈时钟
四通道 SPI 接口支持名为 qspi_sclk_fb_out 的可选反馈时钟引脚,用于高速四通道 SPI 时序模式(内存接口时钟需大于 40MHz)。反馈信号来自 I/O 内部输入,因此需要编程 MIO 引脚 8 并允许其自由切换。编程 MIO_PIN_08 寄存器的可选编程示例参见 12.5.2 节 “MIO 编程”。
使用四通道 SPI 反馈模式时,qspi_sclk_fb_out 引脚应仅连接到上拉或下拉电阻,以设置 MIO 电压模式(vmode)。
当四通道 SPI 时钟频率大于 FQSPICLK2 时,必须将 MIO 8 引脚编程为反馈输出时钟,且 MIO 8 引脚在 PCB 上仅连接到用于引导的上拉 / 下拉电阻。
12.4.2 复位
控制器有两个复位域:APB 接口和控制器本身,可一起或独立控制。每种复位类型的影响总结如表 12-6 所示。
表 12-6:四通道 SPI 复位影响
名称 | APB 接口 | TxFIFO 和 RxFIFO | 协议引擎 | 寄存器 |
---|---|---|---|---|
ABP 接口复位 slcr.LQSPI_RST_CTRL [LQSPI_CPU1X_RST] | 是 | 是 | 否 | 是 |
PS 复位子系统 slcr.LQSPI_RST_CTRL [QSPI_REF_RST] | 否 | 是 | 是 | 否 |
示例:复位 APB 接口和四通道 SPI 控制器
- 设置控制器复位,向 slcr.LQSPI_RST_CTRL [QSPI_REF_RST 和 LQSPI_CPU1X_RST] 位域写入 1
- 清除控制器复位,向 slcr.LQSPI_RST_CTRL [QSPI_REF_RST 和 LQSPI_CPU1X_RST] 位域写入 0
12.5 I/O 接口
12.5.1 接线连接
I/O 信号通过 MIO 引脚可用,四通道 SPI 控制器支持最多两个 SPI 闪存,采用共享或独立总线配置,支持多种配置:
- 四通道 SPI 单 SS,4 位 I/O
- 四通道 SPI 双 SS,8 位并行 I/O
- 四通道 SPI 双 SS,4 位堆叠 I/O
- 四通道 SPI 单 SS,传统 I/O
重要提示:若要使用 QSPI 内存子系统,QSPI 0 必须存在;QSPI 1 为可选,仅在双内存配置时需要,因此不能单独使用 QSPI_1。
单 SS,4 位 I/O
4 位闪存接口连接到控制器配置的框图如图 12-5 所示。
(图 12-5:四通道 SPI 单 SS 4 位 I/O,此处省略图表)
双 SS,8 位并行
控制器支持最多两个 SPI 闪存并行工作,如图 12-6 所示。此配置将最大可寻址 SPI 闪存从 16MB(24 位寻址)增加到 32MB(25 位寻址)。
(图 12-6:四通道 SPI 双 SS,8 位并行 I/O,此处省略图表)
对于 8 位并行配置,数据字的偶数位位于低内存,奇数位位于高内存,控制器在 I/O 和线性模式下均处理数据管理。四通道 SPI 控制器从两个四通道 SPI 设备读取数据,并在将状态数据写入 RXFIFO 之前对两个设备的状态信息执行 OR 操作。表 12-7 显示 8 位并行配置下 32 位数据字的数据位排列,表 12-8 显示双四通道 SPI 并行模式下的四通道 SPI CMD 行为。
表 12-7:四通道 SPI 双 SS,8 位并行 I/O 数据管理
(此处省略复杂表格结构,保留原数据逻辑)
表 12-8:双四通道 SPI 并行模式下的四通道 SPI CMD 行为
命令 | 双并行四通道 SPI 控制器行为 |
---|---|
扇区擦除 | 四通道 SPI 控制器向两个芯片发送擦除命令,每个部分执行 64KB 擦除操作,实际从两个存储器共擦除 128KB |
读 ID | 仅从低闪存总线接收数据并放入 RXD,因此无需组合数据,使用并行闪存模式时,上下闪存必须为相同部件 |
页编程 | 分离偶数和奇数位并在两个存储器中编程,详见表 12-7 |
读 | 从两个设备读取偶数和奇数数据位,并按表 12-7 所示交织 |
RDSR | 两个部分的 WIP 位相或形成读取数据的 LSB,其他 7 位仅来自低总线 |
8 位并行配置下,总可寻址内存大小为 32MB,需要 25 位地址。所有内存访问必须字对齐并具有双字节分辨率。线性模式下,四通道 SPI 控制器将 AXI 地址除以 2,并将得到的地址发送到四通道 SPI 设备;I/O 模式下,软件负责执行地址转换以支持 SPI 24 位地址。
双 SS,4 位堆叠 I/O
为减少 I/O 引脚数,控制器还支持最多两个 SPI 闪存采用共享总线配置,如图 12-7 所示。此配置将最大可寻址 SPI 闪存从 16MB(24 位寻址)增加到 32MB(25 位寻址),但吞吐量与单内存模式相同。注意,此配置不支持设备级 XIP 模式(读指令码 0xbb 和 0xeb)。
(图 12-7:四通道 SPI 双 SS 4 位堆叠 I/O,此处省略图表)
若使用线性四通道 SPI 内存子系统,低 SPI 闪存必须连接,高闪存为可选。总地址空间为 32MB,采用 25 位地址。I/O 模式下,地址的 MSB 由位于寄存器 0xA0 的位 28 的 U_PAGE 定义;线性地址模式下,AXI 地址位 24 决定高或低内存页。所有命令由 I/O 模式下 U_PAGE 选择的设备和线性模式下地址位 24 选择的设备执行。
单 SS,传统 I/O
四通道 SPI 控制器可工作在传统单比特串行接口模式,支持 1x、2x 和 4x I/O 模式,如图 12-8 所示。
(图 12-8:四通道 SPI 单 SS,传统 I/O,此处省略图表)
12.5.2 MIO 编程
四通道 SPI 信号可路由到特定 MIO 引脚,参见表 12-9 “四通道 SPI 接口信号”,接线图如图 12-5 至图 12-8 所示。通用路由概念和 MIO I/O 缓冲器配置在 2.4 节 “PS–PL 电压电平转换器使能” 中说明。
若使用 4 位 I/O 总线,使用四通道 SPI 0;若总线频率大于 40MHz,必须将四通道 SPI 反馈时钟路由到 MIO 引脚 8。
示例:为单个设备编程 I/O
以下步骤适用于上述所有四通道 SPI I/O 接口连接:
- 配置 MIO 引脚 1 为片选 0 输出,向 slcr.MIO_PIN_01 寄存器写入 0x0000_1202:
a. 将四通道 SPI 0 片选路由到引脚 1
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 边沿(温和设置)
e. 使能内部上拉电阻
f. 禁用 HSTL 接收器(因选择 LVCMOS) - 配置 MIO 引脚 2 至 5 为 I/O,向每个 slcr.MIO_PIN_{02:05} 寄存器写入 0x0000_0302:
a. 将四通道 SPI 0 I/O 引脚路由到引脚 2 至 5
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 驱动边沿
e. 禁用内部上拉电阻
f. 禁用 HSTL 接收器 - 配置 MIO 引脚 6 为串行时钟 0 输出,向 slcr.MIO_PIN_06 寄存器写入 0x0000_0302:
a. 将四通道 SPI 0 串行时钟路由到引脚 6
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 边沿(温和设置)
e. 禁用内部上拉电阻
f. 禁用 HSTL 接收器
选项:添加第二个设备片选
以下 I/O 连接需要此步骤:
- 双选择,共享 4 位数据内存接口
- 双选择,独立 4 位数据内存接口
- 配置 MIO 引脚 0 为片选 1 输出,向 slcr.MIO_PIN_00 寄存器写入 0x0000_1302:
a. 将四通道 SPI 1 片选路由到引脚 0
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 边沿(温和设置)
e. 使能内部上拉电阻
f. 禁用 HSTL 接收器
选项:添加第二个串行时钟
双选择、独立 4 位数据内存接口需要此步骤:
5. 配置 MIO 引脚 9 为串行时钟 1 输出,向 slcr.MIO_PIN_09 寄存器写入 0x0000_0302:
a. 将四通道 SPI 1 串行时钟路由到引脚 9
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 边沿(温和设置)
e. 禁用内部上拉电阻
f. 禁用 HSTL 接收器
选项:添加 4 位数据
双选择、独立 4 位数据内存接口需要以下步骤:
6. 配置 MIO 引脚 10 至 13 为 I/O,向每个 slcr.MIO_PIN_{10:13} 寄存器写入 0x0000_0302:
a. 将四通道 SPI 1 I/O 引脚路由到引脚 9 至 13
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 驱动边沿
e. 禁用内部上拉电阻
f. 禁用 HSTL 接收器
选项:添加反馈输出时钟
I/O 接口工作在 40MHz 以上时使用可选反馈时钟,为设置 MIO 电压模式(vmode),它应仅连接到上拉或下拉电阻,还必须使能反馈时钟。
7. 配置 MIO 引脚 8 为反馈时钟,向 slcr.MIO_PIN_08 寄存器写入 0x0000_0302:
a. 将四通道 SPI 反馈时钟输出路由到引脚 8
b. 三态由四通道 SPI 控制(TRI_ENABLE = 0)
c. LVCMOS18(其他电压选项参见寄存器定义)
d. 慢 CMOS 边沿(温和设置)
e. 禁用内部上拉电阻
f. 禁用 HSTL 接收器
12.5.3 MIO 信号
四通道 SPI 闪存信号通过 MIO 多路复用器路由到 MIO 设备引脚,双控制器端口的每一侧可单独使能,或一起作为 8 位 I/O 接口工作。
四通道 SPI 闪存信号路由到 MIO 引脚如表 12-9 所示。
表 12-9:四通道 SPI 接口信号
四通道 SPI 闪存接口 | 数据 I/O 模式 | MIO 引脚 | 控制器默认输入值 | |||||
---|---|---|---|---|---|---|---|---|
信号 | 1 位数据 | 2 位数据 | 4 位数据 | 四通道 SPI 0 | 四通道 SPI 1 | I/O | 名称 | |
闪存片选 | ~ | ~ | ~ | 1 | 0 | O | QSPI{1,0}_SS_B | ~ |
串行时钟 | ~ | ~ | ~ | 6 | 9 | O | QSPI{1,0}_SCLK | ~ |
输出反馈时钟 | ~ | ~ | ~ | 8 | O | QSPI_SCLK_FB_OUT | ~ | |
I/O 0 | 主输出 | I/O 0 | I/O 0 | 2 | 10 | IO | QSPI{1,0}_IO_0 | 0 |
I/O 1 | 主输入 | I/O 1 | I/O 1 | 3 | 11 | IO | QSPI{1,0}_IO_1 | 0 |
I/O 2 | 写保护 | 写保护 | I/O 2 | 4 | 12 | IO | QSPI{1,0}_IO_2 | 0 |
I/O 3 | 保持 | 保持 | I/O 3 | 5 | 13 | IO | QSPI{1,0}_IO_3 | 0 |
你可以将以上内容复制到 Word 文档中,Word 会自动识别部分格式,若有表格错乱等情况,可手动调整表格边框和行列对齐方式以确保格式正确。
示例 1:启动序列(适用于所有模式初始化)
- 配置时钟:参考 12.4.1 节 “Clocks” 配置 QSPI_REF_CLK 和 CPU_1x 时钟。
- 配置 Tx/Rx 信号:参考 12.5.2 节 “MIO Programming” 设置 MIO 引脚(如片选、时钟、I/O 引脚)。
- 复位控制器:通过 slcr.LQSPI_RST_CTRL 寄存器设置并清除复位(先写 1 再写 0 到 [QSPI_REF_RST] 和 [LQSPI_CPU1X_RST] 位域)。
- 配置控制器:参考 12.3.1 节设置波特率、FIFO 宽度、时钟相位等(见示例 2)。
- 选择工作模式:后续可配置为线性寻址模式或 I/O 模式。
示例 2:配置控制器(适用于线性和 I/O 模式)
- 配置
qspi.Config_reg
寄存器:- 设置波特率分频:
[BAUD_RATE_DIV]
(如分频系数 2、4 等)。 - 选择主模式:
[MCDE_SEL] = 1
。 - 选择闪存模式(非传统 SPI):
[LEG_FLSH] = 1
。 - 选择小端模式:
[endian] = 0
。 - 设置 FIFO 宽度为 32 位:
[FIFO_WIDTH]
(具体值参考寄存器定义)。 - 设置时钟相位和极性:
[CLK_PH]
和[CLK_POL]
。
- 设置波特率分频:
- 若波特率分频器为 2(
[BAUD_RATE_DIV] = 0b00
),配置qspi.LPBK_DLY_ADJ
寄存器:- 选择内部时钟:
[USE_LPBK] = 1
。 - 设置时钟延迟:
[DLY0] = 0b00
,[DLY1] = 0b00
。
- 选择内部时钟:
示例 3:线性寻址模式(内存读取)
- 设置手动启动使能为自动模式:
qspi.Config_reg[Man_start_en] = 0
。 - 断言片选:
qspi.Config_reg[PCS] = 0
。 - 编程线性模式配置寄存器:参考表 12-3 设置
qspi.LQSPI_CFG
(如四 I/O 快速读指令 0xEB 对应的值)。 - 使能控制器:
qspi.En_REG[SPI_EN] = 1
。 - 读取数据:从线性地址区域(0xFC00_0000 至 0xFDFF_FFFF)读取数据。
- 禁用控制器:
qspi.En_REG[SPI_EN] = 0
。 - 撤销片选:
qspi.Config_reg[PCS] = 1
。
示例 4:I/O 模式(内存读写)
- 启用手动模式:
qspi.Config_reg[Man_start_en, Manual_CS] = 1
。 - 配置闪存设备:单设备用
qspi.LQSPI_CFG
复位值;双设备并行时,设置TWO_MEM
和SEP_BUS
位。 - 断言片选:
qspi.Config_reg[PCS] = 0
。 - 使能控制器:
qspi.En_REG[SPI_EN] = 1
。 - 写入数据:通过 TXD0-TXD3 寄存器向 TxFIFO 写入 1-4 字节(如命令、地址、数据)。
- 避免 FIFO 溢出:通过
qspi.Intr_status_REG[TX_FIFO_full]
判断 TxFIFO 状态,为空时再写入。 - 使能中断:写入
qspi.Intrpt_en_REG
使能 TxFIFO 非满和 RxFIFO 非空中断。 - 开始传输:
qspi.Config_reg[Man_start_com] = 1
。 - 处理数据:读操作需过滤 RxFIFO 中的虚拟数据;写操作等待传输完成。
- 禁用控制器并撤销片选:
qspi.En_REG[SPI_EN] = 0
,qspi.Config_reg[PCS] = 1
。
示例 5:I/O 模式中断服务程序(处理 Rx/Tx)
- 禁用中断:
qspi.Intrpt_dis_REG[TX_FIFO_not_full, RX_FIFO_full] = 1
。 - 清除中断:读取
qspi.Intr_status_REG
。 - 清空 RxFIFO:若
qspi.Intr_status_REG[RX_FIFO_not_empty] = 1
,通过qspi.RX_data_REG
读取数据,直到 FIFO 为空。 - 填充 TxFIFO:若
qspi.Intr_status_REG[TX_FIFO_not_full] = 1
,通过qspi.TXD0
写入数据,直到 FIFO 满或数据写完。 - 使能中断:
qspi.Intrpt_en_REG[TX_FIFO_not_full, RX_FIFO_full] = 1
。 - 启动传输:
qspi.Config_reg[MANSTRTEN] = 1
。
示例 6:FIFO 响应命令序列(以读数据为例)
- 发送读指令:向
qspi.TXD0
写入0xA2A1_A003
(含命令 0x03 和地址 A0-A2)。 - 发送虚拟数据:向
qspi.TXD0
写入0xD0D1_D2D3
(提供时钟用于接收数据)。 - 读取无效数据:第一次读
qspi.RXD
获取0xYYYY_YYYY
(虚拟数据对应的接收值)。 - 读取有效数据:第二次读
qspi.RXD
获取实际闪存数据(如0x2468ACEF
)。
以下是基于文档内容整理的完整编程实例(含伪代码及注释),涵盖初始化、线性寻址模式、I/O 模式及中断处理等场景:
一、启动序列初始化(适用于所有模式)
c
运行
/* 参考文档12.3节启动序列:1-179至1-184 */
void qspi_init() {// 1. 配置时钟(参考12.4.1节时钟配置:1-322至1-347)// 示例:设置QSPI_REF_CLK为200MHz(源I/O PLL 1000MHz,分频5)slcr.LQSPI_CLK_CTRL = 0x00000501; // 使能时钟,选择I/O PLL,分频5 // 2. 配置MIO引脚(参考12.5.2节MIO编程:1-419至1-496)// 配置片选0(MIO1)、I/O引脚(MIO2-5)、时钟0(MIO6)slcr.MIO_PIN_01 = 0x00001202; // 片选0输出,LVCMOS18,上拉 slcr.MIO_PIN_02 = 0x00000302; // I/O0,LVCMOS18 slcr.MIO_PIN_03 = 0x00000302; // I/O1,LVCMOS18slcr.MIO_PIN_04 = 0x00000302; // I/O2,LVCMOS18slcr.MIO_PIN_05 = 0x00000302; // I/O3,LVCMOS18slcr.MIO_PIN_06 = 0x00000302; // 串行时钟0输出 // 3. 复位控制器(参考12.4.2节复位:1-355至1-361)slcr.LQSPI_RST_CTRL = (1 << QSPI_REF_RST) | (1 << LQSPI_CPU1X_RST); // 置位复位slcr.LQSPI_RST_CTRL = 0; // 清除复位 // 4. 配置控制器核心参数(参考12.3.1节配置:1-185至1-200)qspi.Config_reg = 0x00000000;qspi.Config_reg |= (0x02 << BAUD_RATE_DIV); // 波特率分频(示例:4分频)qspi.Config_reg |= (1 << MCDE_SEL); // 主模式qspi.Config_reg |= (1 << LEG_FLSH); // 闪存模式(非传统SPI)qspi.Config_reg &= ~(1 << endian); // 小端模式qspi.Config_reg |= (0b11 << FIFO_WIDTH); // 32位FIFO宽度 qspi.Config_reg |= (0 << CLK_PH) | (0 << CLK_POL); // 时钟相位/极性// 若波特率分频为2,配置回环延迟(示例:分频为2时)if ((qspi.Config_reg & (0b11 << BAUD_RATE_DIV)) == 0) {qspi.LPBK_DLY_ADJ = (1 << USE_LPBK) | (0 << DLY0) | (0 << DLY1); }
}
二、线性寻址模式(内存读取)
c
运行
/* 参考12.3.2节线性模式:1-206至1-215 */
void qspi_linear_read(uint32_t addr, uint32_t *buf, uint32_t len) {// 1. 配置自动模式及片选qspi.Config_reg &= ~(1 << Man_start_en); // 自动启动 qspi.Config_reg &= ~(1 << PCS); // 断言片选 // 2. 配置线性模式指令(示例:四I/O快速读0xEB,单设备)qspi.LQSPI_CFG = 0x82FF02EB; // 参考表12-3 // 3. 使能控制器qspi.En_REG |= (1 << SPI_EN); // 使能QSPI // 4. 从线性地址区域读取数据(0xFC000000至0xFDFFFFFF)uint32_t *qspi_addr = (uint32_t *)(0xFC000000 + addr); for (uint32_t i = 0; i < len; i++) {buf[i] = qspi_addr[i]; // 像访问普通内存一样读取}// 5. 禁用控制器及片选qspi.En_REG &= ~(1 << SPI_EN); // 禁用QSPI qspi.Config_reg |= (1 << PCS); // 撤销片选
}
三、I/O 模式(读写操作)
3.1 I/O 模式初始化及写操作
c
运行
/* 参考12.3.3节I/O模式:1-216至1-230 */
void qspi_io_write(uint8_t *cmd, uint32_t cmd_len, uint8_t *data, uint32_t data_len) {// 1. 配置手动模式qspi.Config_reg |= (1 << Man_start_en) | (1 << Manual_CS); // 手动启动+片选 // 2. 配置双设备(若需):单设备无需此步// qspi.LQSPI_CFG |= (1 << TWO_MEM) | (1 << SEP_BUS); // 3. 断言片选qspi.Config_reg &= ~(1 << PCS); // 片选使能 // 4. 使能控制器qspi.En_REG |= (1 << SPI_EN); // 5. 写入命令(通过TXD寄存器,参考12.2.3节TXD:1-92至102)for (uint32_t i = 0; i < cmd_len; i += 4) {uint32_t txd_data = 0;// 填充4字节数据(不足补0)for (int j = 0; j < 4 && (i + j) < cmd_len; j++) {txd_data |= (cmd[i + j] << (8 * j));}qspi.TXD0 = txd_data; // 使用TXD0发送命令 }// 6. 写入数据(避免FIFO溢出)for (uint32_t i = 0; i < data_len; i += 4) {// 等待TxFIFO非满while (qspi.Intr_status_REG & (1 << TX_FIFO_full)); uint32_t txd_data = 0;for (int j = 0; j < 4 && (i + j) < data_len; j++) {txd_data |= (data[i + j] << (8 * j));}qspi.TXD0 = txd_data;}// 7. 使能中断(用于后续传输完成通知)qspi.Intrpt_en_REG |= (1 << TX_FIFO_not_full) | (1 << RX_FIFO_not_empty); // 8. 启动传输qspi.Config_reg |= (1 << Man_start_com); // 9. 等待传输完成(通过中断或轮询)while (qspi.Intr_status_REG & (1 << TX_FIFO_not_empty)); // 示例:轮询// 10. 禁用控制器及片选qspi.En_REG &= ~(1 << SPI_EN); qspi.Config_reg |= (1 << PCS);
}
3.2 I/O 模式读操作(含 FIFO 处理)
c
运行
/* 参考12.3.5节读数据序列:1-296至310 */
void qspi_io_read(uint8_t *cmd, uint32_t cmd_len, uint8_t *buf, uint32_t data_len) {// 1. 同写操作步骤1-4:配置手动模式、片选、使能控制器// ...(省略重复代码)// 2. 写入读命令和地址(示例:命令0x03 + 地址A0-A2)qspi.TXD0 = (0x03) | (A2 << 24) | (A1 << 16) | (A0 << 8); // 3. 写入虚拟数据(用于接收闪存数据,长度=数据长度)for (uint32_t i = 0; i < data_len; i += 4) {while (qspi.Intr_status_REG & (1 << TX_FIFO_full));qspi.TXD0 = 0xDDDDDDDD; // 虚拟数据 }// 4. 启动传输qspi.Config_reg |= (1 << Man_start_com);// 5. 读取RxFIFO(过滤虚拟数据对应的无效值)uint32_t rx_idx = 0;while (rx_idx < data_len) {while (!(qspi.Intr_status_REG & (1 << RX_FIFO_not_empty))); // 等待接收数据uint32_t rxd_data = qspi.RX_data_REG; // 读取FIFO // 提取有效字节(忽略前4字节无效值,参考1-304至308)for (int j = 0; j < 4 && rx_idx < data_len; j++) {buf[rx_idx++] = (rxd_data >> (8 * j)) & 0xFF;}}// 6. 禁用控制器及片选(同写操作步骤10)// ...
}
四、I/O 模式中断服务程序(ISR)
c
运行
/* 参考12.3.4节中断处理:1-238至1-256 */
void qspi_isr() {// 1. 禁用中断qspi.Intrpt_dis_REG |= (1 << TX_FIFO_not_full) | (1 << RX_FIFO_not_empty); // 2. 清除中断状态uint32_t intr_status = qspi.Intr_status_REG; // 读状态自动清除 // 3. 处理接收数据if (intr_status & (1 << RX_FIFO_not_empty)) {while (qspi.Intr_status_REG & (1 << RX_FIFO_not_empty)) {uint32_t data = qspi.RX_data_REG; // 读取RxFIFO // 存储数据到缓冲区...}}// 4. 处理发送数据if (intr_status & (1 << TX_FIFO_not_full)) {while ((qspi.Intr_status_REG & (1 << TX_FIFO_full)) == 0 && has_more_data()) {qspi.TXD0 = get_next_tx_data(); // 填充TxFIFO }}// 5. 重新使能中断qspi.Intrpt_en_REG |= (1 << TX_FIFO_not_full) | (1 << RX_FIFO_not_empty); // 6. 启动传输(若需)qspi.Config_reg |= (1 << MANSTRTEN);
}
五、常用命令序列示例(写使能、读状态)
5.1 写使能命令(WREN, 0x06)
c
运行
/* 参考12.3.5节写使能:1-270至279 */
void qspi_write_enable() {// 配置I/O模式(同前)// ...// 发送写使能命令(使用TXD1寄存器,1字节命令)qspi.TXD1 = 0x00000006; // TXD1格式:仅最低字节有效 // 启动传输并等待完成qspi.Config_reg |= (1 << Man_start_com);while (qspi.Intr_status_REG & (1 << TX_FIFO_not_empty));
}
5.2 读状态命令(RDSR, 0x05)
c
运行
/* 参考12.3.5节读状态:1-280至295 */
uint8_t qspi_read_status() {// 配置I/O模式(同前)// ...// 发送读状态命令(TXD2:1字节命令+1字节虚拟数据)qspi.TXD2 = 0x0000DD05; // DD为虚拟数据 // 启动传输qspi.Config_reg |= (1 << Man_start_com);// 读取状态(RxFIFO中第二字节有效)while (!(qspi.Intr_status_REG & (1 << RX_FIFO_not_empty)));uint32_t status = qspi.RX_data_REG;return (status >> 8) & 0xFF; // 有效状态在次低字节
}
说明
- 代码中寄存器名称(如
qspi.Config_reg
、slcr.LQSPI_CLK_CTRL
)及位域(如BAUD_RATE_DIV
)均来自文档 12.3.6 节寄存器概述(1-317 至 320)及对应配置说明。 - 数值配置(如
0x82FF02EB
)参考文档表 12-3(1-138)及示例步骤。 - 实际使用时需根据硬件环境调整地址、数据长度等参数,中断处理需结合系统中断控制器(如 GIC,IRQ ID#51)。