SPI NOR - NOR 存储器件
本文将讲述 SoC 搭配 SPI NOR 作为存储设备使用的相关配置,新物料支持等说明。SoC SPIF 控制器功能如下:
SPI Flash 控制器(SPiF)是一个同步的串行通信接口,允许以更少的软件中断进行快速的数据通信。与 SPI 不同,SPiF 通常设计用于更高速的 Flash 设备,并且仅在 Master 模式下工作。
- 支持多种 SPI 模式
- 标准 SPI
- 双输出/双输入 SPI 和双 I/O SPI
- 四输出/四输入 SPI、Quad I/O 和 QPI
- 可编程串行数据帧长度
- 1 位到 32 位
- 支持单速率传输(STR)模式和双速率传输(DTR)模式
- 高速时钟频率
- 150 MHz(STR 模式)
- 100 MHz(DTR 模式)
- 软件写保护
- 通过软件实现对全部或部分存储区的写保护
- 顶部/底部块保护
- 可编程事务间延迟
- 支持模式 0 模式 1 模式 2 和模式 3
- 支持控制信号配置
- 一个片选信号支持多个外设
- 芯片选择(SPIF_CS)和 SPI 时钟(SPIF_CLK)的极性和相位可配置
SPI NOR 新物料适配
未适配 SPI NOR 物料的报错
U-Boot 未支持物料导致烧录失败:

Kernel 未支持物料导致启动失败:

了解新物料的特性
在拿到一款 SPI NOR 物料时,需要仔细阅读其手册。下图是 PY25Q256HB 物料的介绍页,可以看出支持 SPI,Dual SPI,QPI,DTR 模式,支持 4K 擦写。

换一个物料 BY25Q128ES,手册中没有提到 DTR,这款物料不支持 DTR 模式。

在文档后面电器参数章节,可以找到其运行的最高主频,在之后配置中需要小于这里的最高频率,切勿超频运行。

部分 NOR 手册中还会标明其不同模式下支持的速率,可以作为参考

- 在 DTR 模式下默认 80M,读指令10可以支持100M
一般在文档末尾可以找到 SPI NOR 物料的器件 ID,适配新物料时需要重点关注。

- ID 是
0x852019
还可以找到 NOR 物料的内存布局

- 每个存储设备有 32MByte 空间
- 每个块(扇区)有 64K(或者32K)
- 总共有512(或1024)个块(扇区)
一般使用 64K 模式,则 NOR 物料为 64K * 512 = 32MByte
适配 U-Boot 驱动
添加新物料,U-Boot 需要修改文件 brandy/brandy-2.0/u-boot-2018/drivers/mtd/spi/spi-nor-ids.c,U-Boot 使用一个列表存放支持的物料。我们只需要把新增的物料加入列表即可。
可以先找一下类型生产厂家的型号,例如这里找 PUYA 的型号,加上新物料 py25q256hb

这里通过 INFO 宏定义了一个设备的信息,包括设备名称、ID、扇区大小、扇区数量、页面大小和一些设备特性。还有一个 INFO6 宏,如果芯片的 EXT_ID 较多可以使用这个宏。
1. 宏定义部分
#define INFO(_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
INFO_NAME(_name) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
- INFO宏:这个宏用于定义设备的一些基本信息。它接受六个参数:
_name:设备名称。_jedec_id:设备的JEDEC ID。_ext_id:扩展ID。_sector_size:扇区大小。_n_sectors:扇区数量。_flags:标志,通常是 SPI NOR 的一些附加的配置或特性。
2. 宏中的内容
- INFO_NAME(_name):宏调用
INFO_NAME,传递设备的名称_name。 - .id:这部分将
_jedec_id和_ext_id以特定的方式拆分为字节数组。具体拆分方法:- 将
_jedec_id右移16位、8位、0位,分别取出低8位,组成前三个字节。 - 将
_ext_id右移8位、0位,分别取出低8位,组成后两个字节。 结果是将jedec_id和ext_id组合成一个5字节的 ID,通常是一个设备标识符。
- 将
- .id_len:根据
jedec_id和ext_id是否为0来计算 ID 的长度。具体来说:- 如果
jedec_id为0,表示没有有效的 ID,ID 长度为0。 - 否则,ID 长度是3(固定的前3个字节),如果
ext_id非零,则再加2个字节。
- 如果
- .sector_size:设置扇区的大小为
_sector_size。 - .n_sectors:设置设备的扇区数量为
_n_sectors。 - .page_size:设置页面大小为256字节,这个值是固定的。
- .flags: 设置设备的一些标志(如双读、四读、4字节操作码、使用IO模式等)。
3. 使用这个宏的实例
{ INFO("py25q256hb", 0x852019, 0x0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR) }
- "py25q256hb":设备的名称。
- 0x852019:设备的JEDEC ID。
- 0x0:扩展ID,这里是0,表示没有扩展ID。
- 64 * 1024:扇区大小为64KB。
- 512:扇区数量为512个。
- SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR:这些是设备的特性标志。每个标志位的作用如下:
SECT_4K:表示扇区大小是4KB。SPI_NOR_DUAL_READ:支持SPI NOR的双读模式。SPI_NOR_QUAD_READ:支持SPI NOR的四读模式。SPI_NOR_4B_OPCODES:支持4字节操作码,大于128Mib(16MByte)的设备需要配置。USE_IO_MODE:使用IO模式。USE_RX_DTR:使用接收数据传输模式。
完整的标志位及其 含义如下表所示:
| 标志位 | 位掩码 | 描述 |
|---|---|---|
| SECT_4K | BIT(0) | 支持4KB的扇区。 |
| SPI_NOR_NO_ERASE | BIT(1) | 不需要擦除命令。 |
| SST_WRITE | BIT(2) | 使用SST字节编程模式。 |
| SPI_NOR_NO_FR | BIT(3) | 不支持快速读取模式。 |
| SECT_4K_PMC | BIT(4) | 支持4KB扇区PMC操作。 |
| SPI_NOR_DUAL_READ | BIT(5) | 支持双读模式(Dual Read)。 |
| SPI_NOR_QUAD_READ | BIT(6) | 支持四读模式(Quad Read)。 |
| USE_FSR | BIT(7) | 使用标志状态寄存器(Flag Status Register)。 |
| SPI_NOR_HAS_LOCK | BIT(8) | 支持通过状态寄存器(SR)进行锁定/解锁操作。 |
| SPI_NOR_HAS_TB | BIT(9) | 状态寄存器(SR)支持顶部/底部保护(Top/Bottom Protect)。必须与 SPI_NOR_HAS_LOCK 一起使用。 |
| SPI_S3AN | BIT(10) | 支持Xilinx Spartan 3AN系列的系统闪存。该闪存的制造商ID与Atmel闪存相同,因此无法通过制造商ID进行区分。 |
| SPI_NOR_4B_OPCODES | BIT(11) | 使用专用的4字节地址操作码,支持128Mib以上的内存大小。 |
| NO_CHIP_ERASE | BIT(12) | 不支持芯片擦除。 |
| SPI_NOR_SKIP_SFDP | BIT(13) | 跳过解析SFDP表格。 |
| USE_CLSR | BIT(14) | 使用CLSR命令(Clear Status Register)。 |
| SPI_NOR_INDIVIDUAL_LOCK | BIT(16) | 支持单独的块/扇区锁定模式。 |
| SPI_NOR_HAS_LOCK_HANDLE | BIT(17) | 锁定操作支持有锁定句柄。 |
| SPI_NOR_OCTAL_READ | BIT(18) | 支持八通道读取模式(Octal Read)。 |
| USE_IO_MODE | BIT(19) | 支持地址和数据线宽度可变的IO模式。 |
| USE_RX_DTR | BIT(20) | 支持接收数据传输模式(RX DTR)。 |
| USE_TX_DTR | BIT(21) | 支持发送数据传输模式(TX DTR)。 |
| USE_DQS | BIT(22) | 支持DQS模式。 |
| OCTAL_SPINOR | BIT(23) | 支持八通道SPI NOR模式。 |
| SPI_NOR_STACK_DIE | BIT(24) | 支持使用多芯片堆叠(multi-die)操作。 |
适配 Kernel 驱动
添加新物料,Kernel 需要修改文件 bsp/drivers/mtd/spi-nor-5.4/spi-nor.c,Kernel 使用一个列表存放支持的物料。我们只需要把新增的物料加入列表即可。
可以先找一下类型生产厂家的型号,例如这里找 PUYA 的型号,加上新物料 py25q256hb

这里通过 INFO 宏定义了一个设备的信息,包括设备名称、ID、扇区大小、扇区数量、页面大小和一些设备特性。还有一个 INFO6 宏,如果芯片的 EXT_ID 较多可以使用这个宏。
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
- INFO宏:这个宏用于定义设备的一些基本信息。它接受五个参数:
_jedec_id:设备的JEDEC ID。_ext_id:扩展ID。_sector_size:扇区大小。_n_sectors:扇区数量。_flags:标志,通常是 SPI NOR 的一些附加的配置或特性。
2. 宏中的内容
- .id:这部分将
_jedec_id和_ext_id以特定的方式拆分为字节数组。具体拆分方法:- 将
_jedec_id右移16位、8位、0位,分别取出低8位,组成前三个字节。 - 将
_ext_id右移8位、0位,分别取出低8位,组成后两个字节。 结果是将jedec_id和ext_id组合成一个5字节的 ID,通常是一个设备标识符。
- 将
- .id_len:根据
jedec_id和ext_id是否为0来计算 ID 的长度。具体来说:- 如果
jedec_id为0,表示没有有效的 ID,ID 长度为0。 - 否则,ID 长度是3(固定的前3个字节),如果
ext_id非零,则再加2个字节。
- 如果
- .sector_size:设置扇区的大小为
_sector_size。 - .n_sectors:设置设备的扇区数量为
_n_sectors。 - .page_size:设置页面大小为256字节,这个值是固定的。
- .flags:设置设备的一些标志(如双读、四读、4字节操作码、使用IO模式等)。
3. 使用这个宏的实例
INFO(0x852019, 0x0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR)
- 0x852019:设备的JEDEC ID。
- 0x0:扩展ID,这里是0,表示没有扩展ID。
- 64 * 1024:扇区大小为64KB。
- 512:扇区数量为512个。
- SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | USE_IO_MODE | USE_RX_DTR:这些是设备的特性标志。每个标志位的作用如下:
SECT_4K:表示扇区大小是4KB。SPI_NOR_DUAL_READ:支持SPI NOR的双读模式。SPI_NOR_QUAD_READ:支持SPI NOR的四读模式。SPI_NOR_4B_OPCODES:支持4字节操作码,大于128Mib(16MByte)的设备需要配置。USE_IO_MODE:使用IO模式。USE_RX_DTR:使用接收数据传输模式。
完整的标志位及其含义如下表所示:
| 标志位 | 位掩码 | 描述 |
|---|---|---|
| SECT_4K | BIT(0) | 支持4KB的扇区。 |
| SPI_NOR_NO_ERASE | BIT(1) | 不需要擦除命令。 |
| SST_WRITE | BIT(2) | 使用SST字节编程模式。 |
| SPI_NOR_NO_FR | BIT(3) | 不支持快速读取模式。 |
| SECT_4K_PMC | BIT(4) | 支持4KB扇区PMC操作。 |
| SPI_NOR_DUAL_READ | BIT(5) | 支持双读模式(Dual Read)。 |
| SPI_NOR_QUAD_READ | BIT(6) | 支持四读模式(Quad Read)。 |
| USE_FSR | BIT(7) | 使用标志状态寄存器(Flag Status Register)。 |
| SPI_NOR_HAS_LOCK | BIT(8) | 支持通过状态寄存器(SR)进行锁定/解锁操作。 |
| SPI_NOR_HAS_TB | BIT(9) | 状态寄存器(SR)支持顶部/底部保护(Top/Bottom Protect)。必须与 SPI_NOR_HAS_LOCK 一起使用。 |
| SPI_S3AN | BIT(10) | 支持Xilinx Spartan 3AN系列的系统闪存。该闪存的制造商ID与Atmel闪存相同,因此无法通过制造商ID进行区分。 |
| SPI_NOR_4B_OPCODES | BIT(11) | 使用专用的4字节地址操作码,支持128Mib以上的内存大小。 |
| NO_CHIP_ERASE | BIT(12) | 不支持芯片擦除。 |
| SPI_NOR_SKIP_SFDP | BIT(13) | 跳过解析SFDP表格。 |
| USE_CLSR | BIT(14) | 使用CLSR命令(Clear Status Register)。 |
| SPI_NOR_INDIVIDUAL_LOCK | BIT(16) | 支持单独的块/扇区锁定模式。 |
| SPI_NOR_HAS_LOCK_HANDLE | BIT(17) | 锁定操作支持有锁定句柄。 |
| SPI_NOR_OCTAL_READ | BIT(18) | 支持八通道读取模式(Octal Read)。 |
| USE_IO_MODE | BIT(19) | 支持地址和数据线宽度可变的IO模式。 |
| USE_RX_DTR | BIT(20) | 支持接收数据传输模式(RX DTR)。 |
| USE_TX_DTR | BIT(21) | 支持发送数据传输模式(TX DTR)。 |
| USE_DQS | BIT(22) | 支持DQS模式。 |
| OCTAL_SPINOR | BIT(23) | 支持八通道SPI NOR模式。 |
| SPI_NOR_STACK_DIE | BIT(24) | 支持使用多芯片堆叠(multi-die)操作。 |