OpenRisc-48-or1200的SPRS模块分析
引言
之前,我们在分析or1200的WB模块时(http://blog.csdn.net/rill_zhen/article/details/10220619),介绍了OpenRISC的GPRS(general purpose register set)。除了GPRS之外,大多数的CPU还有SPRS(special purpose register set)。在分析完or1200的数据通路之后,从本小节开始,我们将分析or1200的控制通路。or1200的控制通路包括sprs,except,freeze三个主要的模块。本小节,我们就着重分析一下sprs模块。
1,or1200的寄存器集
1>GPRS
在分析SPRS之前,我们先对之前介绍过的RF模块做一个补充,也是对SPRS分析前的一个铺垫。
在OR1200中,有两种类型的寄存器集,通用寄存器集(GPRs, General Purpose Registers)和特殊功能寄存器集(SPRs,Special Purpose Registers)。
其中GPRs有32个,32个寄存器都是32-bit的,GPRs是软硬件之间的接口,软件可以直接访问这些寄存器,在汇编语言中用r0-r31表示。以一句汇编(l.sw 128(r1), r9)为例,其含义就是,首先将r1中的值加上128,相加结果作为目标地址,将r9的值保存到这个目标地址。
由于r1用作栈指针(stack pointer),所以,这句汇编的作用就是将r9的内容进行压栈操作。
当用C/C++等高级语言进行编程时,编译器会根据GPRs的用法规则,将高级语言的代码变成对应的汇编代码。从这个角度来看的话,GPRs对于程序员来说就是透明的了。
值得注意的是这些寄存器的名字虽然叫‘通用寄存器’,但是对应编译器来说,它们却具有特殊的功能,这一点不仅对于OR1200是这样,对于其它很多CPU也是这样,比如ARM,MIPS等。
GPRS的使用规则,如下表所示。
需要注意的是,这些使用规则在架构手册里面有详细的介绍,如有疑问请参考对应章节,这里不再赘述。
2>SPRS
关于SPRs,在OpenRISC1200的架构手册的第4.3节中已经将所有的寄存器列了出来。限于篇幅,这里就不再一一列举。如果想获得关于特殊功能寄存器的更过细节,请参考其架构手册的相关章节。
需要说明的是,这些寄存器的地址都是16-bit的,其中第15到第11位,共5个bit代表寄存器所属的组,2的5次方是32,所以,SPRs最多可分成32个不同的组;其余10到0位,共11位,是寄存器的地址,2的11次方是2048,所以,每个分组做多可有2048个SPRs。
sprs的地址格式如下所示。
值得注意的是,这些SPRs不能通过16位的寄存器地址直接访问,要想访问这些特殊功能寄存器,需要使用两条专用的指令,l.mtspr和l.mfspr。
这两条指令的意思分别是,mtspr:move to spr;mfspr:move from spr。
在使用的时候,必须是在还要参考通用寄存器的用法,具体如何使用,这已经在上面的内容中阐述过了。
下面通过一个例子来说明一下,如何访问这些SPRs。
SPRs的访问不外乎两种,一种是写,另外一种是读。
先说写,在需要设置SPRs时,使用l.mtspr指令,其指令格式如下。
l.mtspr rA, rB, K
其操作过程可以分为这样几步,
首先将通用寄存器rA中的值与立即数K进行逻辑或运算,运算结果就是想要设置的特殊功能寄存器的16位地址,我们不妨把这个值叫spra,
其次,将通用功能寄存器rB中的值放到spra中。这样就实现了对spra特殊功能寄存器的设置操作。
上述过程可用下面一个式子简洁的表示出来。
spr(rA OR Immediate) ← rB[31:0]
为了更清晰的说明这个过程,我们再举一个更实际的例子。
比如有下面一句汇编:
l.mtspr r0, r9, 32
这行汇编指令是什么意思呢?
根据之前的分析,
首先通用寄存器r0的内容与立即数32进行逻辑或运算,r0恒为0,所以运算结果就是32,这个‘32’就是SPRs其中一个特殊功能寄存器,换成二进制表示是0000_0000_0010_0000,根据上面的SPRs的地址格式,不难得出,其Group index为0,Register index为32,也就是说这个特殊功能寄存器的组号为0,属于系统控制和状态寄存器(System Control and Status registers),组内的相对地址为32,通过查找架构手册,32到47对应的特殊功能寄存器为EPCR0-EPCR15,我们可确定‘EPCR0’就是这条指令所设置的SPR,这个寄存器的内容是异常程序计数器(Exception PC register)的值,具体设置的值是多少呢,那就是通用寄存器r9的内容,通过上面GPRs的使用规则可知,r9用作链接地址寄存器(LR – Link address register)。
上面详细介绍了如何设置SPRs,读取SPRs的值与设置的过程很类似,这里只给出对应的指令格式,其具体含义留给读者自己思考。
读取特殊功能寄存器的汇编指令如下。
l.mfspr rD, rA, K
总之,mtspr指令的作用就是将GPRS中的值写到SPRS,mfspr指令的作用是将SPRS的值写到GPRS。
所以,从这个角度来说,or1200的sprs模块的主要任务就是负责通用寄存器堆和特殊功能寄存器堆之间的数据传递。
如果形象点说的话,SPRS模块就相当于一个网上超市,超市本身的主要任务就是负责给物品生产厂家和消费者提供一个买卖的场所。
这个超市里摆放的物品就是or1200各个模块的寄存器的地址,当然超市本身也会放点超市自己生产的商品(相当于SPRS中的SR(status register))。
要想从这个超市进行买卖商品,只能通过l.mtspr和l.mfspr两条指令,当然,也可以通过无所不能的debug模块。
2,SPRS模块整体分析
上面我们了解了SPRS模块的大体功能,只要有两个,一个是负责执行l.mtspr和l.mfspr指令,一个是负责执行debug模块的操作。
SPRS,从名字上来看,好像这个模块有一堆特殊功能寄存器似的,但实际上并不是这么回事。
上面我们举得例子很形象的说明了SPRS模块的功能,其实,SPRS模块内部只有一个寄存器,就是SR寄存器其它的寄存器都分散在各个模块的本地,SPRS模块只起到一个分发和收集的作用。这一点,一定要理解,如果这一点不清楚的话,直接看RTL代码会一头雾水。为了进一步的了解SPRS模块的作用,我们再举一个例子。
SPRS模块就相当于一个公司的收发室,收发室本身需要知道的是这个公司有没有上班,大门是不是开着等状态信息。如果有一封本公司员工的邮件,收发室只负责通知这个员工来收发室领取,如果某个员工想发邮件,把邮件送到收发室即可,剩下的工作由收发室统一处理。
其整体结构如下:
需要说明的是,SPRS模块将其中只读的寄存器单独放到了cfgr(configure register)模块,这个模块实际上是一个ROM,里面的值是有硬件决定的,一旦把RTL进行综合后,里面的值是不变的。这些值都在or1200_define.v中使用define语句定义。
3,SPRS模块
1>广播总线
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)。这些信号是如何产生的呢?代码如下:
// // Selects for system SPRs // assign cfgr_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:4] == `OR1200_SPR_CFGR)); assign rf_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:5] == `OR1200_SPR_RF)); assign npc_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_NPC)); assign ppc_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_PPC)); assign sr_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_SR)); assign epcr_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_EPCR)); assign eear_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_EEAR)); assign esr_sel = (spr_cs[`OR1200_SPR_GROUP_SYS] && (spr_addr[10:0] == `OR1200_SPR_ESR)); assign fpcsr_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信号线(previous PC)
指示下一条指令的PC值的spr_dat_npc信号线(next PC)
来自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信号线
关于为什么这个广播总线的写操作采用统一的信号线,而读操作却是每个模块设置单独的信号线,我想这是为了减少读操作的时间,试想,如果读操作也采用统一的信号线,那么SPRS的读操作就需要增加额外的逻辑电路,对某些模块来说,会增加一个时钟周期来进行地址发送和等待对应模块的数据。
e,上面的信号线都是通过mtspr和mfspr指令来进行读写,此外SPRS除了负责处理debug系统的操作,所以SPRS模块为debug系统设置了专门的接口(du_addr,du_dat_du,du_read,du_write, du_dat_cpu)。
我给上面的这些信号起了个名字,就叫“广播总线”,除了广播总线之外,还有为调试系统单独设置的接口,只要能把握SPRS模块的整体功能和其整体结构,再看其代码就容易一些了,具体代码如下。
// // Decide if it is debug unit access // assign du_access = du_read | du_write; // // Generate SPR address from base address and offset // OR from debug unit address // assign spr_addr = du_access ? du_addr : (addrbase | {16'h0000, addrofs}); // // SPR is written by debug unit or by l.mtspr // assign spr_dat_o = du_write ? du_dat_du : dat_i; // // debug unit data input: // - read of SPRS by debug unit // - write into debug unit SPRs by debug unit itself // - write into debug unit SPRs by l.mtspr // assign du_dat_cpu = du_read ? to_wbmux : du_write ? du_dat_du : dat_i; // // Write into SPRs when DU or l.mtspr // assign spr_we = du_write | ( ex_spr_write & !du_access ); // // Qualify chip selects // assign spr_cs = unqualified_cs & {32{du_read | du_write | ex_spr_read | (ex_spr_write & sr[`OR1200_SR_SM])}}; // // Decoding of groups // always @(spr_addr) case (spr_addr[`OR1200_SPR_GROUP_BITS]) // synopsys parallel_case `OR1200_SPR_GROUP_WIDTH'd00: unqualified_cs = 32'b00000000_00000000_00000000_00000001; `OR1200_SPR_GROUP_WIDTH'd01: unqualified_cs = 32'b00000000_00000000_00000000_00000010; `OR1200_SPR_GROUP_WIDTH'd02: unqualified_cs (... ...) `OR1200_SPR_GROUP_WIDTH'd31: unqualified_cs = 32'b10000000_00000000_00000000_00000000; endcase
2>SPRS的分组
前面,我们说过SPRS将不同的模块分成了不同的组,这些组是如何划分的呢?
///////////////////////////////////////////////////// // // SPR groups // // Bits that define the group `define OR1200_SPR_GROUP_BITS 15:11 // Width of the group bits `define OR1200_SPR_GROUP_WIDTH 5 // Bits that define offset inside the group `define OR1200_SPR_OFS_BITS 10:0
又具体分成了哪些组呢?
// List of groups `define OR1200_SPR_GROUP_SYS 5'd00 `define OR1200_SPR_GROUP_DMMU 5'd01 `define OR1200_SPR_GROUP_IMMU 5'd02 `define OR1200_SPR_GROUP_DC 5'd03 `define OR1200_SPR_GROUP_IC 5'd04 `define OR1200_SPR_GROUP_MAC 5'd05 `define OR1200_SPR_GROUP_DU 5'd06 `define OR1200_SPR_GROUP_PM 5'd08 `define OR1200_SPR_GROUP_PIC 5'd09 `define OR1200_SPR_GROUP_TT 5'd10 `define OR1200_SPR_GROUP_FPU 5'd11
从中我们看以看出,or1200目前将SPR分成了12个组。
3>system group
其中第0组,也就是SYS组,又包含哪些寄存器呢?
///////////////////////////////////////////////////// // // System group // // // System registers // `define OR1200_SPR_CFGR 7'd0 `define OR1200_SPR_RF 6'd32 // 1024 >> 5 `define OR1200_SPR_NPC 11'd16 `define OR1200_SPR_SR 11'd17 `define OR1200_SPR_PPC 11'd18 `define OR1200_SPR_FPCSR 11'd20 `define OR1200_SPR_EPCR 11'd32 `define OR1200_SPR_EEAR 11'd48 `define OR1200_SPR_ESR 11'd64
从中可以看出,system组包括9个小组,那么system group是如何被设置的呢?or1200_sprs.v中相关代码如下:
// // Output from system SPRs // assign sys_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的定义如下:
// // SR bits // `define OR1200_SR_WIDTH 17 `define OR1200_SR_SM 0 `define OR1200_SR_TEE 1 `define OR1200_SR_IEE 2 `define OR1200_SR_DCE 3 `define OR1200_SR_ICE 4 `define OR1200_SR_DME 5 `define OR1200_SR_IME 6 `define OR1200_SR_LEE 7 `define OR1200_SR_CE 8 `define OR1200_SR_F 9 `define OR1200_SR_CY 10 // Optional `define OR1200_SR_OV 11 // Optional `define OR1200_SR_OVE 12 // Optional `define OR1200_SR_DSX 13 // Unused `define OR1200_SR_EPH 14 `define OR1200_SR_FO 15 `define OR1200_SR_TED 16 `define OR1200_SR_CID 31:28 // Unimplemented
从中,我们可以看出,SR寄存器目前可用的宽度(WIDTH)是17-bit,其余的bit还没有实现。这些bit的含义在架构手册里面有详细的介绍。下面将这17-bit的含义解释如下。
SM:supervisor mode,指示当前CPU的工作模式是user mode还是supervisor模式。
TEE:tick except enable,指示tt模块的异常是不是使能,如果没使能的话tt模块产生的异常将会忽略。
IEE:interrupt except enable,指示中断是否使能。
DCE:data cache enable,指示数据cache是否使能。
ICE:intrusion cache enable,指示指令cache是否使能。
DME:data MMU enable,指示数据MMU是否使能。
IME:instruction MMU enable,指示指令MMU是否使能。
LEE:little endian enable,指示当前CPU的字节序是否是小端。
CE:CID enable,指示context ID是否使能。
F:flag,指示条件分支指令。
CY:carry。(可选)
OV:overflow,CY和OV分别表示上次运算结果是否carry和overflow。那么什么是carry呢?举个例子:假如有一句c语言语句,if(a>b),如果a大于b,那么就叫carry。什么是overflow?这个就不用解释了吧。(可选)
OVE:overflow exception,指示overflow是否会引起exception。(可选)
DSX:delay slot exception,指示引起异常的指令是否在延迟槽。(暂时没使用到)
EPH:exception prefix high,指示异常向量是否放在内存的高地址,也就是说异常向量是放在从0x0开始的地方,还是放在从0xf000_0000开始的地方。
FO:fixed one,这一位恒为1。
TED:指示CPU当前是否在处理异常处理程序。
CID:context ID,指示当前正在运行的进程的ID号。(暂时没实现)
上面我们对SR的所有bit进行了解释,那么这些bit又是如何进行设置的呢?or1200_sprs.v中相关代码如下:
// // What to write into SR // assign to_sr[`OR1200_SR_FO:`OR1200_SR_OVE] = (except_started) ? {sr[`OR1200_SR_FO:`OR1200_SR_DSX],1'b0} : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_FO:`OR1200_SR_OVE] : (spr_we && sr_sel) ? {1'b1, spr_dat_o[`OR1200_SR_FO-1:`OR1200_SR_OVE]} : sr[`OR1200_SR_FO:`OR1200_SR_OVE]; assign to_sr[`OR1200_SR_TED] = (except_started) ? 1'b1 : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_TED] : (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_TED] : sr[`OR1200_SR_TED]; assign to_sr[`OR1200_SR_OV] = (except_started) ? sr[`OR1200_SR_OV] : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_OV] : ov_we ? ovforw : (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_OV] : sr[`OR1200_SR_OV]; assign to_sr[`OR1200_SR_CY] = (except_started) ? sr[`OR1200_SR_CY] : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_CY] : cy_we ? cyforw : (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_CY] : sr[`OR1200_SR_CY]; assign to_sr[`OR1200_SR_F] = (except_started) ? sr[`OR1200_SR_F] : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_F] : flag_we ? flagforw : (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_F] : sr[`OR1200_SR_F]; assign to_sr[`OR1200_SR_CE:`OR1200_SR_SM] = (except_started) ? {sr[`OR1200_SR_CE:`OR1200_SR_LEE], 2'b00, sr[`OR1200_SR_ICE:`OR1200_SR_DCE], 3'b001} : (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_CE:`OR1200_SR_SM] : (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_CE:`OR1200_SR_SM] : sr[`OR1200_SR_CE:`OR1200_SR_SM];
4>其它组
除了system group之外,还有11个组,这些组的工作机制和system group的工作机制是相同的,那么这些组是如何设置的呢?or1200_sprs.v中相关代码如下:
// // MTSPR/MFSPR interface // always @(spr_addr or sys_data or spr_dat_mac or spr_dat_pic or spr_dat_pm or spr_dat_fpu or spr_dat_dmmu or spr_dat_immu or spr_dat_du or spr_dat_tt) begin casez (spr_addr[`OR1200_SPR_GROUP_BITS]) // synopsys parallel_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; endcase end
4,小结
GPRS和SPRS共同组成了CPU和软件交互的接口,对于computer architect而言,这两组寄存器是CPU和软件进行数据交互的重要通道,如果说32个通用的寄存器是CPU的标配的话,那么SPRS就是CPU的特性之所在了,SPRS设计的不同也是和其他CPU区别的重要标志之一。