深入理解计算机系统(4.2)---硬件的魅力

引言

 

  这个系列已经很久没更新了,记得上一篇博文已经是三月份了,实在是抱歉。最近业余时间没有以前充裕了,因此更新一篇博文已经变成了一种奢侈。记得以前刚开始写的时候,最多的时候LZ一天写过3篇博文,现在想想,往事如梦。

  好了,好不容易写一次,就不多说废话了,本文主要介绍一下硬件以及HCL语言的内容。

 

从疑问开始

 

  首先,在介绍本文的内容之前,我们先来思考一个看似简单,却实际比较“高深”的问题。众所周知,计算机归根结底是在和0、1打交道,那么到底0和1是如何被计算机记住的呢?

  怎么样,这个问题是不是有点像数学界的1+1为什么等于2?是否瞬间感觉自己很高大上?

  请稳住,其实这个问题的答案比1+1=2要简单多了。答案就是计算机通过电压来记录0和1。在学习物理的时候大家肯定都听说过电压这个东西,它的单位一般是伏特(V)。比如我们常用的电压一般是220V。计算机当中使用1V来代表1,使用0V代表0。

  解答了这个问题,接下来我们就可以继续我们的设计之路了。

 

设计的基础

 

  要谈设计,就要知道设计的基础。刚才其实我们了解了最基本的基础,就是0和1如何表示,不过这只是最根本的立足之本,我们还需要一些基本的元素。就像你要画一个物体一样,首先是点,然后才能点动成线,进而线动成面,最终面动成体。

  刚才的0和1只不过是点而已,接下来,我们要用点变换成线。这个东西就叫做逻辑门。

  逻辑门是数字电路的基本计算元素,也可以看做是物理结构与逻辑结构的映射,它们实际上是由晶体管组成的。逻辑门接受信号的输入,并根据信号产生一定的输出,而输出则是输入的布尔函数,也就是说,输出只能是0或者1。下图是and门(‘与’门)、or门(‘或’门)和not门(‘非’门)的标准符号,你可以看做它们代表了一组线路的组成方式。

 

  and:               or:                not:

  

  可以看出,and和or都是2个输入,而not为1个输入。前面我们提到了HCL硬件设计语言,上面的三个图分别用HCL表示则是,a&&b、a||b以及!a。

  举个例子,比如对于and门来说,它接受2个输入,如果2个输入都是高位电压,则输出的为高位电压,也就是输出为1。否则的话,都是输出低位电压,也就是输出为0。这些就是建立在刚才1V为1,0V为0的基础之上的。

  

高级设计

 

  现在我们已经掌握了基础,那么在基础之上,我们就可以玩点花样了。比如刚才所提到的逻辑门,它们只能接受1位的输入和1位的输出,比如我们可以计算0&&1为0。那如果我们希望计算更加复杂的表达式该如何做呢,比如10001&&11111=?(注意,这里是位‘与’运算,而不是逻辑运算)。

  答案就是组合电路。道理很简单,一个逻辑门可以计算1位,如果我们弄来32个逻辑门,不就可以计算32位了吗?那么像上面那个10001&&11111的运算,我们就可以采取5个逻辑门去计算。当然,实际上并不是这样的,这个后续我们会更加详细的介绍。

  不过电路往往是复杂的,不能随便乱接,否则电死了谁负责?因此组合的电路需要遵循以下两个原则。

  1、两个逻辑门的输出不能连接到一起,否则它们可能会使线上的信号矛盾,因此可能会得到一个不合法的电压或故障。比如1个0V和1个1V接到一起,会不会出来个0.5V,又或者类似于正负极相接,直接短路了呢。

  2、组合的电路必须是无环的。也就是说输出不能再当做输入,否则会使这个函数产生歧义。这个道理很简单,比如数学上来说,如果c=a+b。我们这里可以把c看做输出,a和b看做输入。如果a又等于c了,那么是不是b就等于0了呢?当然不是,因为a此时其实是a+b,第二轮的c又会等于a+2b,同理,第三轮的c又会等于a+3b。那么到底c=a+b还是c=a+2b还是c=a+3b?(这里有点绕,请各位猿友仔细品味)

  遵循以上原则,我们就可以随意组装电路了。比如在书中的这个电路。

  它代表着输出是a和b是否相等,如果用HCL来描述电路,就是(a && b) || (!a && !b)。我们不难看出来,这个表达式就是编程语言当中的a == b。

  接下来这个图是另外一个书中的例子,它被翻译为多路复用器。其实之所以叫这个,就是因为其中的控制位s是可以复用的。如下图。

  在这个例子当中还不是特别明显,但是之后有更明显的例子说明“复用”的含义。以上这个电路如果用HCL表达,则是(a && s) || (b && !s)。这个电路的含义是如果s为1,则结果为a,否则结果为b。

  在书中对HCL表达式与C语言的表达式做了区分,有以下三点。

  1、逻辑门是持续输出的,但C语言表达式是执行到的时候才会求值。这个区别可以把逻辑门当成一个电路来看,电路是不能断电的,电流会一直存在。

  2、C语言中输入可以是任意整数,而HCL只能是1和0。这点比较好理解。

  3、对于a && b这个符号来说,C语言中的规定是如果前者为假,则后者不会再计算。而HCL当中没有这种说法。

 

按位计算

 

  以上所提到的,哪怕是高级设计,也依然是针对1位进行操作的。那么如何才能真正操作多个位呢,比如平时常用的32位或者64位。

  道理很简单,就是多个1位操作一起进行,例如下面这个图。

  这个图当中每个位相等的判断都是上面所提到的两个not门,两个and门和一个or门组成的组合电路。它们并列的一起进行,最终再通过一个and门,就完成了判断两个32位的数字是否相等的操作。也就是表达式A == B,值得注意的是,这里的A和B都是32位的。

  还有一种类似于C语言中switch语句的表达式,就是如下形式。

  

  它代表的意思就是如果s为1,则输出A,否则输出B,同样,这里的A和B都是32位的。那么使用电路如何表达呢?其实就是上次的复用电路的32位版本。如下图。

  可以看到,s的not值是被复用的,否则的话,这里需要32个not门。值得一提的是,HCL中条件选择表达式中的条件是不需要互斥的,只是按照优先顺序依次选取,这与C语言中的switch是不同的。

  最后一种HCL表达式,是集合的形式。它表示需要拿一个输入信号与某些值做匹配。比如下图当中的S1和S0。

  其中如果S1为code == 2 || code == 3,S0为code == 1 || code == 0。那么HCL可以用另外一种方式表达,即code in {2,3}和code in {1,0}。可以看出,HCL表达式其实就是一种硬件表述方式。

  

存储器和时钟

 

  上面我们介绍的都是组合电路,它们的作用是根据输入来产生一个值。但是刚才也说过,组合电路是一直持续输出的,因此它无法保持一个状态不变。但我们的计算机是需要存储数据的,因此就需要能保存状态的存储设备。存储设备则是由一个时钟控制,时钟就像一个开关一样,它控制着存储设备什么时候更新设备里的值。

  常用的存储设备一般有两种。

  1、时钟寄存器:存储单个位或者单个字。时钟信号来控制寄存器是否要加载输入的值。

  2、随即访问存储器:存储多个字。用地址来选择该读、写哪个字。

  时钟寄存器的典型应用是PC、条件码寄存器以及程序状态。它们都有明确的输入,这意味着它们的值其实是某几个值的一个函数,比如条件码寄存器的输入主要就是逻辑计算单元的值,因此条件码寄存器的值就可以看做是逻辑计算单元的函数。

  时钟寄存器一般是根据时钟信号来更新状态的,而时钟信号就像一个表一样,比如每到12点,寄存器的值就更新一下。就像下面的图示一样,当时钟变化时,值就会变化为y。

  随即访问存储器最典型的例子就是我们的寄存器文件(也就是8个程序寄存器)和随即访问存储器(也就是我们常说的内存)。它们没有明确的输入值,因此不存在函数关系。不论是寄存器文件还是随即访问存储器,都有读和写两种操作,而对于时钟寄存器来说,是无所谓读和写的,因为它只会根据输入的变化改变输出的值,是可以直接连接到电路上去的。

  寄存器文件一般有两个读端口和一个写端口。每个端口都附带一个地址来标识操作的是哪个寄存器,而对于写端口,还有一个输入数据,对于读端口,则还有一个输出数据。具体的图示如下。

  可以看到在寄存器文件的写端口处,有一个时钟(clock)控制着写的操作。当时钟变化时,输入数据的值将会更新到对应的寄存器当中。而对于读数据,则类似于组合电路,根据输入的地址值(src),寄存器文件会输出相应的数据。

  对于随即访问存储器来说,与寄存器文件非常相似。不同的是,随即访问存储器只有一个地址输入,而不是三个,只有一个数据输出而不是两个。具体的图示如下。

  当地址(address)输入,并且输入数据(data in)输入时,如果写(write)输入为1并且时钟(clock)变化时,存储器中地址为输入地址值的值将会变化,值就是输入数据(data in)的值。同样的,如果地址(address)输入,并且写(write)输入为0,则输出数据(data out)会输出地址为输入地址值的值。这里还有一点特殊的是,当读数据的时候,如果地址超出了范围,error信号将会输出为1。回忆一下刚才的HCL语言,error其实就可以看做是一个组合电路的输出,等于(address in > max address ) || (address in < 0),即当输入的地址值不在合法范围内时,它的输出为1。

 

小结

 

  回忆一下本章的内容,其实就是结合HCL语言在讲硬件的组成。而这些硬件,就是Y86处理器需要使用的。比如组合电路,存储器。到本章最后的时候,已经非常接近我们的编程领域了,可以看到有寄存器和内存的出现。但要注意的是,这里的寄存器文件并不等于寄存器,随即访问存储器也不等于内存,LZ只是想让各位猿友更形象的去理解它。

  下一章,就是带着大家一起做一个顺序实现的Y86处理器,这是一个非常低级但却最基础的处理器。如果你慢慢的进入了这个世界,你会觉得这是一件非常有意思的事情。就LZ本身来讲,读这本书的时候,最大的感受就是,每一章刚开始读的时候都非常枯燥无味,但每当读了几节以后,就有种豁然开朗的感觉,这种感觉相信你一定也非常喜欢。

  最后LZ再说明一下,本系列博文都是LZ在自己理解的基础上写的,因此尽管核心内容与原著相似,但语言描述上差异甚大,如果各位猿友看着不习惯的话,不妨去看看原著,或许会有不一样的收获。

posted @ 2014-06-25 01:12  左潇龙  阅读(3273)  评论(3编辑  收藏  举报