您的位置:首页 > 运维架构

自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令

2014-12-15 23:39 676 查看
将陆续上传新书《自己动手写CPU》,今天是第48篇。

9.8 修改OpenMIPS以实现ll、sc指令

9.8.1 LLbit寄存器的实现

LLbit寄存器在LLbit模块中实现,模块接口如图9-30所示,各接口描述如表9-8所示。



LLbit寄存器的代码如下,源文件是本书光盘Code\Chapter9_2目录下的LLbit_reg.v文件。

module LLbit_reg(

input	wire			   clk,
input  wire			   rst,

// 异常是否发生,为1表示异常发生,为0表示没有异常
input  wire                   flush,

// 写操作
input wire			   LLbit_i,
input wire                    we,

// LLbit寄存器的值
output reg                    LLbit_o

);

always @ (posedge clk) begin
if (rst == `RstEnable) begin
LLbit_o <= 1'b0;
end else if((flush == 1'b1)) begin //如果异常发生,那么设置LLbit_o为0
LLbit_o <= 1'b0;
end else if((we == `WriteEnable)) begin
LLbit_o <= LLbit_i;
end
end

endmodule


当有异常发生时,会使得LLbit寄存器的值为0。所以此处有一个输入接口flush,当flush为1时,表示有异常发生(在第11章实现异常处理的时候会详细介绍),从而设置LLbit寄存器的值为0。

9.8.2 修改译码阶段的ID模块

在译码阶段的ID模块要增加对ll、sc指令的译码,根据图9-28给出的ll、sc指令格式可得,确定ll、sc指令的过程如图9-31所示。



其中涉及的宏定义如下,正是ll、sc指令的指令码,在本书附带光盘Code\Chapter9_2目录下的defines.v文件可以找到这些定义。

`define EXE_LL  6'b110000
`define EXE_SC  6'b111000


对译码阶段ID模块的代码做如下修改。完整代码位于本书附带光盘Code\Chapter9_2目录下的id.v文件。

module id(

.......
);

.......

always @ (*) begin
if (rst == `RstEnable) begin
......
end else begin
aluop_o     <= `EXE_NOP_OP;
alusel_o    <= `EXE_RES_NOP;
wd_o        <= inst_i[15:11];           // 默认目的寄存器地址wd_o
wreg_o      <= `WriteDisable;
instvalid   <= `InstInvalid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21];           // 默认的reg1_addr_o
reg2_addr_o <= inst_i[20:16];           // 默认的reg2_addr_o
imm         <= `ZeroWord;
......

case (op)
......
EXE_LL:	 begin                       // ll指令
wreg_o      <= `WriteEnable;
aluop_o     <= `EXE_LL_OP;
alusel_o    <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o        <= inst_i[20:16];
instvalid   <= `InstValid;
end

......

`EXE_SC:	  begin                      // sc指令
wreg_o      <= `WriteEnable;
aluop_o     <= `EXE_SC_OP;
alusel_o    <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
wd_o        <= inst_i[20:16];
instvalid   <= `InstValid;
alusel_o    <= `EXE_RES_LOAD_STORE;
end
......

endmodule


译码工作主要是确定要写的目的寄存器、要读取的寄存器情况、要执行的运算等三个方面。分别介绍如下。

(1)ll指令

要写的目的寄存器:链接加载指令ll需要将加载结果写入通用寄存器,所以设置wreg_o为WriteEnable,同时参考图9-28可知,要写的目的寄存器是指令中的16-20bit,所以设置wd_o为inst_i[20:16]。
要读取的寄存器情况:参考图9-28可知,计算加载目标地址需要使用到地址为base的寄存器值,所以设置reg1_read_o为1,表示通过Regfile模块的读端口1读取寄存器,默认读取的寄存器地址reg1_addr_o是指令的21-25bit,正是ll指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。
要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_LL_OP,表示运算子类型是ll。

(2)sc指令

要写的目的寄存器:条件存储指令sc也需要写通用寄存器,所以也设置wreg_o为WriteEnable。这一点是与其余的存储指令sb、sh、sw等的重要区别。要写的目的寄存器是指令中的16-20bit,所以设置wd_o为inst_i[20:16]。
要读取的寄存器情况:参考图9-28可知,计算存储目标地址需要使用到地址为base的寄存器值,所以设置reg1_read_o为1,表示通过Regfile模块的读端口1读取寄存器,默认读取的寄存器地址reg1_addr_o是指令的21-25bit,正是sc指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。另外,要存储的值是地址为rt的通用寄存器的值,所以设置reg2_read_o为1,表示通过Regfile模块的读端口2读取寄存器,默认读取的寄存器地址reg2_addr_o是指令的16-20bit,正是sc指令中的rt。所以最终译码阶段的输出reg2_o就是地址为rt的寄存器的值。
要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_SC_OP,表示运算子类型是sc。

9.8.3 修改访存阶段

1、修改MEM模块

参考图9-30可知,访存阶段的MEM模块要新增部分接口,新增接口的描述如表9-9所示。



MEM模块主要修改的代码如下,完整代码请参考本书附带光盘Code\Chapter9_2目录下的mem.v文件。

module mem(

......

// 新增的输入接口
input wire                  LLbit_i,
input wire                  wb_LLbit_we_i,
input wire                  wb_LLbit_value_i,

......

// 新增的输出接口
output reg                  LLbit_we_o,
output reg                  LLbit_value_o,

......

);

reg LLbit;         // 保存LLbit寄存器的最新值
......

// 获取LLbit寄存器的最新值,如果回写阶段的指令要写LLbit,那么回写阶段要写入的
// 值就是LLbit寄存器的最新值,反之,LLbit模块给出的值LLbit_i是最新值
always @ (*) begin
if(rst == `RstEnable) begin
LLbit <= 1'b0;
end else begin
if(wb_LLbit_we_i == 1'b1) begin
LLbit <= wb_LLbit_value_i;     // 回写阶段的指令要写LLbit
end else begin
LLbit <= LLbit_i;
end
end
end

always @ (*) begin
if(rst == `RstEnable) begin
......
LLbit_we_o    <= 1'b0;
LLbit_value_o <= 1'b0;
end else begin
......
LLbit_we_o    <= 1'b0;
LLbit_value_o <= 1'b0;、
mem_ce_o <= `ChipDisable;
mem_we <= `WriteDisable;
case (aluop_i)
......
`EXE_LL_OP:	begin               // ll指令的访存输出
mem_addr_o    <= mem_addr_i;
mem_we        <= `WriteDisable;
wdata_o       <= mem_data_i;
LLbit_we_o    <= 1'b1;
LLbit_value_o <= 1'b1;
mem_sel_o     <= 4'b1111;
mem_ce_o      <= `ChipEnable;
end
......
`EXE_SC_OP:	 begin              // sc指令的访存输出
if(LLbit == 1'b1) begin
LLbit_we_o    <= 1'b1;
LLbit_value_o <= 1'b0;
mem_addr_o    <= mem_addr_i;
mem_we        <= `WriteEnable;
mem_data_o    <= reg2_i;
wdata_o       <= 32'b1;
mem_sel_o     <= 4'b1111;
mem_ce_o      <= `ChipEnable;
end else begin
wdata_o       <= 32'b0;
end
end
......

endmodule

MEM模块的代码增加了一个过程,以获得LLbit寄存器的最新值,然后针对ll、sc指令分别给出了对数据存储器的访问信息。

(1)ll指令

给出要访问的数据存储器地址mem_addr_o,其值就是执行阶段计算出来的地址mem_addr_i,参考9.3.2节。
因为是加载操作,所以设置mem_we_o为WriteDisable。
因为要访问数据存储器,所以设置mem_ce_o为ChipEnable。
因为是加载一个字,所以设置mem_sel_o为4'b1111。
要写入通用寄存器rt的值就是从数据存储器加载到的数据mem_data_i,所以设置wdata_o为mem_data_i。
要置LLbit寄存器为1,所以设置LLbit_we_o为1,表示要写LLbit寄存器,同时,设置LLbit_value_o为1,表示要写入LLbit寄存器的值为1。

(2)sc指令

如果LLbit寄存器的值为1,表示之前已执行过ll指令,并且在ll指令执行后、当前sc指令执行前的这段时间内,没有异常发生,此时,sc指令的访存信息如下。

给出要访问的数据存储器地址mem_addr_o,其值就是执行阶段计算出来的地址mem_addr_i,参考9.3.2节。
因为是存储操作,所以设置mem_we_o为WriteEnable。
因为要访问数据存储器,所以设置mem_ce_o为ChipEnable。
因为是存储一个字,所以设置mem_sel_o为4'b1111。
要存储的数据是reg2_i,是从译码阶段传递过来的,其值就是地址为rt的通用寄存器的值。
要置地址为rt的通用寄存器为1,所以设置wdata_o为1。
要置LLbit寄存器为0,所以设置LLbit_we_o为1,表示要写LLbit寄存器,同时,设置LLbit_value_o为0,表示要写入LLbit寄存器的值为0。

反之,如果LLbit的值为0,表示之前没有执行过ll指令,或者在ll指令执行后、当前sc指令执行前的这段时间内,有异常发生,此时,sc指令的访存信息如下。

不修改数据存储器,所以mem_we_o保持默认值WriteDisable,mem_ce_o保持默认值ChipDisable。
不修改LLbit寄存器的值,所以LLbit_we_o保持默认值0。
要置地址为rt的通用寄存器为0,所以设置wdata_o为0。

2、修改MEM/WB模块

从图9-30可知,MEM/WB模块要新增部分接口,新增接口的描述如表9-10所示。



MEM/WB模块要修改的代码如下,作用很直白:在访存阶段没有暂停时,简单地将MEM给出的对LLbit寄存器的写信息传递到访存阶段,完整代码请读者参考本书附带光盘Code\Chapter9_2目录下的mem_wb.v文件。

module mem_wb(

......

input wire                  mem_LLbit_we,
input wire                  mem_LLbit_value,

......

output reg                  wb_LLbit_we,
output reg                  wb_LLbit_value

);

always @ (posedge clk) begin
if(rst == `RstEnable) begin
......
wb_LLbit_we    <= 1'b0;
wb_LLbit_value <= 1'b0;
end else if(stall[4] == `Stop && stall[5] == `NoStop) begin
......
wb_LLbit_we    <= 1'b0;
wb_LLbit_value <= 1'b0;
end else if(stall[4] == `NoStop) begin  // 判断访存阶段是否暂停
......
wb_LLbit_we    <= mem_LLbit_we;
wb_LLbit_value <= mem_LLbit_value;
end
end

endmodule


9.8.4 修改OpenMIPS模块

因为一些模块增加了接口,所以要修改顶层模块OpenMIPS,以将这些新增加的接口按照图9-30所示的关系连接起来。具体修改代码不再给出,读者可以参考本书附带光盘Code\Chapter9_2目录下的openmips.v文件。

注意一点,因为目前还没有实现异常处理,所以可以直接设置LLbit模块的输入接口flush为0,表示没有异常发生,当后续章节实现异常处理后,再将其连接到正确的模块。

下一篇将验证OpenMIPS的ll、sc指令是否实现正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐