Appearance
总线:仲裁、定时、常见总线标准
概念
总线(bus)是多个部件共享的、分时使用的公共信息传输线。
一句话说清它是什么:总线就是"一条公用的路"——好处是线少、成本低、易扩展(加个设备挂上去就行),代价是同一时刻只能有一对设备在通话,所以必须有一套"谁来用、什么时候用"的规矩。
这套规矩分两半:谁先用 = 总线仲裁(本篇第五节);什么时候用、用多久 = 总线定时(第六节)。408 的总线计算题几乎全落在这两块和带宽公式上。
原理
一、总线的四大特性
| 特性 | 含义 | 例子 |
|---|---|---|
| 机械特性 | 尺寸、形状、引脚数、排列 | 插槽有多少根针、间距多少 |
| 电气特性 | 传输方向、电平范围 | 单向还是双向、TTL 电平还是差分 |
| 功能特性 | 每根线的功能 | 哪根是地址、哪根是数据、哪根是中断请求 |
| 时间特性 | 信号的时序关系 | 地址要提前数据几个时钟周期有效 |
二、总线的分类
┌── 片内总线(CPU 芯片内部:寄存器之间、ALU 与寄存器之间)
总线 ─────────┼── 系统总线(计算机各插件板之间,即"主板上的总线")
│ ├── 数据总线(Data Bus) 双向,宽度 = 一次能传的位数
│ ├── 地址总线(Address Bus) 单向(CPU 发出),宽度 = 寻址范围
│ └── 控制总线(Control Bus) 每根单向,整体双向
└── 通信总线(计算机与外设 / 计算机之间:USB、RS-232C)三条必须分清的方向:
- 数据总线双向(CPU 既读也写);
- 地址总线单向(由主设备发出,从设备只接收);
- 控制总线"每根单向、整体双向"(读信号是一根单向线,写信号是另一根单向线,合起来看有来有往)。
地址总线宽度决定寻址范围:
⚠️ 按字节编址 vs 按字编址会改变地址线根数:同样是 4 GB 空间,按字节编址需要 32 根地址线(
三、总线带宽(必考公式)
写成更通用的形式:
三个影响带宽的因素(考试就考这三个):
- 总线宽度
加宽(32 位 → 64 位):带宽翻倍,代价是引脚数翻倍。 - 总线频率提高:带宽线性提高,受信号完整性与物理长度限制。
- 一个总线周期传几次:若地址/数据复用(同一组线先传地址再传数据),则一个周期被劈成两半,带宽大致减半。
总线复用(multiplexed bus):为省引脚,让地址和数据共用同一组线、分时传送。
| 优点 | 缺点 |
|---|---|
| 引脚少、芯片封装小、成本低 | 同一个总线周期内要传两次(先地址后数据),等效带宽下降 |
| 布线简单 | 需要额外的地址锁存器(把地址留住,好让数据传完后还能用) |
四、总线仲裁:谁先用
根本矛盾:多个主设备(CPU、DMA 控制器、协处理器)可能同时要求使用总线,而同一时刻只能有一个主方。
集中式仲裁(三种,必考)
| 方式 | 控制线 | 工作原理 | 优先级 | 特点 |
|---|---|---|---|---|
| 链式查询 | 3 根:BR(请求)、BG(允许)、BS(忙) | BG 沿一条链逐级下传,请求被第一个"有请求且未被响应"的设备截住 | 离总线控制器越近越高(固定) | 线最少、结构最简单;对故障敏感——链上某设备坏了,它后面的设备全部瘫痪;优先级不可改 |
| 计数器定时查询 | 计数器从 0 开始计数,计数值与设备编号相等时该设备获得总线;计数器可复位到任意起点 | 可编程改变 | 线数居中;优先级灵活(从上次的位置继续数 = 轮转公平) | |
| 独立请求 | 每个设备各有一对请求/允许线,由控制器内部排队电路决定让谁用 | 由控制器内部逻辑决定,可编程 | 响应最快(不必逐级传递);线数最多、控制器最复杂 |
链式查询: 设备1 ── 设备2 ── 设备3 ── ...
BR ──────────────────────────┐
BG ──>──>──>──>──>──>──>──> │ (逐级下传, 先到先得)
BS ──────────────────────────┴── 总线控制器链式查询的两个常考结论:
- 离控制器越近优先级越高,因为
BG先到近处的设备。 - 某设备故障会导致它后面的所有设备都不能用(
BG传不下去了)——这是它最致命的问题。
分布式仲裁
不用中央控制器,各设备自己判断(如每个设备有一个唯一的仲裁号,按号竞争)。可靠性高(没有单点故障),但设备逻辑复杂。
五、总线定时:什么时候用、用多久
| 方式 | 核心思想 | 速度 | 适应性 | 复杂度 |
|---|---|---|---|---|
| 同步定时 | 统一时钟,所有操作按固定时钟节拍进行 | 快(无握手往返) | 差(只能迁就最慢设备,或必须把周期设计得足够长) | 简单 |
| 异步定时 | 握手(应答):请求 → 应答,双方互相确认 | 中(有协议开销) | 好(快慢设备都能接) | 中 |
| 半同步定时 | 同步 + 一根 WAIT 线:慢设备可拉长周期 | 较快 | 较好 | 中 |
| 分离事务 | 一个总线周期拆成"发地址/命令"和"传数据"两段,中间总线空出来给别的设备用 | 高(总线利用率高) | 好 | 复杂 |
异步定时的三种互锁方式(握手强度递增):
| 方式 | 握手过程 | 特点 |
|---|---|---|
| 不互锁 | 主方发请求,不等应答就撤销;从方发应答,不等主方确认就撤销 | 最快,最不可靠(双方都"自说自话") |
| 半互锁 | 主方发请求后等应答才撤销;从方发应答后不等主方确认就撤销 | 折中,实现简单 |
| 全互锁 | 主方等应答才撤销请求;从方等请求撤销才撤销应答 | 最可靠,握手往返最多、最慢 |
记忆钩子:"谁等谁"——不互锁谁都不等;半互锁主方等、从方不等;全互锁双方都等。
不互锁: 请求 ─┐ ┌─ 从方不管主方, 自己撤
└──────┘
半互锁: 请求 ────┐ 主方等应答才撤
└───┐
应答 ──────┘ └──── 从方不等, 自己撤
全互锁: 请求 ────┐ 主方等应答
└────┐
应答 ──────┐ └── 从方等"请求撤销"才撤
└───┘六、常见总线标准
| 标准 | 类型 | 宽度 | 频率 | 峰值带宽 | 说明 |
|---|---|---|---|---|---|
| ISA | 系统总线,并行 | 8/16 位 | 8 MHz | 8/16 MB/s | 早期 PC 总线,已淘汰 |
| EISA | 系统总线,并行 | 32 位 | 8 MHz | 32 MB/s | ISA 的扩展 |
| PCI | 系统总线,并行、多路复用 | 32/64 位 | 33/66 MHz | 132 / 264 / 528 MB/s | 地址数据复用,支持即插即用 |
| PCI Express | 串行、点对点 | 每 lane 1 位(双向) | 高 | 每 lane 每代不同 | 不是共享总线,靠交换开关组网 |
| AGP | 专用(显卡) | 32 位 | 66 MHz | 约 266 MB/s 起 | 已被 PCIe 取代 |
| USB 2.0 | 通信总线,串行 | 1 对差分 | — | 480 Mb/s = 60 MB/s | 即插即用、可热插拔 |
| USB 3.0 | 通信总线,串行 | 2 对差分 | — | 5 Gb/s ≈ 625 MB/s | 全双工 |
| SATA 3.0 | 通信总线,串行(磁盘) | 1 对差分 | — | 6 Gb/s ≈ 750 MB/s | 取代并行 ATA |
| RS-232C | 通信总线,串行 | 1 位 | — | 20 kb/s 量级 | 点对点、距离近、慢,用于串口 |
两条趋势线(记忆用):
- 并行 → 串行:并行总线在高频下出现线间串扰与时钟偏斜(各位到达时间不一致),越加宽越难提速;串行总线靠差分信号 + 极高单线速率反超。这就是 PCI 被 PCIe 取代的根本原因。
- 共享总线 → 点对点交换:共享总线有"同一时刻只有一个主方"的硬上限,PCIe 用交换开关(switch)把总线变成一张点对点的网。
示例
例 1:总线带宽计算
某系统总线宽度 32 位、总线时钟 66 MHz,每个总线周期传输一个字(4 B)。求带宽。若要达到 512 MB/s 以上,给出两种改法。
完整计算过程:
第一步,一个总线周期传输的字节数:
第二步,带宽(总线周期 = 1 个时钟周期):
第三步,要达到 512 MB/s 以上的两种改法:
改法一:加宽总线到 64 位(频率不变)
改法二:提高频率到 133 MHz(宽度不变)
常见配置对照表:
| 宽度 | 频率 | 带宽 |
|---|---|---|
| 32 位 | 66 MHz | 264 MB/s |
| 64 位 | 66 MHz | 528 MB/s |
| 32 位 | 133 MHz | 532 MB/s |
| 64 位 | 133 MHz | 1064 MB/s |
| 64 位 | 400 MHz | 3200 MB/s = 3.2 GB/s |
结论:带宽 = 宽度 × 频率,两者谁翻倍效果一样。但加宽的代价是引脚数(64 位数据 + 地址,几百根针),提频的代价是信号完整性(线间串扰、时钟偏斜)。工程上往往提频更划算——这就是"并行转串行"的动因。
C 实现(把这张表交给程序算):
#include <stdio.h>
/* 并行总线带宽: 宽度(位)/8 * 频率(MHz) -> MB/s */
static double bus_bw_mbps(int width_bits, double mhz) {
return width_bits / 8.0 * mhz;
}
int main(void) {
/* 五组典型配置: 宽度(位), 频率(MHz) */
struct { int w; double f; } cfg[] = {
{32, 66.0}, {64, 66.0}, {32, 133.0},
{64, 133.0}, {64, 400.0}
};
int n = (int)(sizeof(cfg) / sizeof(cfg[0]));
for (int i = 0; i < n; i++)
printf("%2d 位 @ %5.0f MHz -> %8.1f MB/s\n",
cfg[i].w, cfg[i].f, bus_bw_mbps(cfg[i].w, cfg[i].f));
/* 反推: 要超过 512 MB/s, 加宽或提频各需要多少 */
printf("--- 要超过 512 MB/s ---\n");
printf("原配置(32 位@66MHz) = %.1f MB/s\n", bus_bw_mbps(32, 66.0));
printf("改 64 位@66MHz = %.1f MB/s\n", bus_bw_mbps(64, 66.0));
printf("改 32 位@133MHz = %.1f MB/s\n", bus_bw_mbps(32, 133.0));
return 0;
}
c 本站为静态站,不提供在线运行;可复制到本地用 gcc / python 执行
预期输出:
32 位 @ 66 MHz -> 264.0 MB/s
64 位 @ 66 MHz -> 528.0 MB/s
32 位 @ 133 MHz -> 532.0 MB/s
64 位 @ 133 MHz -> 1064.0 MB/s
64 位 @ 400 MHz -> 3200.0 MB/s
--- 要超过 512 MB/s ---
原配置(32 位@66MHz) = 264.0 MB/s
改 64 位@66MHz = 528.0 MB/s
改 32 位@133MHz = 532.0 MB/s
width_bits / 8.0里的.0不能省:写成/ 8就变成整数除法,32 位会算成 4(恰好对),但若宽度是 4 位就会算成 0。这类"整数除法吃掉小数"是 C 计算题最常见的失分点。
例 2:同步总线 vs 异步总线(口径题)
某总线每次传输 4 B。方案 A(同步):总线时钟 50 MHz,一次读操作需要 2 个时钟周期。方案 B(异步):握手协议的固定开销为"发地址/命令 20 ns + 传数据 20 ns + 收尾 10 ns",从设备的访问时间为
。分别求 ns 时两种方案的带宽。
完整计算过程:
第一步,同步方案的周期与带宽(周期只取决于时钟,与设备快慢无关):
第二步,异步方案的周期 = 固定开销 + 从设备实际延迟:
第三步,三种从设备下的结果:
| 从设备访问时间 | 异步周期 | 异步带宽 | 协议开销占比 | 相对同步 |
|---|---|---|---|---|
| 20 ns(快速) | 57.1 MB/s | 71.4% | 慢 42.9% | |
| 100 ns(慢速) | 26.7 MB/s | 33.3% | 慢 73.3% | |
| 400 ns(极慢) | 8.9 MB/s | 11.1% | 慢 91.1% |
结论:
- 同步总线带宽固定、与设备快慢无关(100 MB/s),但只能按最慢设备设计周期——若为迁就 400 ns 的设备把周期改长,快设备也一起被拖慢。
- 异步总线"迁就实际速度":设备越慢,协议开销占比越低(11.1%),说明它把时间真正花在了等设备上,而不是花在握手上。
- 异步的带宽上限被"固定协议开销"封死:即使从设备零延迟,
这就是图里那个"协议开销占比 71.4%"的含义——快速设备时,握手本身比干活还费时间。
⚠️ 最容易答错的一句:"异步总线一定比同步慢"是错的。正确的说法是——同步的带宽上限更高(无握手往返),异步的适应性更好(能接快慢不同的设备);对慢设备,异步反而比"为迁就它而被拉长的同步总线"更快。
例 3:单总线为什么喂不饱现代 CPU(带宽不匹配)
某 CPU 主频 1 GHz。理想情况下每个时钟周期取出一条 4 B 指令,另有平均 0.5 次 4 B 的数据访存。求这台 CPU 需要的存储器带宽,并与例 1 的 264 MB/s 单总线对比。
完整计算过程:
第一步,每周期需要的字节数:
第二步,换算成带宽:
第三步,与单总线对比:
结论:CPU 需要的带宽是单总线能提供的 22.7 倍——差了整整一个数量级还多。 若真的把 CPU 直连一条 264 MB/s 的总线,它每跑 1 个周期就要等 22 个周期的数据,主频再高也没用。
现实中的三层补救(这三层缺一不可):
| 层次 | 手段 | 效果 |
|---|---|---|
| 第 1 层:Cache | 把常用数据放在 CPU 内部的高速存储器,大部分访存不出芯片 | 命中率 95% 时,对总线的压力只剩 5% |
| 第 2 层:多通道 / 更宽更快的内存总线 | 多通道 DDR、64 位宽、几百 MHz | 带宽提到几十 GB/s 量级 |
| 第 3 层:点对点总线 | PCIe、HyperTransport 取代共享总线 | 摆脱"同一时刻只有一个主方"的硬上限 |
这道题是第 10 篇"存储层次"与第 12 篇"Cache"的存在理由:层次结构要解决的不是"存储器太慢",而是"总线根本给不了那么多带宽"。
例 4:总线周期利用率与链式查询分析
某同步总线一个读周期含"地址 1 拍 + 数据 1 拍",此外每周期固定插入
个等待拍(等从设备就绪)。总线时钟 50 MHz(20 ns/拍)。求 时的总线利用率与实际带宽。
完整计算过程:
第一步,总线周期拍数:
第二步,利用率(只有地址拍与数据拍是"有效传输",等待拍是空的):
第三步,带宽(每次传 4 B):
逐项计算:
| 周期拍数 | 周期时长 | 利用率 | 带宽 | |
|---|---|---|---|---|
| 0 | 2 | 40 ns | $2/2 = $ 100.0% | 100.0 MB/s |
| 1 | 3 | 60 ns | $2/3 = $ 66.7% | 66.7 MB/s |
| 2 | 4 | 80 ns | $2/4 = $ 50.0% | 50.0 MB/s |
| 3 | 5 | 100 ns | $2/5 = $ 40.0% | 40.0 MB/s |
结论:每多插 1 个等待拍,利用率就掉一档(
链式查询的补充分析(承接例 4 的"空闲"问题):
| 现象 | 原因 | 后果 |
|---|---|---|
| 离控制器近的设备总赢 | BG 逐级下传,先到先得 | 近处设备可能"饿死"远处设备 |
| 链上某设备故障 → 其后全瘫 | BG 传不过去 | 可靠性差,这是链式查询最大的软肋 |
| 响应慢 | BG 要一级一级传 | 设备数越多越慢 |
三种仲裁的取舍:
记住这个"反比":线越多 → 越灵活越快,这就是典型的"空间换时间"。
例 5:Python——总线带宽、定时与仲裁线数验算
import math
def bw(width_bits, mhz):
"""并行总线带宽: 宽度(位) x 频率(MHz) -> MB/s"""
return width_bits / 8 * mhz
print('=== 例 1: 总线带宽 ===')
for w, f in [(32, 66), (64, 66), (32, 133), (64, 133), (64, 400)]:
print(' %2d 位 @ %3d MHz -> %7.1f MB/s = %.3f GB/s' % (w, f, bw(w, f), bw(w, f) / 1000))
base = bw(32, 66)
print(' %d 位@66MHz = %.0f MB/s ; 要超 512 MB/s: 改 64 位得 %.0f, 或提到 133MHz 得 %.0f'
% (32, base, bw(64, 66), bw(32, 133)))
print('\n=== 例 2: 同步 vs 异步 (每次 4 B) ===')
sync_ns = 2 * 20.0
print('同步 50 MHz, 2 拍/次 = %.0f ns -> %.1f MB/s (与设备快慢无关)'
% (sync_ns, 4 / sync_ns * 1000))
FIXED = 20 + 20 + 10 # 命令 + 传数据 + 收尾
for dev in [0, 20, 100, 400]:
tot = FIXED + dev
print('异步 从设备 %3d ns -> 周期 %3d ns -> %6.1f MB/s, 协议开销占 %5.1f%%'
% (dev, tot, 4 / tot * 1000, FIXED / tot * 100))
print(' -> 异步带宽上限(从设备 0 延迟) = %.1f MB/s, 低于同步的 %.1f MB/s'
% (4 / FIXED * 1000, 4 / sync_ns * 1000))
print('\n=== 例 3: CPU 需要多少存储带宽 ===')
need = (4 + 0.5 * 4) * 1e9 / 1e9 # GB/s
print('1 GHz, 每周期 4B 取指 + 0.5 次 4B 访存 -> %.1f GB/s' % need)
print('单总线 32 位@66MHz = %.3f GB/s -> 差 %.1f 倍' % (base / 1000, need / (base / 1000)))
print(' 补救: Cache 命中率 95%% 时, 对总线压力只剩 %.1f%%' % 5.0)
print('\n=== 例 4: 总线周期利用率 ===')
for w in [0, 1, 2, 3]:
tot = 2 + w
print(' 等待 %d 拍 -> 周期 %d 拍(%3d ns), 利用率 %5.1f%%, 带宽 %5.1f MB/s'
% (w, tot, tot * 20, 2 / tot * 100, 4 / (tot * 20) * 1000))
print('\n=== 例 5: 三种集中式仲裁的控制线数 (n 个设备) ===')
for n in [4, 8, 16, 32]:
chain = 3
counter = math.ceil(math.log2(n)) + 2
indep = 2 * n + 1
print(' n=%2d -> 链式查询 %d 根, 计数器定时查询 %d 根, 独立请求 %d 根'
% (n, chain, counter, indep))
print(' 线数 链式 < 计数器 < 独立请求 ; 速度与灵活性反过来')
print(' 链式查询软肋: BG 逐级下传 -> 链上某设备故障, 其后设备全部瘫痪')
python 本站为静态站,不提供在线运行;可复制到本地用 gcc / python 执行
输出对照:例 1 得 264 / 528 / 532 / 1064 / 3200 MB/s(64 位@66MHz 或 32 位@133MHz 都能过 512);例 2 得同步 100 MB/s,异步在
考点
考点
1. 必背公式与结论
- 总线带宽 = 总线宽度(字节) × 总线工作频率;通用式
。 - 可寻址单元数 =
;按字节编址与按字编址会改变地址线根数。地 址 线 根 数 - 数据总线双向、地址总线单向、控制总线"每根单向、整体双向"。
- 集中式仲裁三种:链式查询(3 根线,距离定优先级,故障敏感)、计数器定时查询(
根,优先级可编程)、独立请求( 根,最快最灵活)。 - 总线定时四种:同步(统一时钟、快但不迁就慢设备)、异步(握手、适应性强)、半同步(同步 + WAIT)、分离事务(拆两段,提高利用率)。
- 异步三种握手:不互锁(谁都不等)、半互锁(主方等)、全互锁(双方都等,最慢最可靠)。
- 总线复用(地址/数据共用线)省引脚但等效带宽下降。
2. 高频陷阱
- 带宽单位混乱:
与 (1 B = 8 bit)。USB 2.0 的 480 Mb/s = 60 MB/s,别答成 480 MB/s。这是本考点第一大失分点。 - 把"总线周期"直接当"时钟周期":一个总线周期可能含多个时钟周期(例 4 里
时是 5 拍)。题目给"每周期传 1 字"时两者才相等。 - 地址总线宽度算错编址方式:4 GB 按字节编址要 32 根,**按字(4 B)**编址只要 30 根。
- 链式查询的"故障敏感"漏答:链上某设备故障 → 其后所有设备不能使用,这是它区别于另外两种仲裁的标志性缺点。
- 把"独立请求"的线数答成
:是每设备一对,共 根(含 1 根公共线)。 - 以为"同步总线一定比异步快":对慢设备,异步可能更快(不必为迁就最慢者而拉长固定周期)。准确的对比要带前提。
- 三种握手的"谁等谁"记混:不互锁谁都不等;半互锁只有主方等;全互锁两边都等。
- 总线复用后带宽"减半"的粗算不能滥用:减半只是"一趟劈成两趟"的粗略说法,具体要按题目给的周期划分算。
- 把 PCIe 当"共享总线":PCIe 是点对点串行链路 + 交换开关,不是共享总线,不能用
直接算,要按 lane 数与每 lane 速率算。 - "提高总线频率就能无限提高带宽":受信号完整性限制(线间串扰、时钟偏斜、传输线效应),这正是并行总线向串行演进的原因。
3. 解题模板("总线计算题")
① 问带宽 -> BW = 宽度(字节) x 频率 ; 或 = 一次传输字节 / 总线周期时长
② 问地址线 -> 位数 = log2(可寻址单元数) ; 先确认按字节还是按字编址
③ 问定时耗时 -> 同步: 周期 = 节拍数 x 时钟周期
异步: 周期 = 固定协议开销 + 从设备延迟
④ 问利用率 -> eta = 有效传输拍 / 总线周期总拍
⑤ 问仲裁 -> 报线数 + 报优先级规则 + 报故障特性4. 与相邻章节的接口
- 第 10~12 篇(存储系统 / Cache):存储总线是"CPU—主存"这条路;层次结构的根本动因之一就是"总线带宽给不了那么快"(例 3 的 22.7 倍)。Cache 命中率决定了到底有多少访存要走到总线上。
- 第 33 篇(异常与中断):中断请求线、中断应答线(INTA)都是控制总线的一部分;链式查询识别中断源与总线的链式仲裁是同一套电路思想——
BG逐级下传就是INTA逐级下传。 - 第 41 篇(I/O 系统):DMA 的"周期挪用"抢的就是总线使用权;本章的仲裁与定时机制,正是 DMA 与 CPU 争用总线的规则基础。
- 第 30~32 篇(CPU / 流水线):取指要占总线,"每周期取一条指令"能不能兑现,取决于总线带宽;多总线结构(取指总线 + 数据总线分离)就是最原始的"结构冒险"解法。
小结
- 总线 = 共享 + 分时。好处是线少、易扩展,代价是同一时刻只有一个主方。
- 带宽 = 宽度(字节) × 频率;例 1 中 32 位 @66 MHz = 264 MB/s,64 位或 133 MHz 都能翻到 528/532 MB/s。
- 地址总线单向、数据总线双向、控制总线每根单向;可寻址单元数
,编址方式会改这个数。地 址 线 数 - 集中式仲裁三种:链式(3 根线、距离定优先级、故障敏感)、计数器(线数居中、优先级可编程)、独立请求(
根、最快最灵活)。 - 同步快但有上限、异步灵活但被协议开销封顶:例 2 中同步 100 MB/s,异步零延迟上限只有 80 MB/s;但慢设备场景下异步更合适。
- 单总线喂不饱 CPU:例 3 里 CPU 需要 6 GB/s,单总线只有 264 MB/s,差 22.7 倍——这是 Cache 与点对点总线存在的理由。
- 并行 → 串行、共享 → 点对点,是总线演进的两条主线;PCIe 已不是共享总线。
评论(0)
当前浏览器不允许本地存储,评论无法保存。
还没有评论,来说两句。