第2章 开源嵌入式处理器
本章主要内容
嵌入式处理器种类繁多,但是使用最广泛的是哈佛架构的RISC处理器。OpenRISC 1000是开放源代码RISC处理器簇,OR1200是它的一个32位实现,指令集是CPU硬件与软件的接口。在DemoSoC中,使用了OR1200作为处理器。官方给出的OR1200的性能在同频率的ARM7和ARM9之间。据作者所知,OR1200是目前唯一一个可以免费商用的开源处理器。本章将讲述OpenRISC 1000处理器簇的指令格式、处理器的结构组成,还将阐述OR1200处理器对OpenRISC 1000构架的具体实现、结构组成部分、硬件配置等。不打算更多地了解OR1200 CPU的读者可以略过本章。
2.1 开源嵌入式处理器介绍
2.1.1 OpenRISC 1000构架的主要特点
OpenRISC 1000是开放源代码的RISC处理器簇,提供各种价格/性能的应用。它是32/64bit位装载(Load)和存储(Store)RISC构架,强调执行效率、简单、低功耗、可测量性和多样性。目标是中高执行效率的网络、嵌入便携高计算机环境。
OpenRISC 1000执行特征包括全32/64bit构架、向量、DSP和浮点指令、虚拟内存支持、一致的高速缓存、SMP和SMT支持、支持快速上下文切换。它较突出的特征是有几个指令扩展、提供可配置的通用寄存器、提供可配置的高速缓存和TLB尺寸、支持动态电源管理、有用户提供指令的空间。
应用OpenRISC 1000构架的RISC的源代码在http://www.opencores.org可用,并且被GNU软件开发工具和行为仿真器支持。它们能与http://www.opencores.org上的其他开放源代码的IP核进行接口。Opencores.org鼓励第三方设计和在商业上使用OpenRISC 1000构架的RISC。
OpenRISC 1000构架的主要特征如下:
(1)完全自由和开放的构架。
(2)用特定的物理地址空间提供线性的、32bit或64bit逻辑地址空间。
(3)简单的和长度一致的指令格式,不同的指令集扩展,包括如下指令集。
● 具有32bit宽度指令的OpenRISC基本指令集(ORBIS32/64 OpenRISC Basic Instruction)。它在内存的32bit边界对齐,并且在32bit和64bit数据上进行操作。
● OpenRISC向量/DSP扩展(ORVDX64 OpenRISC Vector/DSP eXtension),它有32bit位宽指令,可操作8、16、32和64bit数据。
● OpenRISC浮点扩展(ORFPX32/64 OpenRISC Floating-Point eXtension),它有32bit位宽指令,操作32和64bit数据。
(4)两种简单内存地址模式,内存地址计算方法如下:
● 寄存器操作数加上一个有符号16bit立即数得到有效地址。
● 寄存器操作数和有符号16bit立即数相加,这个16位立即数被存有计算过的有效地址的寄存器操作数更新。
(5)大多数指令的两个寄存器操作数(或一个寄存器和一个常量)执行结构放在第三个寄存器中。
(6)单个32条目或窄16条目通用寄存器文件。
(7)保持尽可能全流水线运行的分枝延迟槽。
(8)支持分开的指令和数据高速缓存/MMU(Harvard构架)或统一的指令和数据高速缓存/MMU(标准构架)。
(9)允许某一功能被执行的弹性构架定义。
(10)不同的、分开的例外简化了例外模型。
(11)在寄存器集、高速缓存和MMU中支持快速上下文切换。
OpenRISC 1000命名规则是:第一个数字“1”表示是OpenRISC 1000构架序列,第二个数字“0”表示具体应用的构架。后两位数字表示实际应用中的具体配置。
2.1.2 寻址模式
当执行一个内存访问指令或分支指令或当支取随后的一个指令时,处理需要计算一个有效地址。如果有效地址和操作数长度的和超过了逻辑地址空间的最大有效地址,内存操作数从最大有效地址循环回到有效地址0。OpenRISC 1000寻址模式有寄存器间接寻址和PC相对寻址。
1.寄存器间接寻址
Load/Store指令使用这种寻址模式将一个有符号的16bit立即数值加到指令指定的通用寄存器所存储的值上,得到一个有效地址。如图2-1所示为当使用寄存器间接寻址模式时,一个有效地址是如何计算得到的。
图2-1 寄存器间接寻址
2.PC(程序计数)相对寻址
分支指令使用这种寻址模式,它存有一个有符号扩展的26bit立即数,并把它加到一个程序计数寄存器存储的数据上,得到有效地址。在目的地PC执行之前,延迟槽里的指令被执行。如图2-2所示为当使用PC相对寻址模式时,一个有效地址如何计算得到的。
图2-2 PC相对寻址
2.1.3 bit位和byte字节次序
字节次序定义了组成半个字、单个字和双字的字节流在内存中如何排序。为了简化OpenRISC的使用,构架默认时使用了MSB(Most Significant Byte)字节次序或称为大端字节序(Big Endian Byte)。但如果构架使用了字节重排序硬件,它也支持LSB(Least Significant Byte)字节次序或称为小端字节序。重排序使用bit位SR[LEE]激活。
表2-1列出了双字在大端字节序时字节及bit位在内存中的排序。单个字和半个字的字节及bit位排序使用类似的机制。
表2-1 双字的字节及bit位次序
对齐和非对齐的访问方式如下:
如果操作数的地址是操作数长度的整数倍,那么,内存中的操作数是地址对齐的。构架支持非对齐的访问,但默认的行为是对非对齐操作数的访问将导致一个对齐例外。OR32指定是4个字节和字对齐的。内存操作数对齐时地址低位的特征如表2-2所示。
表2-2 内存操作数对齐时地址低位的特征
2.1.4 寄存器集
OpenRISC 1000构架的处理器包括几种类型寄存器,分别为用户级通用寄存器和特殊寄存器、超级监管者级特殊寄存器和依赖于硬件单元的寄存器。寄存器的详细说明可参阅《OpenRISC 1000 Architecture Manual》。
用户级通用和特殊寄存器在用户模式(user)和超级监管者模式(supervisor)下都可被访问。超级监管者寄存器仅在超级监管者模式下操作(即SR[SM]=1)时才可被访问。
依赖于硬件单元的寄存器通常仅在超级监管者模式下可被访问,但有个别的除外。
1.特殊寄存器
所有单元的特殊寄存器被分成32组,每组中的寄存器使用不同的寄存器地址(编号)区分。一个组中可以放有几个不同单元或处理器的寄存器。在寄存器地址解码中,由于一些寄存器仅在超级监管者模式下可被访问,因此,SR[SM]位也被使用。指令l.mtspr和l.mfspr被用来读写特殊寄存器。特殊寄存器的分组说明如表2-3所示。
表2-3 特殊寄存器(SPR Special Purpose Register)的分组
OpenRISC 1000构架处理器需要至少使用第0组特殊寄存器。其他的特殊寄存器是可选的,并且只有单元存在时,单元相应的特殊寄存器组才存在。
16bit的SPR地址由5bit组序号(15~11bit位)和11bit寄存器序号(10~0bit位)组成。表2-4列出了第0组特殊寄存器的说明。其中,带有R*的SPR表示如果SR[SUMRA]被设置,用户模式下对SPR的访问是可读的。
表2-4 第0组特殊寄存器说明
2.通用寄存器(GPR)
32个通用寄存器被标识为R0~R31,并且在32bit处理器中是32bit位宽,在64bit处理器是64bit位宽。它们存储浮点数据、向量或内存指针。GPR可被ORBIS、ORVDX和ORFPX指令作为源和目的地寄存器进行访问。
R0被用作常量0,R0是否实际上硬件连线到0依赖于构架的应用。R0永远不应该被用做目的地寄存器。
一个构架的应用可能有几套GPR组,并把它们用做映像(shadow)寄存器,无论何时,一个新的例外发生时,它们之间发生切换。当前使用的一套通过SR[CID]值被鉴别。CID为进程上下文的序号,值为0~15,即可能有16套通用寄存器组,共512个寄存器(32×16=512)。
构架的应用RISC在复位期间不需要初始化GPR到0,必要时复位例外处理句柄负责初始化GPR到0。
3.支持定制GPR数量
程序可能被编译使用少于32个寄存器,编译代码时不用的寄存器被禁止(设置作为固定寄存器)。这些代码在正常的构架应用RISC中也可执行。如果配置寄存器被使用,CPUCFGR[CGF]指出构架应用RISC是否有完全的32个通用寄存器或少于32个寄存器。
4.超级监管者寄存器
超级监管者寄存器(Supervision Register,SR)是32位特殊寄存器,仅能在超级监管模式下使用l.mtspr/l.mfspr指令访问。SR值定义了处理器的状态。
5.例外程序计数寄存器
例外程序计数寄存器(Exception Program Counter Registers)有EPCR0~EPCR15,它们是超级监管者级别的特殊寄存器,使用l.mtspr/l.mfspr指令在超级监管模式下访问。如果PCMRx[SUMRA]被激活,用户模式下是可读的。在一个例外产生后,EPCR被设置到被例外中断的指令的程序计数器(PC)地址。如果仅一个EPCR出现在构架应用RISC中,在例外识别再次在SR中被激活之前,它必须被例外处理例程存储。
6.例外有效地址寄存器
例外有效地址寄存器(Exception Effective Address Registers)有EEAR0~EEAR15,它们是的特殊的超级监管者级别的寄存器,使用l.mtspr/l.mfspr指令在超级监管模式下访问。如果SR[SUMRA]被激活,用户模式下是可读的。
在一个例外产生后,EEAR被设置到被例外指令产生的有效地址(EA effective address)。如果仅一个EEAR出现在构架应用RISC中,在例外识别再次在SR中被激活之前,它必须被例外处理例程存储。
7.例外超级监管者寄存器
例外超级监管者寄存器(Exception Supervision Registers)有ESR0~ESR15,它们是的特殊的超级监管者级别的寄存器,使用l.mtspr/l.mfspr指令在超级监管模式下访问。
在一个例外产生后,超级监管者寄存器(SR)被复制到ESR。如果仅一个ESR出现在构架应用RISC中,在例外识别再次在SR中被激活之前,它必须被例外处理例程存储。
8.下一个和前一个程序计数寄存器
下一个和前一个程序计数寄存器(Next and Previous Program Counter)是NPC和PPC。它们代表刚执行完的指令地址或将被执行指令的地址。
这两个寄存器和GPR寄存器仅被一个外部的调试器因调试目的而映射到SPR空间。应用程序应该使用l.jal指令获得当前程序计数和使用算术指令获得GPR寄存器的值。
9.浮点控制状态寄存器
浮点控制状态寄存器(FPCSR,Floating Point Control Status Register)是一个32位特殊寄存器,使用l.mtspr/l.mfspr指令在超级监管者模式下进行访问,如果SR[SUMRA]被激活,用户模式下是可读的。
FPCSR控制浮点环形模式、可选地控制浮点例外的产生并提供浮点状态标识。在每次浮点指令被完成,状态标识被更新,并且能用来判断引起浮点例外的原因。
如果浮点例外被激活,那么FPCSR状态标识必须在浮点例外处理例程中被清除。状态标识通过写0到所有的状态位来被清除。
2.1.5 指令集及指令格式
OpenRISC 1000指令集的主要特征是有简单和统一长度的指令格式,5个指令子集(即ORBIS32、ORBIS64、ORFPX32、ORFPX64、ORVDX64)。OpenRISC基本指令集ORBIS32/64使用对齐的32bit位宽指令,操作32位和64位数据。OpenRISC向量/DSP扩展ORVDX64使用对齐的32bit位宽指令,操作8位、16位、32位和64位数据。OpenRISC浮点扩展ORFPX32/64使用对齐的32bit位宽指令,操作32位和64位数据。还有保留的操作代码给定制指令。
每个子集的指令按照重要性还被分成两个指令类Class I和Class II,分类说明如表2-5所示。
表2-5 OpenRISC 1000指令分类
指令被分成指令类,仅基本的指令类在OpenRISC 1000构架的RISC中是要求的。5个指令子集说明如下:
(1)ORBIS32包括32位整数指令、基本的DSP指令、32位的Load/Store指令、程序流指令和特殊指令;
(2)ORBIS64包括64位整数指令、64位Load/Store指令;
(3)ORFPX32包括单精度浮点指令;
(4)ORFPX64包括双精度的浮点指令、64位Load/Store指令;
(5)ORVDX64包括向量指令、DSP指令。
指令按寄存器的使用分为R类型指令、I类型指令和J类型指令,分别说明如下:
1.R类型指令
R类型指令使用寄存器rA和寄存器rB作为源数据寄存器,rD作为目的寄存器,在ALU中rA与rB的数据经运算后放入rD。因此存在着寄存器堆与ALU模块的A、B、D引脚的连接。
2.I类型指令
I类型指令包括运算指令、Load指令、Store指令、分支指令等的带操作数的指令。
3.J类型指令
J类型指令指跳转指令。在跳转指令的处理过程中,需要把程序计数器PC加4后的高4位和指令字中的跳转地址合并;然后,把合并的结果作为下一个PC的值,输出到程序计数器PC中。
CPU的指令按用途可分为Load/Store指令、跳转指令、ALU和特殊指令几大类,绝大部分指令在31~26bit定义。
Load/Store指令从存储器(如数据高速缓存)装载数据到寄存器堆或从寄存器堆存储数据到存储器。
跳转指令通过改变程序计数器来改变程序执行的顺序,在跳转指令之后紧跟着延迟槽,在延迟槽中的指令为延迟指令。延迟指令在程序流跳转之前被执行。这种延迟转移功能是为流水线而设计的。
ALU指令包括算术运算和逻辑运算指令。特殊指令指同步类指令、空操作指令等。
OpenRISC 1000指令寻址方式有寄存器寻址、立即数寻址、基址偏移量寻址、PC相对寻址、伪直接寻址。寄存器寻址是把操作数放在寄存器堆中;立即数寻址是指操作数为一个常数,包含在指令中;基址偏移量寻址指操作数放在寄存器中,存储器地址由一个寄存器的内容加上指令中的常数得到;PC相对寻址是指转移指令计算转移地址,PC的相对值是指令中的一个常数;伪直接寻址指跳转指令形成转移地址,指令中的26位目标地址值与PC的高4位拼接而成,形成30位的寄存器“字地址”。
CPU的指令格式与硬件逻辑电路直接相关,简单较少的格式对应简单高效的逻辑电路。OpenRISC 1000指令集指令长度为32位,指令的格式只有简单的几种,常用指令使用31~26bit进行大类划分,并将常用的寄存器操作指令类别定义为0x38。另外将比较设置标识指令放在31~21bit位。具体格式说明如下:
1.31~26bit为0x38的指令
31~26bit为0x38的指令都是寄存器之间整数逻辑运算指令,指令字符中含有“l”字符,下面以l.add为例说明这类指令的格式。
指令书写格式为:l.add rD,rA,rB
指令运算方法如下:
rD[31:0] < - rA[31:0] + rB[31:0] SR[CY] < - carry SR[OV] < - overflow
指令的格式如表2-6所示。D、A、B表示的是通用寄存器编号地址。9~8bit将指令分成小类,每小类由3~0bit组成16条指令。
表2-6 l.add指令格式表
9~8bit将指令分成小类,每小类的指令分别说明如下:
0x38 0x0类指令是常见的加、减、逻辑运算(或、异或等)运算指令,如l.addc、l.and、l.cmov、l.exths、l.extws、l.ff1、l.or、l.sll、l.sub、l.xor等。
0x38 0x01类指令是字节扩展、查找第一次出现“1”的位数等指令,如l.extbs、l.extwz、l.fl1、l.srl等。
0x38 0x02类指令是右移算术指令l.sra。
0x38 0x3类指令主要是乘法与除法指令,如l.div、l.divu、l.extbz、l.mul、l.mulu、l.ror。
2.31~26bit为0x31的指令
31~26bit为0x31的指令有l.mac和l.msb,它们是乘法累加与乘法减,下面以l.mac为例说明指令格式。
指令书写格式为:l.mac rA,rB
指令的运算方法如下:
temp[31:0] < - rA[31:0]*rB[31:0] MACHI[31:0]MACLO[31:0] < - temp[31:0] +MACHI[31:0]MACLO[31:0]
指令l.mac的运算是将rA×rB的结果裁剪到32bit,并加到特殊寄存器CHI[31:0]MACLO[31:0]上。所有的操作数当做符号整数看待。指令l.mac格式如表2-7所示。
表2-7 指令l.mac格式表
3.31~26bit为0x32的指令
31~26bit为0x32的指令是浮点运算指令,由7~0bit区分具体的命令。浮点运算指令包括加、减、乘、除、大小比较、浮点与整数的转换等运算。浮点指令又分为单精度和双精度指令。它们由CPU中的专门浮点运算单元来执行的。在OR1200中没有浮点运算单元,因些不支持浮点指令。
浮点运算指令用格式“lf.*.d”或“lf.*.s”表示,其中,“lf”表示浮点运算,“d”表示双精度,“s”表示单精度。下面以lf.add.d为例说明浮点运算指令的格式。
指令书写格式为:lf.add.d rD,rA,rB
指令的运算方法如下:
rD[63:0] < - rA[63:0] + rB[63:0]
指令lf.add.d是浮点双精度加法,它的格式如表2-8所示。
表2-8 指令lf.add.d格式表
浮点双精度指令还有lf.div.d、lf.ftoi.d、lf.itof.d、lf.madd.d、lf.mul.d、lf.rem.d、lf.sfeq.d、lf.sfge.d、lf.sub.d。
浮点单精度指令还有lf.add.s、lf.div.s、lf.ftoi.s、lf.itof.s、lf.madd.s、lf.mul.s、lf.rem.s、lf.sfeq.s、lf.sfge.s、lf.sub.s。
4.31~26bit为0xa的指令
31~26bit为0xa的指令是向量运算指令,7~0bit区分具体的命令。向量运算指令是由向量运算单元完成,是64bit运算的指令。OR1200没有向量运算单元。向量运算指令的书写格式是“lv.*.b”或“lv.*.h”,其中,“lv”表示是向量运算指令,“b”表示是字节元素,“h”表示是半字元素。
下面以指令lv.add.b为例说明向量运算指令格式。指令lv.add.b是向量半字元素有符号的加法。
指令书写格式为:lv.add.b rD,rA,rB
指令的运算方法如下:
rD[7:0] < - rA[7:0] + rB[7:0] rD[15:8] < - rA[15:8] + rB[15:8] rD[23:16] < - rA[23:16] + rB[23:16] rD[31:24] < - rA[31:24] + rB[31:24] rD[39:32] < - rA[39:32] + rB[39:32] rD[47:40] < - rA[47:40] + rB[47:40] rD[55:48] < - rA[55:48] + rB[55:48] rD[63:56] < - rA[63:56] + rB[63:56]
指令lv.add.b格式如表2-9所示。
表2-9 指令lv.add.b格式
5.31~26bit为0x2e的指令
31~26bit为0x2e的指令是带直接操作数位移动指令,它用7~6bit位区别具体的指令,这4个带直接操作数位移动指,即l.rori、l.slli、l.srai、l.srli具有同一格式,它们是右旋转、左移位、右算术移位、右逻辑移位指令。
下面以指令l.rori说明指令的格式。
指令l.rori的作用如下:
6bit立即数指定bit位置序号,rA向右旋转。结果放入rD。立即数5bit以上被忽略。
指令l.rori的书写格式为:l.rori rD,rA,L
指令l.rori运算方法如下:
rD[31-L:0] < - rA[31:L] rD[31:32-L] < - rA[L-1:0]
指令l.rori的格式如表2-10所示。
表2-10 指令l.rori的格式
6.31~26bit位的寄存器跳转指令
l.jalr和l.jr指令都是无条件跳转指令,跳转的地址放在rB寄存器中。区别在于l.jalr把延迟槽后的地址被放在链接寄存器中。下面l.jalr为例说明指令格式。
指令l.jalr的作用如下:
通用寄存器rB存有跳转的有效地址,程序在一个指令延迟后无条件跳转到EA。在延迟槽后的地址被放在链接寄存器中。不允许指定链接寄存器为rB。
指令书写格式为:l.jalr rB
指令的运算方法如下:
PC <- rB LR <- DelayInsnAddr + 4
指令l.jalr的格式如表2-11所示。
表2-11 指令l.jalr的格式
7.其他的31~26bit位指令
其他的31~26bit位指令绝大多数是带直接操作数的指令,由于直接操作数所占的位较多,因此,这些指令大多数因为直接操作数可能较大而没有用位进行指令分类,而是直接由31~26bit位区分具体的指令。
具体说明如下:
(1)带直接操作数运算、装载指令
有同一格式带直接操作数运算、装载指令有l.addic、l.andi、l.lbs、l.lbz、l.ld、l.lhs、l.lhz、l.lws、l.lwz、l.mfspr、l.muli、l.ori和l.xori,这些指令的格式一样,指令中“lb”表示装载字节,“ld”表示装载双字,“lw”表示装载字,“lh”表示装载半字。结尾的“s”表示用符号进行位扩展,即32bit中用符号位填充高字节。结尾的“z”表示用“0”进行位扩展。
下面以指令l.addic为例说明指令模式。
指令书写格式为:l.addi rD,rA,I
指令的运算方法如下:
rD[31:0] <- rA[31:0] + exts (Immediate) SR[CY] <- carry SR[OV] <- overflow
指令l.addic的格式如表2-12所示。
表2-12 指令l.addic的格式
(2)带直接操作数乘法、存储指令
有同一格式带直接操作数乘法、存储指令有l.maci、l.mtspr(移到通用寄存器的值到特殊寄存器)、l.sb(存储字节)、l.sd(存储双字)、l.sh(存储半字)、l.sw(存储字)。
下面以指令l.maci为例说明指令格式。
指令l.maci的作用如下:
通用寄存器rA的值乘以立即数,结果被裁剪到32bit后,加到特殊寄存器MACHI和MACLO上,所有的操作数被当做有符号整数看待。
指令l.maci的书写格式为:l.maci rA,I
指令l.maci的运算方法如下:
temp[31:0] < - rA[31:0] * exts(Immediate) MACHI[31:0]MACLO[31:0] < - temp[31:0] +MACHI[31:0]MACLO[31:0]
指令l.maci的格式如表2-13所示。
表2-13 指令l.maci的格式
(3)带直接操作数的跳转指令
带直接操作数的跳转指令有l.bnf、l.cust1(8条用户定义指令)、l.j、l.jal、l.rfe(从例外中返回)。
下面以指令l.bnf为例说明指令格式。
指令l.bnf的作用如下:
立即值被左移2bit,符号扩展到程序计数器宽度,并接着加到分支指令的地址上,结果是分支的有效地址EA。如果flag被设置,程序分支到EA延迟一个指令周期。
指令l.bnf的书写格式为:l.bf N
指令l.bnf的运算方法如下:
EA < - exts(Immediate < < 2)+ BranchInsnAddr PC < - EA if SR[F] set
指令l.bnf的格式如表2-14所示。
表2-14 指令l.bnf的格式
(4)31~26bit位为0x6指令
31~26bit位为0x6指令有l.movhi和l.macrc,下面分别对这两条指令进行说明。
指令l.movhi说明如下:
指令l.movhi的作用如下:
16bit立即数被0扩展,左移16bit,并被放入通用寄存器rD。
指令l.movhi的书写格式为:l.movhi rD,K
指令l.movhi的运算方法如下:
rD[31:0] < - extz(Immediate)< < 16
指令l.movhi的格式如表2-15所示。
表2-15 指令l.movhi的格式
指令l.macrc说明如下:
指令l.macrc的作用如下:
一旦在MAC流水线中所有指令被完成,MAC的内容被放入通用寄存器rD,同时,MAC累加器被清空。
指令l.macrc的书写格式为:l.macrc rD
指令l.macrc的运算方法如下:
synchronize-mac rD[31:0] <- MACLO[31:0] MACLO[31:0],MACHI[31:0] <-0
指令l.macrc的格式如表2-16所示。
表2-16 指令l.macrc的格式
8.31~21bit位寄存器比较设置标识指令
31~21bit位寄存器比较设置标识指令是比较寄存器rA和rB,并将比较结果设置寄存器SR的F位,即设置SR[F],用来表示比较等式是否成立。
31~21bit位寄存器比较设置标识指令有l.sfges、l.sfgeu、l.sfgts、l.sfgtu、l.sfles、l.sfleu、l.sflts、l.sfltu、l.sfne。指令中字符的含义为“sf”表示Set Flag,“ge”表示Great and Equal(大于或等于),“gt”表示Great(等于),“le”表示Little and Equal,“ne”表示Not Equal,“u”表示unsigned(无符号的),“s”表示Signed(有符号的)。
下面以指令l.sfeq为例说明指令格式。
指令l.sfeq的作用如下:
如果rA与rB相等,比较标识被设置,否则被清除。
指令l.sfeq的书写格式为:l.sfeq rA,rB
指令l.sfeq的运算方法如下:
SR[F] < - rA[31:0] == rB[31:0]
指令l.sfeq的格式如表2-17所示。
表2-17 指令l.sfeq的格式
9.31~21bit位立即操作数比较设置标识指令
31~21bit位立即操作数比较设置标识指令是比较寄存器rA和立即操作数,并将比较结果设置寄存器SR的F位,即设置SR[F],用来表示比较等式是否成立。
31~21bit位立即操作数比较设置标识指令有l.sfgesi、l.sfgeui、l.sfgtsi、l.sfgtui、l.sflesi、l.sfleui、l.sfltsi、l.sfltui、l.sfnei。指令中的字符i表示立即操作数Immediate。
下面以指令l.sfeq为例说明指令格式。
指令l.sfeqi的作用如下:
如果相等,设置比较标识。
指令l.sfeqi的书写格式为:l.sfeqi rA,I
指令l.sfeqi的运算方法如下:
SR[F] < - rA[31:0] == exts(Immediate)
指令l.sfeqi的格式如表2-18所示。
表2-18 指令l.sfeqi的格式
10.31~0bit位同步指令
31~0bit位同步指令是不需要参数的特殊指令,同步指令有l.csync、l.msync(内存同步)、l.psync(流水线同步)。
下面以指令l.csync为例说明指令格式。
指令l.csync的作用如下:
上下文同步指令的执行导致在处理器内所有操作的完成和指令流水线的刷新。当所有的操作完成时,RISC核心假定有一个空的指令流水线,并刷新在所有单元(如MMU)的上下文。
指令l.csync的书写格式为:l.csync
指令l.csync的运算方法为:context syncronization()
指令l.csync的格式如表2-19所示。
表2-19 指令l.csync的格式
11.31~16bit位系统调用或陷阱指令
31~16bit位系统调用或陷阱指令有l.sys和l.trap(陷阱)。如果在SR寄存器中指定的位被设置时,l.trap指令的执行导致陷阱例外。陷阱例外是对操作系统的请求,或者请求调试设置执行某一调试服务。立即数被用来选择SR寄存器的哪个bit位被l.trap指令测试。
下面以指令l.sys为例说明指令格式。
指令l.sys的作用如下:
系统调用指令的执行导致系统调用例外。系统调用例外是一个对操作系统提供系统服务的请求。立即值用来指定哪种服务被请求。可替代地,被ABI定义的GPR(通用寄存器)能被用来指令系统服务。
指令l.sys的书写格式为:l.sys K。
指令l.sys的运算方法如下:
system-call-exception(K)
指令l.sys的格式如表2-20所示。
表2-20 指令l.sys的格式
12.31~24bit位空操作指令
31~24bit位空操作指令l.nop说明如下:
指令l.nop的作用如下:
这条指令除了至少花费一个时钟周期外不做任何操作。它常被用来填充延迟槽间隙。立即数能被用做仿真目的。
指令l.nop的书写格式为:l.nop K。
指令l.nop的运算方法为:无。
指令l.nop的格式如表2-21所示。
表2-21 指令l.nop的格式
2.1.6 例外模型
例外(Exception)机制允许处理器由于在指令的执行过程中外部信号、错误或不常用条件的触发导致切换到超级监管者状态。当例外发生时,有关处理器的状态信息被存储到某些寄存器,并且处理器开始在预定地址为每个例外运行。例外的处理以超级监管者模式开始。
OpenRISC 1000构架对快速例外处理有特殊的支持,也称快速上下文切换支持。这允许非常快速的中断处理。这种快速通过通用寄存器和特殊寄存器的映像获得。构架要求所有例外按严格指令流相应的次序被处理。当一个指令引起的例外被识别到,任何出现在这个指令之前没有执行的指令被取消。
当一个例外处理例程正在执行时例外能发生,并且多个例外可能变成嵌套的。快速例外的支持允许例外的快速嵌套,直到所有的映像的寄存器被使用。如果上下文切换没被使用,嵌套的例外应该没有发生。
1.例外分类
所有的例外能被描述成有精度的或没有精度的,以及同步的或异步的。同步的例外由指令引起,异步的例外由外部对处理器的事件引起的。例外分类如表2-22所示。
表2-22 例外分类
无论何时一个例外发生,当前PC被存到当前的EPCR,并且新PC按照表2-23所示的向量地址被设置。
表2-23 例外类型和引起的条件
2.例外处理
当一个例外发生时,当前/下一个PC被存储到当前的EPCR,除了如果当前的指令在延迟槽中。如果PC指向延迟槽指令,PC-4被存储到当前的EPCR,并且SR[DSX]被设置。表2-24定义了当前/下一个PC和有效的地址。
表2-24 在例外之后EPCR和EEAR的值。
SR被存储到当前的ESR。当前的EPCR/ESR由SR[CID]鉴别。如果快速上下文切换没有被使用,那么当前的EPCR/ESR总是EPCR0/ESR0。
如果总线错误、IMMU页错误、DMMU页错误、I-TLB失靶和D-TLB miss失靶这些例外之一发生时,当前的EEAR被用有效的地址设置。
如果使用了快速上下文切换,SR[CID]与新例外一起使用,这将导致使用一套新的映像寄存器。如果SR[CID]与当前的例外一起溢出,range(范围)例外被触发。
然后,如果没设置SR[CE],快速上下文切换将失效。这时,必须首先存储所有被例外处理例程修改的寄存器。所有的例外设置一个新SR,在这个新SR里,禁止了MMU(禁止了地址翻译),打开了超级监管者模式,并且禁止了Tick定时器例外和中断。即SR[DME]=0,SR[IME]=0,SR[SM]=1,SR[IEE]=0和SR[TEE]=0。
当例外处理例程存储了足够机器状态信息时,SR[TTE]和SR[IEE]再次激活,从而不阻塞Tick定时器和外部中断。
当l.rfe指令从一个例外处理例程返回时,SR和PC被恢复。如果SR[CE]设置时,CID将自动递减,并且恢复以前的机器状态。另外,必须恢复以前被例外处理例程存储的通用寄存器。
3.快速上下文切换(可选的)
快速上下文切换是在例外发生时降低寄存器存储到堆栈的时间的一种技术。快速上下文切换仅能处理一种类型的例外,这样它依赖于软件指出引起例外的原因。使用软件能快速处理中断处理函数的调用和线程切换。硬件应该有在一个时钟周期内切换上下文的能力。
上下文也能在一个例外期间切换,或者通过以超级监管者模式使用寄存器CXR(上下文寄存器Context Register)。对所有上下文来说,CXR是一样的。
(1)在超级监管者模式改变上下文
读/写寄存器CXR由两个部分组成,其中低16位代表当前上下文寄存器集,高16位代表当前的CID。不能在用户模式访问CCID;写CCID将引起一个立即上下文改变;读CCID将返回运行(当前的)上下文ID。在CID=0的上下文也称为主上下文。上下文寄存器如表2-25所示。
表2-25 上下文寄存器(CXR)
CCRS有如下两个功能。
■ 当例外发生时,它持有前一个CID;
■ 它被用来访问其他上下文的寄存器。
(2)例外引起的上下文切换
当一个例外发生并且激活了快速切换时,CCID被复制到CCRS,并接着被设置到0,这样切换到了主上下文。
主上下文的功能如下:
■ 在线程之间切换;
■ 处理例外;
■ 准备、存储CID到CID表或从CID表装载、释放CID。
CXR应该尽可能快地存储到一个通用寄存器,以便允许更多的例外嵌套。
表2-26显示了一个样例,说明怎样使用CID表。通常,空闲的例外上下文不必相等的。
表2-26 多个上下文切换
表2-26中,装载了4个线程上下文,并且软件能以超级监管者模式使用主上下文在它们之间自由切换。当一个例发生时,首先需要判断引起例外的原因,并切换到下一个空闲例外上下文。因为例外可能被嵌套,因此需要有更多的空闲上下文。这样为了切换一个新的例外,一些上下文需要被存储到内存。
在主上下文里处理上下文存储/恢复和切换的算法应尽可能简单。它还应该足够的寄存器来存储信息,这些信息包括当前运行的CID、下一个例外。线程周期信息、内存中上下文表的指针、CXR的复制。
如果中断号是重要的,可使用一些延迟中断调用机制。主上下文算法应该存储中断刚刚传递的I/O信息,以便后面运行时使用,并且尽可能快地从主上下文返回。
(3)访问其他上下文的寄存器
这种操作仅在超级监管者模式下进行。在基本指令集中,使用l.mtspr和l.mfspr指令访问映像寄存器。
2.1.7 内存管理
1.内存模型
OpenRISC 1000使用弱次序内存模型,以获得较高的内存效率,访问次序由软件来严格控制。页设计为弱次序内存(Weakly-Ordered-Memory)页,能使用预取来乱序访问指令和数据。内存以字节单位用地址索引,以2字节边界对齐、半字形式进行访问,单个字以4字节边界对齐、双字以8字节边界对齐访问。
内存同步指令l.msync允许程序控制Load和Store操作执行的次序,它确保程序发取的内存访问在下一个指令执行前完成。
软件信号量需要原子地访问内存,OpenRISC 1000构架提供了两个指令执行读-修改-写操作,这两个指令列出如下:
l.lwa rD,I(rA) l.swa I(rA),rB
指令l.lwa从内存装载单个字,并为一个后继符合条件的存储操作创建一个内存位置的保留,一个编程不可见的特殊寄存器用来保存这个内存位置的地址,用于原子地读-修改-写操作。
如果另外一个读操作读同一内存位置,或者另一个l.lwa执行或软件清除保留寄存器,将取消为随后的l.swa指令保留的内存位置。
如果在相应的l.swa执行时,内存保留还有效,l.swa存储通用寄存器rB到内存,如果保留被取消,l.swa不执行操作。
2.内存管理
内存管理涉及页表结构、MMU各种例外、MMU寄存器等,MMU组织和TLB大小等内存硬件的设置不包括在体系结构定义中,对OpenRISC 1000编程模型不可见。
OpenRISC 1000内存管理单元(Memory Management Unit,MMU)主要特征有支持32位和64位有效地址、支持35位物理地址空间大小、三种不同的页尺寸(32G、16M、8K字节)、使用1~3级而表进行地址翻译、页保护、支持并发多线程(simultaneous multi-threading,SMT)。
32GB的页需要在64位地址下使用BD/I区域翻译缓存(Area Translation Buffer,ATB)翻译,16MB的页需要使用D/I ATB翻译,8KB页需要使用D/I TLB(Translation Lookaside Buffer)。
MMU的主要功能是翻译有效地址到物理地址,还提供基于页的各种级别的访问保护。OpenRISC 1000处理器需要取指令单元和数据访问单元的地址翻译,通常根据页表将有效地址映射到物理地址。
TLB保存了最近使用的页地址翻译,ATB可以翻译16MB和32GB的页,如果TLB和ATB同时匹配同一个虚拟地址,将使用TLB。
32位有效地址(EA)的内存分为Level 1和Level 2页,基于二级页表进行翻译。对于不需要最小8KB页的虚拟内存匹配可以使用一级页表。32位有效地址的二级页表映射如图2-3所示。
图2-3 内存分为L1和L2页
64位有效地址的内存被分为Level 0、Level 1和Level 2页,基于三级页表进行翻译。64位有效地址的三级页表映射如图2-4所示。
图2-4 内存分成L0、L1和L2页
TLB完成了有效地址到物理地址的翻译,如果翻译失败将引发MMU例外。TLB失靶例外仅发生在用软件进行TLB重载的OpenRISC 1000处理器上。页出错例外可能由页表中的PTE或页访问保护引发。
MMU与例外处理机制一起给操作系统提供了使用以页为单位的虚拟内存环境。
2.1.8 高速缓存模型和高速缓存一致性
高速缓存管理寄存器的功能依赖于具体高速缓存的实现和内存/高速缓存访问属性的设置。对于一个在OpenRISC 1000处理器应用上运行的程序来说,软件应该假定是Harvard高速缓存模型。在没有高速缓存的处理器中,构架应保证对高速缓存寄存的写不会停止软件的执行。例如,一个没有高速缓存的处理器应该简单忽略对高速缓存管理寄存器的写入。一个带有Stanford高速缓存模型的处理器应该简单地忽略对指令高速缓存管理寄存器的写入。
由高速缓存管理引起的内存访问不会留下记录(不像Load/Store指令),并且不会引起例外。指令高速缓存不需要与内存或其他处理器的高速缓存保持一致。软件必须保证指令高速缓存中的指令与内存中修改过的指令一致。典型方法有数据高速缓存阻塞回写(内存的更新)、使用l.csync指令等待更新完成、设置指令高速缓存块无效(清除指令高速缓存阻塞)和刷新流水线。
1.数据高速缓存管理
(1)数据高速缓存块预取(可选的)
数据高速缓存块预支取寄存器(DCBPR)可以控制将内存的数据块预取到高速缓存,内存访问不被记录(不像Load/Store指令),不能引起任何例外。数据高速缓存块预取可提高执行效率。
(2)数据高速缓存块刷新
数据高速缓存块刷新寄存器(DCBFR)可以控制数据高速缓存块的刷新,对于有内存一致性要求的处理方法如下:
■ 未被修改的数据高速缓存块在所有的处理器无效。
■ 被修改的数据高速缓存块被写回到内存,并且数据高速缓存块在所有的处理器无效。
■ 在本地处理器上失靶的数据高速缓存块,引起在其他处理器的被修改高速缓存数据块写回到内存,并设置缓存块为无效。如果其他处理器存在没有修改的数据高速缓存块,则缓存块仅在所有的处理器被置为无效。
不要求内存一致性的处理方法如下:
■ 未被修改的数据高速缓存块在本地的处理器上被置为无效。
■ 修改过的数据高速缓存块被写回到内存,并在本地的处理器上置为无效。
■ 在本地的处理器上失靶的高速缓存块不引起任何动作。
(3)数据高速缓存块无效
数据高速缓存块无效寄存器(DCBIR)用来控制数据高速缓存块无效的处理方法,要求内存一致性的处理方法如下:
■ 未被修改的数据高速缓存块在所有的处理器无效。
■ 被修改的数据高速缓存块在所有的处理器无效。
■ 在本地的处理器上失靶的数据高速缓存块,引起其他处理器上的数据高速缓存块失效。
不要求内存一致性的处理方法如下:
■ 未被修改的数据高速缓存块在所有的处理器无效。
■ 被修改的数据高速缓存块无效。
■ 在本地的处理器上失靶的高速缓存块不引起任何动作。
(4)数据高速缓存块回写
数据高速缓存块回写寄存器(DCBWBR)控制数据高速缓存块的回写操作,如果要求内存一致性,那么在任何处理器中修改过的数据高速缓存块应被写回到内存。如果不要求内存一致性,那么本地处理器中修改过的数据高速缓存被写回到内存。
(5)数据高速缓存块锁存(可选的)
数据高速缓存块锁寄存器(DCBLR)是用来锁住本地处理器上的数据高速缓存块,如果所有的高速缓存通道上同一设置的所有块被锁住,那么,高速缓存重填充可以自动解锁最近最少使用的块。
2.指令高速缓存管理
(1)指令高速缓存块预取(可选的)
指令高速缓存块预取寄存器(ICBPR)控制指令的预取操作,将内存的指令块预取到指令高速缓存,提高了CPU执行效率。
(2)指令高速缓存块无效
指令高速缓存块无效寄存器(ICBIR)是控制指令高速缓存块无效时的操作,如果要求内存一致性,那么所有处理器中相应的指令高速缓存块被置为无效。如果不要求内存一致性,那么本地处理器中相应的指令高速缓存块被置为无效。
(3)指令高速缓存块锁存(可选的)
指令高速缓存块锁寄存器(ICBLR)用来锁住本地处理器上的相应的指令高速缓存块。如果所有的高速缓存通道上同一设置的块被锁住,那么,高速缓存重填充可以自动解锁最近最少使用的块。本地处理器上高速缓存块的失靶不引起任何动作。
3.高速缓存/内存一致性
高速缓存一致性系统的主要角色是与其他高速缓存及与内存同步高速缓存,并提供同一内存的映像给使用这个内存的设备。
构架提供了几个特征使用高速缓存一致性。在不提供与PTE属性(如没有使用内存管理单元)进行高速缓存一致性的系统中,高速缓存一致性可通过外在明确的高速缓存管理来提供。
系统中通过PTE(Page Table Entry,页表条目)的属性设置在页到页基础上提供高速缓存与虚拟内存的一致性。这些属性具体如下:
■ 是否高速缓存一致性(CC属性位Cache Coherent Attribute)
■ 是否继承的高速缓存(CI属性位Caching-Inhibited Attribute)
■ 是否回写高速缓存(WBC属性位Write-Back Cache Attribute)
当内存/高速缓存属性被改变,高速缓存应该反映新的属性设置,这通常意味着高速缓存块应该被刷新或置为无效的。
(1)指定作高速缓存一致性的页
在硬件执行高速缓存一致性相对慢的系统里,这个属性可改进系统的执行性。不需要高速缓存一致性的页设置CC=0,需要高速缓存一致性的页设置CC=1。当一个对共享资源的访问发生时,本地处理器将声明一些高速缓存一致性信号,并且如果其他处理器的高速缓存中有目标位置的复制时,它们将做出反应。
为了改进单处理器系统的执行性能,内存页不应该指定为CC=1。
(2)指定作为高速缓存继承的页
指定CI=1的内存页在内存访问时总是直接进入主内存,而忽略所有的高速缓存;指定CI=1的页不装进高速缓存,并且目标内容在高速缓存中应该永不可用。为了阻止在高速缓存中目标位置的任何意外复制,无论何时操作系统设置一个内存页为高速缓存继承,它应该刷新相应的高速缓存块。
除了单个访问被l.msync或l.csync或l.psync分开外,多个访问可以被融合成联合的访问。
(3)指定作回写高速缓存页的页
指定WBC=0的内存页在Store操作时,Store操作在数据高速缓存和内存中同时执行。如果一个系统使用了多级高速缓存,Store操作必须至少在其他处理器或设备感知到的内存体系中进行。
除了单个Store操作被l.msync或l.csync或l.psync分开外,多个Store操作可以融合成联合的Store操作。一个Store操作可能引起高速缓存块任何部分写回到主内存。
指定WBC=1的内存页在Store访问操作时仅对本地数据高速缓存进行。当要求进行复制回(copy-back)操作时,来自本地的数据高速缓存的数据能被复制到其他的高速缓存和主存。WBC=1改进了系统执行性能,然而,它要求在数据高速缓存控制器中,高速缓存支持侦听硬件,以保证高速缓存的一致性。
2.1.9 调试单元
调试单元协助软件开发者调试他们的系统。它提供了Watchpoints、Breakpoints和程序流控制寄存器。Watchpoint和Breakpoint是程序或数据流匹配被编程在调试寄存器里的条件触发的事件。
Watchpoints除了当引起一个Breakpoint影响程序执行外,不干预程序流的执行。Watchpoints能被执行计数单元(Performance Counters Unit)计数。
Breakpoint不像Watchpoint,它还挂起当前程序流的执行,并开始陷阱例外处理。Breakpoint是Watchpoints的可选结果。
OpenRISC 1000构架定义了8套调试寄存器集。附加的调试寄存器集能被构架应用的RISC本身所定义。调试单元是可选的,并且调试单元的出现是由寄存器UPR[DUP]位来指明的。调试单元的特征列出如下:
● 可选的构架应用。
● 8个构架定义的调试值/比较寄存器集。
● 在指令支取EA、Load/Store EA和Load/Store数据上匹配有符号/无符号的条件。
● 为复杂的Watchpoints组合匹配条件。
● Watchpoints能被执行计数单元计数。
● Watchpoints能产生断点(陷阱例外)。
● 给附加的Watchpoint产生计数Watchpoints。
DVR/DCR寄存器用来将支取或LoadStore EA和Load/Store数据指令与存储在DVR中的值进行比较。Watchpoints能作为Breakpoint计数和报告。
2.1.10 执行计数单元
执行计数器能被用来给诸如L1指令或数据高速缓存失靶、分支指令、流水线停止这样的预定义事件计数。来自执行计数器单元的数据能用做下面的目的。
● 通过开发较好的应用程序级算法、优化操作系统例程改进执行性能,并改进这些系统的硬件结构(如内存子系统)。
● 改进将来OpenRISC 1000,并增加将来的增强到OpenRISC构架。
● 帮助系统开发者调试和测试他们的系统。
OpenRISC 1000构架定义了8个执行计数器,还可定义补充的执行计数器。
2.1.11 电源管理
OpenRISC 1000构架定义5种特征减少电源消耗,列出如下:
● 减速(slow down)特征
● 小睡(doze)模式
● 睡眠(sleep)模式
● 挂起(suspend)模式
● 动态时钟调速(dynamic clock gating)特征
slow down特征在外部时钟产生电路降低频率,从而降低电源消耗。通常在操作系统空闲时设置。
当软件激活了doze模式,则软件进程被挂起,处理器内部单元的时钟被关闭,除了内部的Tick定时器和可编程中断外。然而,处理器以外的片上模块继续正常工作。当一个中断发生时,处理器应从doze模式进入正常模式。
在睡眠状态,所有的处理器内部单元被关闭,时钟减速,还可降低处理器电压。当一个中断发生时,处理器从睡眠状态进入正常状态。
在suspend模式,所有的处理器内部单元被关闭,时钟减速,还可降低处理器电压。当处理器复位时,处理器从suspend状态进入正常状态。软件可使用复位例外处理函数更新系统内存,并用suspend之前的状态更新RISC。
如果激活了时钟调速特征,则自动关闭处理器内部单元的时钟子树,如PU/VU、IC、DC、IMMU和DMMU单元。
2.1.12 可编程中断控制器
OpenRISC 1000构架定义了一个可支持32个中断输入的中断控制器,它通过掩码寄存器(PIC Mask Register,PICMR)和状态寄存器(PIC Status Register,PICSR)来控制可掩码的32个中断输入。如图2-5所示为可编程中断控制器的功能框图,32位中断输出后,经PICMR的掩码操作,决定哪些中断是可用的,并传送到处理器触发中断例外,同时,中断的状态保存在寄存器PICSR中。
图2-5 可编程中断控制器框图
2.1.13 Tick定时器
Tick定时器用来作为调度操作系统和用户任务的时间基或高精度时间参考,每个时钟周期定时计数寄存器(Tick Timer Count Register,TTCR)加1,当计数值达到模式寄存器(Tick Timer Mode Register,TTMR)设定值,触发中断。Tick定时器框图如图2-6所示。
图2-6 Tick定时器框图
Tick定时器最大定时为2^32个时钟,中断之间最大时间周期为2^28个时钟,时钟可以掩码,可提供单个运行、可重启动计数器或连接计数器。