从上一节FlexSPI的框图中可知,SEQ_CTL
实现了对外部存储器的时序控制。不同的存储器有着不同的时序,这个时序就是由LUT
(Look Up Table
)指定的。LUT
有它自己的寄存器,当我们设置好之后,外部存储器的读、写和擦除等操作就会根据LUT
寄存器中配置执行。另外除了配置LUT
表格外,还需要配置FlexSPI的一些参数,以适应不同的硬件上的设计和不同的外部存储器。
文章目录
- 1 LUT表格的组成
- 2 FlexSPI结构体配置
- 2.1 flexspi_config_t
- 2.2 flexspi_device_config_t
- 3 总结
1 LUT表格的组成
LUT
表格的组成如下图所示,一个表格中包含多个Sequence
,一个Sequence
包含8个Instruction
,而Instruction
则是由opcode
、num_pads
和operand
三个字段组成。
(1)opcode
这是由FlexSPI硬件上定义的一些固定的操作码,如读数据操作、空指令、停止指令等都有其对于的操作码。
Name | Opcode | purpose | Transmit Data | Bits/Bytes/Cycle Number |
---|---|---|---|---|
CMD_SDR/CMD_DDR | 0x01/0x21 | 命令代码 | Operand[7:0] | 8 bits |
RADDR_SDR/RADDR_DDR | 0x02/0x22 | 行地址 | Row_address[31:0] | 由operand[7:0]指定行地址的bits |
CADDR_SDR/CADDR_DDR | 0x03/0x23 | 列地址 | Column_address[31:0] | 由operand[7:0]指定列地址的bits |
MODE1_SDR/MODE1_DDR | 0x04/0x24 | 模式序列 | Operand[0] | 1 bit |
MODE2_SDR/MODE2_DDR | 0x05/0x25 | 模式序列 | Operand[1:0] | 2 bit |
MODE4_SDR/MODE4_DDR | 0x06/0x26 | 模式序列 | Operand[3:0] | 4 bit |
MODE8_SDR/MODE8_DDR | 0x07/0x27 | 模式序列 | Operand[7:0] | 8 bit |
WRITE_SDR/WRITE_DDR | 0x08/0x28 | 写数据 | IP TX FIFO or AHB_TX_BUF | AHB Burst size or IPCR1[IDATSZ] |
READ_SDR/READ_DDR | 0x09/0x29 | 读数据 | - | AHB Burst size or IPCR1[IDATSZ] |
LEARN_SDR/LEARN_DDR | 0x0A/0x2A | 读数据/引导位(学习时序) | - | 由operand[7:0]决定 |
DATSZ_SDR/DATSZ_DDR | 0x0B/0x2B | 当前序列读/写数据长度 | 主要用于FPGA,取决于其内部逻辑 | 由operand[7:0]决定 |
DUMMY_SDR/DUMMY_DDR | 0x0C/0x2C | 延时等待 | - | 由operand[7:0]决定Dummy的cycle(SCLK) |
DUMMY_RWDS_SDR/DUMMY_RWDS_DDR | 0x0D/0x2D | HyperBus设备中用于数据和命令的时钟同步 | - | 与operand[7:0]和DQS pin有关 |
JMP_ON_CS | 0x1F | 序列跳转 | - | - |
STOP | 0x00 | 停止序列 | - | - |
- 对于
JMP_ON_CS
和STOP
来说,它们的num_pads
会被忽略;而其它的指令则根据不同的指令指定num_pads
(2)num_pads
与外部存储器通信时用的数据线的个数,取值如下:
#define FLEXSPI_1PAD 0
#define FLEXSPI_2PAD 1
#define FLEXSPI_4PAD 2
#define FLEXSPI_8PAD 3
比如对于八线的HyperRAM来说,传输数据时这个参数就是FLEXSPI_8PAD
,即数据在硬件连接的所有线上传输。但对于某些指令来说,num_pads
只需要设置为FLEXSPI_1PAD
,如停止指令,它只需要一个数据位即可。
(3)operand
即前面的操作码对应的参数。
2 FlexSPI结构体配置
对于不同的外部存储器来说,有不同的行/列寻址宽度、不同的存储器大小等参数,而这些参数都是在FlexSPI相关结构体中配置。首先我们就以HyperRAM的初始化代码为例,看一下FlexSPI的初始化代码:
flexspi_device_config_t deviceconfig = {
.flexspiRootClk = 332000000,
......//略
};
flexspi_config_t config;
/* Get FLEXSPI default settings and configure the flexspi. */
FLEXSPI_GetDefaultConfig(&config);
FLEXSPI_Init(EXAMPLE_FLEXSPI, &config);
/* Configure RAM settings according to serial RAM feature. */
FLEXSPI_SetFlashConfig(EXAMPLE_FLEXSPI, &deviceconfig, kFLEXSPI_PortA1);
/* Update LUT table. */
FLEXSPI_UpdateLUT(EXAMPLE_FLEXSPI, 0, customLUT, ARRAY_SIZE(customLUT));
/* Do software reset. */
FLEXSPI_SoftwareReset(EXAMPLE_FLEXSPI);
从上面的代码可以知道,在初始化过程中,flexspi_config_t
和flexspi_device_config_t
这两个结构体需要我们进行填充。其中flexspi_config_t
主要是对FlexSPI的一些功能的配置,而flexspi_device_config_t
则是对特定的存储器的一些时序、参数配置。
2.1 flexspi_config_t
flexspi_config_t
结构体的定义如下,各个参数的详细解释我都写在注释中了。
typedef struct _flexspi_config
{
/* 读数据使用的时钟源 */
flexspi_read_sample_clock_t rxSampleClock;
/* 在没有外部驱动时,SCK将以固定频率产生脉冲 */
bool enableSckFreeRunning;
/* 使能PORTA和PORTB的数组组合来支持8位访问 */
bool enableCombination;
/* 是否使能低功耗Dozeoze模式 */
bool enableDoze;
/* 将时钟速率减缓一半,减慢传输速度但增大容错率 */
bool enableHalfSpeedAccess;
/* 使能SCKB作为SCKA的差分时钟,若使能PORTB的设备无法访问 */
bool enableSckBDiffOpt;
/* 若使能FLASHA1CRx寄存器的配置会共享给其它的设备 */
bool enableSameConfigForAll;
/* 命令执行的超时周期,防止系统卡在等待某个状态上,系统将在ahbGrantTimeoutCyle*1024个串行时钟周期后返回超时 */
uint16_t seqTimeoutCycle;
/* IP命令执行的超时周期,系统将在ipGrantTimeoutCycle*1024个AHB时钟周期后返回超时 */
uint8_t ipGrantTimeoutCycle;
/* IP发送FIFO大小,与串口watermark类似,只有满了才发 */
uint8_t txWatermark;
/* 接收FIFO大小,只有满了才收 */
uint8_t rxWatermark;
struct
{
/* FlexSPI从AHB总线获取执行命令的访问权限的超时时间 */
uint8_t ahbGrantTimeoutCycle;
/* AHB读写访问超时周期 */
uint16_t ahbBusTimeoutCycle;
/* FlexSPI支持暂停当前命令序列而稍后执行,该值表示在后续恢复命令前等待的空闲周期数 */
uint8_t resumeWaitCycle;
/* 使能在每次数据传输完成后清除RX/TX的buffer */
bool enableClearAHBBufferOpt;
/* 控制是否移除AHB读取突发起始地址对齐限制,若使能,突发读取地址没有字节对齐限制 */
bool enableReadAddressOpt;
/* 使能AHB读取预取功能:在完成当前AHB突发读取后,预先读取一些额外的数据到缓冲区中以加快后续读取,但这会增加功耗 */
bool enableAHBPrefetch;
/* 是否开启AHB写缓冲访问:在执行写命令后,不等待其执行完毕就返回,允许后续指令继续执行,提高系统的并发性 */
bool enableAHBBufferable;
/* 使能AHB总线缓存读取,若命中则从缓存中读取,但要确保数据的一致性 */
bool enableAHBCachable; /*!< Enable AHB bus cachable read access support. */
} ahbConfig;
} flexspi_config_t;
2.2 flexspi_device_config_t
flexspi_device_config_t
结构体的定义如下:
typedef struct _flexspi_device_config
{
/* FLEXSPI串行根时钟频率 */
uint32_t flexspiRootClk;
/* 是否启用第二个串行时钟线:硬件上连了两个时钟线 */
bool isSck2Enabled;
/* 外部存储设备的大小,以KB为单位 */
uint32_t flashSize;
/* 定义在连续的CS信号之间的时间间隔单位:1或256个串行时钟周期 */
flexspi_cs_interval_cycle_unit_t CSIntervalUnit;
/* CS线的保持时间,单位为CSIntervalUnit */
uint16_t CSInterval;
/* CS线的保持时间 */
uint8_t CSHoldTime;
/* CS线设置时间,即在发送片选信号之前需要等待的时间 */
uint8_t CSSetupTime;
/* 在读取数据之后,数据保持有效的时间 */
uint8_t dataValidTime;
/* 列地址宽度 */
uint8_t columnspace;
/* 是否使用字地址来访问外部设备,有的存储设备支持按字传输,如读0地址的数据将返回0~3地址的数据(4字节) */
bool enableWordAddress;
/* AHB写命令的序列ID:对应LUT表格中的顺序 */
uint8_t AWRSeqIndex;
/* AHB写命令的序列号 */
uint8_t AWRSeqNumber;
/* AHB读命令的序列ID:对应LUT表格中的顺序 */
uint8_t ARDSeqIndex;
/* AHB读命令的序列号 */
uint8_t ARDSeqNumber;
/* AHB写等待单位 */
flexspi_ahb_write_wait_unit_t AHBWriteWaitUnit;
/* AHB写等待间隔,乘以AHB写等待单位得到AHB写等待周期数,即写完后等待外部存储器处理完数据的时间 */
uint16_t AHBWriteWaitInterval;
/* 是否启用写入屏蔽。指示在向外部设备写入数据时是否将FLEXSPI的DQS引脚用作写入屏蔽 */
bool enableWriteMask;
} flexspi_device_config_t;
AHB读/写命令的序列号?
以写命令的序列号AWRSeqNumber
为例:对于某些存储设备(例如HyperFlash/HyperRam/Serial NAND),Flash编程访问是通过几个命令序列完成,也就是说AHB写命令将触发LUT中第(AWRSeqNumber+1
)个命令序列。
3 总结
本节介绍了LUT表格的组成和FlexSPI配置结构体的各个参数的含义,有了这些基础知识后,我们就可以初始化不同的外部存储器设备了。下一节将举一个例子,通过外部存储器与FlexSPI连接的原理图和外部存储器的时序手册,来看看这些参数是如何初始化的。