深入理解操作系统(9)第四章:处理器体系结构(1)Y86指令集+逻辑设计(包括:ISA/存储器/HCL/冒险/CISC,RISC/verilog/逻辑门/组合电路/多路复用器/ALU/(典型)硬件寄存

深入理解操作系统(9)第四章:处理器体系结构(1)前沿+Y86指令集+逻辑设计和HCL(包括:ISA/Y86指令集/存储器/HCL/冒险/CISC,RISC/指令编码/verilog/逻辑门/(字级)组合电路/(字级)多路复用器/四路复用器/ALU/硬件寄存器/典型寄存器文件)

1. 前沿

1.1 ISA(指令集体系结构)

我们知道处理器必须执行一系列指令,每条指令执行某个简单操作,例如两个数相加。
指令被编码为由一个或多个字节序列组成的二进制格式。

一个处理器支持的指令和指令的字节级编码称为它的ISA(指令集体系结构)。
(Instruction-set architecture 指令集体系结构)

例如 Intel IA32、IBM/Motorola PowerPC和 Sun MicrosystemsSPARO,都有不同的ISA。

1.1.1 ISA作用

ISA在编译器编写者和处理器设计人员之间提供了一个概念抽象层,编译器编写者只需要知道允许哪些指令,以及它们是如何编码的;而处理器设计者必须建造出执行这些指令的处理器。

1.1.2 ISA模型

ISA模型看上去应该是顺序指令执行,也就是先取出一条指令,等到它执行完毕,再开始下一条。
然而,与一个时刻只执行一条指令相比,通过同时处理多条指令的不同部分,处理器可以获得较高的性能。

1.2 本章目标:

本章将简要介绍处理器硬件的设计。

我们将研究一个硬件系统执行某种ISA指令的方式。

1.2.1 为什么要了解处理器设计?

很可能永远都不会自己设计处理器。那么为什么你还应该了解处理器设计呢?

1. 从智力方面来说,处理器设计是非常有趣的。
	学习处理器是怎样工作的本身就是一件很有意义的事情。
	而格外有趣的事情是了解作为计算机科学家和工程师日常生活一部分的一个系统的内部工作原理,
	特别是很多人都还不了解它。处理器设计包括许多好的工程实践原理。
	它需要完成复杂的任务,而结构又要尽可能简单。

2. 理解处理器是如何工作的能帮助理解整个计算机系统是如何工作的

3. 虽然很少有人设计处理器、但是许多人设计包含处理器的硬件系统。
	嵌入式系统的设计者必须了解处理器是如何工作的,
	因为这些系统通常是在比桌面系统更低抽象级别上进行设计和编程的。

1.3 Y86指令集

1.3.1 什么是Y86指令集

因为受到A32指令集的启发,而它又被称为“X86”,所以我们称我们的指令集为“Y86”指令集。(字母:X,Y,Z)

1.3.2 Y86指令集特点

与IA32相比,Y86指令集的数据类型、指令和寻址方式都要少一些,它的字节级编码也比较简单。
不过它仍然足够完整,能让我们写一些简单的处理整数的程序。

1.3.3 Y86指令集的准备工作/知识(HCL)

1. 会提供一些数字硬件设计的背景
2. 会描述处理器中使用的基本构件块,以及它们是如何连接起来和操作的
3. 还将介绍一种描述硬件系统控制部分的简单语言,HCL
	(HCL Hardware Control Language,硬件控制语言)
4. 最后,我们会用它来描述我们的处理器设计

1.4 Y86指令集的设计步骤

1.4.1 第一步:基于顺序操作、功能正确的Y86处理器

第一步:基于顺序操作、功能正确但是有点不实用的Y86处理器
作为设计处理器的第一步,我们给出一个基于顺序操作、功能正确但是有点不实用的Y86处理器。
这个处理器每个时钟周期执行一条完整的Y86指令。所以它的时钟必须足够慢,以允许在一个周期内完成所有的动作。
这样一个处理器是可以实现的,但是它的性能远远低于相同硬件应该能达到的性能。

1.4.2 第二步:创建一个流水线化的处理器

以这个顺序设计为基础,我们进行一些改造,创建一个流水线化的处理器(pipelined processor)

这个处理器将每条指令的执行分解成五步,每个步骤由一个独立的硬件部分或阶段( stage)来处理
指令步经流水线的各个阶段,且每个时钟周期有一条新指令进入流水线。所以,处理器可以同时执行五条指令的不同阶段。为了使这个处理器保留Y86ISA的顺序的性质,就要求处理很多冒险或冲突(hazard)条件。

冒险:就是一条指令的位置或操作数依赖于其他仍在流水线中的指令。

时钟周期
https://blog.csdn.net/lqy971966/article/details/110234641

1.5 测试工具

工具来研究和测试我们的处理器设计

其中包括Y86的编译器、在你的机器上运行Y86程序的模拟器,
还有针对两个顺序处理器设计和一个流水线化处理器设计的模拟器
这些设计的控制逻辑是在用HCL符号表示的文件中描述的。

2. Y86-64指令集体系结构

2.1 程序员可见的状态

Y86程序中的每条指令都会读取或修改处理器状态的某些部分。这称为程序员可见状态
这里的“程序员”既指用汇编代码写程序的人,也包括产生机器级代码的编译器

图 4.1

在这里插入图片描述

最有用的条件码是:

ZF: 零标志。
	最近的操作得出的结果为0
	如:if(0 == bIsFlag)
	
SF: 符号标志。
	最近的操作得到的结果为负数。
	如: (t < 0)

OF: 溢出标志(overflow 溢出)。
	最近的操作导致一个二进制补码溢出——正溢出或负溢出
	(a < 0 == b < 0) && (t < 0 != a < 0)

2.2 Y86-64指令

Y86的处理器状态类似于IA32。有八个程序寄存器%eax、%ecx、%edx、%ebx、%esi、%edi、%espρ和%ebp,处理器每个程序寄存器存储一个字。
寄存器%esp被入栈、出栈、调用和返回指令作为栈指针。而其他寄存器没有固定的含义或固定值。
有三个一位的条件码:ZF、SF和OF,它们保存着有关最近的算术或逻辑指令造成影响的信息。

什么是存储器?

存储器,从概念上来说就是一个很大的字节数组保存着程序和数据

2.2.1 Y86-64指令的简单描述(字节级编码)

图4.2 给出了Y86ISA中各个指令的简单描述。
图4.2
在这里插入图片描述

图中左边是指令的汇编码表 右边是指令编码

图4.2还给出了指令的字节级编码。

指令编码从1个字节到6个字节不等。(0-5)
一条指令含有一个单字节的指令指示符(第一个字节表明指令的类型),可能含有一个单字节的寄存器指示符,
	如:call xxx  80 //第一个字节值8表明其指令指示符,第二字节0表示寄存器指示符
还可能含有一个四字节的常数字。字段fn指明是某个整数操作(OP)或是某个分支条件(jXX)。
	如: irmovl ra 9(rb) //四个常数值是9
所有的数值都用十六进制表示。

这个指令集就是我们处理器实现的目标。
Y86指令集基本上是IA32指令集的一个子集。它只包括四字节整数操作,寻址方式比较少,操作也较少。因为我们只有四字节数据,所以称之为“字(word)”。
汇编码和IA32程序的GAS表示非常类似

2.2.2 Y86-64指令的更多细节

下面是不同Y86指令的更多细节。

1. IA32的movl指令分成了四个不同的指令:irmovl、rrmovl、mrmovl和 rmmovl,
	分别显式地指明源和目的的格式。源可以是立即数(ⅱ)、寄存器(r)或存储器(m)。
	指令名字的第一个字母就表明了源的类型。目的可以是寄存器(r)或存储器(m)。
	指令名字的第二个字母指明了日的的类型。
	在决定如何实现它们时,显式地指明数据传送的这四种类型是很有帮助的。

	两个存储器传送指令中的存储器引用方式是简单的基址加位移形式。
	在地址计算中我们不支持第二变址寄存器( second index register)和任何寄存器值的伸缩( scaling)。
	
	同IA32一样,我们不允许从一个存储器地址直接传送到另一个存储器地址。
	另外,我们也不允许将立即数传送到存储器

2. 有四个整数操作指令,就是图42中的OPl。它们是add、subl、and和xorl。
	它们只对寄存器数据进行操作,而IA32还允许对存储器数据进行这些操作。
	这些指令会设置个条件码ZF、SF和OF(零、符号和溢出)
	
3. 七个跳转指令(图42中的jxx)是jmp、jle、jl、je、jne、jge和j。
	根据分支指令的类型和条件代码的设置来选择分支。分支条件和IA32的一样(见图3.11)。
	
4. call 指令将返回地址入栈,然后跳到目的地址。ret指令从这样的过程调用中返回
	
5. push和popl指令实现了入栈和出栈,就像在IA32中一样。

6. halt指令停止指令的执行。IA32中有一个与之相当的指令,叫 hlt.
	Ia32的应用程序不允许使用这条指令,因为它会导致整个系统停止。
	我们在Y86程序中用halt指令来停止模拟器

2.2.3 Y86-64指令功能码

图4.3
给出了整数操作和分支指令的具体编码。
在这里插入图片描述

2.2.4 Y86-64寄存器标识符(八个程序寄存器%eax-%esp)

图4.4
在这里插入图片描述

八个程序寄存器中每个都有相应的0~7的寄存器标识符( register ID)。Y86中的寄存器编号跟IA32中的相同。
程序寄存器被存在CPU中的一个寄存器文件中,这个寄存器文件就是一个小的、以寄存器ID作为地址的随机访问存储器。
ID值8用于指令编码中,在我们的硬件设计中,当需要指明不应访问任何寄存器时,我们就用这个值来表示。

2.2.5 指令编码长度不一

有的指令只有一个字节长,而有的需要操作数的指令编码就更长一些。
首先,可能有附加的寄存器指示符字节( register specifier byte),指定一个或两个寄存器。
没有寄存器操作数的指令,例如分支指令和调用指令就没有寄存器指示符字节。
那些只需要一个寄存器操作数的指令(imovi,pushl和popl)将另一个寄存器指示符设为8。
这种约定在我们的处理器实现中非常有用。

2.2.6 四字节常数字

有些指令需要一个附加的四字节常数字( constant word)。
这个字能作为irmovl的立即数数据,作为rmmov和mrmovl的地址指示符的位移量,以及分支指令和调用指令的目的地址。

2.2.7 例子: rmmovl %esp,0x12345(%edx)的字节编码

1. 图4.2看出,rmmovl的第一个字节为40
2. 图4.4看出,esp寄存器指令时4,edx寄存器是2,分别放在ra,rb字段。看图4.2
3. 0x12345放在D位置,看图4.2,四个字节,前面补0,结果是 0x00 01 23 45
4. 常数字反序:45 23 01 00
5. 指令的编码结果: 40 42 45 23 01 00 

2.2.8 指令编码必须有惟一的解释

指令集的一个重要性质就是字节编码必须有惟一的解释。

任意一个字节序列要么是一个惟一的指令序列的编码,要么就不是一个合法的字节序列。
Y86就具有这个性质,因为每条指令的第一个字节有惟一的代码和功能组合,给定这个字节,我们就可以决定所有其他附加字节的长度和含义。这个性质保证了处理器可以无二义性地执行目标代码程序。

只要从序列的第一个字节开始处理,即使代码嵌入在程序中其他字节中。我们仍然可以很容易地确定指令序列。反过来说,如果不知道一段代码序列的起始位置,我们就不能准确地确定怎样将序列划分成单独的指令。对于试图直接从目标代码字节序列中抽取出机器级程序的反汇编程序和其他一些工具来说,这就带来了问题

2.3 指令编码

2.3.1 比较IA32和Y86的指令编码

同IA32中的指令編码相比,Y86的编码简单得多,但是也没那么简洁。
在所有的Y86指令中寄存器字段的位置都是固定的,而在不同的IA32指令中,它们的位置是不一样的。即使最多只有8个寄存器,我们也对寄存器采用了4位编码。IA32只用了3位编码。所以IA32能将入栈或出栈指令放在一个字节里,5位字段表明指令类型,剩下的3位是寄存器指示符。IA32可以将常数值编码成1、2或4个字节,而Y86总是将常数值编码成4个字节。

2.3.2 RISC和CISC指令集

1A32有时被称为“复杂指令集计算机”(CISC—读作“sisk”),与“精简指令集计算机”(RISC读作“risk”)相对。
从历史上看,从最早的计算机发展而来,先出现了CISC,后出现CISC。

图 4.4.11
在这里插入图片描述

RISC和CISC指令集比较

2.3.3 Y86与RISC和CISC的关系

Y86指令集既有CISC指令集的属性,也有RISC指令集的属性。
和CISC一样,它有条件码、指令长度可变,以及栈密集的过程链接。和RISC一样的是,它采用 load/store体系结构和规则编码regularencoding)。
Y86指令集可以看成是采用的CISC指令集(IA32),但又根据某些RISC的原理进行了简化。

2.4 代码例子

int Sum(int *Start, int Count)
{
	int sum = 0;
	while(Count){
		sum += *Start;
		Start++;
		Count--;
	}
	
	return sum;
}

2.4.1 汇编代码:

[root@localhost hello]# gcc -S y86.c -o y86.s
[root@localhost hello]# cat y86.s
		.file   "y86.c"
		.text
		.globl  Sum
		.type   Sum, @function
Sum:
.LFB0:
		.cfi_startproc
		pushq   %rbp
		.cfi_def_cfa_offset 16
		.cfi_offset 6, -16
		movq    %rsp, %rbp
		.cfi_def_cfa_register 6
		movq    %rdi, -24(%rbp)		//rdi = Start
		movl    %esi, -28(%rbp)		//esi = Count
		movl    $0, -4(%rbp)		//sum = 0;
		jmp     .L2					//jmp 无条件转移指令
.L3:
		movq    -24(%rbp), %rax
		movl    (%rax), %eax		//eax = sum
		addl    %eax, -4(%rbp)		//sum += *Start;
		addq    $4, -24(%rbp)		//Start++;
		subl    $1, -28(%rbp)		//Count--;
.L2:
		cmpl    $0, -28(%rbp)		//while(Count)
		jne     .L3					//jne 条件转移指令
		movl    -4(%rbp), %eax
		popq    %rbp
		.cfi_def_cfa 7, 8
		ret
		.cfi_endproc
.LFE0:
		.size   Sum, .-Sum
		.ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)"
		.section        .note.GNU-stack,"",@progbits
[root@localhost hello]#

2.4.2 Y86指令结果:

图4.5
在这里插入图片描述

3. 逻辑设计和硬件控制语言HCL

3.1 本节概要

3.1.1 高低电压/1和0,数字系统组成

在硬件设计中,电子电路被用来计算位的函数( functions on bits),以及在各种存储器元素中存
储位。

大多数现代电路技术都是用信号线上的高电压或低电压来表示不同的位值。
通常的技术中逻辑1是用1.0伏特左右的高电压表示的,而逻辑0是用0.0伏特左右的低电压表示的。

要实现一个数字系统需要三个主要的组成部分:

1. 计算位的函数的组合逻辑
2. 存储位的存储器元素
3. 存储器元素更新的时钟信号

本节中,我们简要描述这些不同的组成部分。我们还将介绍HCL( hardware control language硬件控制语言),我们用这种语言来描述不同处理器设计的控制逻辑。

在此我们只是简略地描述HCL,HCL完整的参考请见附录A。

3.1.2 现代逻辑设计发展(HDL:verilog )

硬件设计者曾经描绘示意性的逻辑电路图来进行电路设计(最早是用纸和笔画,后来是用计算机图形终端)。
现在,大多数设计都是用HDL来表达的.HDL是一种文本表示,看上去和编程语言类似,但是它是用来描述硬件结构而不是程序行为的

最常用的语言是 Verilog,它的语法类似于C

另一种VHDL,它的语法类似于编程语言Ada.这些语言本来都是用来表示数字电路的模拟模型的,在20世纪80年代中期,研究者开发出了還辑合成( logic synthesis)程序,它可以根据HDL的描述生成有效的电路设计。
现在出现了许多商用的合成程序,它们已经成为产生教字电路的主要技术。

从手工设计电路到合成生成的转变就好像从写汇编程序到写高级语言程序,再用編译器来产生机器代码的转变一样

3.2 逻辑门

逻辑门是数字电路的基本计算元素。它们产生的输出,等于它们输入位值的某个布尔函数。
图4.8
在这里插入图片描述

布尔函数AND、OR和NOT的标准符号
布尔操作的逻辑门下面是对应的HCL表达式。

正如你看到的那样,我们采用了C中的逻辑运算符的语法:AND用&&表示,OR用表示,而NOT用!表示。
我们不用C中的位运算符&、和~。是因为逻辑门只对一个位进行操作不是整个字。

逻辑门总是活动的( active)。一旦的输入变化了,在很短的时间内,输出就会相应地

3.3 组合电路和HCL布尔表达式

3.3.1 组合电路

组合电路:
将很多的逻辑门组合成一个网,我们就能得到计算块,即组合电路。

3.3.2 组合电路的两条限定

如何组成这个网有两条限定:

1. 两个或多个逻辑门的输出不能接在一起
	否则它们可能会使线上的信号矛盾,导致一个不合法的电压或电路故障
2. 这个网必须是无环的
	也就是在网中不能有路径经过一系列的门而形成一个回路,这样的路会导致该网络的计算函数有歧义

3.3.3 简单组合电路的例子

图4.9
在这里插入图片描述

这是一个我们觉得非常有用的简单组合电路的例子

它有两个输入a和b,有惟一的输出eq。
当a和b都是1(从上面的AND门可以看出)或都是0(从下面的AND门可以看出)时,输出为1。

用HCL来写这个网的函数就是:

bool eq = ( a && b) || (!a && !b)

这段代码简单地定义了位级(数据类型bool表明了这一点)信号eq,它是输入a和b的函数。
从这个例子可以看出HCL使用了C风格的语法将一个信号名与一个表达式联系起来。
不过与C不一样,我们不把它看成执行了一次计算并将结果放入存储器中某个位置。相反,它只是用名字来称谓一个表达式

3.3.4 简单有用的组合电路:多路复用器

图4.10
在这里插入图片描述

图4.10给出了另一个简单但很有用的组合电路,称为多路复用器( multiplexor)。
多路复用器根据输入控制信号的值,从一组不同的数据信号中选出一个。在这个单个位的多路复用器中,两个数据信号是输入位a和b,控制信号是输入位s。

当s为1时,输出等于a;
当s为0时,翰出等于b。

在这个电路中,我们可以看出两个AND门决定了是否将它们相对应的数据输入传送到OR门。
当s为0时,上面的AND门将传送信号b(因为这个门的另一个输入是!s),
而当s为1时,下面的AND门将传送信号a。接下来,我们来写输出信号的HCL表达式,使用的就是组合逻辑中相同的操作:

3.3.5 组合逻辑电路和C中逻辑表达式异同

我们的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。
	而组合逻辑没有部分求值这条规则,逻辑门只是简单地响应它们输入的变化。

3.4 字级的组合电路和HCL整数表达式

3.4.1 对数据字进行操作的电路

通过将逻辑门组成一个更大的网,我们可以构造出能计算更加复杂函数的组合逻辑。
通常,我们设计了能对数据字(data words)进行操作的电路。它们是一些位级的信号,代表一个整数或一些控制模式。
例如,我们的处理器设计将包括有很多字,字的大小为4~32位,代表整数、地址、指令代码和寄存器标识符。

3.4.2 字级相等测试电路

执行字级计算的组合电路是根据输入字的各个位,用逻辑门来计算输出字的各个位。
图:4.11
在这里插入图片描述

例如图4.11中的一个组合电路,它测试两个32位字A和B是否相等。也就是,当且仅当A的每一位都和B的相应位相等时,输出才为1。这个电路是用32个图4.9中所示的那样的单个位相等电路实现的。这些单个位电路的输出用一个AND门连接起来,形成这个电路的输出。

为了简化,在HCL中,我们将所有字级的信号都声明为int,而不指定字的大小。
在特性比较完善的硬件描述语言中,每个字都可以声明有特定的位数。HCL允许比较字是否相等,因此图4.1所示的电路的函数可以在字级上表达成

bool EQ = (A = = B)

这甲参数A和B是int型的。注意我们使用和C中一样的语法习惯表示赋值,而是相等运算符。
如图4.11中右边所示的那样,在画字级电路的时候,我们用中等粗度的线来表示携带字的单个位的线路,而用虚线来表示布尔信号结果。

3.4.3 字级多路复用电路

图4.12
字级的多路复用器电路
在这里插入图片描述

int out = [
		s : A;
		1 : B; //1 表明如果前面没有请客被选中,那就选择它
];

图4.12给出的是字级的多路复用器电路。

这个电路根据控制输入位s,产生一个32位的字Out,等于两个输入字A或者B中的一个。

这个电路由32个相同的子电路组成,每个结构都类似于图4.10中的位级多路复用器。不过这个字级的电路并没有简单地复制32次位级多路复用器,它只产生一次!s,然后在每个位的地方都重复使用它,从而减少反相器或非门(inverters)的数量。

在我们的处理器中会用到很多种多路复用器。它使得我们能根据某些控制条件,从许多源中选出一个字。
在HCL中,多路复用函数是用情况(case)表达式来描述的。情况表达式的通用格式如下

[
	select1:	expr1;
	select2:	expr2;
	……
	selectn:	exprn;
	
]

它包含一系列的情况。

3.4.4 四路复用器

图4.411
在这里插入图片描述

这个电路根据控制信号s1和s0,从四个输入字A,B,C,D中选择一个,这里用一个两位的进制数作为控制信号。
我们可以用HCL来表示这个电路,用布尔表达式描述控制位模式的不同组合

int out4 = [
		!s1 && !s0 	:	A;	#00
		!s1 		: 	B;	#01
		!S0			:	C;	#10
		1			: 	D;	#11
];

右边的注释(任何以#开头到行尾结束的文字都是注释)表明了s1和s0的什么组合会导致该种情况会被选中。
可以看到选择表达式有时可以简化,因为只有第配的情况才会被选中。
例如第二个表达式可以写成!s1,而不用写得更完整!s&&s0,因为另一种可能s1等于0已经出现在第一个选择表达式

3.4.5 找最小值

需求:找出A B C 中的最小值

图4.412
在这里插入图片描述

3.4.6 ALU

组合逻辑电路设计成在字级数据上执行许多不同类型的操作。具体的设计已经超出了我讨论的范围。
算术/逻辑单元(ALU)是一种很重要的组合电路,图4.13是它的一个抽象的图示。

图4.13

在这里插入图片描述

这个电路有三个输入:两个标号为A和B的数据输入,以及一个控制输入。
根据控制输入的设置电路会对数据输入执行不同的算术或逻辑操作。

这个ALU中画的四个操作对应于Y86指令集支持的四种不同的整数操作,而控制值和这些操作的功能码相对应(图43)。我们还注意到减法的操作数顺序,是输入A减去输入B。之所以这样做,是为了使这个顺序与sub指令的参数顺序一致

3.5 集合关系

在我们的处理器设计中,很多时候都需要将一个信号与许多可能匹配的信号做比较,以此来测正在处理的某些指令代码是否属于某一类指令代码。
下面来看一个简单的例子,我们想从一个两位信号代码中选择高位和低位来为图4.12中的四路复用器产生信号sl和s0,如下图所

图4.2.4
在这里插入图片描述

在这个电路中,两位的信号代码可以用来控制对四个数据字A,B,C和D的选择。根据可能
的代码值,可以用相等测试来表示信号s1和s0的产生:

bool sl= code ==2 || code ==3
bool s0= code ==1 || code = 3

3.6 存储器和时钟

组合电路从本质上讲不存储任何信息,只简单的响应输入,得到输出。

为了产生时序电路,也就是有状态并且在这个状态上进行计算的系统,我们必须引入按位存储信息的设备。
我们考虑两类存储器设备:

1. 时钟寄存器(简称寄存器)存储单个位或字。时钟信号控制寄存器加载输入值

2. 随机访问存储器(简称存储器)存储多个字,用地址来选择该读或该写哪个字。
	随机访问存储器的例子包括:
		处理器的虚拟存储器系统
		硬件和操作系统软件结合起来使处理器以在一个很大的地址空间内访问任意的字,寄存器文件,
			在此,寄存器标识符作为地址
			
		在IA32或Y86处理器中,寄存器文件有八个程序寄存器(%eax、%ecx等)

3.6.1 硬件寄存器 和 程序寄存器

正如我们看到的那样,在讲硬件和机器级编程时,单词“寄存器”有些细微的差别。

1. 在硬件中,寄存器直接将它的输入和输出线连接到电路的其他部分。
2. 在机器级编程中,寄存器代表的是CPU中为数不多的可寻址的字,这里的地址是寄存器ID。
	这些字通常都存在寄存器文件中,
	虽然我们会看到硬件有时可以直接将一个字从一个指令传送到另一个指令,
	以避免先写寄存器文件再读出来的延迟。

需要避免歧义时,我们会分别称呼这两类寄存器为“硬件寄存器”和“程序寄存器”。
8个程序寄存器:eax,ebx,ecx,edx,esi,edi,esp,ebp

3.6.2 硬件寄存器

图4.14
在这里插入图片描述

图4.14给出了一个硬件寄存器,以及它是如何工作的。
大多数时候,寄存器都保持在稳定状态(用x表示),产生的输出等于它的当前状态。
信号沿着寄存器前面的组合逻辑传播,这时,产生新的寄存器输入(用y表示),但只要时钟是低电位,寄存器的输出就仍保持不变。
当时钟变成高电位的时候,输入信号就加载到寄存器,成为下一个状态y,这个状态就成为寄存器的新输出,直到下一个时钟上升沿( ising clock edge)的时候。
特别指出的是寄存器被作为电路不同部分中的组合逻辑之间的屏障。只有在每个升沿时,值才会从寄存器的输入传送到输出。

3.6.3 典型寄存器文件

图4.2.5 典型的寄存器文件
在这里插入图片描述

寄存器文件有两个读端口(A和B),还有写端口(W)。
这样一个多端口随机访问存储允许同时进行多个读和写操作图中所示的寄存器文件中,电路可以读两个程序寄存器的值,同时更新第三个寄存器的状态。每个端口都有一个地址输入,表明该选择哪个程序寄存器,另外还有个数据输出或对应该程序寄存器的输入值。

地址是用图4.4(8个常用程序寄存器)中编码表示的寄存器标识符。两个读端口有地址输入srcA和srcB(“ source A”和“ source B”的缩写)和数据输出vaA和valB(“ valueA”和“ value e”的缩写)。写端口有地址输入dstW(“ destination W”的缩写),以及数据输入valW(“ value w”的缩写)。
虽然寄存器文件不是组合电路(因为它有内部的存储),但是从中读取字的操作与以地址为输入数据为输出的一块组合逻辑是一样的。当srcA或srcB被设成某个寄存器ID时,在一段延迟之后相应程序寄存器的值就会出现在valA或valB上。

例如,将srcA设为3,就会读程序寄存器%ebx的值,然后这个值就会出现在输出valA上。时钟信号按照类似于将值加载进时钟寄存器一样的方式控制向寄存器文件写入字。每次时钟上升时,输入valW上的值被写入输入dstW上的寄存器ID指示的程序寄存器。当dstW设为特殊的ID值8时,不会写任何程序寄存器。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值