一、概述
在串行flash中,比如nor flash,由于flash厂家品牌较多,对于用户来说,一些操作比如扇区和块擦除指令,大小等等,能够做到统一指令更好,但事实上,并非所有的厂家都是这么做。所以有这么一家组织叫JEDEC定义了一套适用串行flash的标准来进行规范化,也就是JESD216标准。
该标准从最初的JESD216发展到现在,最新已经是JESD216F.02了。发展历程和版本的不同点,也在协议文档中一一举出,这里不做阐述。最新的JESD216文档可以自己下载了解。
那么这个标准中,对于上述的指令差异,标准定义了一个table表,也就是本文需要阐述的重点,SFDP。该表有很多para table,每个table都会有描述flash相关的参数指令,比如上述所说的扇区擦除指令,只要该flash遵守该标准,那么就可以在para table中读到该扇区擦除指令。
本文以W25Q64JV的华邦flash、JESD216D版本为例阐述。
二、定义
JESD216标准文档长达162页,不多也不少,但是如果需要自己去源码定义这么多table,那是有点难以短时间能够接受过来。
hpm_sdk中的组件components中,有个serial_nor组件文件夹,里面有个sfdp_def.h头文件,从命名可知道,这是一个关于sfdp的相关定义。这些定义和结构体能够帮助快速去了解其JESD216标准。
该文件描述的最新标准是JESD216D,本文就基于该版本进行简单的阐述说明。
(一)SFDP表读取
在标准文档中,开头就是描述了读取SFDP指令处理,描述中主要大致理解为:
1、SFDP读取指令是0x5A,hpm_sdk中对应的宏定义是kSerialFlash_ReadSFDP
2、SFDP区域中,想读取的地址空间,都为三字节字段,也就是24位地址。
3、在要读取数据之前,也就是发完地址之后,必须有8个spi clock时钟的dummu等待。比如单线SPI读取的情况下,那么需要一字节的dummy。四线读取下需要4个字节dummy。(一般都是采用单线SPI读取)
4、SPI时钟最好控制在50M以下读取。
由上述描述,我们可以利用hpm的spi驱动封装个读取sfdp的API接口。
static hpm_stat_t hpm_spi_nor_read_sfdp(SPI_Type *ptr, uint32_t addr, uint32_t *buffer, uint32_t bytes)
{
spi_control_config_t control_config = {0};
uint8_t cmd = kSerialFlash_ReadSFDP;/*指令为5A*/
/*地址设置24位*/
ptr->TRANSFMT |= SPI_TRANSFMT_ADDRLEN_SET(sizeof(uint32_t) - 2);
spi_master_get_default_control_config(&control_config);
control_config.master_config.cmd_enable = true; /* cmd phase control for master */
control_config.master_config.addr_enable = true; /* address phase control for master */
control_config.master_config.addr_phase_fmt = spi_address_phase_format_single_io_mode;
control_config.common_config.trans_mode = spi_trans_dummy_read;
control_config.common_config.data_phase_fmt = spi_single_io_mode;
control_config.common_config.dummy_cnt = spi_dummy_count_1;
return spi_transfer(ptr, &control_config, &cmd, &addr, NULL, 0, (uint8_t *)buffer, bytes);
}
(二)SFDP标头结构
在标准中,该结构的地址在0x000000。标头结构包括了:SFDP Header和Parameter Header两个标头,而Parameter Header可以是多个,这取决于SFDP Header的NPH参数,这个参数决定了Parameter Header的数量,而Parameter Header的parameter length又决定了parameter table的数量。
所以如果需要读取parameter table,必须得先读取SFDP Header以及Parameter Header。
1、SFDP Header
在标准文档中定义知道,SFDP Header的结构如下,同样hpm_sdk中的sfdp_def也实现了定义。
signature就是标识符,ASCII就是“SFDP”,需要读到该标识符才可解析。
param_hdr_num:指的是parameter header的数量,长度+1。
在这里需要解释下minor_rev主版本,在上面中,hpm已经定义了版本,比如JESD216D就是 kSfdp_Version_Minor_D。
#define kSfdp_Version_Major_1_0 (1U)
#define kSfdp_Version_Minor_0 (0U) /* JESD216 */
#define kSfdp_Version_Minor_A (5U) /* JESD216A */
#define kSfdp_Version_Minor_B (6U) /* JESD216B */
#define kSfdp_Version_Minor_C (7U) /* JESD216C */
#define kSfdp_Version_Minor_D (8U) /* JESD216D */
这些定义是在哪里看到的,我们可以看到版本差异的一节,可以看到,比如JESD216B定义为5
2、Parameter Header
这里hpm_sdk的sfdp_def同样有定义。
需要解释三个参数。
parameter_id:这是表示当前的flash支持的指定功能prameter table的ID值,比如Basic SPI protocol这个ID值,表示该flash支持24bit地址下常规SPI操作,比如是否支持1-4-4 qspi读取,flash的容量空间多少,擦除扇区指令等等。通过该参数可以知道flash支持的功能。这个参数分为MSB LSB,需要整合一起。
这个参数,可以在标准中找到,同时hpm_sdk中的sfdp_def也同样定义了。
table_length_in_32bit :parameter table的数量,需要注意的是,这是四字节对齐,比如该值为2,那么就需要2*4=8,表示友8个parameter table表。
parameter_table_pointer[3]: 表示读取parameter table的24bit 地址,从这个地址读取所有的parameter table。
3、Parameter table
table表比较多,特别是支持Basic SPI protocol的ID,支持20个字的参数。每个字都有指定的意义。不过hpm_sdk的sfdp同样也做好了定义
三、验证
比如截取3rd parameter table。该table定义了1-4-4(cmd一线,地址和数据四线)读取指令:需要的dummy clock、需要的mode dummy clock,指令值。
那么我们先读取该table值,可以看到:
那么我们翻开操作的flash型号的手册,查找到0xeb指令。也就是在QSPI下的读取指令。从时序图可以看到。
说明sfdp表读取出来的参数跟flas手册对应的指令参数完全一致。