The Element Of Computer System 笔记(第三周)

第三章 时序逻辑

前两章构建的所有布尔芯片和算术芯片都是组合芯片(combindational chips)。组合芯片计算那些“输出结果仅依赖于其输入变量的排列组合”的函数。这些相关的简单芯片提供很多重要的处理功能(像ALU),但是它们吧能维持自身的状态。计算机不仅要能计算值,而且还需要存取数据,因而这些芯片必须配备记忆单元(memory elements)来保存数据,这些记忆单元是由时序芯片(sequential chips)组成。

记忆单元的实现是复杂的过程,涉及了同步、时钟和反馈回路。其中的大部分能够被封装到称为触发器(flip-flop)的底层时序门(sequential gate)中。

背景知识

时钟(Clock) 在大多数计算机中,时间的流逝是用主时钟(master clock)来表示的,它提供连续的交变信号序列。其精确的硬件实现通常基于振荡器(oscillator),其在两个信号值0-1,或称“低电平-高电平(low-high,tick-tock)”之间交替变化。两个相连的上升沿之间的时间间隙称为时钟的周期(cycle)。

触发器(Flip-Flops) 计算机最基本的时序单元是称为触发器的设备,它有几种变体。在这里我们使用称为数据触发器(Data Flip-Flop,DFF或称D触发器)的变体,其接口包含1比特位输入和1比特位输出。另外,DFF有个时钟输入,根据主时钟信息连续地交变。数据和时钟的输入使得DFF能够实现基于时间的行为out(t)=in(t-1),这里in和out是门的输入和输出值,t是当前时钟周期。换句话说,DFF简单地将前一个时间周期的输入值作为当前周期的输出。

寄存器(Registers) 寄存器是具有记忆功能的设备,它能够“存储”或称“记忆”某一时刻的值,实现经典的存储行为out(t)=out(t-1)。从另一个方面来说,DFF仅能够输出它前一时钟周期的输入,也就是out(t)=in(t-1)。这就告诉我们,可以通过DFF来实现寄存器,只需将后面的输出反馈到它的输入就可以了,这样生成的设备如图3.1的中间部分所示。这样,在任何时刻t,这个设备的输出都会重现它在时刻t-1的值。
在这里插入图片描述
图3.1的中间部分所示的设备是不正确的。首先,因为无法告知DFF的输入什么时候从in获取,什么时候从out获取,所以我们不明确如何才能给这个设备输入新的数据值。一般来说,芯片设计的规则规定内部管脚的扇入(fan-in)必须为1,也就是它们只能有一个单独的输入源。

我们可以在设计中使用多路转化器(multiplexor)。这个多路转换器的"选择位(select bit)" 可以称为整个寄存器芯片的"加载位(load bit)";如果希望寄存器开始存储一个新值,可以把这个值置于in输入口,然后将load位设为1;如果希望寄存器一直存储它的内部值直到新的指令到来,可以将load位设为0。

实现保存1比特位的基本机制后,可以轻松构建任意位宽的寄存器,这可以通过多个1比特寄存器构成的阵列来实现,以构建可保存多比特位值的寄存器(如图3.2所示)。此类寄存器的基本设计参数是它的宽度,即它所保存的比特位的数量——比如,16、32、64。此类寄存器的多位容量通常用字(word)来表示。

内存(Memories) 一旦具备了表示字的基本能力,就可以构建任意长度的存储块了。如图3.3所示,可以通过将很多寄存器堆叠起来形成随机存取RAM单元来实现。随机存取内存(RAM,Random Access Memory)这个概念由此而来:在RAM上能够随机访问被选择的字而不会受限于访问顺序。也就是说,我们要求内存中的任何字(无论具体物理位置在哪里)都能以相等的速度被直接访问。
在这里插入图片描述
在这里插入图片描述
实现方法:根据各自被存取的位置——也就是n位RAM中每个记忆单元——分配一个唯一的地址(address,一个从0到n-1之间的整数)。然后,构建一个由n-寄存器构成的阵列,再构建一个逻辑门,使得该逻辑门能够对给定的地址j,找到地址j对应的记忆单元。

典型的RAM设备接收三种输入:数据输入、地址输入和加载位。地址指定了在当前时钟周期里哪一个RAM寄存器被访问。进行读操作时(load=0),RAM的输出立即发出被选中的记忆单元的值。在进行写操作(load=1)时,被选择的记忆单元将在下一个时间周期内被赋予新的输入值,从此RAM将开始发出该新输入值。

RAM设备的基本设计参数是它的数据宽度(即每个字的宽度)和它的大小(RAM中的字的数目)。现代计算机一般采用32-位宽或者64-位宽的RAM。

计数器(Counter) 计数器是一种时序芯片,它的状态是整数,每经过一个时间周期,该整数就增加1个单位,执行函数out(t)=out(t-1)+c,这里c就是1。计数器在数字系统中担当非常重要的角色。比如,典型的CPU包括一个程序计数器(program counter),它的输出就是当前程序中下一步将要执行的指令地址。

计数器芯片可以通过将标准寄存器的输入/输出逻辑和附加的常数组合逻辑相结合来实现。一般来说,计数器必须要配一些附加的功能块,比如将计数置零、加载新的计数值,或者用减操作来取代增操作。

时间问题 到目前本章所介绍的芯片都是时序芯片(sequential chip)。简单来说,时序芯片就是直接或间接地嵌入一个或多个DFF门的芯片。从功能的角度来说,时序芯片被DFF门赋予了维持状态(如内存单元)或者对状态进行操作(如计数器)的能力。
在这里插入图片描述
时序芯片输出有个重要的作用,能够被用来对整个计算机系统进行同步。假设,我们指示算数逻辑单元(ALU)计算x+y,其中x是与ALU邻近的RAM寄存器的值,y是矩ALU较远RAM寄存器的值。由于各种物理条件上的限制(距离、阻力、干涉、随机噪音,等等),代表x和y的电信号可能会在不同的时刻到达ALU。然而,对于组合逻辑芯片(combinational chip)而言,ALU并没有时间概念——ALU只负责不间断地把出现在输入端的值加起来。因此,ALU的输出稳定到正确的x+y结果需要一些时间。在结果值稳定之前,ALU会产生垃圾值。

ALU的输出总是被发送到某种类型的时序芯片(寄存器、RAM存储单元等等),只要保证:在设计计算机时钟时,时钟周期的长度要比一个比特在计算机系统中两个物理距离最长的芯片之间的传输时间稍长。这样就能够在时间上保证时序芯片能够更新其状态(在下个时钟周期的开始处),也就是保证它从ALU哪里接收到的输入值是有效的。简单的说,就是将一组相互独立的硬件组件同步为一个协调统一的系统。

规范详述

这一节介绍一组时序芯片:

  • D触发器(DFFs)
  • 寄存器(基于DFFs)
  • 存储块(基于寄存器)
  • 计数器芯片(基于寄存器)

D触发器

DFF门(data flip-flop gate)是最基本是时序设备,也是用于设计所有记忆单元的基本组件。DFF门包含1比特位输入和1比特位数据输出,如下所示:
在这里插入图片描述
跟Nand,门一样,DFF门在我们的计算机体系中处于非常底层。可以说,计算机中的所有时序芯片(寄存器、内存、计数器)都基于大量的DFF门。

这些DFF门都连接同一个主时钟。在每个时钟周期的启始点,计算机中所有DFF的输出都被赋予它们上个时钟周期的输入。在其他时间,DFF被“锁存(latched)”了,这意味着他们的输入将暂时不会影响他们的输出。这个传导性操作将影响到组成系统的上百万个DFF门,大约每秒十亿次(依据计算机的时钟频率而定)。

通过同时向系统中所有DFF提供统一的主时钟信号,来实现计算机硬件对时间的相关性(time dependency),硬件仿真器能够以软件的方式模拟出相同的效果。只要计算机体系结构是确定的,那么仿真的结果就将是一致的;只要所涉及的芯片中包含DFF门,那么整个芯片乃至在该硬件层之上构建的其他所有芯片都将继承对时间的相关性。按照定义,这些芯片都称为时序芯片(sequential chip)。

DFF的物理实现很复杂,它使用反馈回路(feedback loops,仅次于Nand门的经典设计)来连接几个基本的逻辑门。本书将这个复杂过程忽略,并将DFF当做原始构建模块。因此,硬件仿真器提供了内置DFF实现供其他芯片使用。

寄存器(Register)

1比特位寄存器,可以称为比特(Bit),或二进制单元(binary cell),用来存储信息的1个比特位(0或1)。芯片接口包括:一个输入管脚(它传输一个比特数据);一个能够使该寄存器进行写操作的Load管脚;一个输出管脚(用来输出寄存器的当前状态)。二进制单元的接口图和API如下所示:

这里芯片的意思为若load为真,则将in中的数据输出,这里是写操作。否则,进行记忆操作,进行存储。
在这里插入图片描述
寄存器芯片的API本质上时是一样的,除非输入管脚和输出管脚被设计成处理多位数据值。
在这里插入图片描述
比特和寄存器芯片的读/写行为几乎是一样的:

读(Read):要读取寄存器的内容,只需要获取它的输出。

写(Write):要将新数据值d写入寄存器,只需将d置于in输入管脚,并将load输入管脚的值设为1。这样,在下一个时钟周期内,寄存器被赋予了新的数据值,它的输出编程了d。

存储(Memory)

可进行直接访问的记忆单元也称为RAM,是由n个w-位寄存器组成的阵列,配有直接访问电路(direct access circuitry)。寄存器的个数(n)和每个寄存器的宽度(w)
分别称为内存的尺寸(size)和宽度(width)。我们将开始构建一组这样的存储体系,都是16位宽,但是大小不同:RAM8、RAM64、RAM512、RAM4K和RAM16K。所有这些内存芯片都有相同的API,因此我们在参数化的图表里面描述它们。
在这里插入图片描述

读:要读取编号为m的寄存器,我们先将m置于address输入管脚。RAM的直接存取逻辑将选中
编号为m的寄存器,该寄存器于是将它的输出发送到RAM的输出管脚。这是个不涉及时间的组
合逻辑操作。
写:要将新的数据值d写入编号为m的寄存器,我们同样将m置于addess输入管脚,并将d置于in
输入管脚,然后确认load位为1。这样使得RAM的直接存取逻辑去选中m号寄存器,load位来使
能写操作。在下一个时钟周期里,被选中的寄存器将会被赋予新的数据值d,RAM的输出值也变
成了d。

计数器(Count)

计数器是一个独立的抽象,但是可以通过它的一些应用来进行描述。比如,考虑一个指令地址计数器,它的内容是计算机下一时钟周期要读取和执行的指令地址。在大多数情况下,计数器在每个时钟周期内做简单的“加1”操作,因此计算机能够获取程序的下一条指令。在其他情况下,比如要“跳跃去执行编号为n的指令”时,我们希望能够将计数器设为n,然后它继续进行默认的计数行为,n+1、n+2,等等。甚至,将计数器清零。在任何时候可通过将计数器置0来让程序重新执行。简单地说,我们需要一个可重载的、可重置的计数器。

这样,我们的计数器芯片接口就与寄存器的接口十分相似,只是计数器芯片有两个附加的控制位
reset和inc。当inc=1时,计数器在每个时钟周期自加,输出值out(t)=out(t-1)+1。如果想要
将计数器重置为0,。就将reset位置为1;如果想要将其初始化为某个计数值d,就将d置于in输入
管脚然后将load位置1。具体细节在计数器API里面有描述,它的操作实例如图3.5所示。

在这里插入图片描述
在这里插入图片描述

芯片的具体实现(Implementation)

DFF门(D触发器)

在这里插入图片描述
这个DFF芯片是已给出的,所以不需要去自己实现,直接使用就可以了。

bit register(寄存器)

1比特位寄存器,可以称为比特(Bit),或二进制单元(binary cell)。
在这里插入图片描述
这里load的取值,我们可以从前文中了解到,将load位设为1时,寄存器将存储一个新值,也就是out(t)=in(t-1);将load位设为0,寄存器将一直存储到它内部值知道新的指令到来,就是out(t)=out(t-1)

具体实现代码如下

/**
 * 1-bit register:
 * If load[t] == 1 then out[t+1] = in[t]
 *                 else out does not change (out[t+1] = out[t])
 */

CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    // Put your code here:
    Mux(a=a,b=in,sel=load,out=o);
    DFF(in=o,out=out,out=a);
}

在这里插入图片描述
具体实现代码如下

/**
 * 16-bit register:
 * If load[t] == 1 then out[t+1] = in[t]
 * else out does not change
 */

CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
    // Put your code here:
    Bit(in=in[0],out=out[0],load=load);
    Bit(in=in[1],out=out[1],load=load);
    Bit(in=in[2],out=out[2],load=load);
    Bit(in=in[3],out=out[3],load=load);
    Bit(in=in[4],out=out[4],load=load);
    Bit(in=in[5],out=out[5],load=load);
    Bit(in=in[6],out=out[6],load=load);
    Bit(in=in[7],out=out[7],load=load);
    Bit(in=in[8],out=out[8],load=load);
    Bit(in=in[9],out=out[9],load=load);
    Bit(in=in[10],out=out[10],load=load);
    Bit(in=in[11],out=out[11],load=load);
    Bit(in=in[12],out=out[12],load=load);
    Bit(in=in[13],out=out[13],load=load);
    Bit(in=in[14],out=out[14],load=load);
    Bit(in=in[15],out=out[15],load=load);
}
RAM8(8-位寄存器)

对图3.3的观察可便于我们理解。要实现RAM8芯片,就要将一组8个寄存器排列起来。接着,必须构建一个组合逻辑,把从RAM8“in”输入端的输入值装载到被选中的某个记忆单元内。依此类推,还需构建一个组合逻辑,其对给定的address值选择正确的寄存器,然后将它的out输出值送到RAM8的out输出上。提示:这个组合逻辑我们已经在第一章中实现了。

具体实现代码如下

/**
 * Memory of 8 registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then 
 * the in value is loaded into the memory location specified by address 
 * (the loaded value will be emitted to out from the next time step onward).
 */

CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];

    PARTS:
    // Put your code here:
    DMux8Way(in=load,sel=address,a=in0,b=in1,c=in2,d=in3,e=in4,f=in5,g=in6,h=in7);
    Register(in=in,load=in0,out=o0);
    Register(in=in,load=in1,out=o1);
    Register(in=in,load=in2,out=o2);
    Register(in=in,load=in3,out=o3);
    Register(in=in,load=in4,out=o4);
    Register(in=in,load=in5,out=o5);
    Register(in=in,load=in6,out=o6);
    Register(in=in,load=in7,out=o7);
    Mux8Way16(a=o0,b=o1,c=o2,d=o3,e=o4,f=o5,g=o6,h=o7,sel=address,out=out);
}
RAM64(64-寄存器)

在这里插入图片描述

具体实现代码如下

/**
 * Memory of 64 registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then 
 * the in value is loaded into the memory location specified by address 
 * (the loaded value will be emitted to out from the next time step onward).
 */

CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];

    PARTS:
    // Put your code here:
    DMux8Way(in=load,sel=address[3..5],a=in0,b=in1,c=in2,d=in3,e=in4,f=in5,g=in6,h=in7);
    RAM8(in=in,load=in0,address=address[0..2],out=o0);
    RAM8(in=in,load=in1,address=address[0..2],out=o1);
    RAM8(in=in,load=in2,address=address[0..2],out=o2);
    RAM8(in=in,load=in3,address=address[0..2],out=o3);
    RAM8(in=in,load=in4,address=address[0..2],out=o4);
    RAM8(in=in,load=in5,address=address[0..2],out=o5);
    RAM8(in=in,load=in6,address=address[0..2],out=o6);
    RAM8(in=in,load=in7,address=address[0..2],out=o7);
    Mux8Way16(a=o0,b=o1,c=o2,d=o3,e=o4,f=o5,g=o6,h=o7,sel=address[3..5],out=out);
}
PC(计数器)

具体实现代码如下所示

/**
 * A 16-bit counter with load and reset control bits.
 * if      (reset[t] == 1) out[t+1] = 0
 * else if (load[t] == 1)  out[t+1] = in[t]
 * else if (inc[t] == 1)   out[t+1] = out[t] + 1  (integer addition)
 * else                    out[t+1] = out[t]
 */

CHIP PC {
    IN in[16],load,inc,reset;
    OUT out[16];

    PARTS:
    // Put your code here:
    Inc16(in=t,out=inco);
    Mux16(a=t,b=inco,sel=inc,out=out0);
    Mux16(a=out0,b=in,sel=load,out=out1);
    Mux16(a=out1,b=false,sel=reset,out=out2);
    Register(in=out2,load=true,out=t,out=out);
}
RAM512

芯片实现代码如下:

/**
 * Memory of 512 registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then 
 * the in value is loaded into the memory location specified by address 
 * (the loaded value will be emitted to out from the next time step onward).
 */

CHIP RAM512 {
    IN in[16], load, address[9];
    OUT out[16];

    PARTS:
    // Put your code here:
    DMux8Way(in=load,sel=address[6..8],a=in0,b=in1,c=in2,d=in3,e=in4,f=in5,g=in6,h=in7);
    RAM64(in=in,load=in0,address=address[0..5],out=o0);
    RAM64(in=in,load=in1,address=address[0..5],out=o1);
    RAM64(in=in,load=in2,address=address[0..5],out=o2);
    RAM64(in=in,load=in3,address=address[0..5],out=o3);
    RAM64(in=in,load=in4,address=address[0..5],out=o4);
    RAM64(in=in,load=in5,address=address[0..5],out=o5);
    RAM64(in=in,load=in6,address=address[0..5],out=o6);
    RAM64(in=in,load=in7,address=address[0..5],out=o7);
    Mux8Way16(a=o0,b=o1,c=o2,d=o3,e=o4,f=o5,g=o6,h=o7,sel=address[6..8],out=out);
}
RAM4K

具体实现代码如下所示:

/**
 * Memory of 4K registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then 
 * the in value is loaded into the memory location specified by address 
 * (the loaded value will be emitted to out from the next time step onward).
 */

CHIP RAM4K {
    IN in[16], load, address[12];
    OUT out[16];

    PARTS:
    // Put your code here:
    DMux8Way(in=load,sel=address[9..11],a=in0,b=in1,c=in2,d=in3,e=in4,f=in5,g=in6,h=in7);
    RAM512(in=in,load=in0,address=address[0..8],out=o0);
    RAM512(in=in,load=in1,address=address[0..8],out=o1);
    RAM512(in=in,load=in2,address=address[0..8],out=o2);
    RAM512(in=in,load=in3,address=address[0..8],out=o3);
    RAM512(in=in,load=in4,address=address[0..8],out=o4);
    RAM512(in=in,load=in5,address=address[0..8],out=o5);
    RAM512(in=in,load=in6,address=address[0..8],out=o6);
    RAM512(in=in,load=in7,address=address[0..8],out=o7);
    Mux8Way16(a=o0,b=o1,c=o2,d=o3,e=o4,f=o5,g=o6,h=o7,sel=address[9..11],out=out);
}
RAM16K

具体代码实现如下所示:

/**
 * Memory of 16K registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then 
 * the in value is loaded into the memory location specified by address 
 * (the loaded value will be emitted to out from the next time step onward).
 */

CHIP RAM16K {
    IN in[16], load, address[14];
    OUT out[16];

    PARTS:
    // Put your code here:
    DMux4Way(in=load,sel=address[12..13],a=in0,b=in1,c=in2,d=in3);
    RAM4K(in=in,load=in0,address=address[0..11],out=o0);
    RAM4K(in=in,load=in1,address=address[0..11],out=o1);
    RAM4K(in=in,load=in2,address=address[0..11],out=o2);
    RAM4K(in=in,load=in3,address=address[0..11],out=o3);
    Mux4Way16(a=o0,b=o1,c=o2,d=o3,sel=address[12..13],out=out);
}

观点

本章介绍的所有内存系统的基础是触发器,并将这种门电路看作是原始的构建模块。在硬件知识的教材中,通常是从基本的组合逻辑门(比如Nand门)开始讲述,使用合适的反馈环来实现触发器。对于标准的实现方法,首先要构建一个简单的(无时钟的)的双态(bi-stable)触发器,也就是能被置于两种状态中的某一种。与是,带时钟的触发器则是通过两个这种简单的触发器的级联实现的,第一个触发器在时钟ticks时被设定,第二个在时钟tocks被设定。这种“主-从”设计使得整个触发器具有所期望的时钟同步功能。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值