03

时序逻辑

让电路记住状态

阅读量:6 · 预计 9 分钟读完

触发器寄存器计数器RAM
阅读进度4%

第三章 时序逻辑

导读

前两章我们讨论了组合逻辑电路,其中输出仅取决于当前输入。然而,计算机不仅需要计算,还需要记忆。时序逻辑电路引入了时间维度,使电路能够存储状态信息,这是构建计算机内存和寄存器的基础。

本章将带你从最基本的存储元件——触发器开始,逐步构建寄存器、寄存器文件和RAM。你将理解时钟信号在同步电路中的核心作用,掌握时序电路的设计方法,并为后续章节中构建处理器和计算机体系结构奠定基础。

时序逻辑是数字系统设计中最关键的概念之一。理解时序电路的工作原理,对于理解计算机如何执行程序、如何处理数据至关重要。

核心概念详解

3.1 时序逻辑基础

时序逻辑电路与组合逻辑电路的根本区别在于:时序电路的输出不仅取决于当前输入,还取决于电路的历史状态。

时序电路的数学模型

时序电路可以用以下方程描述:

状态方程:S(t+1) = f(S(t), I(t))
输出方程:O(t) = g(S(t), I(t))

其中:

  • S(t)是t时刻的状态
  • I(t)是t时刻的输入
  • O(t)是t时刻的输出

时序电路的分类

同步时序电路:状态变化由时钟信号控制,所有触发器在同一时钟边沿更新状态。

异步时序电路:状态变化不依赖时钟信号,输入变化直接导致状态改变。

在Nand2Tetris和现代计算机系统中,我们主要使用同步时序电路,因为它更可靠、更易于设计。

3.2 存储元件:触发器

触发器是时序逻辑的基本存储元件,能够存储1位二进制信息。

SR锁存器

SR锁存器是最基本的存储元件:

输入:S(Set), R(Reset)
输出:Q, Q'(Q的反)

功能:
- S=1, R=0: Q=1 (置位)
- S=0, R=1: Q=0 (复位)
- S=0, R=0: 保持原状态
- S=1, R=1: 不允许(不定态)

SR锁存器可以用两个交叉耦合的Nor门实现:

Q = Nor(R, Q')
Q' = Nor(S, Q)

D锁存器

D锁存器解决了SR锁存器的不定态问题:

输入:D(Data), enable
输出:Q

功能:
- enable=1时:Q = D (透明模式)
- enable=0时:Q保持原值 (保持模式)

D锁存器的问题是在enable=1期间,输出会随输入变化,这可能导致不稳定。

D触发器(边沿触发)

D触发器通过主从结构解决了D锁存器的问题:

输入:D, clock
输出:Q

功能:
- 时钟上升沿:Q = D
- 其他时刻:Q保持不变

D触发器只在时钟边沿采样输入,其他时间忽略输入变化,这大大提高了电路的稳定性。

在Nand2Tetris中,我们使用一个基本的1位存储元件:

hdl
CHIP FlipFlop {
    IN in;
    OUT out;
    // 在时钟上升沿:out(t+1) = in(t)
}

3.3 寄存器设计

寄存器是存储多位数据的时序逻辑电路。

1位寄存器

1位寄存器使用一个触发器实现:

hdl
CHIP Bit {
    IN in, load;
    OUT out;
    
    PARTS:
    Mux(a=out, b=in, sel=load, out=muxOut);
    FlipFlop(in=muxOut, out=out);
}

工作原理:

  • 当load=1时,在下一个时钟上升沿,out=in
  • 当load=0时,out保持不变

16位寄存器

16位寄存器由16个1位寄存器组成:

hdl
CHIP Register {
    IN in[16], load;
    OUT out[16];
    
    PARTS:
    Bit(in=in[0], load=load, out=out[0]);
    Bit(in=in[1], load=load, out=out[1]);
    // ... 16位
    Bit(in=in[15], load=load, out=out[15]);
}

所有位共享同一个load信号,实现同步加载。

特殊寄存器

在Hack计算机中,有两个特殊的寄存器:

  • A寄存器:可以存储数据或地址
  • D寄存器:只能存储数据

这些寄存器在机器语言编程中起重要作用。

3.4 RAM设计

RAM(随机存取存储器)是计算机的主要存储设备。

RAM8:8个寄存器的RAM

hdl
CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];
    
    PARTS:
    // 使用3-8译码器选择寄存器
    DMux8Way(in=load, sel=address, a=l0, b=l1, c=l2, d=l3, 
             e=l4, f=l5, g=l6, h=l7);
    
    // 8个寄存器
    Register(in=in, load=l0, out=r0);
    Register(in=in, load=l1, out=r1);
    // ... 8个寄存器
    
    // 使用8选1 Mux选择输出
    Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7,
              sel=address, out=out);
}

RAM64和更大容量RAM

通过层次化设计可以构建更大容量的RAM:

  • RAM64:8个RAM8
  • RAM512:8个RAM64
  • RAM4K:8个RAM512
  • RAM16K:4个RAM4K

这种层次化结构体现了数字系统设计的核心思想:通过组合简单组件构建复杂系统。

内存寻址

内存寻址是RAM设计的关键:

  • n位地址可以寻址2^n个存储单元
  • 每个单元存储16位数据(在Hack计算机中)
  • 地址译码器将二进制地址转换为选择信号

3.5 程序计数器(PC)

程序计数器是一种特殊的寄存器,用于存储下一条要执行的指令的地址。

PC功能规格

输入:
- in[16]:16位输入
- load:加载控制
- inc:递增控制
- reset:重置控制

输出:
- out[16]:16位输出

功能:
- 如果reset=1:out=0
- 否则如果load=1:out=in
- 否则如果inc=1:out=out+1
- 否则:out保持不变

PC实现

hdl
CHIP PC {
    IN in[16], load, inc, reset;
    OUT out[16];
    
    PARTS:
    // 根据控制信号选择输入
    // reset优先级最高
    // 然后是load
    // 然后是inc
    // 最后是保持
    
    // 使用Mux选择输入
    // 使用Register存储
    // 使用Add16实现递增
}

PC的设计体现了优先级控制的思想:reset > load > inc > hold。

3.6 时钟信号

时钟信号是同步时序电路的心脏。

时钟的特性

时钟周期:时钟信号从一个上升沿到下一个上升沿的时间间隔。

时钟频率:每秒的时钟周期数,单位是Hz。现代计算机的时钟频率通常在GHz级别。

占空比:高电平时间占整个周期的比例,通常为50%。

时钟的作用

同步:确保所有触发器在同一时刻更新状态

定时:控制数据在电路中的流动时机

协调:协调不同组件的工作节奏

时钟偏斜

在实际电路中,时钟信号到达不同触发器的时间可能不同,这称为时钟偏斜。时钟偏斜可能导致电路工作不稳定,是高速电路设计中的重要问题。

3.7 时序电路设计方法

设计时序电路需要遵循系统化的方法。

状态机设计

有限状态机(FSM)是设计时序电路的重要工具:

Mealy机:输出取决于当前状态和当前输入

O(t) = g(S(t), I(t))
S(t+1) = f(S(t), I(t))

Moore机:输出仅取决于当前状态

O(t) = g(S(t))
S(t+1) = f(S(t), I(t))

状态图

状态图是描述状态机行为的图形工具:

  • 圆圈表示状态
  • 箭头表示状态转换
  • 箭头上标注输入/输出

状态表

状态表是描述状态机行为的表格工具:

当前状态输入下一状态输出
S00S10
S01S01

重要知识点

知识点1:同步vs异步设计

同步设计

  • 优点:设计简单,易于验证,可靠性高
  • 缺点:速度受限于最慢路径,功耗较高
  • 应用:现代计算机CPU、内存控制器

异步设计

  • 优点:速度更快,功耗更低
  • 缺点:设计复杂,难以验证,可靠性问题
  • 应用:特殊高性能应用

知识点2:建立时间和保持时间

建立时间(Set-up time):在时钟边沿之前,输入必须保持稳定的最小时间。

保持时间(Hold time):在时钟边沿之后,输入必须保持稳定的最小时间。

违反建立时间或保持时间会导致触发器进入亚稳态,产生不可预测的输出。

知识点3:亚稳态

当触发器的建立时间或保持时间被违反时,触发器可能进入亚稳态:

  • 输出既不是0也不是1
  • 需要一段时间才能稳定到有效电平
  • 可能导致系统故障

解决亚稳态的方法:

  • 使用同步器(两级触发器)
  • 确保满足建立时间和保持时间要求
  • 使用更快的触发器

知识点4:内存层次结构

现代计算机使用层次化的内存结构:

  • 寄存器:最快,容量最小,在CPU内部
  • 高速缓存(Cache):较快,容量中等
  • 主存(RAM):较慢,容量较大
  • 辅存(磁盘):最慢,容量最大

这种层次结构在速度、容量和成本之间取得平衡。

知识点5:RAM的类型

SRAM(静态RAM)

  • 使用触发器存储数据
  • 速度快,功耗低
  • 成本高,密度低
  • 用于高速缓存

DRAM(动态RAM)

  • 使用电容存储数据
  • 需要定期刷新
  • 速度较慢,功耗较高
  • 成本低,密度高
  • 用于主存

常见误区

误区1:混淆锁存器和触发器

锁存器是电平敏感的,触发器是边沿敏感的。在同步设计中,我们通常使用触发器而不是锁存器,因为触发器更稳定。

误区2:忽视时序约束

时序约束(建立时间、保持时间)对于电路的正确工作至关重要。忽视这些约束可能导致电路工作不稳定。

误区3:认为RAM是即时访问的

实际上,RAM访问需要时间。访问延迟是计算机性能的重要限制因素。现代计算机使用高速缓存来缓解这个问题。

误区4:不理解PC的作用

程序计数器是计算机执行指令的关键。它确保指令按正确的顺序执行,并支持跳转和分支操作。

误区5:过度简化时序分析

时序分析是数字电路设计的重要环节。简单的功能仿真不能保证电路在实际工作中的正确性。必须进行完整的时序分析。

实践应用

实践1:实现1位寄存器

hdl
CHIP Bit {
    IN in, load;
    OUT out;
    
    PARTS:
    Mux(a=out, b=in, sel=load, out=muxOut);
    FlipFlop(in=muxOut, out=out);
}

实践2:实现16位寄存器

hdl
CHIP Register {
    IN in[16], load;
    OUT out[16];
    
    PARTS:
    Bit(in=in[0], load=load, out=out[0]);
    Bit(in=in[1], load=load, out=out[1]);
    Bit(in=in[2], load=load, out=out[2]);
    Bit(in=in[3], load=load, out=out[3]);
    Bit(in=in[4], load=load, out=out[4]);
    Bit(in=in[5], load=load, out=out[5]);
    Bit(in=in[6], load=load, out=out[6]);
    Bit(in=in[7], load=load, out=out[7]);
    Bit(in=in[8], load=load, out=out[8]);
    Bit(in=in[9], load=load, out=out[9]);
    Bit(in=in[10], load=load, out=out[10]);
    Bit(in=in[11], load=load, out=out[11]);
    Bit(in=in[12], load=load, out=out[12]);
    Bit(in=in[13], load=load, out=out[13]);
    Bit(in=in[14], load=load, out=out[14]);
    Bit(in=in[15], load=load, out=out[15]);
}

实践3:实现RAM8

hdl
CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];
    
    PARTS:
    DMux8Way(in=load, sel=address, a=l0, b=l1, c=l2, d=l3,
             e=l4, f=l5, g=l6, h=l7);
    
    Register(in=in, load=l0, out=r0);
    Register(in=in, load=l1, out=r1);
    Register(in=in, load=l2, out=r2);
    Register(in=in, load=l3, out=r3);
    Register(in=in, load=l4, out=r4);
    Register(in=in, load=l5, out=r5);
    Register(in=in, load=l6, out=r6);
    Register(in=in, load=l7, out=r7);
    
    Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7,
              sel=address, out=out);
}

实践4:实现程序计数器

hdl
CHIP PC {
    IN in[16], load, inc, reset;
    OUT out[16];
    
    PARTS:
    // 实现递增
    Add16(a=out, b=true, out=incOut);
    
    // 根据控制信号选择输入
    // 优先级:reset > load > inc > hold
    Mux16(a=out, b=incOut, sel=inc, out=incOrHold);
    Mux16(a=incOrHold, b=in, sel=load, out=loadOrNot);
    Mux16(a=loadOrNot, b=false, sel=reset, out=regIn);
    
    Register(in=regIn, load=true, out=out);
}

实践5:构建RAM64

hdl
CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];
    
    PARTS:
    // 使用3位地址选择8个RAM8中的一个
    DMux8Way(in=load, sel=address[3..5], a=l0, b=l1, c=l2, d=l3,
             e=l4, f=l5, g=l6, h=l7);
    
    // 使用低3位地址选择每个RAM8中的寄存器
    RAM8(in=in, load=l0, address=address[0..2], out=r0);
    RAM8(in=in, load=l1, address=address[0..2], out=r1);
    RAM8(in=in, load=l2, address=address[0..2], out=r2);
    RAM8(in=in, load=l3, address=address[0..2], out=r3);
    RAM8(in=in, load=l4, address=address[0..2], out=r4);
    RAM8(in=in, load=l5, address=address[0..2], out=r5);
    RAM8(in=in, load=l6, address=address[0..2], out=r6);
    RAM8(in=in, load=l7, address=address[0..2], out=r7);
    
    Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7,
              sel=address[3..5], out=out);
}

实践6:时序电路测试

测试时序电路需要特别注意时钟信号:

// 测试寄存器
set in 1234, set load 1, tick, tock;
// 验证 out = 1234

set in 5678, set load 0, tick, tock;
// 验证 out 保持 1234

注意:tick表示时钟上升沿,tock表示时钟下降沿。

本章小结

本章我们深入学习了时序逻辑的原理和设计方法。从基本的触发器开始,我们逐步构建了寄存器、RAM和程序计数器。

核心要点回顾

时序逻辑基础:时序电路的输出取决于当前输入和历史状态。

触发器:基本的存储元件,能够在时钟边沿存储1位信息。

寄存器:由多个触发器组成,用于存储多位数据。

RAM:由多个寄存器组成,提供随机存取能力。

程序计数器:特殊的寄存器,用于存储下一条指令的地址。

时钟信号:同步时序电路的核心,协调所有组件的工作。

关键技能掌握

  • 理解触发器的工作原理和特性
  • 能够设计和实现寄存器
  • 掌握RAM的层次化设计方法
  • 理解程序计数器的作用和实现
  • 掌握时序电路的测试方法

与后续章节的联系

本章构建的时序组件是后续章节的基础:

  • 第四章将介绍机器语言,使用这些寄存器进行编程
  • 第五章将把ALU、寄存器和PC组合成完整的计算机
  • 后续章节将在这个基础上构建软件系统

学习建议

理解时钟的作用:时钟是时序电路的核心,必须深入理解

重视时序约束:建立时间和保持时间对于电路正确性至关重要

动手实践:亲手实现每个组件,加深理解

测试验证:时序电路的测试比组合电路更复杂,需要特别注意

通过本章的学习,你已经掌握了时序逻辑的核心知识。这些知识对于理解计算机如何存储和处理数据至关重要,也为后续章节的学习奠定了坚实基础。时序逻辑将"记忆"带入了数字系统,使计算机能够执行复杂的程序,这是计算能力的关键所在。