PSRAM 软件设计规范
编制日期 2026-09-23 | 版本号 V1.0
面向 XTX PSRAM(伪静态随机存储器)系列(SPI / Quad SPI / OPI x8 DDR / HyperBus)的固件驱动设计约定 —— 覆盖 器件识别与模式配置、读延迟(dummy / latency)、写立即完成(无 WREN / 无忙等)、 CS# 刷新窗口(tCEM)、低功耗状态机与 XIP 内存映射,逐条给出可落地的命令序列、延迟预算与分片准则。
| 文档编号 | PSRAM-SW-SPEC-001 |
|---|---|
| 版本 / 状态 | V1.0 · 正式发布 |
| 适用器件 | XTX PSRAM 系列(Quad SPI 64/128 Mb 与 OPI x8 DDR 128/256 Mb),并兼容 AP Memory / ISSI / Winbond 同类命令集 |
| 参考标准 | PSRAM_电路设计指南.html · HyperBus™ / HyperRAM™ 协议规范 · 目标器件 datasheet |
| 配套文档 | PSRAM_电路设计指南.html(硬件 / 原理图 / 布线约束) |
0. 目的与范围
本规范约束 PSRAM 器件在嵌入式固件中的软件行为:命令序列、延迟配置、刷新窗口管理、低功耗状态切换、XIP 映射与异常恢复。凡本文与器件 datasheet 冲突,以 datasheet 为准。
- 统一命令序列:读(03h/0Bh/6Bh/EBh)、写(02h/38h)、复位(66h→99h)、模式配置,各自固化为一套标准流程。
- 统一延迟判据:所有读都必须按接口频率配置 dummy / initial latency,频率变了延迟就要重配,不能写死。
- 统一刷新窗口:任何单笔事务的
CS#低电平时间不得超过 tCEM(典型 4 µs),长事务必须分片,保证内部自刷新有窗口。 - 统一低功耗语义:明确 Active / Standby / Deep Sleep / DPD 四态的进入命令、退出代价与数据保持性,尤其 DPD 唤醒后须重初始化。
一句话结论:PSRAM 是「带内部刷新的 SRAM」 ——
所有写都是立即完成的(没有 WREN、没有忙标志、没有擦除);所有读都要先熬过一段延迟(dummy / initial latency)才能拿到数据;
而它最容易被忽略的死穴是 CS# 不能一直拉低——超过 tCEM 会阻断内部刷新,高温下数据悄悄丢失;把 NOR 的 wait_busy() 搬过来会死等;把 PSRAM 当真正 SRAM 用会掉数据。
文档使用说明
- 本文是《PSRAM 电路设计指南》的软件配套篇:硬件篇解决「怎么接、怎么供电、怎么走线」,本文解决「驱动怎么写、延迟怎么配、刷新窗口怎么留、低功耗怎么切」。
- 命令码、寄存器位、时序值以典型 Quad SPI / OPI PSRAM 命令集与《PSRAM 电路设计指南》给出,作为 XTX PSRAM 驱动开发的基线框架;具体器件(型号、MID、模式寄存器命令字、延迟/突发位定义)必须对照对应 datasheet 核实后填入。
- 文中 C 代码为协议骨架伪代码,演示流程与判断条件,非特定平台可直接编译的完整实现;
spi_transfer()、now_us()等为平台相关底层。
① 任何单笔事务 CS# 低电平时间必须 ≤ tCEM——典型 4 µs,长读长写必须分片,这是数据不丢的前提;
② 读延迟必须按当前频率配置——频率升一档,dummy / latency 往往要多配 1~2 个周期,写死值会在高频下读出错位数据;
③ PSRAM 写无忙等、无 WREN——照搬 NOR 的 wait_busy() 会死等,残留的 06h 会被当成未知命令。
1 · 器件定位与存储组织
PSRAM 不是「能字节写的 Flash」,也不是「完全不用管的 SRAM」。它是 DRAM 内核 + 片内自刷新控制器,对外呈现 SRAM 式线性寻址。软件设计的第一件事,是把「线性地址空间」和「刷新窗口」这两个概念刻进脑子里。
1.1 器件定位与关键参数
数据取自《PSRAM 电路设计指南》第 1~2 章器件参数与第 5 章刷新/延迟;具体数值以目标器件 datasheet 为准。
| 项目 | Quad SPI PSRAM | OPI x8 DDR PSRAM | 软件影响 |
|---|---|---|---|
| 容量 / 结构 | 8/16/32/64/128 MB 线性 | 16/32/64/128 MB 线性 | 地址位宽随容量变(23~28 bit) |
| 命令模型 | cmd + addr + dummy + data | cmd(1~2 CK) + addr(4 CK) + latency + data | 命令解析逻辑不同 |
| 读延迟 | dummy cycles(4~10,随频率) | initial latency 6~8 CK(可配) | 延迟必须按频率配置 |
| 峰值带宽 | ~133 MB/s @ 133 MHz | ~400 MB/s @ 200 MHz DDR | 高速场景优先 OPI |
| 选通 | 无(SCK 采样) | RWDS / DQS | OPI 需读训练 |
| 写使能 | 多数无需 WREN | 多数无需 WREN | 残留 NOR WREN 逻辑是 bug |
| 刷新窗口 | tCEM ≤ 4 µs | tCEM ≤ 4 µs | 所有接口通用硬约束 |
1.2 地址空间与线性映射
PSRAM 内部是单一连续的线性地址空间,没有「页/块/扇区」层级,也没有「cache 寄存器」中间缓冲。软件寻址时只需要一个地址计数器,从 0x000000 到 容量-1,读写都按字节(或按总线位宽的字)推进:
| 容量 | 地址位宽 | 地址范围(示例) | 地址字节数 |
|---|---|---|---|
| 8 MB (64 Mb) | 23 bit | 0x000000 ~ 0x7FFFFF | 3 B |
| 16 MB (128 Mb) | 24 bit | 0x000000 ~ 0xFFFFFF | 3 B |
| 32 MB (256 Mb) | 25 bit | 0x000000 ~ 0x1FFFFFF | 3 / 4 B |
| 64 MB (512 Mb) | 26 bit | 0x000000 ~ 0x3FFFFFF | 4 B(OPI/HyperBus) |
Quad SPI 命令通常只能带 3 字节地址(覆盖到 16 MB)。当容量 > 16 MB(地址超过 24 bit)时,要么器件支持「4 字节地址模式」命令,要么改用 OPI / HyperBus(天然带 4 字节地址)。用 3 字节地址去访问 32 MB 器件,高地址会回卷到低位,表现为「写进 A 却从 B 读回」。
1.3 接口模式:SPI / Quad / OPI / HyperBus
| 维度 | SPI / Quad SPI | OPI x8 DDR | HyperBus(HyperRAM) | 并行 ADMUX |
|---|---|---|---|---|
| 线数 | 1 / 2 / 4 | 8(DDR) | 8(DDR)+ RWDS | ADQ[15:0]+控制 |
| 命令/地址 | 串行命令字节 + 地址 | 命令 1~2 CK + 地址 4 CK | 48 bit CA(6 B / 3 CK) | 地址锁存 |
| 延迟 | dummy cycles | initial latency 6~8 CK | CR0 配 3~7 CK | 固定等待状态 |
| 选通 | 无 | RWDS / DQS | RWDS | 无 / WAIT# |
| 主机 IP | 普通 QSPI | OPI 控制器(含 Cache) | HyperBus 控制器 | FSMC / EBI |
| 上电默认 | SPI 单线 | SPI 单线,需切 | HyperBus 模式 | 并行时序 |
OPI PSRAM 上电后默认处于 SPI 单线模式,必须先用低速 SPI 完成识别与模式寄存器配置,再切换到高速模式。切模式命令与切换后的首条命令之间要留足 tCPH;切换后第一笔访问往往还要按新延迟重新训练。注意:HyperRAM(HyperBus)上电即进入 HyperBus 模式,没有 SPI 回退,不能走「先 SPI 配置再切换」流程。
1.4 与 SPI NOR / SDRAM 的关键差异(移植必读)
| 维度 | SPI NOR | PSRAM(本文) | SDRAM / DDR | 移植后果 |
|---|---|---|---|---|
| 读 | 发地址 + dummy 出数据 | 发地址 + 延迟出数据 | 行激活 + 列读 | 延迟机制相同,数值不同 |
| 写 | 06h + 02h + 等忙 | 直接写,立即完成,无 WREN | 激活 + 写突发 | 照搬 WREN/等忙会死等或报错 |
| 擦除 | 必须(扇区/块) | 无 | 预充电/刷新 | 不需要、也不存在擦除命令 |
| 刷新 | 无 | 片内自刷新,主机须留 CS# 窗口 | 主机管理刷新 | PSRAM 自刷新但占用 CS# 高区间 |
| 数据保持 | 掉电保持 | 掉电即失 | 需持续供电刷新 | 不能当代码存储用 |
| ECC | 无 | 无(靠训练/校验) | 无 | 无 ECC 判读 |
| CS# 约束 | 可长期拉低 | ≤ tCEM(4 µs) | 按命令 | 当 SRAM 长拉低会丢数据 |
① 把 PSRAM 当 SRAM:CS# 一直拉低做长 DMA,超过 tCEM 后高温下数据悄悄损坏;
② 照搬 NOR 的 wait_busy():PSRAM 没有忙标志,轮询一个不存在的状态位会死等;
③ 写前发 06h:残留的 WREN 逻辑会被 PSRAM 当未知命令,可能进入异常状态。
2 · 术语与缩写
本章统一术语口径,避免「延迟/突发/刷新窗口」在文档与代码里指的不是同一件事。
2.1 结构与命令术语
| 缩写 / 术语 | 全称 | 本文中的含义 |
|---|---|---|
| PSRAM | Pseudo Static RAM | 伪静态随机存储器:DRAM 内核 + 片内自刷新控制器,对外呈 SRAM 接口 |
| OPI | Octal Peripheral Interface | 8 线外设接口,时钟双沿(DDR)传数,200 MHz 下达 400 MB/s |
| HyperBus | HyperBus™ (HyperRAM) | Cypress/Infineon 定义的 x8 DDR 总线,48 bit CA 包 + RWDS |
| Latency | Initial Latency | 读命令发出到首字节有效之间的器件内部取数周期(OPI/HyperBus 可配) |
| Dummy | Dummy Cycles | SPI/Quad 读命令中地址后插入的空周期,等效于 Latency |
| tCEM | CS# Low Max Time | CS# 最长低电平时间(典型 4 µs),硬约束,超时阻断内部刷新 |
| tCPH | CS# High Min Time | 两次事务间 CS# 最短高电平(15~22 ns),刷新窗口也在此区间 |
| RWDS | Read/Write Data Strobe | OPI/HyperBus 双向随路选通;读时指示可变延迟与刷新冲突 |
| Burst | Burst Length | 一次命令连续读写的字节数(16/32/64/128 B),可线性或回卷 |
| Wrap | Wrapped Burst | 突发到行/页边界回卷,XIP Cache 行填充常用 |
| DPD | Deep Power-Down | 最低功耗态,多数型号不保证数据保持,唤醒须重初始化 |
| Self-Refresh | 片内自刷新 | 内部控制器周期性刷新 DRAM 阵列,对主机透明但占用刷新窗口 |
| XIP | eXecute-In-Place | 代码直接在外扩 PSRAM 上取指执行,无需拷进内部 SRAM |
| CA | Command-Address | HyperBus 的 48 bit 命令-地址包(6 字节 / 3 CK DDR) |
2.2 本文书写约定
- 命令码用小写十六进制带后缀
h书写(如0xEB),与 datasheet 保持一致;本文统一用0x前缀。 - 寄存器位写作
CR0[2]形式,方括号内为 bit 位序(bit0 为最低位)。 - 时序符号沿用 datasheet:tCEM(CS# 低上限)、tCPH(CS# 高下限)、tVSL(上电稳定)、tRST(复位恢复)。「典型值 / 最大值」成对给出时,软件分片与超时一律按最大值(最严)预算。
- 延迟符号:
Latency/Dummy表示读前空周期数,数值随接口与频率变化,必须参数化。 - 代码示例为 C 伪代码,省略 SPI/QSPI/OPI 传输底层实现,只表达命令序列、延迟与分片判据。
3 · 驱动架构与初始化
PSRAM 的驱动比 NOR 少两层(没有坏块、没有 ECC 判读),但多一层刷新窗口管理和一层延迟配置。这两层放错位置,后面所有优化都是白做。
3.1 分层职责
| 层 | 职责 | 禁止做的事 |
|---|---|---|
| 应用层 | 业务调用 read/write/memcpy;不感知接口 | 直接拼 SPI 命令、直接算 tCEM 分片 |
| 存储管理层 | CS# 分片、低功耗调度、XIP 映射、刷新窗口保证 | 单笔事务 CS# 低 > tCEM |
| 命令协议层 | 03h/0Bh/6Bh/EBh 读、02h/38h 写、66h/99h 复位 | 省略延迟配置、残留 WREN |
| 硬件抽象层 | SPI/QSPI/OPI 传输、CS# 时序、时钟频率 | 在事务中途切换时钟频率 |
3.2 上电初始化流程
上电后器件处于SPI 单线默认模式,延迟取默认值,且数据未定义(上电瞬间 RAM 内容随机)。初始化必须按下列顺序执行:
/* 上电初始化:返回 0 表示成功 */
int psram_init(void)
{
uint8_t id[3];
delay_ms(3); /* 上电稳定余量 ≥1 ms;器件 tPU 典型 150~500 µs */
psram_reset(); /* 66h -> 99h,复位到已知态 */
if (psram_read_id(id) != 0) return -E_NODEV; /* 9Fh */
if (id[0] != XTX_MID) return -E_NODEV; /* 厂商 ID 不匹配 */
/* 进入目标接口模式(以 datasheet 命令字为准) */
psram_enter_quad_mode(); /* 典型 0x35;OPI 则发 OPI 切换命令 */
/* 配置模式/配置寄存器:突发长度、驱动强度、延迟、刷新范围 */
psram_wr_mode_reg(PSR_MREG_ALL);
/* OPI/HyperBus 需读训练;SPI/Quad 跳过 */
if (iface_is_high_speed())
psram_read_training();
/* 自检:写已知 pattern 再读回比对 */
if (psram_self_test() != 0) return -E_SELFTEST;
return 0;
}
① 上电先软件复位。很多「PSRAM 第一次读出来是 FF、第二次才正常」的问题,根因是上电后器件停留在某个不确定状态(或上一会话残留的 Quad/OPI 模式),第一条命令未被正确解析。复位到已知态能避开绝大多数「玄学」首读失败。
② 切模式后第一笔访问要按新延迟重新训练。OPI/HyperBus 在切到高速模式后,延迟周期与采样窗口都变了,仍用 SPI 时代的延迟/采样参数会读出错位数据。
3.3 器件识别:ID 与配置寄存器
- 9Fh 读 ID(厂商 + 容量/接口):拿到 MID 判定厂商,容量码决定地址位宽与最大地址。这是最快的筛选路径,用于初始化早期排除「没焊上 / 型号不对」。
- 读配置/模式寄存器(CR0/CR1 或 MR):回读当前突发长度、延迟、驱动强度、刷新范围,确认器件实际配置与驱动期望一致;不一致则写配置对齐。
- 自检回读:在一段已知地址写固定 pattern(如 0xAA/0x55 交替),再读回比对。校验不过说明接口模式/延迟/采样配置错,应降级频率重试而非继续初始化。
如果固件要兼容多家器件的同一份驱动,应以读回的配置寄存器实际值为准:它给出真实的突发长度与延迟能力,可避免把「128 Mb 器件按 64 Mb 地址空间访问」这类错误。只读 ID + 硬编码参数的方案,换料就要改代码。
4 · 配置寄存器与模式寄存器体系
PSRAM 没有 NOR 式「状态寄存器 + 配置寄存器」的标准划分,但高速型号(OPI/HyperBus)普遍通过配置寄存器(CR0/CR1)或模式寄存器(MR)来控制延迟、突发、驱动强度与刷新。理解这些寄存器,就理解了整颗器件的可调行为。
4.1 寄存器总览
| 寄存器 | 名称 | 关键位 | 上电默认 | 是否易失 |
|---|---|---|---|---|
| MR / CR0 | 模式 / 配置寄存器 0 | 延迟 (Latency)、固定/可变延迟、突发类型、驱动强度、深度休眠使能 | 延迟取默认、突发取默认 | 多为易失(写后即生效) |
| CR1 | 配置寄存器 1 | 温度补偿刷新 TCSR、刷新范围、半/混合休眠 | 全阵列刷新 | 多为易失 |
| RDID | Read ID 寄存器 | 厂商 / 容量 / 接口 | 固定 | 只读 |
| — | (写保护) | 多数 PSRAM 无块保护位 | — | — |
Quad SPI PSRAM 的「模式寄存器」命令字与位定义,OPI 的 CR0/CR1 地址,HyperBus 的 0x000800/0x000801,三家可能完全不同。本规范的位定义是典型示例,写入前必须对照目标 datasheet 的寄存器表逐位核对。驱动里应把「寄存器地址 + 位定义」集中到一张器件表里。
4.2 模式寄存器(进入 Quad / OPI)
接口模式切换是 PSRAM 初始化最关键的一步,常见两种机制:
| 机制 | 命令 / 做法 | 说明 | 软件约定 |
|---|---|---|---|
| Enter Quad Mode | 0x35 | 进入 Quad 模式(部分型号靠 0xEB 自动进入) | 切后所有地址/数据走 4 线 |
| OPI 切换 | 厂商定义切换命令 | SPI 单线 → OPI x8 DDR | 切后留 tCPH,第一笔重训练 |
| HyperBus 模式 | 上电即 HyperBus / 或切换 | 48 bit CA 包 | 控制器须发 CA 而非 SPI 命令 |
切换命令发出后,器件需要一点时间稳定到新接口;若立即发第一笔高速访问,可能落在切换过渡期,导致命令被当旧模式解析。切模式后先 CS# 拉高一段(≥ tCPH,建议多留余量),再开始正常访问。
4.3 延迟 / 突发配置位
| 位域 | 名称 | 作用 | 软件约定 |
|---|---|---|---|
| Latency[2:0] | 初始延迟 | 读前空周期数(3~7 CK) | 按当前频率选,频率升则增多 |
| Fixed/Var | 固定/可变延迟 | 可变延迟下 RWDS 可追加 1 CK | 推荐固定延迟(时序确定) |
| Burst[1:0] | 突发长度 | 16/32/64/128 B,线性或回卷 | XIP 用回卷匹配 Cache 行 |
| Wrap | 回卷使能 | 突发到页边界回卷 | XIP 取指常用 |
4.4 驱动强度与刷新配置位
| 位域 | 名称 | 作用 | 软件约定 |
|---|---|---|---|
| DS[1:0] | 驱动强度 | 25/50/75/100% 输出驱动 | 高速/长走线加驱动;先训练再定 |
| TCSR | 温度补偿刷新 | 高温下刷新率加倍 | 使能(默认多已开) |
| Refresh Range | 刷新范围 | 全阵列 / 半阵列 | 全阵列(数据完整优先) |
| Deep Sleep EN | 深度休眠使能 | 允许进入 DPD | 低功耗产品才置位 |
调整驱动强度是信号完整性手段而非性能手段:高速读出错时,先训练延迟链,再决定是否加驱动或降频。
4.5 配置写入准则
/* 配置寄存器写:读-改-写-校验(以目标寄存器地址/命令为准) */
int psram_wr_cfg(uint8_t reg, uint8_t mask, uint8_t val)
{
uint8_t cur = psram_rd_cfg(reg); /* ① 读原值 */
uint8_t next = (cur & ~mask) | (val & mask); /* ② 只改目标位 */
if (next == cur) return 0; /* 无需写 */
psram_wr_cfg_raw(reg, next); /* 下发 */
if (psram_rd_cfg(reg) != next) /* ③ 回读校验 */
return -E_VERIFY;
return 0;
}