首页 > 代码库 > or1200中IMMU分析(再续)
or1200中IMMU分析(再续)
以下内容摘自《步步惊芯——软核处理器内部设计分析》一书
ITLB代码分析
ITLB是IMMU中的主要模块,其实现也相对独立、简单。本节对ITLB的代码进行分析。ITLB的输入输出接口如图10.10所示,图中左边是输入接口,右边是输出接口。
因为在ITLB中实现了第2组特殊寄存器,所以有spr_cs、spr_write、spr_addr、spr_dat_i、spr_dat_o等接口,这些接口的含义在分析特殊寄存器类指令的时候已经学习过,应该是非常熟悉的。剩下的输入输出接口含义如下:
- tlb_en:输入的ITLB使能信号
- vaddr:输入的有效地址
- hit:表示ITLB是否命中
- ppn:如果命中,那么ppn输出物理地址的13-31位
- uxe:如果命中,那么uxe就是TR_RAM表中uxe属性位的值
- sxe:如果命中,那么sxe就是TR_RAM表中sxe属性位的值
- ci:如果命中,那么ci就是TR_RAM表中ci属性位的值
ITLB内部有MR_RAM、TR_RAM两个表,这两个表都是通过单口RAM实现的,单口RAM的代码如下:
or1200_spram.vmodule or1200_spram ( clk, ce, we, addr, di, doq ); parameter aw = 10; //地址线、数据线的宽度都可配置 parameter dw = 32; input clk; //时钟输入 input ce; //片选信号 input we; //写使能信号 input [aw-1:0] addr; //输入地址 input [dw-1:0] di; //输入数据 output [dw-1:0] doq; //输出数据 reg [dw-1:0] mem [(1<<aw)-1:0] reg [aw-1:0] addr_reg; //地址寄存器 // Data output drivers assign doq = mem[addr_reg]; //输出数据 // always @(posedge clk) if (ce) addr_reg <= addr; //寄存地址变量到addr_reg // always @(posedge clk) if (we && ce) mem[addr] <= di; //写入RAM endmodule // or1200_spram
代码很明了,就是使用数组实现了一个RAM,FPGA综合工具会自动选择芯片的存储单元实现这个RAM。在ITLB中例化了两个单口RAM,分别作为MR_RAM表、TR_RAM表。MR_RAM表的表项对应ITLBW0MRx寄存器,TR_RAM表的表项对应ITLBW0TRx寄存器。在五种情况下会使用到ITLB:
(1)地址翻译
(2)使用指令l.mfspr读取ITLBW0MRx
(3)使用指令l.mfspr读取ITLBW0TRx
(4)使用指令l.mtspr写ITLBW0MRx
(5)使用指令l.mtspr写ITLBW0TRx
读者需要结合这五种情况理解ITLB代码,代码如下(为了便于说明,笔者改变了代码的顺序):
or1200_immu_itlb.vmodule or1200_immu_tlb( clk, rst, tlb_en, vaddr, hit, ppn, uxe, sxe, ci, spr_cs, spr_write, spr_addr, spr_dat_i, spr_dat_o);parameter dw = `OR1200_OPERAND_WIDTH;parameter aw = `OR1200_OPERAND_WIDTH;input clk;input rst;input tlb_en;input [aw-1:0] vaddr;output hit;output [31:`OR1200_IMMU_PS] ppn;output uxe;output sxe;output ci;input spr_cs;input spr_write;input [31:0] spr_addr;input [31:0] spr_dat_i;output [31:0] spr_dat_o;//###################### MR_RAM #######################//例化MR_RAM表,因为有64项,所以地址的宽度是6;另外数据宽度是14,在之前已有说明 or1200_spram # (.aw(6), .dw(14)) itlb_mr_ram ( .clk(clk), .ce(tlb_mr_en), .we(tlb_mr_we), .addr(tlb_index), .di(tlb_mr_ram_in), .doq(tlb_mr_ram_out) );//MR_RAM使能的情况有两种:(1)要进行地址翻译,此时tlb_en为1;(2)使用l.mfspr、//l.mtspr访问ITLBW0MRx寄存器。对于第二种情况的判断条件就是spr_addr[7]是否为0,//如果为0那么就是ITLBW0MRx寄存器,反之是ITLBW0TRx寄存器assign tlb_mr_en = tlb_en | (spr_cs & !spr_addr[`OR1200_ITLB_TM_ADDR]);//如果是使用指令l.mtspr写ITLBW0MRx寄存器,那么tlb_mr_we为1assign tlb_mr_we = spr_cs & spr_write & !spr_addr[`OR1200_ITLB_TM_ADDR];//如果是使用指令l.mtspr写ITLBW0MRx寄存器,那么tlb_mr_ram_in就是要写入的值//可见只取了spr_dat_i的[31:19]、spr_dat_i[0],参考表10.2可知,spr_dat_i[31:19]//就是有效地址的19-31位,spr_dat_i[0]正是标志位Vassign tlb_mr_ram_in = {spr_dat_i[`OR1200_ITLB_TAG], spr_dat_i[`OR1200_ITLBMR_V_BITS]};//MR_RAM、TR_RAM的访问地址,分两种情况:(1)要进行地址翻译,此时需要读取的//MR_RAM、TR_RAM的地址就是提供的有效地址的13-18位,即vaddr[18:13];(2)使用//指令l.mfspr、l.mtspr访问ITLBW0MRx、ITLBW0TRx寄存器,此时需要读取的MR_RAM、//TR_RAM的地址就是spr_addr[5:0]assign tlb_index = spr_cs ? spr_addr[`OR1200_ITLB_INDXW-1:0] : vaddr[`OR1200_ITLB_INDX];//MR_RAM的输出,将高13bit赋值给vpn,最低bit赋值给v,参考图10.7可以理解assign {vpn, v} = tlb_mr_ram_out;//将查询得到的vpn与CPU提供的有效地址的19-31位作比较,如果相等,且v等于1,那么//ITLB命中,hit为1,反之ITLB失靶,hit为0assign hit = (vpn == vaddr[`OR1200_ITLB_TAG]) & v;//####################### TR_RAM #########################//例化TR_RAM表,因为有64项,所以地址的宽度是6;另外数据宽度是22,在之前已说明or1200_spram # (.aw(6), .dw(22)) itlb_tr_ram (.clk(clk), ce(tlb_tr_en), .we(tlb_tr_we), .addr(tlb_index), .di(tlb_tr_ram_in), .doq(tlb_tr_ram_out) ); //MR_RAM使能的情况有两种:(1)要进行地址翻译,此时tlb_en为1;(2)使用l.mfspr、//l.mtspr访问ITLBW0TRx寄存器assign tlb_tr_en = tlb_en | (spr_cs & spr_addr[`OR1200_ITLB_TM_ADDR]);//如果是使用指令l.mtspr写ITLBW0TRx寄存器,那么tlb_tr_we为1assign tlb_tr_we = spr_cs & spr_write & spr_addr[`OR1200_ITLB_TM_ADDR];//如果是使用指令l.mtspr写ITLBW0TRx寄存器,那么tlb_tr_ram_in就是要写入的值//可见只取了spr_dat_i的[31:13]、spr_dat_i[7]、spr_dat_i[6]、spr_dat_i[1],//参考10.3.3节assign tlb_tr_ram_in = {spr_dat_i[31:`OR1200_IMMU_PS], spr_dat_i[`OR1200_ITLBTR_UXE_BITS], spr_dat_i[`OR1200_ITLBTR_SXE_BITS], spr_dat_i[`OR1200_ITLBTR_CI_BITS]};//TR_RAM的输出,将高19bit赋值给ppn,剩下的3bit分别赋值给uxe、sxe、ci,参考//图10.7可以理解assign {ppn, uxe, sxe, ci} = tlb_tr_ram_out;//################### 读出的特殊寄存器的值 ###############//如果使用指令l.mfspr读取ITLBW0MRx,实际就是从MR_RAM中对应的地址读出数据,将该//数据按照ITLBW0MRx的格式变换,作为spr_dat_o的值输出;如果使用指令l.mfspr读取//ITLBW0TRx,实际就是从TR_RAM中对应的地址读出数据,将该数据按照ITLBW0TRx的格式//变换,作为spr_dat_o的值输出;assign spr_dat_o = (!spr_write & !spr_addr[`OR1200_ITLB_TM_ADDR]) ? //读ITLBW0MRx {vpn, tlb_index, {`OR1200_ITLB_TAGW-7{1'b0}}, 1'b0, 5'b00000, v} : (!spr_write & spr_addr[`OR1200_ITLB_TM_ADDR]) ? //读ITLBW0TRx {ppn, {`OR1200_IMMU_PS-8{1'b0}}, uxe, sxe, {4{1'b0}}, ci, 1'b0} :32'h00000000;endmodule
从ITLB的代码可知,ITLB并不判断异常,在IMMU模块中会例化ITLB,同时使用ITLB的输出hit、sxe、uxe判断是否命中或者是否违反页保护策略,这一点会在后面进一步分析。
在IMMU模块内部例化ITLB的代码如下:
or1200_immu_top.v……or1200_immu_tlb or1200_immu_tlb( .clk(clk), .rst(rst), .tlb_en(itlb_en), .vaddr(icpu_adr_i), .hit(itlb_hit), .ppn(itlb_ppn), .uxe(itlb_uxe), .sxe(itlb_sxe), .ci(itlb_ci), .spr_cs(itlb_spr_access), .spr_write(spr_write), .spr_addr(spr_addr), .spr_dat_i(spr_dat_i), .spr_dat_o(itlb_dat_o));