SSI 设备与 SPI 闪存模型

QEMU 的同步串行接口(SSI)总线建模控制器与一个选定外设之间的全双工字传输。 包括 m25p80 在内的大多数 SPI 闪存模型都通过该总线连接控制器。

本页说明 SPI 快速读取空周期处理中,控制器模型与闪存模型的职责边界。 这一边界很重要,因为真实控制器通常通过寄存器配置空周期, 闪存模型却只能看到 ssi_transfer() 传入的字节流。

SSI 传输粒度

ssi_transfer() 传输一个 SSI 字。 实现常见 SPI NOR 命令流的闪存模型,通常一次消费一个 8 位字:

  • 命令操作码;

  • 地址字节;

  • 可选模式或连续读取字节;

  • 空字节;

  • 数据字节。

SSI 核心不建模单独时钟边沿,也不建模活动 SPI 数据线数量。 如果真实事务的空阶段以时钟周期表示, 负责在 SSI 总线上生成传输的设备模型必须将其转换为若干空字节传输。

闪存模型的职责

SPI 闪存模型负责闪存命令语义:

  • 识别哪些操作码;

  • 需要多少地址字节;

  • 命令是否包含模式字节;

  • 返回数据前必须消费多少空字节;

  • 不同厂商快速读取命令的行为差异。

m25p80 的 needed_bytes 是字节数,不得存放原始空周期数。 数据手册按周期描述空阶段时,按该阶段总线宽度转换为字节:

dummy_bytes = DIV_ROUND_UP(dummy_cycles * dummy_bus_width, 8)

m25p80 建模的 SPI NOR 快速读取中,空阶段采用与地址阶段相同的宽度。 例如 DOR、QOR 等仅输出阶段使用双线或四线的读取命令, 其命令、地址和空阶段都使用单线,只有数据阶段使用双线或四线。 DIOR、QIOR 等双 I/O 或四 I/O 命令则在地址和空阶段均使用较宽总线。

如果空阶段不能精确表示为整数次 SSI 字节传输, 模型应向上取整并记录这一限制,而不是默默把周期数当成字节数。

控制器模型的职责

控制器模型负责控制器硬件行为:

  • 客户机可见寄存器如何选择命令、地址宽度、总线宽度和空周期数;

  • 客户机是否在发送 FIFO 中提供空字节;

  • 内存映射、直接读取或其他自动模式是否由控制器生成空阶段;

  • 控制器生成传输前后,片选状态如何改变。

客户机通过发送 FIFO 或手动路径写入空字节时, 控制器应像其他客户机字节一样传给 ssi_transfer(),不应替闪存增加额外空传输。

硬件寄存器要求控制器生成空阶段时, 控制器必须先发送空字节,再让数据传输到达闪存模型。 应按照控制器在空阶段使用的总线宽度转换周期数。例如:

  • 单数据线上的 8 个空周期转换为 1 个空字节;

  • 双数据线上的 8 个空周期转换为 2 个空字节;

  • 四数据线上的 8 个空周期转换为 4 个空字节。

控制器不应复制闪存专用操作码表来猜测哪些命令需要空周期。 自动模式已有足够硬件配置判断是否生成空阶段; 手动模式则以客户机提供的字节流为准。

避免重复计算

闪存看到的每个空字节传输都必须只由一方生成:

  • 客户机通过控制器发送空字节时,由控制器转发、闪存消费。

  • 客户机设置空周期寄存器时,由控制器转换为空字节传输、闪存消费。

  • 闪存虽然知道某命令需要空字节,但不在 SSI 总线上创建传输。

不要在控制器中窥探手动模式操作码流,再根据闪存命令注入额外空传输。 这会把闪存命令语义混入控制器,并在闪存正确处理空字节计数后变得脆弱。

源码中的示例

以下模型体现了这一边界:

  • hw/block/m25p80.c 将快速读取所需空字节数保存在 needed_bytes。 厂商专用辅助函数将手册中的空周期转换为模型预期的字节流。

  • hw/ssi/aspeed_smc.c 根据寄存器为直接快速读取模式生成空字节传输, 但手动用户模式写入按客户机字节直接转发。

  • hw/ssi/npcm7xx_fiu.c 将直接读取的空阶段配置转换为读取数据前发送的空字节数量。

审查清单

添加或修改 SPI 闪存控制器或闪存模型时,应检查:

  • 驱动闪存状态机的空阶段计数是否以字节为单位?

  • 如果寄存器保存周期,转换是否基于空阶段的总线宽度?

  • 客户机手动提供的空字节是否直接转发,没有额外注入?

  • 自动生成的空阶段是否由控制器模型处理?

  • 闪存专用操作码知识是否保留在闪存模型,而没有复制到控制器窥探路径?