第七十 芯片
芯片(Chip),学名集成电路(Integrated Circuit, IC)。它是把成千上万乃至万亿个晶体管、电阻、连线,用微米乃至纳米级的工艺“雕刻“在一小片硅上的产物。指甲盖大小,却承载着整个信息社会的算力:手机里的 SoC、服务器里的 CPU/GPU、内存条上的 DRAM、固态硬盘里的 NAND、汽车里的 MCU、耳机里的蓝牙芯片……本章沿着“沙子 → 晶圆 → 晶体管 → 电路 → 芯片 → 系统“这条链条,讲清楚芯片是什么、怎么做出来、怎么分类、怎么设计,以及软件工程师为什么也应该懂一点芯片;最后用 Rust 写几段与芯片打交道的代码。
一、从沙子到芯片
芯片的原料是沙子。这不是比喻:沙子(石英砂)的主要成分是二氧化硅 $\mathrm{SiO_2}$,而硅正是半导体工业的基石。
石英砂(SiO2)
│ 高温还原:SiO2 + 2C → Si + 2CO↑
▼
冶金级硅(纯度 ~98%)
│ 西门子法提纯
▼
电子级硅(纯度 99.9999999%,即 9N,杂质以十亿分之一计)
│ 直拉法(Czochralski):籽晶旋转提拉,长成单晶硅棒
▼
单晶硅锭(直径 300 mm,长约 2 m)
│ 切片 → 研磨 → 抛光
▼
晶圆(Wafer,厚约 0.775 mm,表面平整到原子级)
│ 光刻 + 刻蚀 + 掺杂 + 沉积 …… 反复上百次
▼
布满芯片的晶圆(每片可容纳数百到上万个 die)
│ 测试 → 切割(Dicing)
▼
晶粒(Die)
│ 封装(Packaging)→ 引线键合 / 倒装 / 2.5D / 3D
▼
芯片(Chip)
| 概念 | 说明 |
|---|---|
| 晶圆(Wafer) | 硅单晶切片,主流尺寸 300 mm(12 英寸),面积越大单片可产的芯片越多、单位成本越低 |
| 晶粒(Die) | 晶圆上独立的一颗芯片,切下来就是“裸片“ |
| 良率(Yield) | 合格晶粒占全部晶粒的比例,直接决定成本 |
| 封装(Package) | 把裸片保护起来,引出引脚,并兼顾散热、供电与信号完整性 |
| 制程(Process Node) | 常说的 7 nm / 5 nm / 3 nm,是一代工艺的名称 |
一个常被忽略的事实:芯片的面积就是钱。良率随面积呈指数下降(缺陷落在哪颗晶粒上是随机的),所以“把芯片做大“从来不是免费的,这正是后面要讲的 Chiplet(芯粒)路线兴起的根本原因。
每片晶圆能切出多少颗芯片,有个经典近似公式($d$ 为晶圆直径,$A$ 为单颗面积,$d_{\text{edge}}$ 为边缘不可用宽度):
$$ N_{\text{die}} \approx \frac{\pi \left(\frac{d}{2}\right)^2}{A} - \frac{\pi d}{\sqrt{2A}} $$
第一项是“按面积能放多少“,第二项是圆形边缘的损失。若缺陷密度为 $D$,面积 $A$ 的泊松良率模型为:
$$ Y \approx e^{-A \cdot D} $$
面积翻倍,良率就按指数衰减——这就是为什么先进节点的超大芯片(如 GPU)价格高昂,而小芯片(如 MCU)可以便宜到几毛钱。
二、晶体管:芯片的“开关“
2.1 从电子管到集成电路
| 年份 | 事件 | 意义 |
|---|---|---|
| 1904 | 真空电子管(弗莱明) | 第一代电子器件,体积大、发热高、寿命短 |
| 1947 | 贝尔实验室发明点接触晶体管(巴丁、布拉顿,肖克利理论奠基) | 固体器件取代电子管,可控、可靠、省电 |
| 1958 | 基尔比(TI)做出第一块集成电路 | 元件与连线集成在一块材料上 |
| 1959 | 诺伊斯(仙童)发明硅平面工艺 | 用氧化层与光刻实现“印刷式“布线,可量产 |
| 1971 | Intel 4004 | 第一颗商用微处理器:2300 个晶体管,10 μm 工艺 |
| 2011 | Intel 22 nm FinFET | 晶体管从“平面“走向“立体“ |
| 2022 | 三星 3 nm GAA(环栅)量产 | 栅极四面环绕沟道,继续缩小 |
2.2 MOSFET 如何当开关
数字电路只需要两种状态:0 和 1。MOSFET(金属-氧化物-半导体场效应晶体管)正好是一个电压控制的开关:
栅极 G(Gate)
│ ← 加电压,形成电场
┌────┴────┐
│ 绝缘氧化层 │ 栅压足够高 → 下方感应出导电沟道
└────┬────┘
源极 S ──┴── 漏极 D 沟道导通 → 电流可流 → 逻辑 1
(Source) (Drain) 沟道截止 → 电流不通 → 逻辑 0
衬底 / 体(Body,通常接地)
把它接成互补的一对(PMOS + NMOS)就是 CMOS 反相器:输入为高时 NMOS 导通、PMOS 截止,输出被拉到低;输入为低时相反。CMOS 的妙处是稳态几乎不耗电,只有翻转时才有动态功耗——这让“几十亿个开关塞进指甲盖“成为可能。
2.3 为什么要把晶体管做小
动态功耗的经典公式是:
$$ P_{\text{dynamic}} = \alpha C V^2 f $$
($\alpha$ 为翻转活动率,$C$ 为负载电容,$V$ 为电压,$f$ 为频率)。把尺寸缩小到原来的 $1/k$,电容 $C$ 和电压 $V$ 都会下降,于是同样的电路更快也更省电——这就是整条产业链拼命缩小的动力。
2.4 摩尔定律与它的黄昏
| 定律 / 效应 | 内容 | 现状 |
|---|---|---|
| 摩尔定律 | 摩尔 1965 年提出“集成电路上晶体管数量每年翻一番“,1975 年修正为约每两年翻一番 | 经济与物理双重压力下明显放缓,但“每代密度提升“仍在继续 |
| 登纳德缩放 | 尺寸缩小时,功率密度保持不变(电压与尺寸同步等比缩小) | 约 2005—2007 年前后失效,因为它需要电压持续下降,而阈值电压不能无限降 |
| 频率墙 | 主频不再是性能增长的主引擎 | 2004 年前后 Intel 放弃 4 GHz 的 Pentium 4 后续计划,主频长期停留在数 GHz |
| 多核时代 | 既然不能更快,就把更多核放在一起 | 从单核到多核、到异构(大核 + 小核 + 加速器) |
登纳德缩放失效的直接后果是**“暗硅”(Dark Silicon):芯片上的晶体管多到无法同时全速开动,否则散热根本压不住。于是设计者转向两件事——专用化(为特定任务造专用电路)与三维化**(堆叠、Chiplet、先进封装)。
| 年代 | 代表芯片 | 晶体管数 | 制程 |
|---|---|---|---|
| 1971 | Intel 4004 | 2,300 | 10 μm |
| 1978 | Intel 8086 | 29,000 | 3 μm |
| 1993 | Intel Pentium | 3,100,000 | 0.8 μm |
| 2000 | Pentium 4 | 42,000,000 | 180 nm |
| 2006 | Core 2 Duo | 291,000,000 | 65 nm |
| 2022 | NVIDIA H100(GPU) | 约 800 亿 | 4 nm |
| 2023 | Apple M2 Ultra | 约 1340 亿 | 5 nm |
从 2300 到上千亿,五十年间翻了近八位数。
三、芯片是怎么造出来的
制造一颗芯片,要在晶圆上反复“盖楼“上百次。工序可分为前道(Front-End,做器件)与后道(Back-End,做连线与封装)。
循环 100+ 次(每层图案重复一遍):
┌──────────────────────────────────────────────┐
│ ① 氧化/沉积 生长或淀积一层薄膜(SiO2、SiN…) │
│ ② 涂胶 旋涂光刻胶 │
│ ③ 光刻 掩膜版图案 → 曝光 → 显影 │ ← 最关键、最贵
│ ④ 刻蚀 把图案"刻"进材料(干法/湿法) │
│ ⑤ 掺杂 离子注入,形成 n 型 / p 型区域 │
│ ⑥ 退火 修复晶格损伤、激活掺杂 │
│ ⑦ 平坦化 化学机械抛光(CMP),为下一层铺平 │
└──────────────────────────────────────────────┘
│
▼
后道:金属互连(铜,十几层)→ 晶圆测试 → 切割 → 封装 → 成品测试
3.1 光刻:把图案“印“到硅上
光刻(Lithography)的原理和洗照片类似:光刻胶见光变质,显影后留下图案,再用它当“模具“去刻蚀。因为光学衍射极限,能印多细的线取决于光的波长:
| 技术 | 光源波长 | 说明 |
|---|---|---|
| 早期紫外(g/i-line) | 436 / 365 nm | 微米时代 |
| DUV(KrF / ArF) | 248 / 193 nm | 配合浸没式(用水提高数值孔径)、多重曝光,撑到了 7 nm 时代 |
| EUV(极紫外) | 13.5 nm | 全反射镜系统,真空环境,目前先进节点的唯一选择 |
EUV 光刻机的复杂程度堪称人类工业之最:把锡液滴用高功率激光轰击成等离子体发出 13.5 nm 极紫外光,再经多层反射镜(不能用透镜,因为 EUV 会被空气和玻璃吸收)聚焦到晶圆上。目前 EUV 光刻机由 ASML 独家供应。
3.2 洁净、设备与产业链
- 洁净度:先进制程要求 ISO 1~3 级洁净室(每立方米 ≥0.1 μm 的尘埃粒子不超过 10 个)。作为参照,一根头发直径约 70 μm——一颗灰尘落在晶圆上,就等于一颗芯片报废。
- 设备:光刻(ASML)、刻蚀与沉积(泛林 Lam、应用材料 AMAT、东京电子 TEL)、量测与检测(KLA)、离子注入(AMAT/AIBT)等。
- 掩膜版(Photomask):每层图案需要一块光罩,一套先进节点掩膜的成本可达数百万甚至上千万美元,开模就是赌注,所以初创公司常用 MPW(多项目晶圆,几家共用一次流片)来摊薄费用。
四、制程节点:名字与现实的差距
“7 nm”“5 nm”“3 nm”并不是某个晶体管尺寸,而是一代工艺的营销名称。真实指标要看金属间距、鳍片间距、接触栅间距、标准单元高度等参数。比如所谓“3 nm“,其金属间距仍在 20 nm 量级。
| 世代 | 晶体管结构 | 大致年代 | 代表 |
|---|---|---|---|
| ≥ 32 nm | 平面(Planar) | 至 2011 年 | 各类传统 CMOS |
| 22 nm ~ 3 nm 级 | FinFET(鳍式,栅极包三面) | 2011—2022 | Intel 22 nm 起,台积电/三星延续 |
| 3 nm 及以下 | GAA / 纳米片(栅极四面环栅) | 2022 起 | 三星 3 nm、台积电 2 nm 一代 |
| 未来方向 | CFET(上下堆叠互补器件) | 研发中 | 3D 晶体管 |
当“缩小“越来越难,产业链转向几条新路:
| 路线 | 思路 | 例子 |
|---|---|---|
| More Moore | 继续缩尺寸、换结构 | GAA、CFET、High-K 金属栅、应变硅 |
| More than Moore | 不缩尺寸,改做功能集成 | 先进封装、Chiplet、3D 堆叠 |
| 专用化 | 为特定任务定制电路 | NPU/TPU、ISP、视频编解码器、加密引擎 |
| 新材料 | 替代受限的材料 | 铜互连、低 k 介质、新沟道材料 |
一个有趣的转变:随着尺寸缩小,连线(互连)的延迟已经超过晶体管本身的开关延迟。芯片性能的瓶颈从“器件“变成了“布线“——这也是 3D 堆叠与 Chiplet 越来越重要的原因。
五、芯片家族:通用与专用的光谱
芯片千差万别,但可以用两把尺子衡量:通用性(能不能跑任意程序)与能效(每瓦性能)。二者通常相互矛盾:
通用 ←────────────────────────────────────────────→ 专用
灵活低效 高效死板
CPU ──── GPU ──── DSP ──── FPGA ──── ASIC
任意程序 数据并行 信号处理 可编程硬件 固定功能
每瓦性能:低 ────────────────────────────────→ 高
| 类型 | 全称/含义 | 特点 | 典型用途 |
|---|---|---|---|
| CPU | Central Processing Unit | 通用、强控制流、大缓存、乱序执行 | 服务器、PC、手机主核 |
| GPU | Graphics Processing Unit | 海量简单核心、SIMT 并行、高带宽显存 | 图形、AI 训练/推理 |
| NPU / TPU | 神经网络处理器 | 矩阵乘加阵列、低精度量化、极省电 | 手机 AI、数据中心推理 |
| DSP | Digital Signal Processor | 乘加指令、定点优化、实时性 | 音频、通信基带、电机控制 |
| FPGA | 现场可编程门阵列 | 硬件可编程、可反复改、吞吐极高 | 原型验证、网络加速、小批量 |
| ASIC | 专用集成电路 | 为单一任务定制,性能功耗最优 | 矿机、加密引擎、视频编解码 |
| MCU | 微控制器 | CPU + Flash + RAM + 外设,低成本低功耗 | 家电、汽车电子、工业控制 |
| SoC | System on Chip | 把 CPU/GPU/NPU/基带/内存控制器等整合在一颗芯片上 | 手机、平板、IoT |
| PMIC | 电源管理芯片 | 电压转换、时序控制、电量计 | 一切电子设备 |
| 射频芯片 | RF | 收发、功放、滤波 | 通信、Wi-Fi、蓝牙、雷达 |
| CMOS 图像传感器 | CIS | 光电转换 | 摄像头、手机、车载 |
以一颗手机 SoC 为例,它内部大致是这样一个“片上城市“:
┌───────────── 手机 SoC ─────────────┐
│ CPU 集群(大核 + 小核) │
│ GPU(图形与通用并行计算) │
│ NPU(AI 推理) │
│ ISP(图像信号处理,拍照用) │
│ DSP / 音频编解码 │
│ 基带 / 射频接口 │
│ 内存控制器(LPDDR) │
│ 安全岛(安全启动、密钥存储) │
│ 各种 IO:USB、PCIe、显示、摄像头 │
└────────────────────────────────────┘
六、存储器芯片与存储层次
计算芯片负责“算“,存储芯片负责“记“。二者工艺路线完全不同:逻辑芯片追求速度,存储芯片追求密度(单位面积存更多比特),所以逻辑与存储通常分开制造,再通过先进封装拼在一起。
| 类型 | 存储原理 | 易失性 | 速度 | 容量/成本 | 典型用途 | 擦写寿命 |
|---|---|---|---|---|---|---|
| SRAM | 6 个晶体管组成的双稳态触发器 | 易失 | 极快(约 1 ns) | 单位面积贵 | CPU 的 L1/L2/L3 缓存 | 无限 |
| DRAM | 1 晶体管 + 1 电容,靠电荷存储 | 易失(需刷新) | 快(约 50~100 ns) | 中等 | 主存 | 无限 |
| NAND Flash | 浮栅/电荷陷阱存电荷,按页读写、按块擦除 | 非易失 | 慢(μs 级) | 便宜、密度高 | SSD、U 盘、手机存储 | 约 10³~10⁴ 次/块 |
| NOR Flash | 同上,但支持随机按字节读 | 非易失 | 较快 | 较贵 | 单片机程序存储、固件 | 约 10⁴~10⁵ 次 |
| HBM | DRAM 裸片垂直堆叠 + TSV 硅通孔 | 易失 | 极快、带宽极高 | 很贵 | AI 加速卡、高端 GPU | 无限 |
DRAM 的“易失“来自它的存储方式:信息是电容上的一点点电荷,会漏掉,所以必须周期性刷新(JEDEC 通常规定 64 ms 内把全部行刷新一遍)。这也是“内存断电就丢数据“的物理原因。
6.1 存储层次金字塔
计算机用“容量越大越慢越便宜“的多层结构,把少量快存储放在离 CPU 最近的地方:
容量 / 延迟 / 成本 存储介质
┌──────────────────────────┐
│ 寄存器 几百字节 │ < 1 ns (位于 CPU 内部)
├──────────────────────────┤
│ L1 缓存 几十 KB │ ~1 ns SRAM
├──────────────────────────┤
│ L2 缓存 几百 KB~几 MB │ ~5 ns SRAM
├──────────────────────────┤
│ L3 缓存 几 MB~几十 MB │ ~15 ns SRAM
├──────────────────────────┤
│ 主存(DRAM) 几 GB~几 TB │ ~100 ns DRAM
├──────────────────────────┤
│ SSD(NAND) 几百 GB~几 TB │ ~100 μs NAND Flash
├──────────────────────────┤
│ HDD / 网络存储 TB~PB 级 │ ~10 ms 磁盘
└──────────────────────────┘
越靠上:越快、越小、越贵
这套结构之所以有效,靠的是局部性原理:程序在一段时间内倾向于访问最近访问过的数据(时间局部性)和邻近的数据(空间局部性)。硬件据此按**缓存行(Cache Line,通常 64 字节)**整块搬运,而不是按字节搬运。
6.2 亲测:存储层次的延迟阶梯
教科书上的数字到底对不对?自己量一遍最清楚。顺序扫描测不出层次差异(CPU 预取器会把延迟隐藏掉),要用指针追逐:让每次访问的地址依赖上一次的结果,预取器就无能为力了。
use std::time::Instant;
/// 用线性同余发生器生成一个 0..n 的伪随机排列(Fisher–Yates 洗牌)
fn permutation(n: usize, seed: u64) -> Vec<usize> {
let mut idx: Vec<usize> = (0..n).collect();
let mut state = seed;
let mut next = move || {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(state >> 33) as usize
};
for i in (1..n).rev() {
let j = next() % (i + 1);
idx.swap(i, j);
}
idx
}
/// 指针追逐:访问顺序随机,且下一次访问的地址依赖上一次的结果,
/// 因此 CPU 无法预取,测到的是真实的存储层次延迟。
fn chase_ns_per_access(bytes: usize, rounds: usize) -> f64 {
let n = bytes / std::mem::size_of::<usize>();
let perm = permutation(n, 0x9E37_79B9);
// link[k] 指向"随机序列中 k 的下一个元素",把查表变成链表
let mut link = vec![0usize; n];
for k in 0..n {
link[perm[k]] = perm[(k + 1) % n];
}
let mut cur = 0usize;
// 预热一遍
for _ in 0..n {
cur = link[cur];
}
let mut best = f64::MAX;
for _ in 0..rounds {
let start = Instant::now();
for _ in 0..n {
cur = link[cur]; // 依赖加载:必须等上一次读回结果
}
let ns = start.elapsed().as_nanos() as f64 / n as f64;
if ns < best {
best = ns;
}
}
std::hint::black_box(cur);
best
}
fn main() {
let sizes: [(usize, &str); 5] = [
(16 * 1024, "16 KB L1 数据缓存"),
(256 * 1024, "256 KB L2 缓存"),
(4 * 1024 * 1024, "4 MB L3 缓存"),
(32 * 1024 * 1024, "32 MB L3 / 主存交界"),
(256 * 1024 * 1024, "256 MB 主存"),
];
println!("{:<26} {:>12}", "工作集", "每次访问(ns)");
for (bytes, label) in sizes {
println!("{:<26} {:>12.1}", label, chase_ns_per_access(bytes, 3));
}
}
在一台普通台式机上的实测结果:
工作集 每次访问(ns)
16 KB L1 数据缓存 1.4
256 KB L2 缓存 5.5
4 MB L3 缓存 12.3
32 MB L3 / 主存交界 109.9
256 MB 主存 149.7
阶梯清晰可见:L1 约 1.4 ns,L2 约 5.5 ns(5 倍),L3 约 12 ns(10 倍),一旦超出缓存容量跌到主存,就是 100 ns 以上(约 100 倍)。这也解释了为什么“算法复杂度相同、常数不同“的两个实现,性能可能相差十倍——差别往往在访问模式是否对缓存友好。
由此派生的两个工程结论:(1) 遍历数组比遍历链表快,因为数组是连续访问、链表是随机跳跃;(2) 多线程时若两个线程频繁写同一缓存行里的不同变量,缓存行会在核间来回“乒乓“,性能反而比单线程更差——这叫伪共享(False Sharing),通常用填充(padding)把变量对齐到不同缓存行来解决。
6.3 往三维要空间
当平面缩放变难,存储芯片转向“向上长“与“叠起来“:
| 技术 | 做法 | 效果 |
|---|---|---|
| 3D NAND | 把存储单元垂直堆叠成上百层 | 密度大幅提升,成本持续下降 |
| TSV(硅通孔) | 在裸片上打孔穿金属,把多层垂直连通 | 缩短互连长度,提高带宽与能效 |
| HBM | 多层 DRAM 用 TSV 叠在硅中介层上,紧邻 GPU | 位宽从 32/64 bit 跃升到上千 bit,带宽达 TB/s 级 |
| Chiplet | 把不同功能的小芯片(含存储)用先进封装拼装 | 良率更高、可混用不同工艺 |
七、芯片是怎么设计出来的
制造是“盖楼“,设计则是“画图纸“。一颗复杂芯片的设计流程大致如下:
① 需求与规格 功耗、性能、面积、成本(PPA)目标
▼
② 架构 / 微架构 多少核?缓存多大?几条流水线?
▼
③ RTL 设计 用 Verilog / VHDL / Chisel 写"硬件的行为"
▼
④ 功能仿真验证 跑海量测试用例,比对期望结果(验证占大量工时)
▼
⑤ 逻辑综合 RTL → 门级网表(用代工厂提供的标准单元库)
▼
⑥ DFT 插入 扫描链、内建自测(BIST),保证可测性
▼
⑦ 布局布线(PnR) 把百万级门与连线摆到版图上
▼
⑧ 签核(Sign-off) 时序收敛、功耗、DRC/LVS、电磁与热分析
▼
⑨ GDSII 交付 交给代工厂(称为"流片",Tape-out)
用 Rust 的流水线类比:RTL 是源码,综合是编译,布局布线是最优化加目标码生成,签核是测试与审计——软件工程师其实很熟悉这套思维方式(可对照第六十九“编译器与解释器“)。
| 环节 | 说明 | 工具/语言 |
|---|---|---|
| 前端(Front-End) | 规格、RTL 编写、功能验证 | Verilog/VHDL/SystemVerilog、Chisel、仿真器 |
| 验证 | 占项目工作量的大头,甚至超过设计本身 | UVM 方法学、形式化验证、FPGA 原型 |
| 后端(Back-End) | 综合、布局布线、时序与物理验证 | EDA 工具链 |
| 版图与制造 | GDSII、掩膜、光刻 | 代工厂 |
7.1 EDA:芯片设计的“编译器“
设计几十亿晶体管的芯片,人手工画是不可能的,全靠 EDA(Electronic Design Automation) 工具。这个市场高度集中:
| 厂商 | 代表工具 | 领域 |
|---|---|---|
| Synopsys | Design Compiler、VCS、PrimeTime | 综合、仿真、时序签核 |
| Cadence | Innovus、Virtuoso、Palladium | 布局布线、模拟设计、硬件仿真 |
| Siemens EDA(原 Mentor) | Calibre、Questa | 物理验证、仿真验证 |
EDA 是芯片产业链上最容易被忽视、却最难替代的一环:它同时依赖半导体工艺知识、算法与几十年的工程积累,被称为“卡脖子的卡脖子“。
7.2 商业模式:谁设计、谁制造
一颗芯片从想法到成品,往往要经过多家公司:
| 模式 | 含义 | 代表 |
|---|---|---|
| IDM | 设计、制造、封测全都自己做 | Intel、三星、德州仪器 |
| Fabless | 只设计,不建厂 | NVIDIA、AMD、高通、苹果、华为海思 |
| Foundry | 只代工制造 | 台积电、三星、格罗方德、中芯国际 |
| OSAT | 只做封装测试 | 日月光、安靠、长电科技、通富微电 |
| EDA / IP | 卖工具与可复用模块 | Synopsys、Cadence、ARM(IP) |
| 设备 / 材料 | 卖光刻机、刻蚀机、硅片、光刻胶 | ASML、泛林、应用材料、信越、沪硅产业 |
IP 复用是现代芯片设计的基石:ARM 卖 CPU 核(Cortex 系列),RISC-V 提供开放指令集,内存控制器、PCIe 控制器、SerDes 等都可以买现成的 IP,Fabless 公司只需把注意力集中在差异化部分。可以说,芯片设计已经变成“搭积木 + 少量自研“。
7.3 一次流片要花多少钱
| 项目 | 量级 |
|---|---|
| 先进节点(如 5 nm)完整设计投入 | 数亿美元(含人力、EDA 授权、IP 授权、验证) |
| 一套先进节点掩膜 | 数百万 ~ 上千万美元 |
| 一次 MPW 试验流片 | 几十万 ~ 数百万美元(多家共享晶圆) |
| 成熟节点(如 180 nm MCU) | 从几万到几十万美元起 |
所以芯片行业的规律是:流片一次,就是一次无法回退的下注。这也解释了为什么仿真验证要如此“过度“——与其在硅片上发现 bug,不如在电脑里发现。
八、指令集架构:芯片与软件的契约
硬件与软件之间有一份“契约“,它就是指令集架构(Instruction Set Architecture, ISA):软件按 ISA 编写,硬件按 ISA 实现。
| 概念 | 含义 | 例子 |
|---|---|---|
| ISA(架构) | 程序员与编译器看到的抽象:有哪些指令、寄存器、内存模型、异常机制 | x86-64、ARMv9、RISC-V |
| 微架构(Microarchitecture) | ISA 的具体实现:流水线级数、缓存大小、乱序窗口宽度 | Zen 5、Golden Cove、Neoverse |
同一个 ISA 可以有完全不同的微架构——就像同一份 C 源码可以被不同编译器编译出不同机器码。ISA 管兼容,微架构管性能:只要 ISA 不变,换一代 CPU 老程序仍能跑(这叫二进制兼容)。
8.1 CISC 与 RISC
| 维度 | CISC(复杂指令集) | RISC(精简指令集) |
|---|---|---|
| 指令数量与长度 | 多、不等长、功能复杂 | 少、定长、职责单一 |
| 内存访问 | 许多指令可直接操作内存 | 只有 load/store 能访存 |
| 实现风格 | 微码译码,硬件更复杂 | 硬布线,利于流水线与编译器优化 |
| 功耗效率 | 相对低 | 相对高 |
| 代表 | x86(Intel/AMD) | ARM、RISC-V、LoongArch |
如今两者已互相借鉴:x86 内部把复杂指令译成类似 RISC 的微操作执行,而 ARM 也在不断扩展指令集。
8.2 三大阵营与授权模式
| 阵营 | 开放性 | 授权方式 | 生态 |
|---|---|---|---|
| x86 | 封闭 | Intel 与 AMD 交叉授权,外部几乎无法进入 | PC / 服务器软件生态最厚 |
| ARM | 半开放 | 内核 IP 授权(用 Cortex 核)或架构授权(自研核) | 手机、嵌入式、云原生服务器 |
| RISC-V | 开放 | 指令集标准开放,可自由扩展与自研核 | IoT、嵌入式、新型加速器、教学 |
授权模式对产业格局影响巨大:买 ARM 的 Cortex 核就像买成套家具,省事但差异化空间小;买架构授权就像买建材自己设计,投入大但可以做苹果 M 系列那样的自研核;而 RISC-V 则像开源硬件——门槛低、可定制,但需要自建生态。
8.3 指令在芯片里怎么跑:流水线
CPU 执行一条指令,要经历多个阶段。把多个阶段重叠起来,就像工厂流水线一样,每个时钟周期都能完成一条指令:
时钟 T1 T2 T3 T4 T5 T6
────┼─────┼─────┼─────┼─────┼─────┼─────
指令1│ 取指 │ 译码 │ 执行 │ 访存 │ 写回 │
指令2│ │ 取指 │ 译码 │ 执行 │ 访存 │ 写回
指令3│ │ │ 取指 │ 译码 │ 执行 │ 访存
指令4│ │ │ │ 取指 │ 译码 │ 执行
但当遇到分支与依赖时,流水线会“停转“。现代高性能处理器用一整套技术填补这些空拍:
| 技术 | 解决的问题 |
|---|---|
| 分支预测 | 不知道下一条指令是哪条?先猜(准确率可达 99% 以上) |
| 乱序执行(OoO) | 指令互相依赖时,先执行不受影响的那些 |
| 超标量 / 多发射 | 一个周期同时发射多条指令 |
| SIMD 向量指令 | 一条指令算多个数据(SSE/AVX/NEON/SVE) |
| 同时多线程(SMT) | 一个物理核呈现为两个逻辑核,填满执行单元 |
| 多级缓存 | 用片上高速缓存掩盖主存延迟 |
8.4 一个简得不能再简的性能模型
处理器性能可以用时间公式拆开(第六十九“编译器“与第三十六“神经网络“里的优化都绕不开它):
$$ T = \frac{IC \times CPI}{f} = IC \times CPI \times T_{\text{cycle}} $$
其中 $IC$ 是指令条数(编译器与算法决定)、$CPI$ 是每条指令平均周期数(微架构决定)、$f$ 是主频(工艺与功耗决定)。三者往反方向拉:指令越复杂 $IC$ 可能越少但 $CPI$ 升高;主频越高但功耗与散热成本急剧上升($P \propto V^2 f$)。硬件与编译器的所有优化,本质上都是在调这三个量。
九、芯片中的功耗、并行与安全
9.1 功耗与散热:真正的物理限制
| 设备 | 典型功耗 | 散热方式 |
|---|---|---|
| 单片机(MCU) | 毫瓦级 | 自然散热 |
| 手机 SoC | 几瓦 ~ 十几瓦 | 石墨/热管,无风扇 |
| 笔记本 CPU | 15 ~ 45 W | 风扇 + 热管 |
| 服务器 CPU | 200 ~ 400 W | 大型散热器 + 风道 |
| 训练用 GPU | 700 W 以上 | 加速卡散热/液冷 |
| 数据中心机柜 | 数十 kW | 机房级液冷与电力调度 |
相应的工程手段有:DVFS(动态调频调压,按负载升降频)、时钟门控与电源门控(不用的模块直接断电)、大小核异构(轻载用小核省电)。
当频率与功耗都碰到墙,剩下的路子就是并行:多核、SIMD、多卡。但要记住 Amdahl 定律:若程序中只有比例 $p$ 可以并行,那么再多核也把总加速比卡在 $1/(1-p)$——通讯、同步、数据划分这些“串行尾巴“永远是并行计算的天花板。
9.2 芯片里的安全
芯片是信任链的根。如果芯片本身不可信,上面跑的一切安全机制都像沙子上的城堡。主要威胁与对策如下:
| 类型 | 例子 | 说明 |
|---|---|---|
| 微架构侧信道 | Spectre(2018)、Meltdown(2018) | 利用分支预测、乱序执行等微架构行为泄露内存内容;修复往往要牺牲性能 |
| 硬件故障攻击 | Rowhammer(2014) | 频繁读写一行 DRAM,导致相邻行位翻转(“比特翻转”),可被利用提权 |
| 侧信道分析 | 功耗分析、电磁分析、时序分析 | 智能卡、银行卡常用于防它;靠旁法推断密钥 |
| 故障注入 | 电压尖脉冲、激光照射、时钟毛刺 | 使芯片在关键步骤算错,绕过安全校验 |
| 物理逆向 | 开盖(decap)、微探针、电子显微镜拍照 | 读出版图与存储内容,或改写电路(FIB) |
| 硬件木马 / 伪劣芯片 | 在设计中植入触发式后门;回收件翻新 | 根源于供应链不透明 |
防护思路可以分成“硬件筑坝“与“软件校验“两类:
| 对策 | 内容 |
|---|---|
| 安全启动与信任根(RoT) | 从不可变的 ROM 开始,逐级校验固件签名后才交出控制权 |
| 安全岛 / TEE | ARM TrustZone、Intel SGX/TDX、AMD SEV:在芯片内划出隔离世界 |
| 硬件密钥与加密引擎 | 密钥永不出芯片(如安全元件 SE、TPM);总线和内存加密 |
| 物理防护 | 屏蔽层、传感器(光照/电压/温度异常检测)、防篡改封装 |
| 可审计的开放硅厂 | OpenTitan 等开放硅信任根项目,让“根“可以被公开审视 |
对软件工程师的实际含义:不要把安全机制建在“硬件一定可信“的假设上。Spectre/Meltdown 之后,跨权限边界的数据泄露不再只靠软件 bug 就能避免;这也是第五十九“软件漏洞“、第六十“网络安全“、第六十一“数据安全“三章反复强调“分层防御“的原因。
十、Rust 与芯片
芯片需要的是确定性:不能意外分配内存、不能有运行期 GC 抖动、不能有隐藏的异常抛出。这正是 Rust 的强项——它既能像 C 一样贴近硬件,又在编译期拦住了大量内存与并发错误。
| 维度 | C | Rust |
|---|---|---|
| 内存安全 | 靠程序员自律,未定义行为风险高 | 编译期借用检查;越界、悬垂、二次释放很难写出来 |
| 并发安全 | 靠约定与审查 | Send/Sync + 所有权,数据竞争在编译期就被拦截 |
| 抽象成本 | 宏、函数指针、代码生成 | 泛型、trait、内联:零成本抽象 |
| 依赖与构建 | 往往手工集成第三方库 | cargo + crates.io,交叉编译一条命令 |
| 运行环境 | 极小 | #![no_std] 下同样可以没有运行时 |
| 生态成熟度 | 几十年积累,驱动与参考代码最丰富 | 增长很快,部分外设与老旧平台仍缺现成库 |
Rust 在嵌入式领域的做法是“把 unsafe 关进笼子“:直接操作寄存器的代码集中在 PAC/HAL 库里,应用层代码全是安全 Rust。即使必须写 unsafe,也能用 #[repr(C)]、core::ptr::read_volatile 等工具把边界划得清清楚。
10.1 Rust 嵌入式与芯片生态
| Crate / 项目 | 用途 |
|---|---|
| cortex-m / cortex-m-rt | Cortex-M 内核访问与启动运行时 |
| riscv / riscv-rt | RISC-V 指令/CSR 与启动运行时 |
| embedded-hal / embedded-nal | 硬件抽象层 trait 标准(写一遍驱动,跑遍各种板子) |
| svd2rust | 从 SVD 描述文件生成外设寄存器访问层(PAC) |
| stm32-rs | 各大 STM32 型号的 PAC 与 HAL 集合 |
| embassy | 异步嵌入式框架(async/await 写裸机并发) |
| RTIC | 基于中断优先级的并发框架,静态保证无数据竞争 |
| esp-hal / rp-hal | ESP32 / RP2040 的 HAL |
probe-rs / cargo-embed | 烧录与调试(替代 OpenOCD) |
| defmt | 极低开销的嵌入式日志框架 |
| heapless | 无堆内存容器(固定容量 Vec/String/Map) |
| bitflags / modular-bitfield | 位标志与位域 |
| crc | 各类 CRC 校验算法 |
| embedded-graphics | 小屏显示的 2D 图形库 |
10.2 与寄存器打交道:位域与校验
芯片编程的日常就是“按位读改写“:配置寄存器里的某个位域,或从寄存器里解出一个有符号数值。下面的例子还顺手实现了嵌入式通信最常用的 CRC-32:
// ---------- 位域读写 ----------
/// 把 value 写入 reg 的 [high:low] 位,其余位保持不变
fn set_bits(reg: &mut u32, high: u32, low: u32, value: u32) {
let width = high - low + 1;
let mask = ((1u32 << width) - 1) << low;
*reg = (*reg & !mask) | ((value << low) & mask);
}
/// 从 reg 中取出 [high:low] 位
fn get_bits(reg: u32, high: u32, low: u32) -> u32 {
let width = high - low + 1;
(reg >> low) & ((1u32 << width) - 1)
}
/// 芯片温度寄存器:低 12 位是补码,1 LSB = 0.0625 °C
fn parse_temperature(raw: u16) -> f64 {
let value = (raw & 0x0FFF) as i16; // 只保留低 12 位
let signed = if value & 0x0800 != 0 {
value - 4096 // 最高位为 1:负数,做符号扩展
} else {
value
};
signed as f64 * 0.0625
}
// ---------- CRC-32 校验 ----------
/// CRC-32(IEEE 802.3,与 zip / 以太网一致)
fn crc32(data: &[u8]) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
let lsb = crc & 1;
crc >>= 1;
if lsb == 1 {
crc ^= 0xEDB8_8320; // 反射后的生成多项式
}
}
}
!crc
}
fn main() {
// 位域:配置一个控制寄存器
let mut ctrl = 0u32;
set_bits(&mut ctrl, 3, 0, 0b1010); // 低 4 位:分频系数
set_bits(&mut ctrl, 7, 4, 0b0110); // 次 4 位:模式
println!("ctrl = {ctrl:#010X} (0b{ctrl:010b})");
println!("分频 = {}, 模式 = {}", get_bits(ctrl, 3, 0), get_bits(ctrl, 7, 4));
// 修改中间位域而不影响其他位
set_bits(&mut ctrl, 7, 4, 0b1111);
println!("改模式后 ctrl = 0b{ctrl:010b}, 分频仍是 {}", get_bits(ctrl, 3, 0));
// 温度:0x03C0 -> 60 °C,0x0FC0 -> -4 °C
for raw in [0x03C0u16, 0x0FC0, 0x0640] {
println!("raw = {raw:#06X} -> {:.4} °C", parse_temperature(raw));
}
println!("CRC-32(\"123456789\") = {:#010X}", crc32(b"123456789"));
}
输出:
ctrl = 0x0000006A (0b0001101010)
分频 = 10, 模式 = 6
改模式后 ctrl = 0b0011111010, 分频仍是 10
raw = 0x03C0 -> 60.0000 °C
raw = 0x0FC0 -> -4.0000 °C
raw = 0x0640 -> 100.0000 °C
CRC-32("123456789") = 0xCBF43926
最后一行是 CRC-32 的标准“校验值“(check value):输入字符串
123456789得到0xCBF43926,与所有标准实现一致。写通信协议时,先拿这个值验证自己的 CRC 实现,是能省掉大量调试的老经验。
10.3 抽象隔离芯片差异:HAL 与测试替身
嵌入式开发最痛苦的事之一是“换个芯片就重写“。Rust 的 trait 把“外设能做什么“抽象出来,驱动只依赖抽象,于是同一份驱动能跑遍所有芯片,而且在电脑上就能测:
/// 最小化的 SPI 抽象(对应 embedded-hal 的 SpiDevice trait)
trait SpiDevice {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), &'static str>;
}
/// 传感器驱动:只依赖抽象,不依赖任何具体芯片
struct Sensor<S: SpiDevice> {
spi: S,
}
impl<S: SpiDevice> Sensor<S> {
fn new(spi: S) -> Self {
Sensor { spi }
}
/// 读取 WHO_AM_I 寄存器(0x0F),用于确认通信链路正常
fn who_am_i(&mut self) -> Result<u8, &'static str> {
let mut buf = [0u8; 1];
// 最高位置 1 表示读操作
self.spi.transfer(&mut buf, &[0x80 | 0x0F, 0x00])?;
Ok(buf[0])
}
}
/// 真实硬件:把读写交给芯片的 SPI 外设寄存器(此处为示意)
#[allow(dead_code)]
struct HardwareSpi {
base: usize,
}
impl SpiDevice for HardwareSpi {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), &'static str> {
// 真实实现里会写 SPI 数据寄存器、轮询状态位、读回数据,
// 例如(示意):
// let dr = (self.base + 0x0C) as *mut u32;
// unsafe { core::ptr::write_volatile(dr, write[0] as u32) };
let _ = (self.base, write);
read[0] = 0x00;
Ok(())
}
}
/// 测试替身:完全不需要硬件,就能验证驱动的协议是否正确
struct MockSpi {
id: u8,
written: Vec<u8>,
}
impl SpiDevice for MockSpi {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), &'static str> {
self.written.extend_from_slice(write);
read[0] = self.id;
Ok(())
}
}
fn main() {
let mut sensor = Sensor::new(MockSpi {
id: 0x6B,
written: Vec::new(),
});
println!("WHO_AM_I = {:#04X}", sensor.who_am_i().unwrap());
println!("驱动发出的字节 = {:02X?}", sensor.spi.written);
}
输出:
WHO_AM_I = 0x6B
驱动发出的字节 = [8F, 00]
这里有两个实用结论:**(1)驱动里的协议逻辑(读寄存器要置高位、发两个字节)在电脑上就验证完了,真正上板子的只剩 HAL 那几十行;(2)**同样的思路可以推广到 I²C、SPI、串口、网络层——embedded-hal 正是把这种模式做成了社区标准。
10.4 裸机点灯:no_std 与寄存器映射
最经典的入门例程:直接写寄存器,让一颗 LED 闪起来。注意所有外设都是“内存映射“的,每个寄存器就是一块固定地址,所以只需要往确定的地址写值:
#![no_std]
#![no_main]
use core::panic::PanicInfo;
use core::ptr::{read_volatile, write_volatile};
// 以 STM32 风格的 GPIOA 为例(具体地址随型号不同,务必查对数据手册)
const GPIOA_MODER: *mut u32 = 0x4800_0000 as *mut u32;
const GPIOA_BSRR: *mut u32 = 0x4800_0018 as *mut u32;
fn delay(cycles: u32) {
for _ in 0..cycles {
core::hint::spin_loop();
}
}
#[no_mangle]
pub extern "C" fn main() -> ! {
unsafe {
// PA5 配置为通用输出模式(MODER5 = 0b01)
let moder = read_volatile(GPIOA_MODER);
write_volatile(GPIOA_MODER, (moder & !(0b11 << 10)) | (0b01 << 10));
loop {
write_volatile(GPIOA_BSRR, 1 << 5); // 置高:点亮
delay(1_000_000);
write_volatile(GPIOA_BSRR, 1 << (5 + 16)); // 置低:熄灭
delay(1_000_000);
}
}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {
core::hint::spin_loop();
}
}
交叉编译到 Cortex-M4F,得到目标文件:
rustup target add thumbv7em-none-eabihf
rustc --target thumbv7em-none-eabihf -C panic=abort --emit=obj -o blink.o blink.rs
几个必须理解的细节:
| 细节 | 为什么必须这样 |
|---|---|
#![no_std] | 裸机没有操作系统,没有 std(没有文件、线程、堆) |
#![no_main] | 启动流程由芯片的复位向量与启动代码接管,不是 std 的 main |
#[panic_handler] | no_std 下必须自己定义 panic 行为(通常死死机或复位) |
read_volatile / write_volatile | 寄存器值可能被硬件改变(或被硬件读到),编译器不允许把它优化掉 |
unsafe | 解引用裸指针本身就是不安全操作;这部分应当尽量局限在 PAC/HAL 层 |
-C panic=abort | 不展开栈(裸机通常没有栈展开的运行时支持) |
链接脚本(memory.x) | 告诉链接器 Flash 与 RAM 的地址范围,把代码和数据放到正确位置 |
| 烧录与调试 | 用 probe-rs/cargo-embed(基于 SWD/JTAG)代替 OpenOCD,且自带 defmt 日志 |
真实项目不要直接写这种魔数地址:用
svd2rust从厂家提供的 SVD 文件生成类型安全的 PAC,或用embassy/RTIC这类框架,既保留零成本,又拿回类型检查与并发安全。
10.5 芯片设计侧的 Rust
Rust 正在从“写固件“渗透到“造芯片“的工具链里:
| 方向 | 项目 |
|---|---|
| 硬件描述语言工具链 | Veryl(用 Rust 实现的现代 HDL 工具链)、rust_hdl(VHDL 解析器与语言服务器) |
| 硅根信任与量产工具 | OpenTitan 的宿主端工具库(如 opentitanlib)用 Rust 编写 |
| RISC-V 工具与模拟 | riscv、riscv-rt 等 crate;可用于指令编解码、模拟器与固件开发 |
| Σ 部安全与功能安全 | Tock OS(面向嵌入式的安全操作系统,见第六十二“操作系统“) |
| 测试/验证加速 | 用 Rust 写测试平台、协议模拟器与固件回归测试 |
一句话总结芯片与软件的关系:芯片是软件的地基,软件是芯片的价值实现。同一个算法,写成 C 还是 Rust、访存是连续还是随机、能否搞成 SIMD/多核,性能可能差出十倍;而这十倍往往不是算法的事,是“知不知道下面那块硅是怎么工作的“的事。懂一点芯片,就不会再让缓存行白白报废。
十一、总结与练习
本章小结
| 知识点 | 要点 |
|---|---|
| 原料与制造 | 沙子 → 电子级硅 → 单晶锭 → 晶圆(300 mm)→ die → 封装 |
| 晶体管 | MOSFET 是电压控制的开关,CMOS 稳态几乎不耗电 |
| 摩尔定律 | 密度增长放缓;登纳德缩放失效 + 频率墙 → 多核与专用化 |
| 制造流程 | 氧化/沉积 → 光刻 → 刻蚀 → 掺杂 → CMP,反复上百次;EUV 13.5 nm 是当前关键 |
| 制程节点 | 3 nm/5 nm 是代数名称而非尺寸;结构从平面 → FinFET → GAA |
| 芯片分类 | CPU/GPU/NPU/DSP/FPGA/ASIC/MCU/SoC;通用性与能效相互矛盾 |
| 存储层次 | SRAM/DRAM/NAND/HBM;局部性原理 + 64 字节缓存行;L1≈1 ns,主存≈100 ns |
| 设计流程 | RTL → 仿真验证 → 综合 → 布局布线 → 签核 → 流片;EDA 三巨头 |
| 商业模式 | IDM / Fabless / Foundry / OSAT / EDA+IP;IP 复用是现代设计基石 |
| ISA | 架构(兼容性)与微架构(性能)分离;x86 / ARM / RISC-V 三大阵营 |
| 性能模型 | $T = IC \times CPI / f$;Amdahl 定律限制并行收益 |
| 芯片安全 | Spectre/Meltdown、Rowhammer、侧信道、硬件木马;安全启动/安全岛/密钥不出芯片 |
| Rust 与芯片 | no_std、PAC/HAL 抽象、volatile 寄存器、probe-rs、defmt、embassy/RTIC |
练习建议
- 位域驱动:为某个真实传感器(如加速度计)写一个寄存器映射模块,把“读 XYZ 轴数据“封装成
read_accel() -> (f32, f32, f32),并用 12/16 位补码处理符号。 - 验证 CRC 实现:用第 10.2 节的代码算出
123456789的 CRC-32,确认结果是0xCBF43926;再试着改成 CRC-16/CCITT,用于串口协议。 - 画出你机器上的延迟阶梯:把第 6.2 节的指针追逐程序改成多线程、多步长版本,找出你机器 L1/L2/L3 的实际容量(观察延迟突变的拐点)。
- 伪共享实验:两个线程各写一个变量,分别放在“同一缓存行相邻位置“与“间隔 64 字节“两种布局下,比较总耗时。
- 交叉编译一个
no_std程序:为thumbv7em-none-eabihf或riscv32imac-unknown-none-elf编译第 10.4 节的代码,再用probe-rs或模拟器(如 QEMU)跑起来。 - 读懂一张数据手册:任选一颗 MCU,找到时钟树、GPIO 寄存器基地址与位定义,手算出“把 PA5 设成输出并拉高“需要对哪几个地址写什么值。
- 算一笔成本账:用第一节的两个公式($N_{\text{die}}$ 与 $Y = e^{-AD}$),估算一个 300 mm 晶圆上、面积 100 mm²、缺陷密度 0.1 个/cm² 的芯片的良品数量与单位成本。
- 安全思考:查一下 Spectre 的公开资料,回答两个问题:它利用的是芯片的哪个微架构特性?为什么“修改软件“很难彻底修好它?