6

(2)计算机是如何读懂0和1的?- CPU (下)

 3 years ago
source link: https://zhuanlan.zhihu.com/p/263045754
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

(2)计算机是如何读懂0和1的?- CPU (下)

航空工程师

本文为《BUT HOW DO IT KNOW – The Basic Principles of Computer for everyone 》笔记,文章绝大部分图片来自于这本书,用visio重新绘制,并增加不同的颜色,更便于阅读。


在前面两篇文章中,我们已经介绍了RAM组成以及CPU中ALU的实现,文章链接如下:

J Pan:(1)计算机是如何读懂0和1的?- RAM

J Pan:(2)计算机是如何读懂0和1的?- CPU (上)

这一篇主要介绍CPU中Control Section的构建。我们先来回忆一下计算机的组成:

根据前面所述,计算机有两大部分组成:CPU和RAM。CPU又包含Control section和ALU以及若干个寄存器等。

上一篇文章说了,对于CPU来说,我们把具体干活的部分称之为ALU,把起调度功能的部分称之为“Control section”,即控制单元。Control section调度过程又分为两部分:指令解析数据准备


一、Control Section基本架构

Control section的基本功能很明确,就是通过解析指令,获得各个寄存器的“读”和“写”使能端,大概样子如下图所示:

左侧为生成的“”使能信号;

右侧为生成的“”使能信号;

“读”使能对象为6个寄存器:RAM、ACC、R0、R1、R2、R3。

“写”使能对象为8个寄存器,除RAM、ACC、R0、R1、R2、R3之外,还包括MAR、TMP。因为TMP只有“写”使能端,“读”一直使能,而MAR则不要“读”使能。

下面我们就以“加法”、“存入”以及“取指令”的过程来看一下这些使能信号是如何产生的。

1.1、如何实现加法

前面我们介绍了stepper的实现方式,总共分成7步,其中step 7的作用是使stepper跳回至step 1,也就是说,我们能够利用的是step 1到step 6。在每一步,都会产生clk e“读”使能信号和clk s “写”使能信号,其先后关系及持续时间为:

可见,“读”信号先使能,尔后“写”信号使能,尔后“读”信号使能结束。这样就能保证“写”操作进行时所用的数据为当前step“读”到的信号。

我们先来看一下step 4到step 6。实现逻辑如上图所示,还记得我们前面说过的吗?——左侧为“读”使能,右侧为“写”使能,我们整理一下上图的逻辑,可以得到:

Step 4:寄存器R1中的数在“读”使能时,传输到BUS,在“写”使能时,写入到TMP里;

Step 5:寄存器R0中的数在“读”使能时传输到BUS,ALU开始工作,此时TMP在上一步已经准备好,两个输入TMP和R0都就位,ALU计算两者的;在“写”使能时,ALU计算的结果存储到ACC,注意,此时不经过BUS。

Step 6:寄存器ACC中的数在“读”使能时,传输到BUS,在“写”使能时,写入到R0里;

总的效果就是:将R0中的数加上R1中的数,存储到R0中


1.2、如何存入数据

前面将计算结果存储到了寄存器R0中, R0是CPU内部寄存器,大多数时候我们想要存到RAM里面,是否可实现呢?请看下图:

在时钟的作用下,数据流向为:

Step 4:寄存器R2中的数在“读”使能时,传输到BUS,在“写”使能时,写入到MAR里;

Step 5:寄存器R0中的数在“读”使能时传输到BUS,在“写”使能时,开始往RAM里写数据,数值为R0,此时RAM中地位MAR存的数据是R2。

总的效果就是:将R0中的数据存到RAM中,存储地址是R2中存的数据。


1.3、如何实现“取指令”

前面我们说了两个例子,分别是将寄存器中的数相加,以及将寄存器中的数存到RAM指定的地址中去。

这是两种完全不同的操作,怎么来区分呢?——在step 1到step 3中实现。为此,我们要再增加两个寄存器:IAR和IR,分别存储指令地址和指令内容。此时,计算机的继续丰满,如下所示:

其中IAR有“读”和“写”两个使能端,也就是地址寄存器中内容可以有“读”和“写”两个操作;IR只有“写”使能端,也就是指令寄存器只能从总线拿数据。

接下来,我们就看一下取指令的过程是如何实现的,如下图:

数据流向如下:

具体过程为:

Step 1:地址寄存器IAR在“读”使能的时候,将数据传输到BUS,在“写”使能的时候,存入MAR,即RAM中地址寄存器;Step 1还有一个动作,后面再说。

Step 2:RAM中地址为MAR的数据,在“读”使能的时候,将数据传输到BUS,在“写”使能的时候,存入指令寄存器IR;

Step 3:为了能让程序自动执行,我们还需要更新IAR,一个最简单的方式就是将IAR加1,这样就对应RAM中IAR下一条指令。加法很容易实现,ALU中就有加法器,具体实现我们先不管,假设现在ALU的计算结果已经保存在ACC中,我们怎么能让程序自动运转起来?——很简单,寄存器ACC在“读”使能的时候,将数据传输到BUS,在“写”使能的时候,存入IAR,对应RAM中下一条指令;

怎么实现ALU中IAR加1的功能呢?我们知道,ALU有两个输入,一个是TMP,一个来自总线BUS,BUS由于连接的东西很多,我们最好不要动他,剩下的就只能在TMP上做做手脚。——解决方案就是在TMP和ALU中间增加一个模块,这个模块要实现的功能是要么传输TMP里面的数据,要么传输1,具体实现方式如下:

不难分析,这个电路的实现的功能很简单,如下:

那IAR加1的这个动作在哪一步实现呢?——step 1。我们知道在step 1的时候,IAR已经在BUS中,这时候我们bus 1=1,我们再将ACC使能,这样IAR+1就存入到了ACC中。


二、ALU指令编码及解析

对于一个8位CPU来说,指令可以分为两大类,一类为ALU相关指令,主要包括需要ALU参与完成的相关指令,比如加法、移位、比较等;另一类是非ALU相关指令,比如加载数据,存储数据,跳转等。

无论哪种指令,都可以分为前4位和后4位,前四位和动作有关,后四位和数据有关

我们先说ALU相关指令,其基本定义如下:

最高位为1表示该条指令为ALU指令,接下来三位为操作码,表示ALU要干的具体工作;后四位的前两位和后两位表示ALU运算所需要的数据所在寄存器的编码,具体定义如上图所示。

比如,有一条ALU(1)指令执行加法(000)运算,被加的内容是Resister 2(10)里的数据与Resister 3(11)的数据,结果存放在Resister 3(11)里面,那么它对应的指令就是:

如果把这条指令(1000 1011)存入RAM中,地址为10,那么当IAR设为10时,开启计算机,它就会从IAR为10的RAM中读取该条指令,通过总线传输至IR,然后在Control Section的控制下,去计算R2和R3之和。

如果ALU指令是单输入指令,比如SHL,SHR或者NOT,数据内容将来自Reg A,经过ALU运算,结果会被存在Reg B中。可以选在从一个寄存器取数,结果存到另外一个寄存器,比如R1→R3;可以选在从一个寄存器取数,结果存到同一个寄存器,比如R2→R2。

如果ALU指令是双输入指令,Reg A和Reg B将会被送到ALU,运算结果送到Reg B,Reg A和Reg B可以是同一个寄存器。

通过以上分析我们知道,ALU面对的是两个输入:Reg A和Reg B。Reg A和Reg B的逻辑相对是比较明确的,那就是Reg A (ALU) Reg B→Reg B,Reg A和Reg B进过ALU运算,结果存在Reg B里面,具体实现方式如下:

数据流总结如下:

Step 4:“读”和“写”使能端有两个条件:指令寄存器IR最高位为1,Stepper 4的clk信号为高;通过控制“读”“写”两个信号,实现Reg B的内容加载到TMP里面;

Step 5:“读”和“写”使能端有两个条件:指令寄存器IR最高位为1,Stepper 5的clk信号为高,实现Reg B的内容加上TMP里面存的Reg A,结果传输至Reg B;Stepper 5的clk信号还作为ALU操作码产生信号的控制,也就是说,只有在step 5才产生非000的ALU操作码。

Step 6:只要不是比较运算(操作码111),都要将ACC的计算计算结果存储至Reg B。

可见,对于ALU指令操作,就是Reg A和Reg B两个寄存器的数据运算和存储。但是呢,我们手上有4个寄存器:R0、R1、R2、R3,每一个Reg A或Reg B可以是R0、R1、R2、R3中的任意一个,因此,我们需要对R0、R1、R2、R3编码,这样,就可以实现对任何一个寄存器的操作,如下图所示:

这样就可以通过指令后四位的寄存器编码,实现对寄存器里面数据的操作。

助记符如下:


三、非ALU指令编码及解析

3.1、如何实现“load”和“store”指令

前面说了,当指令的第一位为1时,是ALU相关指令。接下来我们就看看当第一位不为1时的指令情况,见下图:

最高位0表示非ALU指令,接下来三位为000表示加载数据,001表示存储数据。指令后四位和ALU指令一致,表示寄存器的编码。实现逻辑为:

当前四位为0000时:

Step 4:Reg A的数据存储到RAM中(地址为MAR);

Step 5: RAM中地址为MAR的数据存储到Reg A;

当前四位为0001时:

Step 4:Reg A的数据存储到MAR;

Step 5: Reg B中的数据存储到RAM中(地址为MAR);

助记符如下:


3.2、如何实现“data”指令

当指令前四位为0010时,代表的是数据指令。与之前不同的是,这个指令有2个字节,后面紧跟的字节为要写入寄存器的数据,如下图所示:

实现方式为:

Step 4:读取IAR中的数据,存储到MAR中;bus 1使能,将IAR+1的结果存到ACC中;

Step 5:读取RAM中的数据(地址为IAR),存储到Reg B中;

Step 6:读取ACC中的数据(此时为IAR+1),更新到IAR中;

注意:由于IAR在step 1的时候已经+1了,在step 4又+1,因此,下一条指令将跳过仅邻的后一条指令(为需要载入的数据),从而实现将后一条紧邻的输入读入的功能。

助记符如下:


3.3、如何实现“jump”指令

当指令前四位为0011时,代表的是跳转指令,如下图所示:

实现方式为:

Step 4:Reg B中的数据存储到IAR中,意味着下一次的指令将从IAR(目前为Reg B寄存器中存储的数据),从而实现跳转功能。

助记符如下:


3.4、如何实现“jump to”指令

当指令前四位为0100时,代表的是跳转到指令,注意,这也是一条2字节指令,紧邻的后1字节代表要跳转到的地址,如下图所示:

实现方式为:

Step 4:将IAR中的数据存储到MAR;

Step 5:将RAM中地址为IAR的数据存储到IAR;

也就是IAR中的内容变成了RAM[IAR+1],实现到“跳转到”功能。

助记符如下:


3.5、如何实现“jump if”指令

当然还可以实现更复杂的跳转指令,比如说有条件跳转,如下图所示:

前面我们说了,ALU计算会输出四个标志位,分别为:carry out、a larger、equal和zero。如下图所示:

我们可以把这四个标志位作为判断条件,实现有条件跳转,实现方式如下:

Step 4:IAR传输至MAR,IAR+ 1传输至ACC;

Step 5:ACC传输至IAR;

Step 6:若carry out、a larger、equal或zero其中之一为真,则RAM [IAR] 传输至IAR。

也就是说,新的IAR有两种情况,正常情况下,程序将顺序执行(跳过紧邻的“地址”字节);当carry out、a larger、equal或zero之一为真时,程序跳转到紧邻字节所存储的地址。

助记符如下:

如果我们把指令某个位置1,这样就可以进行组合标志条件跳转,因为我们有4个标志位,共计有16种组合,当然,4个标志位都off时,就不需要跳转,所以我们只对剩下的15个标志位组合感兴趣,其机器语言如下:

两个标志位组合:

三个或四个标志位组合:


3.6、如何实现“clear flags”指令

当我们去做加法或者移位运算时,可能会用到carry进位标志位,比如在有条件跳转时,就会用到这个标志位。

Carry标志位还会作为加法和移位运算的输入,这就带来一个问题:Carry标志位是存在寄存器里面的,会一直保存之前的记录,当我们的运算不需要这个进位的时候,也会被ALU使用,这就可能会出现2+2=5的情况。

有很多种方式可以处理这个问题,现在我们采用一种比较简单的方式来清除标志位,如下图所示:

看着很简洁,但是还是很有技巧的。首先,并没有使能任何寄存器往BUS写数据,这样ALU的输入A就是0000 0000。在Step 4的时候,使能Bus 1,所以此时ALU的输入B为0000 0001。这是并没有任何操作码发送至ALU,因为默认的是ADD操作,这时ALU就会执行0000 0000+0000 0001=0000 0001,此时还可能有进位carry in,所以总的结果可能是0000 0001,也可能是0000 0010,但是肯定不会产生进位carry out。注意此时carry 标志位为off;结果不是零,所以zero标志位也为off;B比A大,所以“‘A larger”和“equal”也为off。也就是说,所有标志位都清零了。

助记符如下:


3.7、如何实现“I/O”指令

当然,我们还需要相关的指令实现CPU和RAM与外部的数据交换,如下图所示:

I/O总线包括数据总线和一些关键的内部信号,包括输入/输出标志位、数据/地址标志位、“读”使能标志位、“写”使能标志位,具体如下:

其指令格式如下,前四位为0111就表示该指令为I/O指令。后四位第一位表示是输入/输出标志,第二位表示数据/地址标志位,最后两位表示地址/数据的内容(存在Reg B中)。

具体实现方式如下:

Step 4:input/output标志位为1,指令前4位为0111时,Reg B寄存器“读”使能,Reg B寄存器内容传输至BUS;I/O clk s标志位为on;

Step 5:input/output标志位为0,指令前4位为0111时,Reg B寄存器“写”使能,BUS数据传输至Reg B寄存器;I/O clk e标志位为on;

助记符如下:


四、控制单元集成

把前面所有介绍的指令用或门全部封装在一起,就可以得到CPU的Control Section了。其实现的功能为:

具体实现方式如下:

把前面所列的所有ALU指令封装在一起,可以得到ALU相关指令的Control Section部分如下:

继续把所有非ALU指令封装在一起,如下:

把所有指令封装在一起,如下图所示:




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK