将陆续上传新书《自己动手写CPU》,今天是第48篇。
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。
在译码阶段的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指令
(2)sc指令
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指令
(2)sc指令
如果LLbit寄存器的值为1,表示之前已执行过ll指令,并且在ll指令执行后、当前sc指令执行前的这段时间内,没有异常发生,此时,sc指令的访存信息如下。
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
因为一些模块增加了接口,所以要修改顶层模块OpenMIPS,以将这些新增加的接口按照图9-30所示的关系连接起来。具体修改代码不再给出,读者可以参考本书附带光盘Code\Chapter9_2目录下的openmips.v文件。
注意一点,因为目前还没有实现异常处理,所以可以直接设置LLbit模块的输入接口flush为0,表示没有异常发生,当后续章节实现异常处理后,再将其连接到正确的模块。
下一篇将验证OpenMIPS的ll、sc指令是否实现正确。
自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令
原文地址:http://blog.csdn.net/leishangwen/article/details/41951375