自己动手写CPU之第九阶段(5)——实现加载存储指令1(修改译码阶段)
来源:互联网 发布:linux下zip版本 编辑:程序博客网 时间:2024/06/02 07:28
将陆续上传新书《自己动手写CPU》,今天是第41篇,我尽量每周四篇,但是最近已经很久没有实现这个目标了,一直都有事,不好意思哈。
开展晒书评送书活动,在亚马逊、京东、当当三大图书网站上,发表《自己动手写CPU》书评的前十名读者,均可获赠《步步惊芯——软核处理器内部设计分析》一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-30
9.3 修改OpenMIPS以实现加载存储指令
9.3.1 修改译码阶段
1、修改ID模块
参考图9-19可知,ID模块要增加接口inst_o,如表9-1所示。
在ID模块还要增加对加载存储指令的分析,根据图9-1、9-2、9-6、9-12给出的加载存储指令的格式可知,这些指令的指令码都是不同的,所以可以直接依据指令码确定是哪一种指令,确定指令的过程如图9-20所示。
其中涉及的宏定义如下,正是各个加载存储指令的指令码,在本书附带光盘Code\Chapter9_1目录下的defines.v文件可以找到这些定义。
`define EXE_LB 6'b100000`define EXE_LBU 6'b100100`define EXE_LH 6'b100001`define EXE_LHU 6'b100101`define EXE_LW 6'b100011`define EXE_LWL 6'b100010`define EXE_LWR 6'b100110`define EXE_SB 6'b101000`define EXE_SH 6'b101001`define EXE_SW 6'b101011`define EXE_SWL 6'b101010`define EXE_SWR 6'b101110
修改译码阶段的ID模块如下。完整代码请参考本书附带光盘Code\Chapter9_1目录下的id.v文件。
module id( ...... output wire[`RegBus] inst_o, // 新增加的输出接口 ......); ...... assign inst_o = inst_i; // inst_o的值就是译码阶段的指令 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_LB:begin // lb指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LB_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_LBU:begin // lbu指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LBU_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_LH:begin // lh指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LH_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_LHU:begin // lhu指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LHU_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_LW:begin // lw指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LW_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_LWL:begin // lwl指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LWL_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; end `EXE_LWR:begin // lwr指令 wreg_o <= `WriteEnable; aluop_o <= `EXE_LWR_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; end `EXE_SB:begin // sb指令 wreg_o <= `WriteDisable; aluop_o <= `EXE_SB_OP; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1; instvalid <= `InstValid; alusel_o <= `EXE_RES_LOAD_STORE; end `EXE_SH:begin // sh指令 wreg_o <= `WriteDisable; aluop_o <= `EXE_SH_OP; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1; instvalid <= `InstValid; alusel_o <= `EXE_RES_LOAD_STORE; end `EXE_SW:begin // sw指令 wreg_o <= `WriteDisable; aluop_o <= `EXE_SW_OP; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1; instvalid <= `InstValid; alusel_o <= `EXE_RES_LOAD_STORE; end `EXE_SWL:begin // swl指令 wreg_o <= `WriteDisable; aluop_o <= `EXE_SWL_OP; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1; instvalid <= `InstValid; alusel_o <= `EXE_RES_LOAD_STORE; end `EXE_SWR:begin // swr指令 wreg_o <= `WriteDisable; aluop_o <= `EXE_SWR_OP; reg1_read_o <= 1'b1; reg2_read_o <= 1'b1; instvalid <= `InstValid; alusel_o <= `EXE_RES_LOAD_STORE; end......
译码工作主要是确定要写的目的寄存器、要读取的寄存器、要执行的运算等三个方面。以下对几个有代表性的指令的译码过程进行说明。
(1)lb指令
- 要写的目的寄存器:加载指令lb需要将加载结果写入目的寄存器,所以设置wreg_o为WriteEnable,同时参考图9-1可知,要写的目的寄存器地址是指令中的16-20bit,所以设置wd_o为inst_i[20:16]。
- 要读取的寄存器:参考图9-1可知,计算加载目标地址需要使用到地址为base的寄存器值,所以设置reg1_read_o为1,表示通过Regfile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的21-25bit,正是lb指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。
- 要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_LB_OP,表示运算子类型是字节加载lb。
(2)lwl指令
- 要写的目的寄存器:加载指令lwl需要将加载结果写入目的寄存器,所以设置wreg_o为WriteEnable,同时参考图9-6可知,要写的目的寄存器地址是指令中的16-20bit,所以设置wd_o为inst_i[20:16]。
- 要读取的寄存器:参考图9-6可知,计算加载目标地址需要使用到地址为base的寄存器值,所以设置reg1_read_o为1,表示通过Regfile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的21-25bit,正是lwl指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。此外,由于lwl指令只是部分的修改目的寄存器,所以还需要读出目的寄存器,与lwl指令加载得到的结果进行组合,最终写入目的寄存器,因此,设置reg2_read_o也为1,表示通过Regfile模块的读端口2读取寄存器的值,默认读取的寄存器地址reg2_addr_o是指令的16-20bit,正是lwl指令中的rt。所以最终译码阶段的输出reg2_o就是地址为rt的寄存器的值。
- 要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储,设置aluop_o为EXE_LWL_OP,表示运算子类型是向左加载lwl。
lwr指令与lwl指令的译码过程类似,只是aluop_o的值不同。
(3)sb指令
- 要写的目的寄存器:存储指令sb不需要写通用寄存器,所以设置wreg_o为WriteDisable。
- 要读取的寄存器:参考图9-2可知,计算存储目标地址需要使用到地址为base的寄存器值,所以设置reg1_read_o为1,表示通过Regfile模块的读端口1读取寄存器的值,默认读取的寄存器地址reg1_addr_o是指令的21-25bit,正是sb指令中的base。所以最终译码阶段的输出reg1_o就是地址为base的寄存器的值。要存储的值是通用寄存器的值,所以设置reg2_read_o为1,表示通过Regfile模块的读端口2读取寄存器的值,默认读取的寄存器地址reg2_addr_o是指令的16-20bit,正是sb指令中的rt。所以最终译码阶段的输出reg2_o就是地址为rt的寄存器的值。
- 要执行的运算:设置alusel_o为EXE_RES_LOAD_STORE,表示运算类型是加载存储指令,设置aluop_o为EXE_SB_OP,表示运算子类型是字节存储sb。
sh、sw、swr、swl指令与sb指令的译码过程类似,只是aluop_o的值不同。
2、修改ID/EX模块
参考图9-19可知,ID/EX模块需要增加部分接口,用于将ID模块新增加的输出信号inst_o传递到执行阶段的EX模块。如表9-2所示。
修改译码阶段的ID/EX模块如下。完整代码位于本书附带光盘Code\Chapter9_1目录下的id_ex.v文件。
module id_ex( ...... input wire[`RegBus] id_inst, // 来自ID模块的信号 ...... output reg[`RegBus] ex_inst // 传递到EX模块); always @ (posedge clk) begin if (rst == `RstEnable) begin ...... ex_inst <= `ZeroWord; end else if(stall[2] == `Stop && stall[3] == `NoStop) begin ...... ex_inst <= `ZeroWord; end else if(stall[2] == `NoStop) begin ...... //在译码阶段没有暂停的情况下,直接将ID模块的输入通过接口ex_inst输出 ex_inst <= id_inst; end endendmodule
下一步将修改执行阶段
- 自己动手写CPU之第九阶段(5)——实现加载存储指令1(修改译码阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令2(修改执行阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令3(修改访存阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令4(修改OpenMIPS顶层模块)
- 自己动手写CPU之第九阶段(4)——加载存储指令实现思路
- 自己动手写CPU之第九阶段(1)——加载存储指令说明1
- 自己动手写CPU之第九阶段(2)——加载存储指令说明2(lwl、lwr)
- 自己动手写CPU之第九阶段(3)——加载存储指令说明2(swl、swr)
- 自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令
- 自己动手写CPU之第九阶段(7)——MIPS32中的LL、SC指令说明
- 自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明
- 自己动手写CPU之第七阶段(10)——除法指令实现过程1
- 自己动手写CPU之第八阶段(3)——转移指令实现过程1
- 自己动手写CPU之第九阶段(6)——修改最小SOPC
- 自己动手写CPU之第六阶段(2)——移动操作指令实现思路
- 自己动手写CPU之第六阶段(3)——移动操作指令的实现
- 自己动手写CPU之第六阶段(4)——验证移动操作指令实现效果
- 自己动手写CPU之第七阶段(2)——简单算术操作指令实现过程
- c++类模板
- cf478A Initial Bet
- 一个数组 里面正数和负数求所有子数组的最大值
- Orchard 模板文件
- 切图工具
- 自己动手写CPU之第九阶段(5)——实现加载存储指令1(修改译码阶段)
- [百度百科]虚函数理解
- cf478B Random Teams
- Android学习之Service练习
- CVS和RCS的一个简单例子
- 常用博客
- 谷歌设计师的Material Design实践心得
- strcpy/strcmp/memcpy/memcmp/strstr/ 的内部实现(转载)
- 浅谈sql server索引结构