以下内容摘自《步步惊芯——软核处理器内部设计分析》一书
ITLB是IMMU中的主要模块,其实现也相对独立、简单。本节对ITLB的代码进行分析。ITLB的输入输出接口如图10.10所示,图中左边是输入接口,右边是输出接口。
因为在ITLB中实现了第2组特殊寄存器,所以有spr_cs、spr_write、spr_addr、spr_dat_i、spr_dat_o等接口,这些接口的含义在分析特殊寄存器类指令的时候已经学习过,应该是非常熟悉的。剩下的输入输出接口含义如下:
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分析(再续),布布扣,bubuko.com
原文:http://blog.csdn.net/leishangwen/article/details/28872843