在硬件设计中,用电子电路来计算对位进行运算的函数,以及在各种存储器单元中存储位。大多数现代电路技术都是用信号线上的高电压或低电压来表示不同的位值。在当前的技术中,逻辑1是用1.0伏特左右的高电压表示的,而逻辑0是用0.0伏特左右的低电压表示的。
要实现一个数字系统需要三个主要的组成部分:计算对位进行操作的函数的组合逻辑、存储位的存储器单元,以及控制存储器单元更新的时钟信号。
4.2.1 硬件描述语言 HDL
曾经,硬件设计者通过描绘示意性的逻辑电路图来进行电路设计(最早是用纸和笔,后来是用计算机图形终端)。现在,大多数设计都是用硬件描述语言(Hardware Description Language,HDL)来表达的。HDL是一种文本表示,看上去和编程语言类似,但是它是用来描述硬件结构而不是程序行为的。最常用的语言是Verilog,它的语法类似于C;另一种是 VHDL,它的语法类似于编程语言 Ada。这些语言本来都是用来表示数字电路的模拟模型的。20世纪80年代中期,研究者开发出了逻辑合成(1ogic synthesis)程序,它可以根据 HDL的描述生成有效的电路设计。现在有许多商用的合成程序,已经成为产生数字电路的主要技术。从手工设计电路到合成生成的转变就好像从写汇编程序到写高级语言程序,再用编译器来产生机器代码的转变一样。
我们的 HCL语言只表达硬件设计的控制部分,只有有限的操作集合,也没有模块化,不过,正如我们会看到的那样,控制逻辑是设计微处理器中最难的部分。我们已经开发出了将 HCL 直接翻译成 Verilog的工具,将这个代码与基本硬件单元的 Verilog 代码结合起来,就能产生 HDL描述,根据这个 HDL,描述就可以合成实际能够工作的微处理器。通过小心地分离、设计和测试控制逻辑,再加上适当的努力,我们就能创建出一个可以工作的微处理器。
4.2.2 逻辑门
逻辑门是数字电路的基本计算单元。它们产生的输出,等于它们输入位值的某个布尔函数。图4-9是布尔函数AND、OR和NOT的标准符号,C语言中运算符的逻辑门下面是对应的HCL表达式:AND用&&表示,OR用I I表示,而NOT用!表示。我们用这些符号而不用C语言中的位运算符&、I和~,这是因为逻辑门只对单个位的数进行操作,而不是整个字。虽然图中只说明了AND和OR门的两个输入的版本,但是常见的是它们作为n路操作,n>2。不过,在HCL中我们还是把它们写作二元运算符,所以,三个输入的 AND 门,输入为a、b和 c,用 HCL 表示就是 a&&b&&c。
逻辑门总是活动的(active)。一旦一个门的输入变化了,在很短的时间内,输出就会相应地变化。
4.2.3 组合电路和HCL布尔表达式
将很多的逻辑门组合成一个网,就能构建计算块(computational block),称为组合电路(combinational circuits)。如何构建这些网有几个限制:
(1)每个逻辑门的输人必须连接到下述选项之一:1)一个系统输入(称为主输入),2)某个存储器单元的输出,3)某个逻辑门的输出。
(2)两个或多个逻辑门的输出不能连接在一起。否则它们可能会使线上的信号矛盾,可
能会导致一个不合法的电压或电路故障。
(3)这个网必须是无环的。也就是在网中不能有路径经过一系列的门而形成一个回路
这样的回路会导致该网络计算的函数有歧义。
图4-10是一个我们觉得非常有用的简单组合电路的例子。它有两个输入a和b,有唯一的输出eq,当a和b都是1(从上面的AND门可以看出)或都是0(从下面的AND门可以看出)时,输出为1。用HCL来写这个网的函数就是:
bool eq =(a && b)||(!a && !b);
图4-11给出了另一个简单但很有用的组合电路,称为多路复用器(multiplexor,通常称为“MUX”)。多路复用器根据输入控制信号的值,从一组不同的数据信号中选出一个。在这个单个位的多路复用器中,两个数据信号是输入位a和b,控制信号是输入位s。当s为1时,输出等于a;而当s为0时,输出等于b。在这个电路中,我们可以看出两个AND门决定了是否将它们相对应的数据输入传送到OR门。当s为0时,上面的AND门将传送信号b(因为这个门的另一个输入是!s),而当s为1时,下面的AND门将传送信号a。接下来,我们来写输出信号的HCL表达式,使用的就是组合逻辑中相同的操作:bool out=(s &&a)||(!s && b);
HCL表达式和C语言中逻辑表达式的区别:
(1)因为组合电路是由一系列的逻辑门组成,它的属性是输出会持续地响应输入的变化。如果电路的输人变化了,在一定的延迟之后,输出也会相应地变化。相比之下,C表达式只会在程序执行过程中被遇到时才进行求值。
(2)C的逻辑表达式允许参数是任意整数,0表示FALSE,其他任何值都表示TRUE而逻辑门只对位值0和1进行操作。
(3)C的逻辑表达式有个属性就是它们可能只被部分求值。如果一个AND或OR操作的结果只用对第一个参数求值就能确定,那么就不会对第二个参数求值了。例如下面的C表达式:
(a&&!a)&& func(b,c)
这里函数func是不会被调用的,因为表达式(a&&!a)求值为0。而组合逻辑没有部分求值这条规则,逻辑门只是简单地响应输入的变化。
4.2.4 字级的组合电路和HCL 整数表达式
HDL逻辑门包括位级、字级和字节级。
通过将逻辑门组合成大的网,可以构造出能计算更加复杂函数的组合电路。通常,我们设计能对数据字(word)进行操作的电路。
在HDL(硬件描述语言)中,逻辑门是构建数字电路的基本单元。常见的逻辑门包括与门(AND)、或门(OR)、非门(NOT)、异或门(XOR)等。这些逻辑门可以在不同的抽象级别上使用,包括位级、字级和字节级:
- 位级:在位级操作中,逻辑门直接作用于单个位(bit)。例如,一个4位的输入信号可以被分解为四个单独的位,每个位可以独立地进行逻辑运算。
- 字级:在字级操作中,逻辑门作用于一个字(word),通常是一个固定的位数,如8位、16位或32位。这种级别的操作适用于处理较大的数据块。
- 字节级:在字节级操作中,逻辑门作用于一个字节(byte),通常为8位。这种级别的操作适用于处理字节级别的数据。
这些不同级别的逻辑门可以根据设计需求选择使用,以实现复杂的数字逻辑功能。
4.2.5 存储器和时钟
组合电路从本质上讲,不存储任何信息。相反,它们只是简单地响应输入信号,产生等于输人的某个函数的输出。为了产生时序电路(sequentialcircuit),也就是有状态并且在这个状态上进行计算的系统,我们必须引入按位存储信息的设备。存储设备都是由同一个时钟控制的,时钟是一个周期性信号,决定什么时候要把新值加载到设备中。考虑两类存储器设备:
(1)时钟寄存器(简称寄存器)存储单个位或字。时钟信号控制寄存器加载输入值。
(2)随机访问存储器(简称内存)存储多个字,用地址来选择该读或该写哪个字。随机访问存储器的例子包括:1)处理器的虚拟内存系统,硬件和操作系统软件结合起来使处理器可以在一个很大的地址空间内访问任意的字;2)寄存器文件,在此,寄存器标识符作为地址。
正如我们看到的那样,在说到硬件和机器级编程时,“寄存器”这个词是两个有细微差别的事情。在硬件中,寄存器直接将它的输入和输出线连接到电路的其他部分。在机器级编程中,寄存器代表的是CPU中为数不多的可寻址的字,这里的地址是寄存器D。这些字通常都存在寄存器文件中,虽然我们会看到硬件有时可以直接将一个字从一个指令传送到另一个指令,以避免先写寄存器文件再读出来的延迟。需要避免歧义时,我们会分别称呼这两类寄存器为“硬件寄存器”和“程序寄存器”。
图4-16更详细地说明了一个硬件寄存器以及它是如何工作的。
下面的图展示了一个典型的寄存器文件:
寄存器文件有两个读端口(A和B),还有一个写端口(W)。这样一个多端口随机访问存储器允许同时进行多个读和写操作。图中所示的寄存器文件中,电路可以读两个程序寄存器的值,同时更新第三个寄存器的状态。每个端口都有一个地址输入,表明该选择哪个程序寄存器,另外还有一个数据输出或对应该程序寄存器的输入值。两个读端口有地址输入srcA和srcB(“source A”和“source B’的缩写)和数据输出 valA和 valB(“value A”和“value B”的缩写)。写端口有地址输入dstw(“destination W”的缩写),以及数据输入 valw(“value W”的缩写)。
虽然寄存器文件不是组合电路,因为它有内部存储。不过,在我们的实现中,从寄存器文件读数据就好像它是一个以地址为输人、数据为输出的一个组合逻辑块。当srcA或srcB被设成某个寄存器ID时,在一段延迟之后,存储在相应程序寄存器的值就会出现在valA或va1B上。例如,将src设为3,就会读出程序寄存器rbx的值,然后这个值就会出现在输出va1上。
向寄存器文件写人字是由时钟信号控制的,控制方式类似于将值加载到时钟寄存器。每次时钟上升时,输入 valw上的值会被写人输入dstw上的寄存器ID指示的程序寄存器。当dstw设为特殊的D值0xF时,不会写任何程序寄存器。由于寄存器文件既可以读也可以写,一个很自然的问题就是“如果我们试图同时读和写同一个寄存器会发生什么?”答案简单明了:如果更新一个寄存器,同时在读端口上用同一个寄存器D,我们会看到一个从旧值到新值的变化。
处理器有一个随机访间存储器来存储程序数据,如下图所示:
这个内存有一个地址输人,一个写的数据输人,以及一个读的数据输出。同寄存器文件一样,从内存中读的操作方式类似于组合逻辑:如果我们在输人address上提供一个地址:并将 write控制信号设置为0,那么在经过一些延迟之后,存储在那个地址上的值会出现在输出data上。如果地址超出了范围,error信号会设置为1,否则就设置为0。写内存是由时钟控制的:我们将 address设置为期望的地址,将data in设置为期望的值,而write 设置为1。然后当我们控制时钟时,只要地址是合法的,就会更新内存中指定的位置。对于读操作来说,如果地址是不合法的,error信号会被设置为1。这个信号是由组合逻辑产生的,因为所需要的边界检查纯粹就是地址输入的函数,不涉及保存任何状态。
真实微处理器中的存储器系统比我们在设计中假想的这个简单的存储器要复杂得多。它是由几种形式的硬件存储器组成的,包括几种随机访问存储器和磁盘,以及管理这些设备的各种硬件和软件机制。