SoC 中 SRAM 的读写原理与总线时序

SRAM (Static Random Access Memory) 是 SoC 中最常见的片上存储器:CPU 的一级/二级缓存、紧耦合内存 (TCM)、总线 FIFO、各种硬件加速器的缓冲区,本质上都是 SRAM 或由 SRAM 阵列构成的。本文从 6T 单元开始,逐步讲到总线节点上的读写并发,再讨论电压变化对时序的影响,以及芯片如何控制时序。

1. SRAM 存储单元:6T 结构

一个经典 SRAM 单元由 6 个晶体管组成 (6T):

  • 两个交叉耦合的反相器 (P1/N1 与 P2/N2),构成双稳态锁存器,存储 1 bit

  • 两个存取管 (access transistor) N3、N4,栅极接字线 WL,把存储节点接到位线上

        flowchart TB
    VDD1["VDD"] --> P1["P1 (PMOS)"]
    VDD2["VDD"] --> P2["P2 (PMOS)"]
    P1 --> Q["存储节点 Q"]
    P2 --> QB["存储节点 QB"]
    Q --> N1["N1 (NMOS)"]
    QB --> N2["N2 (NMOS)"]
    N1 --> GND1["GND"]
    N2 --> GND2["GND"]
    BL["BL 位线"] --- N3["N3 存取管"]
    N3 --- Q
    BLB["BLB 位线(反相)"] --- N4["N4 存取管"]
    N4 --- QB
    WL["WL 字线"] -.->|"栅极"| N3
    WL -.->|"栅极"| N4
    

为什么叫"静态":只要不掉电,交叉耦合反相器的正反馈让 Q/QB 永远稳定在互补的两个电平上,不需要像 DRAM 那样周期性刷新。

假设 Q=1 (VDD)、QB=0 (GND),那么 N1 导通、P2 导通,Q 被 P1 拉高、QB 被 N2 拉低,状态自持。

2. 读操作:预充、放电与灵敏放大

SRAM 的读不是"直接把存储节点的电平送出去",而是通过位线放电 + 灵敏放大完成,分四步:

第一步:预充 (Precharge)。 预充管把两条位线 BL、BLB 都充到 VDD,再断开,位线处于"悬空但带满电荷"的高阻状态。

第二步:字线打开。 WL 拉高,N3/N4 导通,存储节点与位线连通。

第三步:位线差分放电。 假设单元存 Q=1、QB=0:

  • QB 侧:QB 是 GND,位线 BLB 是 VDD → 电流从 BLB 经 N4、N2 流向地,BLB 电压开始缓慢下降

  • Q 侧:Q 是 VDD,BL 也是 VDD → 无压差,不放电

位线电容很大 (一条位线挂几百个单元),而单元电流很小,所以 BLB 只是"缓慢下滑",产生一个 ΔV ≈ 100 mV 左右的微小差分。

第四步:灵敏放大 (Sense Amplifier)。 等 ΔV 足够大,灵敏放大器使能信号 SAE 打开,差分放大器把 100 mV 的小信号迅速放大到全摆幅,送到数据总线上。然后位线重新预充,准备下一次访问。

时钟            ___|‾‾‾|___|‾‾‾|___|‾‾‾|___
预充 PC         ‾‾‾|_______|‾‾‾‾‾‾‾‾‾|‾‾
WL 字线         ____|‾‾‾‾‾‾‾|__________
BLB (读"0"侧)   ~~~~~‾‾‾‾‾‾‾‾\___________   ← 从 VDD 开始缓慢放电
                                \
                                 ΔV ≈ 100mV
SAE 灵敏放大    ____________|‾‾‾‾|________   ← 检测到 ΔV 后打开
数据输出        ________________|‾‾‾|______   ← 全摆幅输出

两个关键细节:

  • 读是破坏性扰动。 放电电流流过 N4/N2 时,QB 节点电位会被轻微抬高 (read disturb)。如果抬高超过单元的静态噪声容限 SNM (Static Noise Margin),单元会意外翻转——读一次就把数据读坏了。所以单元里下拉管 N1/N2 必须比存取管 N3/N4 强,两者宽长比 (cell ratio, β ratio) 一般取 1.5~2,保证扰动被下拉管"按住"。

  • 自定时 (self-timed)。 SAE 什么时候打开?不是靠设计者定一个固定延迟,而是放一条复本位线 (replica bitline):模拟最坏情况下位线放电的速度。电压低、温度高时,复本位线放电同样变慢,SAE 自动后移——这个时机天然跟踪 PVT (工艺/电压/温度) 变化,这是 SRAM 内部"控制时间"的核心手段。

3. 写操作:强驱动压过锁存器

写操作则相反,要主动破坏锁存器的状态:

  1. 写驱动 (write driver) 把 BL 拉到 0、BLB 拉到 1 (假设要写 0 到 Q)

  2. WL 拉高,N3 导通

  3. 此时 Q=VDD,P1 正在把 Q 往上拉,而写驱动通过 N3 把 Q 往下拽。写驱动是"大管子",驱动能力远强于单元内部的 PMOS——Q 被强行拉低

  4. Q 一旦跌破反相器 1 的翻转阈值,正反馈立刻接管:QB 被推高 → 又反过来拉低 Q → 翻转完成,锁存器锁住新值

要保证"压得过",存取管必须比单元内的上拉 PMOS 强 (pull-up ratio, γ ratio)。如果写驱动压不过 P1,就会写失败——这就是写裕量 (write margin)。

读和写对单元的要求是对立的:读要求存取管弱 (防止扰动翻转),写要求存取管强 (压过 PMOS)。6T 单元的两个管子尺寸比例就是在这两个约束之间取平衡点。

4. 总线视角:AXI 通道与 SRAM 控制器

在 SoC 里,CPU 不会直接操作 6T 单元,而是通过总线访问 SRAM。最常用的是 AMBA AXI 总线,它有 5 个独立通道:

通道

方向

作用

AW

主 → 从

写地址

W

主 → 从

写数据

B

从 → 主

写响应

AR

主 → 从

读地址

R

从 → 主

读数据

关键点:读通路 (AR/R) 和写通路 (AW/W/B) 是物理上完全独立的导线,没有共享数据总线。所以"节点一边从上游收读数据、一边向下游发写数据"在物理层就没有冲突——这就是全双工。

        flowchart LR
    M["Master 主设备"]
    N["总线节点\n(互连/桥/SRAM控制器)"]
    S["SRAM 从设备"]
    M -->|"AR: 读地址"| N
    N -->|"AR: 读地址"| S
    S -->|"R: 读数据"| N
    N -->|"R: 读数据"| M
    M -->|"AW/W: 写地址+写数据"| N
    N -->|"AW/W: 写地址+写数据"| S
    S -->|"B: 写响应"| N
    N -->|"B: 写响应"| M
    

每个通道用 VALID/READY 握手:发送方拉高 VALID 表示"我这拍的数据有效",接收方拉高 READY 表示"我能收"。只有 VALID 和 READY 同时为高的那一拍,数据才算被采样。这是数据正确性的第一道防线——数据线电平本身不能说明数据有效,握手信号才能。

注意:SRAM 阵列本身通常是单端口的 (同一时刻要么读要么写),所以控制器的仲裁逻辑会把同时到达的读写请求分时排队。阵列之外的"同时收发"靠的是通道分离 + 控制器内的寄存器缓冲。

5. 总线节点"边读边写":寄存器隔离与流水线

这是最容易困惑的地方:节点 (比如一个总线桥,或者 SRAM 控制器本身) 在同一个时钟周期里,既要从上游采样数据,又要向下游驱动数据,两条数据流为什么不会串?

答案是:节点内部用两级物理独立的寄存器把"收"和"发"隔开——输入寄存器负责从上游捕获,输出寄存器负责向下游驱动,而输出寄存器里放的是上一拍 (甚至更早) 捕获的数据。

        flowchart LR
    U["上游"] -->|"本拍数据"| IR["输入寄存器\n(捕获上游)"]
    IR -->|"上一拍数据"| OR["输出寄存器\n(驱动下游)"]
    OR -->|"本拍输出"| D["下游"]
    CLK["时钟"] --> IR
    CLK --> OR
    

时序上看:

周期              N-1        N         N+1
时钟          ____|‾‾‾‾|____|‾‾‾‾|____|‾‾‾‾|____
上游送出         < A >     < B >     < C >
输入寄存器      [ A ]→    [ B ]→     [ C ]→    ← 每个上升沿捕获上游
输出寄存器      [ Z ]→    [ A ]→     [ B ]→    ← 每个上升沿更新为"上一拍"的输入
下游看到         < Z >     < A >      < B >

第 N 拍的时钟沿上,节点同时做了两件事:

  • 输入寄存器把上游的 B 关进去 (读上游)

  • 输出寄存器把上一拍捕获的 A 锁出来驱动下游 (写下游)

下游在第 N 拍看到的是 A——上游第 N-1 拍的数据。上游第 N 拍送出的 B,最早也要第 N+1 拍才会出现在下游。数据经过节点必然"停留"至少一个时钟周期,这就是流水线的一级 (register slice / pipeline cut)。

换句话说,"边读边写"不是同一份数据在同一拍从上游流到下游,而是两拍数据在节点内部接力:拍 N 收 B、发 A;拍 N+1 收 C、发 B。输入寄存器和输出寄存器是两组物理上不同的触发器,时钟沿同时触发时各干各的,数据线互不连接,所以不存在串扰的物理通路。

对比一下:如果节点不做寄存 (纯组合逻辑直连),下游确实会同拍看到上游数据——但那样就完全没有隔离和缓冲了,长路径时序难以收敛,所以真正的总线节点都会插寄存器。

6. 会不会"写错数据"?——数据归属如何保证

针对"先读了上游数据,写的时候会不会把上游数据写出去/写进自己"的疑问,结论分两层:

正常设计下:不会。 有三道防线保证"写的数据一定是自己的数据":

  1. 通道分离:读数据走 R 通道、写数据走 W 通道,物理导线不同,读回来的数据根本进不了写数据通路

  2. 寄存器隔离:如上一节时序图,下游第 N 拍收到的值最晚在第 N-1 拍的时钟沿之前就锁定了;第 N 拍上游才送来的新数据,不可能在第 N 拍就出现在下游。时序上"读上游"永远晚于"写下游"一拍,写出去的永远是早先已锁存的数据

  3. 握手保护:写数据只有在 VALID && READY 同时为高的拍才被打入写数据寄存器;SRAM 阵列的写端口只连接写数据寄存器,读数据寄存器只从阵列接收。两条通路在寄存器层面物理隔离

以 SRAM 控制器为例:第 N 拍,读数据寄存器 (RDR) 里是从阵列读出的数据 (自己存储的数据),经 R 通道发往下游;同时写数据寄存器 (WDR) 捕获上游 W 通道送来的写数据。第 N+1 拍阵列执行写操作时,写端口接的是 WDR——阵列永远不会把 RDR 的内容写进存储单元,除非设计本身连错了线。

真正可能出问题的场景,都是时序违例,而不是设计意图:

  • 保持时间 (hold) 违例 → 寄存器"透明化":如果数据在时钟沿之后的保持窗口内就发生变化 (数据路径太快,或者时钟偏斜太大),寄存器来不及把旧值锁死,新数据会在同一拍穿透。这正好会表现为"刚读到的上游数据在错误的一拍被写下去"。hold 违例在快工艺角 (高电压、低温、快硅) 下最危险,而且降频解决不了 hold 问题,只能插缓冲器修。

  • 跨时钟域 (CDC) 没有同步器 → 亚稳态:如果上游和节点时钟不同源,采样时刻恰好落在数据跳变沿上,触发器进入亚稳态,采到既非 0 也非 1 的中间值,甚至不同触发器采出互相矛盾的值。解决手段是两级触发器同步器或异步 FIFO。

  • 控制信号出错:写使能 (WEN) 或地址译码出错会把数据写进错误的单元。这与数据来源无关,属于功能错误,ECC 可以检测但不能预防。

所以总结:只要 setup/hold 收敛、跨时钟域有同步器,就不会出现"写了上游数据而不是自身数据"——寄存器隔离在时序上把"读上游"和"写下游"严格错开了一拍。

7. 电压变化对时间差的影响

门的传播延迟近似服从 α 幂律模型 (Sakurai-Newton):

        C_load · VDD
t_d ∝ ─────────────── ≈ VDD / (VDD - Vth)^α
          I_drain

VDD 越高,驱动电流越大,延迟越小;VDD 越低,延迟越大,而且越接近阈值电压 Vth,延迟增长得越陡:

延迟
 ↑
 |                        *
 |                     *
 |                 *
 |             *
 |         *
 |     *
 |  *
 +─────────────────────────→ VDD
      Vth              Vnom

对 SRAM 读周期的影响尤其明显:读电流 ∝ (VDD−Vth)^α,低压下位线放电变慢,从字线打开到 SAE 使能的时间差显著拉长——只不过自定时的复本位线会自动跟着变慢,把 SAE 时机同步后移,抵消了大部分影响。这正是自定时设计在低压场景下的价值。

标准 PVT 工艺角如下:

工艺角

工艺

电压

温度

最坏影响

SS

慢

-10%

高温

setup 违例 (数据来得太晚)

FF

快

+10%

低温

hold 违例 (数据来得太早)

TT

典型

标称

25°C

参考基准

注意温度效应在现代深亚微米工艺里是高温更慢。

另外要区分静态和动态:

  • 静态电压设定 (DVFS 档位) 决定整个芯片的平均速度

  • 动态电压跌落 (IR drop / di/dt 噪声):大量单元同时翻转瞬间把电源拉低几十到上百 mV,局部电路瞬时变慢,可能造成本来收敛的 setup 路径在那一瞬间违例。这就是芯片要留电压裕量和时序裕量的原因

8. 芯片如何控制时序

芯片控制"时间"的机制可以分成五层:

第一层:同步设计 + 静态时序分析 (STA)。 所有时序都以时钟沿为基准,STA 工具用 SDC 约束检查每一条寄存器到寄存器的路径是否满足 setup/hold,并按工艺角留裕量 (OCV/AOCV/POCV 补偿片上变异)。收敛的标准不是"仿真过了",而是"所有角都过了"。

第二层:时钟网络。 PLL (锁相环) 从外部晶振倍频出芯片主时钟,分频器产生各模块时钟,时钟树综合 (CTS) 把时钟均衡地送到每个触发器——skew (时钟到达不同触发器的时间差) 直接影响 hold 裕量。所以 hold 违例的修法是调整时钟树/插缓冲,而不是降频。

第三层:SRAM 内部自定时。 前面说的复本位线:SAE、预充、字线脉冲都由内部模拟路径产生,天然跟踪 PVT,不需要外部精确计时。

第四层:DVFS (动态电压频率调节)。 电压决定速度上限,频率决定工作节奏,两者必须协调:

        flowchart LR
    A["当前工作点 (V, f)"] -->|"升频: 先升电压"| B["V↑ (PMIC/LDO)"]
    B -->|"电压稳定后"| C["f↑ (PLL)"]
    C --> D["新工作点 (V', f')"]
    A -->|"降频: 先降频率"| E["f↓"]
    E -->|"频率稳定后"| F["V↓"]
    F --> D
    

原则是:升频时电压先行 (频率永远不能超过当前电压支撑的上限),降频时频率先行 (电压永远不能低于当前频率所需)。顺序反了就会在过渡期出现 setup 违例。

第五层:AVS (自适应电压调节)。 用片内环形振荡器、canary 电路甚至 Razor 触发器实时监测硅片的实际速度,把电压动态调到"刚好能满足时序"的最低值,省电且抗老化。低压下电压裕量小,这种闭环调节比固定档位更有效。

9. 总结

  • SRAM 读 = 预充 → 字线开 → 位线差分放电 → 灵敏放大;写 = 写驱动强压锁存器翻转。β ratio 保读稳定,γ ratio 保写成功

  • 总线节点"边读边写"靠通道分离 (全双工) + 寄存器隔离 (流水线):本拍写的永远是上一拍已锁存的数据,读上游和写下游在时序上天然错开一拍

  • "写错数据"正常不会发生;唯一接近的失效模式是 hold 违例导致寄存器同拍穿透,以及 CDC 亚稳态——都是时序问题,不是设计问题

  • 电压下降 → 延迟按 (VDD−Vth)^α 规律拉长,SRAM 读放电时间显著变长;自定时复本位线自动补偿

  • 芯片靠 STA 收敛 + PLL/时钟树 + SRAM 自定时 + DVFS (升压先于升频、降频先于降压) + AVS 闭环,五层机制共同控制时序

参考资料

  • Neil Weste, David Harris, CMOS VLSI Design: A Circuits and Systems Perspective (第 4 版),第 12 章 (Array Subsystems)

  • Jan Rabaey, Digital Integrated Circuits: A Design Perspective,第 12 章

  • Sakurai & Newton, Alpha-Power Law MOSFET Model and its Applications to CMOS Inverter Delay and Other Formulas, IEEE JSSC 1990

  • ARM, AMBA AXI and ACE Protocol Specification

  • Seevinck et al., Static-Noise Margin Analysis of MOS SRAM Cells, IEEE JSSC 1987