在分析SPRS之前,我们先对之前介绍过的RF模块做一个补充,也是对SPRS分析前的一个铺垫。
在OR1200中,有两种类型的寄存器集,通用寄存器集(GPRs,GeneralPurposeRegisters)和特殊功能寄存器集(SPRs,SpecialPurposeRegisters)。其中GPRs有32个,32个寄存器都是32-bit的,GPRs是软硬件之间的接口,软件可以直接访问这些寄存器,在汇编语言中用r0-r31表示。以一句汇编(l.sw128(r1),r9)为例,其含义就是,首先将r1中的值加上128,相加结果作为目标地址,将r9的值保存到这个目标地址。
由于r1用作栈指针(stackpointer),所以,这句汇编的作用就是将r9的内容进行压栈操作。当用C/C++等高级语言进行编程时,编译器会根据GPRs的用法规则,将高级语言的代码变成对应的汇编代码。从这个角度来看的话,GPRs对于程序员来说就是透明的了。
值得注意的是这些寄存器的名字虽然叫‘通用寄存器’,但是对应编译器来说,它们却具有特殊的功能,这一点不仅对于OR1200是这样,对于其它很多CPU也是这样,比如ARM,MIPS等。
GPRS的使用规则,如下表所示。
需要注意的是,这些使用规则在架构手册里面有详细的介绍,如有疑问请参考对应章节,这里不再赘述。
sprs的地址格式如下所示。
值得注意的是,这些SPRs不能通过16位的寄存器地址直接访问,要想访问这些特殊功能寄存器,需要使用两条专用的指令,l.mtspr和l.mfspr。
这两条指令的意思分别是,mtspr:movetospr;mfspr:movefromspr。
在使用的时候,必须是在还要参考通用寄存器的用法,具体如何使用,这已经在上面的内容中阐述过了。
下面通过一个例子来说明一下,如何访问这些SPRs。
SPRs的访问不外乎两种,一种是写,另外一种是读。
先说写,在需要设置SPRs时,使用l.mtspr指令,其指令格式如下。l.mtsprrA,rB,K其操作过程可以分为这样几步,
首先将通用寄存器rA中的值与立即数K进行逻辑或运算,运算结果就是想要设置的特殊功能寄存器的16位地址,我们不妨把这个值叫spra,
其次,将通用功能寄存器rB中的值放到spra中。这样就实现了对spra特殊功能寄存器的设置操作。
上述过程可用下面一个式子简洁的表示出来。spr(rAORImmediate)←rB[31:0]
为了更清晰的说明这个过程,我们再举一个更实际的例子。
比如有下面一句汇编:l.mtsprr0,r9,32
这行汇编指令是什么意思呢?
根据之前的分析,
首先通用寄存器r0的内容与立即数32进行逻辑或运算,r0恒为0,所以运算结果就是32,这个‘32’就是SPRs其中一个特殊功能寄存器,换成二进制表示是0000_0000_0010_0000,根据上面的SPRs的地址格式,不难得出,其Groupindex为0,Registerindex为32,也就是说这个特殊功能寄存器的组号为0,属于系统控制和状态寄存器(SystemControlandStatusregisters),组内的相对地址为32,通过查找架构手册,32到47对应的特殊功能寄存器为EPCR0-EPCR15,我们可确定‘EPCR0’就是这条指令所设置的SPR,这个寄存器的内容是异常程序计数器(ExceptionPCregister)的值,具体设置的值是多少呢,那就是通用寄存器r9的内容,通过上面GPRs的使用规则可知,r9用作链接地址寄存器(LR–Linkaddressregister)。
上面详细介绍了如何设置SPRs,读取SPRs的值与设置的过程很类似,这里只给出对应的指令格式,其具体含义留给读者自己思考。
读取特殊功能寄存器的汇编指令如下。l.mfsprrD,rA,K
总之,mtspr指令的作用就是将GPRS中的值写到SPRS,mfspr指令的作用是将SPRS的值写到GPRS。
所以,从这个角度来说,or1200的sprs模块的主要任务就是负责通用寄存器堆和特殊功能寄存器堆之间的数据传递。
如果形象点说的话,SPRS模块就相当于一个网上超市,超市本身的主要任务就是负责给物品生产厂家和消费者提供一个买卖的场所。
这个超市里摆放的物品就是or1200各个模块的寄存器的地址,当然超市本身也会放点超市自己生产的商品(相当于SPRS中的SR(statusregister))。
要想从这个超市进行买卖商品,只能通过l.mtspr和l.mfspr两条指令,当然,也可以通过无所不能的debug模块。
上面我们了解了SPRS模块的大体功能,只要有两个,一个是负责执行l.mtspr和l.mfspr指令,一个是负责执行debug模块的操作。
SPRS,从名字上来看,好像这个模块有一堆特殊功能寄存器似的,但实际上并不是这么回事。
上面我们举得例子很形象的说明了SPRS模块的功能,其实,SPRS模块内部只有一个寄存器,就是SR寄存器其它的寄存器都分散在各个模块的本地,SPRS模块只起到一个分发和收集的作用。这一点,一定要理解,如果这一点不清楚的话,直接看RTL代码会一头雾水。为了进一步的了解SPRS模块的作用,我们再举一个例子。
SPRS模块就相当于一个公司的收发室,收发室本身需要知道的是这个公司有没有上班,大门是不是开着等状态信息。如果有一封本公司员工的邮件,收发室只负责通知这个员工来收发室领取,如果某个员工想发邮件,把邮件送到收发室即可,剩下的工作由收发室统一处理。
其整体结构如下:
需要说明的是,SPRS模块将其中只读的寄存器单独放到了cfgr(configureregister)模块,这个模块实际上是一个ROM,里面的值是有硬件决定的,一旦把RTL进行综合后,里面的值是不变的。这些值都在or1200_define.v中使用define语句定义。
SPRS模块对应的RTL文件是or1200_sprs.v。了解了SPRS模块的功能和结构之后,我们再来分析sprs模块的RTL代码就简单多了。
从上面的分析,我们知道,SPRS模块是各个子模块的寄存器的集散中心。sprs模块起到一个分发和收集的作用,具体通过什么方式实现分发和收集呢?就是“广播总线”,这个广播总线包括,
a,spr_addr,这个是SPR寄存器的具体地址,SPRS的地址是有指令解码阶段解析出来的addrbase和addrofs组合而成。
b,spr_dat_o,这个是写SPR时,具体写的数据。
c,在写某个模块的寄存器时,除了通过spr_addr来确定是哪一个模块之外,SPRS还为某些模块设置了片选信号(spr_cs)和选通信号(xxx_sel)。这些信号是如何产生的呢?代码如下:
////SelectsforsystemSPRs//assigncfgr_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:4]==`OR1200_SPR_CFGR));assignrf_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:5]==`OR1200_SPR_RF));assignnpc_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_NPC));assignppc_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_PPC));assignsr_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_SR));assignepcr_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_EPCR));assigneear_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_EEAR));assignesr_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_ESR));assignfpcsr_sel=(spr_cs[`OR1200_SPR_GROUP_SYS]&&(spr_addr[10:0]==`OR1200_SPR_FPCSR));
d,有了写,当然还有读。对于各个模块的SPR的读操作,SPRS模块没有统一的信号线,不同的模块有单独的信号线,如下:
来自cfgr的spr_dat_cfgr信号线
来自通用寄存器堆(rf)的spr_dat_rf信号线
指示上一条指令的PC值的spr_dat_ppc信号线(previousPC)
指示下一条指令的PC值的spr_dat_npc信号线(nextPC)
来自MAC模块的spr_dat_mac信号线
来自fpu模块的spr_data_fpu信号线
来自PIC模块的spr_dat_pic信号线
来自tt模块的spr_dat_tt信号线
来自pm模块的spr_dat_pm信号线
来自指令MMU的spr_dat_immu信号线
来自数据MMU的spr_dat_dmmu信号线
来自调试单元的spr_dat_du信号线
e,上面的信号线都是通过mtspr和mfspr指令来进行读写,此外SPRS除了负责处理debug系统的操作,所以SPRS模块为debug系统设置了专门的接口(du_addr,du_dat_du,du_read,du_write,du_dat_cpu)。
我给上面的这些信号起了个名字,就叫“广播总线”,除了广播总线之外,还有为调试系统单独设置的接口,只要能把握SPRS模块的整体功能和其整体结构,再看其代码就容易一些了,具体代码如下。
前面,我们说过SPRS将不同的模块分成了不同的组,这些组是如何划分的呢
/////////////////////////////////////////////////////////SPRgroups////Bitsthatdefinethegroup`defineOR1200_SPR_GROUP_BITS15:11//Widthofthegroupbits`defineOR1200_SPR_GROUP_WIDTH5//Bitsthatdefineoffsetinsidethegroup`defineOR1200_SPR_OFS_BITS10:0
又具体分成了哪些组呢?
//Listofgroups`defineOR1200_SPR_GROUP_SYS5'd00`defineOR1200_SPR_GROUP_DMMU5'd01`defineOR1200_SPR_GROUP_IMMU5'd02`defineOR1200_SPR_GROUP_DC5'd03`defineOR1200_SPR_GROUP_IC5'd04`defineOR1200_SPR_GROUP_MAC5'd05`defineOR1200_SPR_GROUP_DU5'd06`defineOR1200_SPR_GROUP_PM5'd08`defineOR1200_SPR_GROUP_PIC5'd09`defineOR1200_SPR_GROUP_TT5'd10`defineOR1200_SPR_GROUP_FPU5'd11从中我们看以看出,or1200目前将SPR分成了12个组。
其中第0组,也就是SYS组,又包含哪些寄存器呢?
////OutputfromsystemSPRs//assignsys_data=(spr_dat_cfgr&{32{cfgr_sel}})|(spr_dat_rf&{32{rf_sel}})|(spr_dat_npc&{32{npc_sel}})|(spr_dat_ppc&{32{ppc_sel}})|({{32-`OR1200_SR_WIDTH{1'b0}},sr}&{32{sr_sel}})|(epcr&{32{epcr_sel}})|(eear&{32{eear_sel}})|({{32-`OR1200_FPCSR_WIDTH{1'b0}},fpcsr}&{32{fpcsr_sel}})|({{32-`OR1200_SR_WIDTH{1'b0}},esr}&{32{esr_sel}});
那么这9个小组的寄存器都包含什么呢?下面介绍几个。
a,cfgr寄存器(2的7次方=64个寄存器),这些寄存器的定义,请参考or1200_cfgr.v文件。
b,RF寄存器,也就是通用寄存器(2的6次方=32),这些寄存器的使用规则,前面已经介绍过了。
c,SR寄存器,这个也是SPRS模块唯一的一个本地的寄存器,这个32-bit寄存器各个bit的定义如下:
////SRbits//`defineOR1200_SR_WIDTH17`defineOR1200_SR_SM0`defineOR1200_SR_TEE1`defineOR1200_SR_IEE2`defineOR1200_SR_DCE3`defineOR1200_SR_ICE4`defineOR1200_SR_DME5`defineOR1200_SR_IME6`defineOR1200_SR_LEE7`defineOR1200_SR_CE8`defineOR1200_SR_F9`defineOR1200_SR_CY10//Optional`defineOR1200_SR_OV11//Optional`defineOR1200_SR_OVE12//Optional`defineOR1200_SR_DSX13//Unused`defineOR1200_SR_EPH14`defineOR1200_SR_FO15`defineOR1200_SR_TED16`defineOR1200_SR_CID31:28//Unimplemented
从中,我们可以看出,SR寄存器目前可用的宽度(WIDTH)是17-bit,其余的bit还没有实现。这些bit的含义在架构手册里面有详细的介绍。下面将这17-bit的含义解释如下。
SM:supervisormode,指示当前CPU的工作模式是usermode还是supervisor模式。
TEE:tickexceptenable,指示tt模块的异常是不是使能,如果没使能的话tt模块产生的异常将会忽略。
IEE:interruptexceptenable,指示中断是否使能。
DCE:datacacheenable,指示数据cache是否使能。
ICE:intrusioncacheenable,指示指令cache是否使能。
DME:dataMMUenable,指示数据MMU是否使能。
IME:instructionMMUenable,指示指令MMU是否使能。
LEE:littleendianenable,指示当前CPU的字节序是否是小端。
CE:CIDenable,指示contextID是否使能。
F:flag,指示条件分支指令。
CY:carry。(可选)
OV:overflow,CY和OV分别表示上次运算结果是否carry和overflow。那么什么是carry呢?举个例子:假如有一句c语言语句,if(a>b),如果a大于b,那么就叫carry。什么是overflow?这个就不用解释了吧。(可选)
OVE:overflowexception,指示overflow是否会引起exception。(可选)
DSX:delayslotexception,指示引起异常的指令是否在延迟槽。(暂时没使用到)
EPH:exceptionprefixhigh,指示异常向量是否放在内存的高地址,也就是说异常向量是放在从0x0开始的地方,还是放在从0xf000_0000开始的地方。
FO:fixedone,这一位恒为1。
TED:指示CPU当前是否在处理异常处理程序。
CID:contextID,指示当前正在运行的进程的ID号。(暂时没实现)
////MTSPR/MFSPRinterface//always@(spr_addrorsys_dataorspr_dat_macorspr_dat_picorspr_dat_pmorspr_dat_fpuorspr_dat_dmmuorspr_dat_immuorspr_dat_duorspr_dat_tt)begincasez(spr_addr[`OR1200_SPR_GROUP_BITS])//synopsysparallel_case`OR1200_SPR_GROUP_SYS:to_wbmux=sys_data;`OR1200_SPR_GROUP_TT:to_wbmux=spr_dat_tt;s`OR1200_SPR_GROUP_PIC:to_wbmux=spr_dat_pic;`OR1200_SPR_GROUP_PM:to_wbmux=spr_dat_pm;`OR1200_SPR_GROUP_DMMU:to_wbmux=spr_dat_dmmu;`OR1200_SPR_GROUP_IMMU:to_wbmux=spr_dat_immu;`OR1200_SPR_GROUP_MAC:to_wbmux=spr_dat_mac;`OR1200_SPR_GROUP_FPU:to_wbmux=spr_dat_fpu;default://`OR1200_SPR_GROUP_DU:to_wbmux=spr_dat_du;endcaseend
GPRS和SPRS共同组成了CPU和软件交互的接口,对于computerarchitect而言,这两组寄存器是CPU和软件进行数据交互的重要通道,如果说32个通用的寄存器是CPU的标配的话,那么SPRS就是CPU的特性之所在了,SPRS设计的不同也是和其他CPU区别的重要标志之一。