本章目录 33 节

← 返回课程首页

第 17 章 数据通路与控制器

1. 本章要解决的问题

第 16 章的有限状态机擅长回答“下一拍做什么”。当任务还要保存多个操作数、选择运算、移动数据并保存结果时,只用状态图会很快变得混乱。

考虑计算

\[ R=(A+B)-C. \]

一种直接实现是把加法器和减法器串起来,在一个周期内完成全部运算。另一种实现只放一个加减法器:第一拍计算 \(A+B\),保存中间结果;第二拍再减去 \(C\)。第二种结构需要回答:

这就是数据通路(datapath)与控制器(controller)的分工。本章回答八个问题:

  1. 什么属于数据,什么属于控制?
  2. 寄存器、MUX、ALU 和总线怎样组成数据通路?
  3. 怎样用寄存器传输描述每个时钟边沿的动作?
  4. 控制器怎样向数据通路发出选择、运算和写使能信号?
  5. 数据通路怎样把比较结果和状态标志送回控制器?
  6. 怎样把多周期运算拆成状态与微操作?
  7. 单周期、多周期和流水线结构怎样权衡资源、周期、延迟与吞吐率?
  8. 怎样检查一个“控制器 + 数据通路”系统的功能和时序?

2. 与第 16 章和阶段复习三的联系

阶段复习三的四拍定时控制器已经包含两类状态:

前者回答“现在该做什么”,后者保存“已经处理了多少数据”。把二者明确分开后:

第 16 章主要研究控制状态。本章把控制状态与数据状态放在同一张结构图中,形成寄存器传输级(register-transfer level, RTL)的系统视角。这里的 RTL 先是一种硬件思考方法;第 19 章再把它写成 Verilog/SystemVerilog。

3. 前置知识快速检查

  1. 带使能寄存器在 EN=0 的有效边沿应写入新数据还是保持旧值?
  2. 2 选 1 MUX 的作用是什么?
  3. ALU 可以完成哪些基础操作?
  4. Moore FSM 的输出取决于当前状态,还是还直接取决于当前输入?
  5. 两个寄存器在同一边沿更新时,右侧表达式读取边沿前还是边沿后的值?

答案是:保持旧值;选择一路数据送往输出;算术、逻辑、比较或移位等由接口规定的操作;只取决于当前状态;读取边沿前的旧值。若第 1、5 题不熟,回看第 12 章;若第 2、3 题不熟,回看第 8、9 章;若第 4 题不熟,回看第 16 章。

4. 学习目标

完成本章后,你应能够:

5. 先把系统拆成两部分

5.1 数据通路:保存、选择、运算和传送数据

数据通路由真正承载多 bit 数据的部件和连接组成,常见部件包括:

数据通路并不自行理解“任务已经进行到第几步”。它只按照当前控制信号完成选择、计算和写入。

5.2 控制器:决定这一拍允许什么动作

控制器通常是 FSM。它读取:

它输出:

观察图 17-1 时,先看箭头方向。紫色箭头从控制器进入数据通路,表示“命令”;绿色箭头返回控制器,表示“运算条件或状态”。多 bit 数据通常留在数据通路内部,不必穿过控制器。

数据通路与控制器的双向关系

图 17-1 控制器发出选择和写使能,数据通路返回状态,二者共享时钟完成逐拍协作

立即检查

count==3 的比较结果应属于控制信号还是状态信号?

它由数据通路中的计数器与比较器产生,并送给控制器作转移判断,因此是状态信号。控制器随后产生的 count_en 才是控制信号。

6. “数据”和“控制”取决于它在接口中的角色

不要只按位宽区分数据与控制。4 bit 操作码通常是控制;1 bit 串行数据仍然是数据。判断方法是看信号表达什么:

例题 1:给接口信号分类

某模块包含 din[7:0]loadalu_op[1:0]zeroresult[7:0]done

zero 虽然只有 1 bit,却不是控制器发出的命令。

变式:zero 只作为最终用户输出、不参与控制转移,它仍是数据通路根据数据产生的状态标志,只是接收者从控制器变成模块外部。

7. 一个寄存器写入动作背后有什么硬件

写出“需要时更新,否则保持”时,硬件不能依靠软件式的“什么都不做”。寄存器 D 输入前通常需要一个 MUX:

\[ R^{+}= \begin{cases} D, & ld_R=1,\\ R, & ld_R=0. \end{cases} \]

这里:

对每一位都可写成 MUX 关系:

\[ R^{+}=ld_R\cdot D+\overline{ld_R}\cdot R, \]

其中乘号和加号表示逐位的 AND 与 OR 选择,不是把两个多 bit 数做普通算术加法。由于 \(ld_R\)\(overline{ld_R}\) 不会同时为 1,两路不会同时被选择。

图 17-2 把一句寄存器传输写法展开成 MUX、寄存器和反馈路径。观察 ld_R=0 时的粗线:旧 \(R\) 被选回 D 输入,所以边沿后保持。

带装载使能寄存器的MUX反馈结构

图 17-2 装载使能通过 MUX 在新数据与旧状态之间选择,时钟边沿执行实际写入

例题 2:读取带装载使能的寄存器

4 bit 寄存器 \(R\) 初始为 0101。三个连续上升沿前的 \((ld_R,D)\) 依次为:

(0,1111),(1,1010),(0,0011)

逐拍结果:

  1. 第一拍 ld_R=0,MUX 选择旧 \(R\),边沿后仍为 0101
  2. 第二拍 ld_R=1,MUX 选择 1010,边沿后变为 1010
  3. 第三拍 ld_R=0,即使 \(D=0011\),边沿后仍为 1010

变式: 若第三拍 ld_R=1,结果才会更新为 0011

8. 寄存器传输与微操作

寄存器传输(register transfer)描述一个有效边沿把数据从哪里送到哪个寄存器。常用写法为

\[ R_B\leftarrow R_A. \]

它表示:在规定控制条件成立的有效边沿,\(R_B\) 写入边沿前 \(R_A\) 的值。箭头不是组合连线,也不表示数据在整周期中持续流动。

微操作(micro-operation)是在一个时钟步骤内对寄存器数据执行的基础操作,例如:

R_B ← R_A              传送
R_T ← R_A + R_B        加法后保存
R_A ← R_A + 1          加一
R_S ← R_S >> 1         右移
R_Z ← 0                清零

若写成条件形式:

EN: R_B ← R_A

表示只在 EN=1 的有效边沿执行,否则 \(R_B\) 保持。

同一拍的多个传输读取旧值

若某一拍同时规定

R_A ← R_B
R_B ← R_A

两个寄存器会交换旧值。它们共享时钟并在同一边沿采样:\(R_A\) 读取旧 \(R_B\)\(R_B\) 读取旧 \(R_A\)。这与程序按行执行完全不同。

同一拍能否执行多个微操作

可以,只要硬件资源和连接支持。例如两个独立寄存器可在同一边沿分别装载。但若两个操作同时要求使用唯一一台 ALU 完成不同运算,就发生资源冲突,必须拆到不同周期或增加第二台 ALU。

9. 总线把多 bit 数据成组传送

总线(bus)是一组并行信号线。例如 8 bit 总线有 8 条逻辑信号,共同传送一个 8 bit 位模式。图中一条粗线只是绘图简写,不是物理上只有一根线。

多个源要驱动同一内部数据入口时,基础同步设计通常使用 MUX 明确选择唯一来源:

bus = MUX(sel, R_A, R_B, R_C, ALU_out)

一个选择值只接通一个数据源。若多个普通逻辑输出直接并联到同一节点并给出相反电平,会形成电气冲突。

在很多现代 ASIC 和 FPGA 设计流程中,芯片内部的“共享总线”最终由 MUX 网络实现。三态驱动更多出现在芯片引脚或特定工艺结构中;本章内部数据通路统一按 MUX 理解。

10. 贯穿设计:计算 \(R=(A+B)-C\)

现在设计一个 4 bit 无符号多周期运算模块。输入为 \(A_{in}\)\(B_{in}\)\(C_{in}\)start,输出为 \(R_{out}\)busydone。所有输入先假定与 CLK 同步。

接口规格:

  1. 空闲时采到 start=1,同时装载三个输入;
  2. 下一拍计算 \(A+B\),只保留 4 bit 结果,最高进位单独保存为 carry_add
  3. 再下一拍计算临时结果减 \(C\),保存 4 bit 结果;
  4. 完成状态持续一个周期,done=1
  5. 运行期间忽略新的 start
  6. 高有效同步复位优先级最高,使状态回空闲并清零全部数据寄存器。

本例故意使用固定 4 bit 运算,溢出按模 16 截断。若接口需要数学整数结果,就应扩宽中间结果与输出,这属于规格选择,不是控制器能够补救的问题。

11. 第一步:列出需要保存的数据

运算跨越多个周期,外部输入不能默认一直不变。启动时先把它们复制进内部寄存器:

为什么不能只保存 \(R_T\)?因为装载输入与执行加法之间有一个时钟边界。若外部 \(A_{in}\)\(B_{in}\) 在任务期间改变,而内部没有输入寄存器,第二拍的运算可能混入新数据。

例题 3:识别必须保存的值

若接口额外规定:“从接受 startdone 期间,外部必须保持 \(A_{in}\)\(B_{in}\)\(C_{in}\) 不变”,内部可直接在后续周期读取这些端口,减少三个输入寄存器。

代价是模块把保持责任交给上游,接口耦合更强。本章采用启动拍内部捕获,使任务接受后不再依赖外部输入。

变式: 若只规定 \(C_{in}\) 保持稳定,则仍应保存 \(A_{in}\)\(B_{in}\),而 \(C\) 可直接从接口读取。结构必须与接口契约一致。

12. 第二步:排出逐拍寄存器传输

把任务拆成三个工作步骤:

LOAD:R_A ← A_in,R_B ← B_in,R_C ← C_in
ADD: {R_K,R_T} ← R_A + R_B
SUB: R_R ← R_T - R_C

完成指示另占一个 DONE 状态。控制状态可定义为:

LOAD 不必单独占一个状态。IDLE 中采到 start=1 的边沿可以同时装载输入并进入 ADD。控制状态和寄存器数据都在该边沿后更新。

图 17-3 展示支持这些传输的最小数据通路。先沿蓝色粗线看数据,再沿紫色虚线看选择与装载。ALU 左输入在 \(R_A\)\(R_T\) 中选择,右输入在 \(R_B\)\(R_C\) 中选择;同一台 ALU 分两拍复用。

四位多周期加减运算数据通路

图 17-3 输入寄存器、两个操作数 MUX、共享 ALU、临时寄存器和结果寄存器组成的数据通路

13. 第三步:把微操作翻译成控制信号

定义以下控制信号:

信号 作用
ld_in 同时装载 \(R_A\)\(R_B\)\(R_C\)
sel_x 0 选 \(R_A\),1 选 \(R_T\)
sel_y 0 选 \(R_B\),1 选 \(R_C\)
alu_sub 0 做加法,1 做减法
ld_t 把 ALU 的低 4 bit 结果写入 \(R_T\),并把最高进位写入 \(R_K\)
ld_r 把 ALU 结果写入 \(R_R\)

把同一周期需要的控制位排成一行,称为控制字(control word)。本例可写为:

控制状态/条件 ld_in sel_x sel_y alu_sub ld_t ld_r 微操作
IDLE & start 1 X X X 0 0 装载三个输入
ADD 0 0 0 0 1 0 \(\{R_K,R_T\}\leftarrow R_A+R_B\)
SUB 0 1 1 1 0 1 \(R_R\leftarrow R_T-R_C\)
IDLE 未启动 0 X X X 0 0 全部数据寄存器保持
DONE 0 X X X 0 0 结果保持,给出完成指示

表中的 X 表示这一拍对应选择不会被任何寄存器采样,因此对可见状态更新没有影响。它不是模拟不确定电压,也不是允许多路同时驱动。实际 RTL 常给这些位确定默认值,以避免锁存器和无谓翻转。

例题 4:从微操作得到控制字

要执行

\[ R_R\leftarrow R_T-R_C, \]

必须:

  1. sel_x=1,把 \(R_T\) 送到 ALU 左端;
  2. sel_y=1,把 \(R_C\) 送到 ALU 右端;
  3. alu_sub=1,选择减法;
  4. ld_r=1,允许结果寄存器在边沿写入;
  5. ld_t=0ld_in=0,其余数据寄存器保持。

只设置 alu_sub=1 不会自动保存结果;只设置 ld_r=1 而 MUX 选择错误,则会把错误运算写入 \(R_R\)

变式: 加法拍对应 sel_x=0sel_y=0alu_sub=0ld_t=1;同一边沿保存低 4 bit 和最高进位。

14. 第四步:控制器状态图与输出译码

控制器状态转移为:

IDLE: start=0 → IDLE
IDLE: start=1 → ADD,同时 ld_in=1
ADD:  无条件 → SUB
SUB:  无条件 → DONE
DONE: 无条件 → IDLE

busy=1 可定义在 ADDSUB 状态;done=1 只定义在 DONE 状态。ld_tld_r、MUX 选择和 ALU 操作都可由当前状态译码产生,是 Moore 控制。ld_in 依赖 IDLE & start,是一个有条件的装载脉冲,时间上必须满足输入与寄存器的采样要求。

图 17-4 把状态、每拍控制动作和数据通路反馈放在一起。这个例子没有数据相关分支,所以数据通路暂时不必返回 zerocarry 来决定转移;carry_add 只作为结果状态保存。若改成“结果为零就提前结束”,zero 就会成为数据通路返回控制器的状态信号。

多周期运算控制器状态与控制动作

图 17-4 控制状态给每一拍分配唯一的寄存器传输,完成状态提供整周期 done

15. 第五步:用具体数值逐拍验证

例题 5:计算 \(R=(3+5)-2\)

A_in=0011,B_in=0101,C_in=0010

初始状态 IDLE,所有内部寄存器为 0。在第 1 个边沿采到 start=1

边沿后 控制状态 \(R_A\) \(R_B\) \(R_C\) \(R_T\) \(R_K\) \(R_R\) done
0 IDLE 0 0 0 0 0 0 0
1 ADD 3 5 2 0 0 0 0
2 SUB 3 5 2 8 0 0 0
3 DONE 3 5 2 8 0 6 1
4 IDLE 3 5 2 8 0 6 0

边沿 1 只装载输入并进入 ADD;边沿 2 才保存加法结果;边沿 3 保存减法结果并进入 DONE。输出寄存器在回到 IDLE 后继续保持 6,直到下一项任务的 SUB 拍更新。

图 17-5 画出同一过程。注意控制状态名称表示“这一周期将执行的动作”:处于 ADD 周期时组合 ALU 计算加法,在该周期末的边沿把结果写入 \(R_T\) 并进入 SUB

多周期加减运算逐拍时间线

图 17-5 输入装载、加法、减法和完成指示分布在连续时钟边界上

变式: 若输入为 \(A=14\)\(B=5\)\(C=3\),4 bit 加法得到 \(R_T=3\)carry_add=1;随后 \(R_R=0\)。若希望得到数学结果 16,必须扩宽中间和与减法通路。

16. 控制状态与数据状态不要混为一谈

在上述系统中:

有限状态机加上受控数据通路,常称为带数据通路的有限状态机(finite-state machine with datapath, FSMD)。设计时分开画图,验证时必须一起逐拍检查,因为控制状态决定谁写入,而数据寄存器的内容又可能决定控制状态怎样分支。

一个判断标准

如果某个量的每种可能数值都用一个 FSM 状态表示,状态数可能迅速膨胀。例如 8 bit 计数值有 256 种,把它们全部画成控制状态没有教学或实现收益。更清楚的做法是:

17. 资源共享:用时间换硬件

资源共享(resource sharing)是让多个不同周期的微操作复用同一硬件资源。本章贯穿例子让加法和减法复用一台 ALU,因此需要两个运算周期。

共享成立的前提是这些操作不会要求在同一周期同时发生。控制器通过 MUX 选择操作数,并通过 alu_op 选择功能。共享通常带来:

“一个 ALU 一定比两个 ALU 省一半面积”不是可靠结论。MUX、寄存器、控制器和布线同样占用资源,综合结果还取决于位宽、库单元和时序目标。

18. 时钟周期、延迟和吞吐率是三个量

时钟周期

\(T_{clk}\) 是相邻有效边沿之间的时间。它必须覆盖本周期最长的寄存器到寄存器组合路径及建立时间预算。

延迟

延迟(latency)是一次任务从接受到结果可用所需的时间。若固定需要 \(N_{cycle}\) 个周期,可写成

\[ t_{latency}=N_{cycle}T_{clk}. \]

这里必须先明确起点与终点。例如本例从接受 start 的边沿到 done=1 的边沿经历 2 个完整周期;若把启动前的准备周期也算入,口径会不同。

吞吐率

吞吐率(throughput)描述单位时间可完成多少项任务。若每隔 \(II\) 个周期才能接受一项新任务,其中 \(II\) 是启动间隔(initiation interval),理想吞吐率为

\[ Throughput=\frac{1}{II\cdot T_{clk}}=\frac{f_{clk}}{II}. \]

本例在 DONE 后才回到 IDLE,若新任务只能在空闲边沿接受,必须按接口逐拍确定 \(II\)。缩短时钟周期不一定能抵消额外的多周期等待。

19. 单周期、多周期与流水线

三种结构可能实现相同数学功能,却具有不同硬件组织。

单周期

加法器与减法器串联,一项任务在一个长周期内完成。优点是控制简单、每周期可接受一项任务;缺点是组合路径长,并需要两套算术硬件。

多周期

使用一个共享 ALU,分加法拍和减法拍。单周期组合路径缩短,硬件可复用;一项任务占用多个周期,且同一 ALU 忙碌时不能同时处理另一项任务。

流水线

加法器和减法器之间插入寄存器。第一项任务进入减法级时,第二项任务可以同时进入加法级。流水线把长组合路径分段:

观察图 17-6 时,横向看一项任务经过多少周期,纵向看同一周期有几项任务并行。多周期结构在时间上复用一台 ALU;流水线结构在空间上保留多个运算级,让不同任务重叠。

单周期多周期与流水线比较

图 17-6 单周期压入一个长组合周期,多周期复用硬件,流水线用级间寄存器换取任务重叠

例题 6:用给定延迟比较三种实现

题设给出教学用简化参数:4 bit 加法器最大延迟 300 ps,减法器最大延迟 320 ps,操作数 MUX 最大延迟 60 ps,每一级寄存器的 \(t_{cq,max}+t_{setup}\) 及其他时钟预算合计 100 ps。

  1. 单周期串联:

    \[ T_{clk,min}=300+320+100=720\ \text{ps}. \]

  2. 共享 ALU 多周期:每个运算周期先经过 MUX,再经过最慢 ALU 操作:

    \[ T_{clk,min}=60+320+100=480\ \text{ps}. \]

    加法和减法占两个运算周期,所以仅运算部分至少需要 \(2\times480=960\) ps。

  3. 两级流水线:使用独立加、减法级,中间插入寄存器:

    \[ T_{clk,min}=\max(300,320)+100=420\ \text{ps}. \]

    一项任务的两级延迟为 \(2\times420=840\) ps;流水线填满后可达到每 420 ps 接受一项的理想吞吐节奏。

这个例子说明:最短时钟周期、单项任务延迟和连续任务吞吐率不会自动同时最优。

变式: 若级间寄存器预算很大,过度切分流水级可能不再提高频率,因为每一级都要承担寄存器开销。

20. 控制器与数据通路之间的组合环路

合法的同步结构可以形成跨周期反馈:

状态寄存器 → 控制逻辑 → 数据通路 → 状态信号 → 下一状态逻辑 → 状态寄存器

中间只要由寄存器在周期边界截断,就是普通寄存器到寄存器路径。危险结构是同一周期内没有寄存器隔开的组合环路,例如:

控制信号 → ALU/MUX → zero → 控制信号

如果 zero 立即改变 MUX 选择,而新的 MUX 选择又立即改变 zero,组合网络可能没有稳定的单向求值关系。设计时应让控制输出由当前状态与已定义输入产生,并通过下一个边沿更新状态;若需要数据相关分支,确保组合依赖无环。

21. 数据相关分支怎样工作

把贯穿例子改为“若 \(A+B=0\),跳过减法并直接完成”。数据通路在 ADD 周期形成 ALU 结果并产生 zero_alu。有两种清楚的实现:

  1. ADD 周期末同时把和写入 \(R_T\),并由控制器在该边沿采样稳定的 zero_alu,选择下一状态 DONESUB
  2. 先保存 \(R_T\),下一周期用寄存后的 zero_t=(R_T=0) 决定转移。

第一种少一拍,但 ADD 周期的控制路径包含“寄存器 → MUX/ALU → 零检测 → 下一状态逻辑 → 状态寄存器”,可能成为关键路径。第二种多一拍,时序边界更简单。选择取决于周期目标和接口延迟要求。

22. 接口握手:何时可以接受,何时结果有效

一个多周期模块至少要明确三件事:

start/busy/done 是简化握手接口。它不是唯一方案,但足以建立周期契约。若 start 只高一拍且模块当时忙碌,事件会被丢弃;若系统不允许丢事件,就需要请求保持、队列或请求—应答协议。

23. 从功能结构回到物理时序

数据通路与控制器分开画,不代表它们在时序上互不影响。至少检查下列路径:

  1. 数据寄存器 → MUX → ALU → 数据寄存器;
  2. 数据寄存器 → 比较器 → 下一状态逻辑 → 状态寄存器;
  3. 状态寄存器 → 控制译码 → MUX/ALU → 数据寄存器;
  4. 状态寄存器 → 下一状态逻辑 → 状态寄存器;
  5. 外部同步输入 → 控制逻辑 → 状态或数据寄存器。

每条路径分别做最大延迟建立检查和最小延迟保持检查。控制器很小,不代表控制路径一定短;高扇出装载使能、复杂状态译码和跨模块布线都可能增加延迟。

24. 完整设计流程

面对一个多周期数字任务时,按下面顺序工作:

  1. 写清接口契约。 规定输入何时有效、请求何时接受、结果何时有效、忙碌时怎样处理新请求;
  2. 列出必须保存的数据。 包括输入副本、中间结果、计数值和最终结果;
  3. 列出需要的微操作。 传送、加减、移位、比较、清零分别写清;
  4. 安排逐拍调度。 同一拍的操作必须有足够硬件且没有资源冲突;
  5. 画数据通路。 放置寄存器、MUX、ALU、比较器和总线;
  6. 定义控制信号与状态信号。 每个信号只有一个明确方向和含义;
  7. 建立控制状态。 一个状态对应一组清楚的周期动作;
  8. 列控制字或输出表。 对每个状态给出所有写使能、选择与操作码;
  9. 定义复位、默认值和非法恢复。 数据与控制状态都进入已知安全行为;
  10. 逐拍验证。 同时记录控制状态、寄存器值、控制字和接口输出;
  11. 检查边界情况。 位宽截断、零值、最大值、重复启动、暂停或错误输入;
  12. 检查 CDC 与时序。 先同步异步输入,再对全部寄存器路径做建立/保持检查。

25. 常见误区与反例

误区 1:数据通路就是“很多组合逻辑”

数据通路通常包含大量寄存器。它不仅计算数据,也保存操作数、中间结果和输出。

误区 2:控制器保存全部数据

FSM 状态只保存控制阶段。把每个数据值展开为控制状态会导致状态爆炸;多 bit 数据应保存在数据寄存器中。

误区 3:写了 \(R_A\leftarrow R_B\),数据就立即移动

寄存器传输在规定有效边沿执行。边沿之前,组合路径只是在准备候选输入。

误区 4:两行寄存器传输像程序一样从上到下执行

同一拍的寄存器同时读取旧值并更新。交换两个寄存器不需要临时变量,只要数据通路支持双向输入选择。

误区 5:ALU 选择正确,结果就会自动保存

组合 ALU 只产生候选结果。目标寄存器的装载使能必须在正确边沿有效。

误区 6:未使用的控制位保持 X 最省事

表格中的 X 表示功能无关。实际组合控制应给出确定默认值,减少锁存器推断、未知传播和无谓切换。

误区 7:共享一台 ALU 只会节省面积

共享增加 MUX、控制和完成周期,也可能改变功耗与关键路径。它是资源、性能和复杂度之间的权衡。

误区 8:时钟更快就代表一项任务完成得更快

任务延迟等于周期数与周期时间的共同结果。多周期结构的时钟更短,任务仍可能需要更长总时间。

误区 9:流水线一定降低单项任务延迟

流水线主要提高吞吐率和可达频率。级间寄存器会增加周期边界,单项任务延迟可能增加。

误区 10:done=1 时结果自然有效

接口必须保证 done 与结果寄存器的更新关系。若二者来自不同组合路径,还要检查下游采样时刻;本章用寄存结果配合 Moore DONE 状态提供清楚边界。

误区 11:外部输入可以在后续运算拍继续直接读取

只有接口明确要求上游保持数据时才成立。更独立的模块在接受请求时捕获输入。

误区 12:数据状态和控制状态分开后可以分别验证

错误常发生在二者交界:控制信号晚一拍、MUX 选错、写使能漏开、状态标志取自错误数据。逐拍表必须同时记录两侧。

26. 工程中的实际意义

数据通路与控制器的划分出现在大量数字系统中:

这个划分也使验证更有条理。控制器检查“状态和优先级是否正确”,数据通路检查“运算和传送是否正确”,系统级检查“正确控制是否作用在正确数据上”。第 19、20 章会把这些规则写成 RTL 与自检查测试。

27. 与贯穿式交通信号项目的连接

贯穿式综合项目可以按本章方法划分:

time_up=1 时,数据通路没有权力自行跳到下一灯光阶段;它只报告计时结束。控制器根据当前阶段、安全规则和车辆请求决定下一状态,并命令计时器清零。这正是图 17-1 的双向协作。

28. 本章知识链

文字任务与接口契约
        ↓
列出要保存的数据 + 列出每步微操作
        ↓
逐拍调度,检查资源冲突
        ↓
数据通路:寄存器 + MUX + ALU/比较器 + 总线
        ↕
控制信号向下,状态信号向上
        ↕
控制器:状态寄存器 + 下一状态逻辑 + 控制字
        ↓
逐拍联合验证控制状态与数据状态
        ↓
检查接口、位宽、CDC、建立与保持
        ↓
比较单周期 / 多周期 / 流水线的资源与性能

29. 本章小结

  1. 数据通路保存、选择、运算和传送多 bit 数据,控制器决定每个周期执行哪些动作。
  2. 控制信号从控制器进入数据通路,状态信号从数据通路返回控制器。
  3. 寄存器写使能通常由输入 MUX 与反馈路径实现;未写入时选择旧值保持。
  4. 寄存器传输描述有效边沿发生的数据更新,右侧读取边沿前旧值。
  5. 微操作是一个周期内完成的基础传送或运算,多个微操作能否并行取决于硬件资源。
  6. 总线是多根并行信号的集合;多个内部数据源通常由 MUX 选择。
  7. 控制字把一拍中的写使能、MUX 选择和 ALU 操作集中列出。
  8. FSMD 的控制状态表示任务阶段,数据状态保存操作数、中间结果和计数值。
  9. 资源共享用更多周期和控制复杂度换取较少运算资源。
  10. 时钟周期、单项任务延迟与吞吐率是三个不同指标。
  11. 流水线让不同任务在不同级重叠,主要提高吞吐率,不保证降低单项延迟。
  12. 完整设计必须联合检查控制、数据、接口、位宽、CDC 和所有寄存器时序路径。

30. 练习

基础题

  1. 用一句话分别说明数据通路和控制器的任务。
  2. 列出数据通路中五类常见部件。
  3. ld_ralu_op[1:0]zerodata_in[7:0] 分别属于控制、状态还是数据?
  4. 写出带装载使能寄存器的下一值关系。
  5. 寄存器传输 \(R_B\leftarrow R_A\) 在什么时刻执行?右侧读取哪个时刻的值?
  6. 什么是微操作?给出一个传送和一个算术微操作例子。
  7. 为什么内部共享总线通常需要 MUX?
  8. 什么是控制字?

分析与计算题

  1. 4 bit 寄存器 \(R\) 初始 0110,三拍 \((ld_R,D)\)(1,1001)、(0,1111)、(1,0011),写出每拍后的 \(R\)
  2. 同一拍执行 \(R_A\leftarrow R_B\)\(R_B\leftarrow R_A\)。初始 \(R_A=3\)\(R_B=9\),边沿后分别是多少?
  3. 两个微操作 \(R_X\leftarrow R_A+R_B\)\(R_Y\leftarrow R_C-R_D\) 能否同拍完成?分别讨论只有一台 ALU和有两台独立 ALU 的情况。
  4. 对贯穿例子的控制信号,写出 ADD 状态完整控制字。
  5. 对贯穿例子输入 \(A=4\)\(B=7\)\(C=3\),写出从接受 start 到回到 IDLE 的控制状态、\(R_T\)\(R_R\)done
  6. 对输入 \(A=15\)\(B=3\)\(C=2\),按 4 bit 模运算求 \(R_T\)carry_add 和最终 \(R_R\)。再说明若想保留数学结果,应改动哪里。
  7. 一个 8 bit 计时器有控制状态 IDLE/RUN/DONE。指出控制状态、数据状态、控制信号和返回状态信号各一个例子。
  8. 某任务固定经过 5 个周期,\(T_{clk}=4\) ns。求任务延迟。若 \(II=2\),理想吞吐率是多少项/秒?
  9. 某单周期通路包含 280 ps 与 350 ps 两段串联逻辑,寄存器及预算合计 120 ps。求最小时钟周期和最高频率。
  10. 把第 17 题两段逻辑之间加入级间寄存器,每级都承担 120 ps 寄存器及预算。求流水线最小时钟周期、两级延迟和填满后的理想吞吐率。

综合题

  1. 为操作 \(R=(A\land B)+C\) 设计两周期共享数据通路:列出所需寄存器、组合单元、MUX 和逐拍微操作。
  2. 把第 19 题翻译成控制状态与控制字。至少包含输入装载、AND/保存、ADD/保存和完成指示。
  3. 一个模块在忙碌时直接忽略单周期 start。说明什么情况下会丢任务,并给出两种接口改进方法。
  4. 某控制器使用组合信号 zero 立即反过来改变生成 zero 的 ALU 输入选择。指出结构问题,并给出同步改写方向。
  5. 为贯穿式交通信号项目画出文字版“控制器—数据通路”边界:列出至少三个控制状态、两个数据寄存器、两个控制信号和两个状态信号。
  6. 独立设计一个 4 bit“重复加一到目标值”模块:启动时装载初值 init 和目标 target;每个运行拍把当前值加一;达到目标后 done=1 一周期。写出接口、数据通路、状态、逐拍规则、非法状态恢复和至少六个测试场景。规定 init=target 时怎样处理。

31. 练习答案

展开第 17 章练习答案

题 1

数据通路保存、选择、运算和传送数据;控制器根据当前阶段、输入和状态信号决定每拍的数据通路动作。

题 2

寄存器、MUX、ALU、比较器、移位器或计数器、总线均可作为答案,列出其中五类即可。

题 3

  • ld_r:寄存器装载控制;
  • alu_op[1:0]:ALU 操作控制;
  • zero:数据通路返回的状态;
  • data_in[7:0]:输入数据。

位宽不是分类依据。

题 4

\[ R^{+}= \begin{cases} D,&ld_R=1,\\ R,&ld_R=0. \end{cases} \]

也可写成 \(R^{+}=ld_R D+\overline{ld_R}R\),其中逻辑运算逐位作用。

题 5

它在控制条件成立的有效时钟边沿执行,右侧读取边沿前 \(R_A\) 保存的旧值。

题 6

微操作是一个时钟步骤内对寄存器数据执行的基础传送或运算。例如传送 \(R_B\leftarrow R_A\),算术操作 \(R_T\leftarrow R_A+R_B\)

题 7

多个数据源不能用普通逻辑输出同时直接驱动同一内部节点。MUX 根据选择信号只接通一个来源,给总线或目标输入确定值。

题 8

控制字是一组在同一周期共同定义数据通路动作的控制位,通常包含寄存器写使能、MUX 选择、ALU 操作码、计数使能等。

题 9

拍后 ld_R \(D\) \(R\)
1 1 1001 1001
2 0 1111 1001
3 1 0011 0011

题 10

两个寄存器同拍读取旧值,因此边沿后 \(R_A=9\)\(R_B=3\),完成交换。

题 11

只有一台 ALU 时,两种不同运算争用同一资源,不能同拍完成,必须分两个周期。若有两台独立 ALU,且四个源寄存器到两台 ALU、两台 ALU 到两个目标寄存器的连接和写使能齐全,则可以同拍完成。

题 12

ADD 状态控制字为:

ld_in=0,sel_x=0,sel_y=0,alu_sub=0,ld_t=1,ld_r=0

它选择 \(R_A\)\(R_B\) 做加法,并在周期末写入 \(R_T\) 与 1 bit 进位寄存器 \(R_K\)

题 13

\(4+7=11\)\(11-3=8\)

边沿后 状态 \(R_T\) \(R_R\) done
接受启动 ADD 0 0 0
加法完成 SUB 11 0 0
减法完成 DONE 11 8 1
下一拍 IDLE 11 8 0

输入寄存器在接受启动的边沿同时装载 4、7、3。

题 14

\[ 15+3=18=1\_0010, \]

所以 4 bit \(R_T=0010=2\)carry_add=1。随后

\[ R_R=2-2=0. \]

数学表达式的完整结果应为 16。要保留它,至少把中间寄存器、结果寄存器、ALU 与相关 MUX 扩为足够位宽,并明确 \(C\) 的扩展方式;只扩宽输出端不能恢复已经截断的进位。

题 15

  • 控制状态:RUN
  • 数据状态:8 bit count
  • 控制信号:count_encount_clear
  • 返回状态信号:terminal=(count==limit)

题 16

任务延迟为

\[ t_{latency}=5\times4\ \text{ns}=20\ \text{ns}. \]

理想吞吐率为

\[ \frac{1}{2\times4\ \text{ns}}=1.25\times10^8\ \text{项/s}. \]

题 17

\[ T_{clk,min}=280+350+120=750\ \text{ps}. \]

\[ f_{max}=\frac{1}{750\ \text{ps}}\approx1.333\ \text{GHz}. \]

题 18

最慢级为 350 ps,因此

\[ T_{clk,min}=350+120=470\ \text{ps}. \]

两级单项延迟为

\[ 2\times470=940\ \text{ps}. \]

填满后理想吞吐率为

\[ \frac{1}{470\ \text{ps}}\approx2.128\times10^9\ \text{项/s}. \]

它的单项延迟比原单周期 750 ps 更长,但连续任务吞吐率更高。

题 19

一种两运算周期结构包括:输入寄存器 \(R_A,R_B,R_C\),临时寄存器 \(R_T\),结果寄存器 \(R_R\),一个 AND 单元和一个加法器。若要求真正共享同一“运算单元”,可以使用支持 AND 与 ADD 的 ALU,并在两拍间复用。

逐拍微操作:

LOAD:R_A ← A,R_B ← B,R_C ← C
AND: R_T ← R_A AND R_B
ADD: R_R ← R_T + R_C

ALU 输入 MUX 左侧在 \(R_A/R_T\) 中选择,右侧在 \(R_B/R_C\) 中选择;操作码选择 AND 或 ADD。

题 20

可用 IDLE、AND、ADD、DONE 四个状态:

状态/条件 输入装载 左选 右选 ALU 操作 \(R_T\) \(R_R\) 下一状态
IDLE & start 1 X X X 0 0 AND
AND 0 \(R_A\) \(R_B\) AND 1 0 ADD
ADD 0 \(R_T\) \(R_C\) ADD 0 1 DONE
DONE 0 X X 确定默认 0 0 IDLE

done 只在 DONE 为 1。

题 21

start 只高一个周期,而该周期模块处于非 IDLE 状态,请求不会被采纳,下一周期它已消失,因此任务丢失。改进方法包括:

  1. 上游保持 start,直到模块返回 readyaccept
  2. 在模块入口增加请求寄存器或 FIFO,把忙碌期间的事件保存下来。

还可采用完整的请求—应答握手。

题 22

这形成 MUX 选择 → ALU → zero → MUX 选择 的无寄存器组合环路,输出可能无法得到唯一稳定解,也不适合普通同步时序分析。可让 MUX 选择只由当前寄存控制状态产生,把 zero 作为下一状态条件,在时钟边沿后再改变控制状态;或先把 ALU 结果/状态寄存,再在下一拍使用。

题 23

一个合格划分示例:

  • 控制状态:MAIN_GREENMAIN_YELLOWSIDE_GREENSIDE_YELLOW
  • 数据寄存器:阶段计数 timer_count、阶段限值 phase_limit
  • 控制信号:timer_entimer_clear
  • 状态信号:time_up、同步后的 vehicle_pending
  • 控制器输出:两组灯光译码与配置装载使能。

time_up 只报告比较结果,真正的灯光转移由控制状态与安全规则决定。

题 24

一种完整设计如下。

接口:输入 CLK、同步高有效 clearstartinit[3:0]target[3:0];输出 value[3:0]busydone。只在空闲接受启动,运行时忽略新启动。

数据通路:当前值寄存器 \(R_V\)、目标寄存器 \(R_T\)、4 bit 加一器、相等比较器 equal=(R_V==R_T)。控制信号为 ld_inputsinc_v;状态信号为 equal

控制状态:

  • IDLE:等待启动;
  • CHECK:检查初值是否已经等于目标;
  • RUN:每拍加一;
  • DONE:完成指示一周期。

规则:

任意状态 clear=1:state←IDLE,R_V←0,R_T←0
IDLE & start:R_V←init,R_T←target,state←CHECK
CHECK & equal:state←DONE
CHECK & !equal:state←RUN
RUN:R_V←R_V+1;若加一后的值等于目标,则下一状态←DONE,否则保持 RUN
DONE:下一状态←IDLE
非法状态:state←IDLE,并清零数据寄存器

这里规定 init=target 时不执行加一,CHECK 后直接进入 DONE。4 bit 加一按模 16 回卷,因此任意初值最终都能到达任意 4 bit 目标;若规格禁止回卷,应加入大小比较和错误状态。

至少六个测试场景:

  1. init=3,target=5,检查 3→4→5;
  2. init=7,target=7,检查直接完成;
  3. init=15,target=1,检查模 16 回卷 15→0→1;
  4. 运行期间再次 start,确认忽略;
  5. 运行中 clear=1,确认同步回空闲并清零;
  6. 强制非法控制编码,确认恢复;
  7. 检查 done 只高一个完整周期;
  8. 检查回到空闲后 value 的保持或清零行为与接口规格一致。

32. 自测清单

若第 1、7 项不稳定,请重画图 17-1,并只让多 bit 操作数留在数据通路。若第 3、4、8 项不稳定,请重做例题 5 的逐拍表。若第 9~11 项不稳定,请遮住图 17-6 的文字,重新解释三行时间格。

33. 下一章衔接

本章的数据主要保存在少量独立寄存器中。随着数据数量增加,为每个值单独画一只寄存器和一组 MUX 会迅速变得笨重。第 18 章将学习存储器基础:地址怎样从大量存储位置中选择一项,ROM、SRAM、DRAM 和寄存器文件的接口与用途有何不同,以及读写时序怎样接入数据通路与控制器。