以下内容摘自《步步惊芯——软核处理器内部设计分析》一书
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.v module 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.v module 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为1 assign 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]正是标志位V assign 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为0 assign 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为1 assign 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) );
or1200中IMMU分析(再续)
时间: 2024-10-04 18:42:35