首页 > 代码库 > or1200中IMMU分析(再续)

or1200中IMMU分析(再续)

以下内容摘自《步步惊芯——软核处理器内部设计分析》一书

 

 

ITLB代码分析

      ITLBIMMU中的主要模块,其实现也相对独立、简单。本节对ITLB的代码进行分析。ITLB的输入输出接口如图10.10所示,图中左边是输入接口,右边是输出接口。

 

 

      因为在ITLB中实现了第2组特殊寄存器,所以有spr_csspr_writespr_addrspr_dat_ispr_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_RAMTR_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


 

      代码很明了,就是使用数组实现了一个RAMFPGA综合工具会自动选择芯片的存储单元实现这个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.mtsprITLBW0MRx

      (5)使用指令l.mtsprITLBW0TRx

      读者需要结合这五种情况理解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的输出hitsxeuxe判断是否命中或者是否违反页保护策略,这一点会在后面进一步分析。

      在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));