📄 chapter2.htm
字号:
<BR>RMW和锁指令在一个大的多处理器系统中效率是不好的。我们将在5.8.4节中解释加 <BR>载关联和条件存储的操作。在这里,下面是对它们功能的一些介绍。
<BR><BR>ll是一个普通的加载一个word的指令,但是它在一个特殊的内部寄存器中保持这个
<BR>地址的记录。sc是一个存储一个word的指令,但是它只在如下条件下才存储: <BR><BR>*
自从上次在这同样地址上的ll指令之后,CPU没有发生任何中断或异常,并且 <BR><BR>*
(对多处理器系统),没有别的CPU发出写操作或试图一个写操作并且写的地址包括 <BR>了ll指令使用的地址。
<BR><BR>sc指令会返回一个值来告诉程序存储是否成功。 <BR><BR>虽然ll和sc指令是为多处理器系统设计的,也可以被用在单处理器系统上。从而可
<BR>以实现一个semaphore而不需要关闭中断。 <BR><BR>封闭循环转移(可能循环)
<BR><BR>高效的MIPS代码要求编译器能够在大多数延迟槽上安排有用的工作。在许多情况下,
<BR>逻辑上在跳转指令之前的那条指令是合适的选择。显然,如果这个跳转指令是一个
<BR>条件跳转并且在其之前的那个指令是计算这个跳转条件的,那么就不能把其之前的 <BR>那条指令放入延迟槽中。
<BR><BR>这种情况在包含一个循环的跳转中经常出现。循环越小,编译器就越难找到一个之 <BR>前的指令并方入延迟槽中。
<BR><BR>在一个循环里面,编译器的第二种选择是在延迟槽中存放一个跳转指令的目的地的
<BR>那条指令的备份。并且将跳转目标地址提高一个word。这个调整不会使得程序变小,
<BR>但确实能使程序运行加快。但是这个方法通常是不可能的。当一个循环结束时,在
<BR>延迟槽里的指令将会被执行,这使得编译器很难判断这个行为是否会造成任何损害。
<BR><BR><BR>在这里编译器需要的是一个只有在跳转被执行时延迟槽里的指令才被执行的跳转指 <BR>令。这是MIPS
III指令集可以提供的功能。这些指令称之为“可能跳转”(branch <BR>likely)--这个命名非常容易迷惑人。它们的助记符是在清b有的指令助记符后面加
<BR>一个"l":因此beq产生begl指令。其他依此类推。 <BR><BR>条件异常 <BR><BR>随着MIPS III,
提供了一系列指令可以依据一个条件来使CPU进入异常处理:测试 <BR>条件与“set if ..."指令是一样的。这些指令在C语言中没有相应的语法,但是可
<BR>以用来实现那种动态检查数组越界的编程语言。 <BR><BR>扩充的浮点数
<BR><BR>R6000将浮点寄存器扩充到了64位宽度。但是我把其当作是MIPS III扩充到64位的一 <BR>部分。如果新的MIPS
II有浮点处理器(不太可能),一般而言是32位的。 <BR><BR>2.8 基本地址空间 <BR><BR>相对於其他CISC CPU,
MIPS处理器对地址空间的使用有些细微的不同。这一点有时 <BR>会使人迷惑。请仔细阅读这一节的第一部分。我们将先介绍32位CPU的情况,然后再
<BR>介绍64位。耐心点你将会在以后知道我为什么这样做。 <BR><BR>下面是一些概述。在MIPS CPU里,你的程序中的地址不一定是芯片真正访问的物理
<BR>地址。我们分别称之为:□ <BR>'7b序地址和物理地址。 <BR><BR>一个MIPS CPU可以运行在两种优先级别上, 用户态和核心态。MIPS
CPU从核心态到 <BR>用户态的变化并不是CPU工作不一样,而是对於有些操作认为是非法的。在用户态,
<BR>任何一个程序地址的首位是1的话,这个地址是非法的,对其存取将会导致异常处理。 <BR>另外,在用户态下,一些特殊的指令将会导致CPU进入异常状态。
<BR><BR>在32位下,程序地址空间划分为4个大区域。每个区域有一个传统的名字。对於在这 <BR>些区域的地址,各自有不同的属性:
<BR><BR>kuseg: 0x000 0000 - 0x7FFF FFFF (低端2G):这些地址是用户态可用的地址。在
<BR>有MMU的机器里,这些地址将一概被MMU作转换。除非MMU的设置被建立好,这2G地址 <BR>是不可用的。
<BR><BR>对於没有MMU的机器,存取这2G地址的后依具体机器相关。你的CPU具体厂商提供的
<BR>手册将会告诉你关于这方面的信息。如果想要你的代码在有或没有MMU的MIPS处理器 <BR>之间有兼容性,尽量避免这块区域的存取。
<BR><BR>kseg0: 0x8000 0000 - 0x9FFF FFFF(512M): 这些地址映射到物理地址简单的通过
<BR>把最高位清零,然后把它们映射到物理地址低段512M(0x0000 0000 - 0x1FFF FFFF)。
<BR>因为这种映射是很简单的,通常称之为“非转换的“地址区域。
<BR><BR>几乎全部的对这段地址的存取都会通过快速缓存(cache)。因此在cache设置好之前,
<BR>不能随便使用这段地址。通常一个没有MMU的系统会使用这段地址作为其绝大多数程
<BR>序和数据的存放位置。对於有MMU的系统,操作系统核心会存放在这个区域。 <BR><BR>kseg1: 0xA000 0000 - 0xBFFF
FFFF(512M): 这些地址通过把最高3位清零的方法来 <BR>映射到相应的物理地址上,与kseg0映射的物理地址一样。但kseg1是非cache存取的。
<BR><BR><BR>kseg1是唯一的在系统重启时能正常工作的地址空间。这也是为什么重新启动时的入 <BR>口向量是0xBFC0
0000。这个向量相应的物理地址是0x1FC0 0000。 <BR><BR>你将使用这段地址空间去存取你的初始化ROM。大多数人在这段空间使用I/O寄存器。
<BR>如果你的硬件工程师要把这段地址空间映射到非低段512M空间,你得劝说他。 <BR><BR>kseg2: 0xC000 0000 - 0xFFFF
FFFF (1G): 这段地址空间只能在核心态下使用并且 <BR>要经过MMU的转换。在MMU设置好之前,不能存取这段区域。除非你在写一个真正的
<BR>操作系统,一般来说你不需要使用这段地址空间。 <BR><BR>2.8.1 简单系统的寻址
<BR><BR>MIPS的程序地址很少与真正的物理地址一致。但对於简单的嵌入式软件而言可以用
<BR>kseg0和kesg1这两段地妒7d空间。它们朝物理地址的映射关系是非常直接了当的。 <BR><BR><BR>从0x20000
0000开始的512M物理地址空间在上述kseg0, kseg1 和kseg2中没有任何 <BR>的映射。你可以通过设置MMU
TLB的方式来访问,或者使用64位CPU的一些其他额外 <BR>的空间。 <BR><BR>2.8.2 核心与用户权限
<BR><BR>在核心态下(CPU启动时),PU可以作任何事情。在用户态下,2G之上的地址空间是非
<BR>法的。任何存取将会导致系统异常处理。注意的是,如果一个CPU有MMU,这意味着
<BR>所有的用户地址在真正访问到物理地址之前必须经过MMU的转换,从而使得OS可以防 <BR>止用户程序随便乱用。对於一个没有内存映射的OS,MIPS
CPU的用户态其实是多余 <BR>的。 <BR><BR>另外,在用户态下,一个指令,特别是那些CPU控制指令,是不能使用的。
<BR><BR>要提及的是,当你作核心态和用户态切换时,并不意味着□c能的改变,只不过是意
<BR>味着某些功能在用户态下不能使用了。在核心态下,与用户态一样,CPU可以存取低 <BR>段地址空间。这个存取也是通过MMU的转换。这一点与用户态下一样。
<BR><BR>另外要注意的是,虽然如果把操作系统运行在核心态下,平常的代码运行在用户态
<BR>下是一种不错的选择。但如果反之也不为过。有些系统,包括□c多实时操作系统, <BR>都是全部运行在核心态下。 <BR><BR>2.8.3
64位CPU的地址空间 <BR><BR>MIPS地址的形成是通过一个16位的偏移量和一个寄存器。在MIPS III或更高版本的
<BR>CPU里,一个寄存器是64位。因此一个程序地址是64位的。这样大的地址空间允许我 <BR>们耐心的将其划分。请参阅图2.2。
<BR><BR>首先要注意的是64位内存映象是包含在32位内存映象里面的。这是个有点奇怪的方 <BR>法,就象Dr.
who的“Tardis”--里面比外面要大的多。这一点是通过2.7.3节介绍的 <BR>规则来实现的:当模拟一个32位指令集的适合,寄存器存放的是其32
位的带符合位 <BR>扩展的64位值。因此,一个32位程序存取的是64位程序空间的最低和最高的2G。换
<BR>句话说,64位CPU的地址空间的最低和最高区域是和32位情况下一样的,64位扩展的 <BR>地址部分在这两者之间。
<BR><BR>在实践中,扩展的用户地址空间和超级用户权限的地址空间一般而言没有太大的用 <BR>处,除非你在写一个虚拟内存操作系统。因此许多MIPS
III的使用者仍然定义32位 <BR>的指针。64位下那些大块的不需要MMU转换的窗口可以克服kseg0和kseg1 512M的局
<BR>限,但是我们可以通过对MMU编程来同样达到这一点。 <BR><BR>2.8.4 流水线hazard
<BR><BR>任何一个有流水线的CPU硬件对於那些不能满足严格的一个时钟周期规则的操作都将
<BR>会存在一个延迟。体系结构的设计者要决定这些延迟中的哪一些对於编程员是可见 <BR>的。将时序
上的缺点隐含起来使得程序员的编程模型简单,比如,CPU究竟在干什 <BR>么。当然与此同时,这将对硬件实现引入复杂性。将调度问题留给程序员和其软件
<BR>工具将简化硬件部分,但同时产生编程和移植的问题。 <BR><BR>正如我们已经提过几次,MIPS体系结构使得其一些缺点/特点是可见的。程序员和编
<BR>译器要负责配合CPU使得其正常工作。下面一些是关于流水线的方面: <BR><BR>* 跳转延迟:在所有的MIPS
CPU里,紧跟着跳转指令的指令(在延迟槽中)会被CPU执 <BR>行,即使跳转成左5c。在MIPS
II指令集中引入的“可能跳转”(branch-likely)指 <BR>令中,在延迟槽中的指令只会在跳转被接受的情况下被执行。详细可见8.4.4关于”
<BR>可能跳转“的基本原理。程序员或编译器必须找到一个有用的,至少是无害的指令 <BR>放在延迟槽中。但是,除非你指定,汇编器将会使得跳转延迟是透明不可见的。
<BR><BR>*加载延迟:在MIPS I指令集里,load指令后面的指令(在加载延迟槽)不能使用刚用
<BR>load加载的数据。一个有用的或无害的指令需要放在加载延迟槽里来将数据加载和
<BR>数据使用分开。与跳转延迟一样,除非你指定,汇编器将会使得这个延迟处理对你 <BR>是透明不可见的。
<BR><BR>*整数乘法/除法问题:整数乘法部件是和ALU部件分开的,没有实现“精确异常”
<BR>(请参阅5.1节关于精确异常的定义)。解决方法很简单,通常是通过汇编器--在读取
<BR>上一个乘除法的结果值之后,你需要避免立刻启动下一个乘除法运算。为什么这个 <BR>解决方法是必须的和足够的很负责(请参阅5.1节)。
<BR><BR>*浮点数(协处理器1)的缺点:任何一个浮点运算几乎都要花费多个CPU时钟周期来完 <BR>成,MIPS
FPA通常有多个独立的流水线部件。在这种情况下,硬件可以把流水线隐 <BR>含起来;FP计算可以与其后的指令并行的执行。当一个指令读取一个尚未完成的浮
<BR>点计算的结果寄存器时,CPU就会停止下来。编译器需要大量的优化工作在这方面,
<BR>比如重复指令比率表,各种目标CPU的延迟表等。当然,你没必要依赖这些来使得你 <BR>的程序工作。
<BR><BR>如果一个浮点计算没有流水线hazard,并不意味着浮点运算协处理器与整数运算部 <BR>件的交互没有流水线hazard。这里面有两方面原因。
<BR><BR>第一,从浮点运算器移动数据到整数寄存器的指令--mfcl, 传送数据的时刻是在下
<BR>一个时钟周期,与“load”具有同样的时序要求。就象load一样,在MIPS 1 CPU中,
<BR>这是个hazard,但在后来的硬件中,被利用硬件的内置锁(interlock)解决了。优化 <BR>的编译器会利用延迟槽完成一些有用的工作。
<BR><BR>第二,测试一个浮点运算的条件的指令不能直接跟在产生那个条件的浮点比较操作 <BR>后面。对大多数MIPS CPU实现,需要一个指令的延迟。
<BR><BR>* CPU控制指令问题:这个部分非常容易迷惑人。当你改变CPU状态寄存器的内容时,
<BR>你潜在地在影响发生在流水线所有阶段的东西。因为关于CPU控制系统的结构描述是
<BR>与具体的实现有关,因此没有ISA指令集方面的规则可以遵循。遗憾的是CPU厂商至 <BR>今没有提供有关相应的文档。
<BR><BR>请参阅第三章关于MIPS CPU控制指令的总结,然后请阅读附录A关于R4000 CPU的时 <BR>序问题。
<BR> </P><BR></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -