1.1 计算机基本工作原理
1.1.1 冯·诺依曼结构基本思想
世界上第一台真正意义上的电子数字计算机是在1935~1939年间由美国艾奥瓦州立大学物理系副教授约翰·文森特·阿塔那索夫(John Vincent Atanasoff)和其合作者克利福特·贝瑞(Clifford Berry,当时还是物理系研究生)研制成功的,用了300个电子管,取名为ABC(Atanasoff-Berry Computer)。不过这台机器只是个样机,并没有完全实现阿塔那索夫的构想。
1946年2月,在美国研制成功了真正实用的电子数字计算机ENIAC(Electronic Numerical Integrator and Computer),不过,其设计思想基本来源于ABC,只是采用了更多的电子管,运算能力更强大。它的负责人是约翰·W.莫克利(John W.Mauchly)和J.普罗斯帕·艾克特(J.Presper Eckert)。他们制造完ENIAC后就立刻申请并获得了美国专利。就是这个专利导致了ABC和ENIAC之间长期的“世界第一台电子计算机”之争。
1973年美国明尼苏达地区法院给出正式宣判,推翻并吊销了莫克利的专利。虽然他们失去了专利,但是他们的功劳还是不能抹杀,毕竟是他们按照阿塔那索夫的思想完整地制造出了真正意义上的电子数字计算机。
现在国际计算机界公认的事实是:第一台电子计算机的真正发明人是美国的约翰·文森特·阿塔那索夫(1903—1995)。他在国际计算机界被称为“电子计算机之父”。
ENIAC的研制主要是为了解决美军复杂的弹道计算问题。它用十进制表示信息,通过设置开关和插拔电缆手动编程,每秒钟能进行5000次加法运算或50次乘法运算。1944年夏季的一天,冯·诺依曼巧遇美国弹道实验室的军方负责人戈尔斯坦。于是,冯·诺依曼被戈尔斯坦介绍加入了ENIAC研制组。在ENIAC研制的同时,冯·诺依曼等人开始考虑研制另一台电子计算机EDVAC(Electronic Discrete Variable Automatic Computer)。1945年,冯·诺依曼以“关于EDVAC的报告草案”为题,起草了长达101页的报告,发表了全新的存储程序(stored-program)通用电子计算机方案,宣告了现代计算机结构——冯·诺依曼结构的诞生。
“存储程序”方式的基本思想是:必须将事先编好的程序和原始数据送入存储器后才能执行程序,一旦程序被启动执行,计算机能在无须操作人员干预下自动完成逐条指令取出和执行的任务。
从20世纪40年代计算机诞生以来,尽管硬件技术已经经历了电子管、晶体管、集成电路和超大规模集成电路等发展阶段,计算机体系结构也取得了很大发展,但绝大部分通用计算机硬件的基本组成和计算机工作方式仍然具有冯·诺依曼结构特征。
冯·诺依曼结构的基本思想主要包括以下几个方面。
1)采用“存储程序”工作方式。
2)计算机内部以二进制形式表示指令和数据;每条指令由操作码和地址码两部分组成,操作码指出操作类型,地址码指出操作数的地址;由一串指令组成程序。
3)计算机由运算器、控制器、存储器、输入设备和输出设备5个基本部分组成。
4)存储器不仅能存放数据,也能存放指令,数据和指令在形式上没有区别,但计算机应能区分它们;控制器应能自动执行指令;运算器应能进行算术运算,也能进行逻辑运算;操作人员可以通过输入/输出设备使用计算机。
1.1.2 冯·诺依曼模型机基本结构
根据冯·诺依曼结构的基本思想可以给出一个冯·诺依曼结构模型机的基本硬件结构。如图1.1所示,模型机中主要包括:
1)用来存放指令和数据的主存储器,简称主存或内存;
2)用来进行算术逻辑运算的部件,即算术逻辑部件(Arithmetic Logic Unit, ALU),在ALU操作控制信号ALUop的控制下,ALU可以对输入端A和B进行不同的运算,得到结果F;
3)用于自动逐条取出指令并进行译码的部件,即控制部件(Control Unit, CU),也称控制器;
4)用来和用户交互的输入设备和输出设备。
在图1.1中,为了临时存放从主存取来的数据或运算的结果,还需要若干通用寄存器组成通用寄存器组(General Purpose Register set, GPRs),ALU的两个输入端A和B的数据来自通用寄存器;ALU运算的结果会产生标志信息,例如,结果是否为0(零标志ZF)、是否为负数(符号标志SF)等,这些标志信息需要记录在专门的标志寄存器中;从主存取来的指令需要临时保存在指令寄存器(Instruction Register, IR)中,最简单的指令格式包含操作码字段op和地址码字段addr;CPU为了自动按序读取主存中的指令,还需要有一个程序计数器(Program Counter, PC),在执行当前指令的过程中,自动计算出下一条指令的地址并送到PC中保存。通常把控制部件、运算部件和各类寄存器互连组成的电路称为中央处理器(Central Processing Unit, CPU),简称处理器。
图1.1 模型机的基本硬件结构
CPU需要从通用寄存器中取数据到ALU中进行运算,或者把ALU运算的结果保存到通用寄存器中,因此,需要给每个通用寄存器编号;同样,主存中每个单元也需要编号,称为主存单元地址,简称主存地址。通用寄存器和主存都属于存储部件。通常,计算机中的存储部件从0开始编号,例如,图1.1中4个通用寄存器编号分别为0、1、2和3,对应的二进制数占两位,分别为00、01、10、11;16个主存单元编号分别为0~15,对应的二进制数占4位,分别为0000、0001、0010、0011、0100、0101、0110、0111、1000、1001、1010、1011、1100、1101、1110、1111。可以看出,用0和1按顺序进行排列组合,就能表示十进制数的值。关于各类信息的二进制编码方式将在第3章详细介绍。
为了从主存取指令和存取数据,CPU需要通过传输介质和主存相连。通常把连接不同部件进行信息传输的介质称为总线,其中包含分别用于传输地址信息、数据信息和控制信息的地址线、数据线和控制线。CPU访问主存时,需先将主存地址、读/写命令分别送到总线的地址线、控制线,然后通过数据线发送或接收数据。CPU送到地址线的主存地址应先存放在主存地址寄存器(Memory Address Register, MAR)中,发送到数据线或从数据线取来的信息存放在主存数据寄存器(Memory Data Register, MDR)中。
如果把食堂或饭店类比为计算机,则后厨就是中央处理器,厨房里的水池、灶台等相当于ALU,碗碟盘子就是通用寄存器,厨师的大脑相当于控制器,厨房外面的货架相当于主存储器,货物采购员和外卖小哥相当于输入/输出设备。客户需要的菜单(其中包括做菜的每道工序)相当于程序,每个基本操作工序相当于指令。碗碟盘子和货架格子都按序进行了编号。做菜前,菜单和原材料都存放在货架上,碗碟盘子都为空。根据做菜过程可大致想象程序和指令如何在计算机中执行。
1.1.3 程序和指令的执行过程
冯·诺依曼结构计算机的功能通过执行程序实现,程序的执行过程就是所包含的所有指令的执行过程。指令(instruction)是用0和1表示的一串0/1序列,用来指示CPU完成一个特定的原子操作,例如,取数指令(load)从主存单元中取出数据存放到通用寄存器中;存数指令(store)将通用寄存器的内容写入主存单元;加法指令(add)将两个通用寄存器的内容相加后送入结果寄存器,传送指令(mov)将一个通用寄存器的内容送到另一个通用寄存器,如此等等。
指令通常被划分为若干个字段,有操作码、地址码等字段。操作码字段指出指令的操作类型,如取数、存数、加、减、传送、跳转等;地址码字段指出指令所处理的操作数的地址,如寄存器编号、主存单元编号等。
因为冯·诺依曼结构计算机只能处理0和1表示的二进制信息,因此,指令及操作数、操作数的地址(寄存器编号和主存单元地址)、控制信号等都是用0和1表示的二进制序列。下面用一个简单的例子说明在图1.1所示计算机上程序和指令的执行过程。
假定图1.1所示模型机字长为8位;有4个通用寄存器r0~r3,编号分别为0~3;有16个主存单元,编号为0~15。每个主存单元和CPU中的ALU、通用寄存器、IR、MDR的宽度都是8位,PC和MAR的宽度都是4位;连接CPU和主存的总线中有4位地址线、8位数据线和若干位控制线(包括读/写命令线)。该模型机采用8位定长指令字,即每条指令有8位。指令格式有R型和M型两种,如图1.2所示。
图1.2 定长指令字格式
图1.2中,op为操作码字段,R型指令的op为0000、0001时,分别定义为寄存器间传送(mov)和加(add)操作,M型指令的op为1110和1111时,分别定义为取数(load)和存数(store)操作;rs和rt为通用寄存器编号;addr为主存单元地址。图1.2中,R[r]表示编号为r的通用寄存器中的内容,M[addr]表示地址为addr的主存单元的内容,“←”表示从右向左传送数据。
例如,指令“1110 0110”中高4位op为1110,说明是取数指令load,因而是M型指令,功能为R[0]←M[addr],指令中后4位addr为0110,表示主存地址,因此该指令的功能为R[0]←M[0110]或R[0]←M[6],表示将主存6(对应二进制为0110)号单元中的内容取到0号寄存器。以此类推,可看出指令“0001 0001”的功能为R[0]←R[0]+R[1],表示将0(对于二进制数00)号和1(对应二进制数01)号寄存器内容相加的结果送至0号寄存器。
若在该模型机上实现高级语言中的赋值语句“z=x+y;”,假定x和y分别存放在主存5号和6号单元中,结果z存放在7号单元中,则相应程序段在主存单元中的初始内容如图1.3所示。
图1.3 实现z=x+y的程序段在主存部分单元中的初始内容
如图1.3所示,该程序共有5条指令和3个数据(两个源操作数和一个结果数据,通常将结果数据称为目的操作数),单个指令和数据都占8位,正好存放在一个主存单元中,因此程序共占用了8个存储单元,分别是第0~7这8个主存单元。为了便于理解用0和1表示的二进制形式指令的功能,通常对指令采用符号表示形式,例如,第1条指令I1(1110 0110)对应的符号表示为“load r0,6#”。根据符号表示,很容易看出是一条取数(load)指令,功能为从主存6号单元取数到0号寄存器。
“存储程序”工作方式规定,程序执行前,需将程序包含的指令和数据先送入主存,一旦启动程序执行,则计算机必须能够在无须操作人员干预下自动完成逐条指令取出和执行的任务。如图1.4所示,一个程序的执行就是周而复始执行一条一条指令的过程。每条指令的执行过程包括:从主存取指令、对指令进行译码、PC增量(图中的PC+“1”表示PC的内容加上当前这一条指令的长度)、取操作数并执行、将结果送至主存或寄存器保存。
图1.4 程序执行过程
程序执行前,首先将程序的起始地址存放在PC中,取指令时,将PC的内容作为地址访问主存。每条指令执行过程中,都需要计算下一条将要执行指令的主存地址,并送到PC中。当前指令执行完后,根据PC的值到主存中取到的是下一条将要执行的指令,因而计算机能够周而复始地自动取出并执行一条一条指令。
对于图1.3中的程序,程序首地址(即指令I1所在地址)为0,因此,程序开始执行时,PC的内容为0000。根据程序执行流程,该程序运行过程中,所执行的指令顺序为I1→I2→I3→I4→I5。每条指令在图1.1所示模型机中的执行过程及结果如图1.5所示。
图1.5 实现z=x+y功能的每条指令的执行过程及结果
如图1.5所示,在图1.1的模型机中执行指令I1的过程如下:指令I1存放在第0单元,故取指令操作为IR←M[0000],表示将主存0单元中的内容取到指令寄存器IR中,故取指令阶段结束时,IR中的内容为1110 0110;然后,将高4位1110(op字段)送到控制部件进行指令译码;同时控制PC进行“+1”操作,PC中的内容变为0001;因为是取数指令,所以控制器产生“主存读”控制信号Read,控制在取数并执行阶段将Read信号送到控制线上、指令后4位的0110(addr字段)作为主存地址送至MAR并自动送到地址线上,经过一段时间以后,主存将0110(6#)单元中的33(变量y)送到数据线上,并自动存储在MDR中;最后由控制器控制将MDR内容送0号通用寄存器。因此,指令I1的执行结果为R[0]=33。其他指令的执行过程类似。程序最后执行的结果为主存0111(7#)单元的内容(变量z)变为49,即M[7]=49。
指令执行的各阶段都包含若干微操作,微操作需要相应的控制信号(control signal)进行控制。例如,取指令阶段的功能可以表示为IR←M[PC],对应以下三个微操作,其执行顺序如下:
1)MAR←PC。将PC的内容送到MAR,而MAR中的内容会自动送到地址线上。
2)控制线←Read。将“主存读”(Read)命令送到控制线上,主存从控制线上接收“主存读”命令后,会自动对地址线上给定的主存单元进行读操作,读出的数据通过数据线自动传送到MDR寄存器。
3)IR←MDR。将MDR中的内容送到指令寄存器IR中。
类似地,取数阶段的功能可以表示为R[0]←M[addr],对应的微操作及其执行顺序如下:MAR←addr;控制线←Read;R[0]←MDR。存数阶段的功能可以表示为M[addr]←R[0],对应的微操作及其执行顺序如下:MAR←addr;MDR←R[0];控制线←Write。在ALU中进行加法运算的功能可以表示为R[0]←R[0]+R[1],对应的微操作及其执行顺序如下:A←R[0]和B←R[1]同时执行;ALUop←add;R[0]←F。这里,ALUop←add表示将ALU操作控制信号设置为加(add)运算。
ALU操作有加(add)、减(sub)、与(and)、或(or)、传送(mov)等类型,如图1.1所示,ALU操作控制信号ALUop可以控制ALU进行不同的运算,例如,当ALUop←mov时,ALU的输出F=A;当ALUop←add时,ALU的输出F=A+B。
这里的Read、Write、mov、add等是一种微操作控制信号,都是控制部件对指令中的op字段进行译码后送出的,例如,若op=0000,则ALUop=mov;若op=0001,则ALUop=add。图1.1中的虚线所示的就是控制信号线。
每条指令在执行过程中,所包含的微操作具有先后顺序关系,需要定时信号进行定时。通常,CPU中所有微操作都由时钟信号进行定时,时钟信号(clock signal)的宽度称为一个时钟周期(clock cycle)。一条指令的执行时间包含一个或多个时钟周期。