移动操作指令的实现

移动操作指令的实现

1. 移动操作指令说明

MIPS32指令集架构中定义的移动操作指令共有六条:movn、movz、mfhi、mthi、mflo、mtlo,后四条指令涉及对特殊寄存器HI、LO的读写操作。

HI、LO寄存器用于保存乘法、除法结果。当用于保存乘法结果时,HI寄存器保存结果的高32位,LO寄存器保存低32位;当用于保存乘法结果时,HI寄存器保存余数,LO寄存器保存商。

在这里插入图片描述
这六条指令都是R类型指令,指令码都是6’b000000,都为SPECIAL类型指令;同时第6~10bit都是5’b00000,可以根据功能码判断是哪条指令。

  • function:001011;MOVN(Move Conditional on Not Zero)
    汇编格式:MOVN rd, rs, rt
    功能描述:if rt != 0 then rd = rs

  • function:001010;MOVZ(Move Conditional on Zero)
    汇编格式:MOVZ rd, rs, rt
    功能描述:if rt = 0 then rd = rs

  • function:010000;MFHI(Move From HI)
    汇编格式:MFHI rd
    功能描述:将 HI 寄存器的值写入到寄存器 rd 中
    操作定义:GPR[rd] ← HI

  • function:010010;MFLO(Move From LO)
    汇编格式:MFLO rd
    功能描述:将 LO 寄存器的值写入到寄存器 rd 中
    操作定义:GPR[rd] ← LO

  • function:010001;MTHI(Move To HI)
    汇编格式:MTHI rs
    功能描述:将寄存器 rs 的 值 写入到 HI 寄存器中
    操作定义:GPR[rd] ←HI

  • function:010011;MTLO(Move To LO)
    汇编格式:MTLO rs
    功能描述:将寄存器 rs 的 值 写入到 LO 寄存器中
    操作定义:GPR[rd] ← LO

2. 移动操作指令实现思路

2.1 movn、movz指令实现思路

与第五章实现思路类似,在译码阶段给出运算类型alusel_o、运算子类型aluop_o等信号的值,同时读取地址为rs rt的通用寄存器的值,新增:依据rt寄存器的值,判断是否要将rs中的值写入目的寄存器中。将结果送入执行阶段。

2.2 mthi、mtlo指令实现思路

在译码阶段,读出地址为rs的通用寄存器的值。由于mthi、mtlo指令不写通用寄存器,所以wreg_o为WriteDisable,wd_o为ZeroWord。
在执行阶段,确定要写HI、LO寄存器的情况,以及要写入的值
写回阶段才去修改HI、LO寄存器的值

2.3 mfhi、mflo指令实现思路

这两条指令需要读HI、LO寄存器的值,设计在执行阶段才能读取到。

2.4 改变数据流

在这里插入图片描述

  • 添加了HILO模块,并且该模块添加到了写回阶段
  • 将HI、LO寄存器的值前递到执行模块,在执行模块添加了一个选择模块,用于选择参与运算的数据。如果是mfhi、mflo指令,那就会选择传递过来的HI、LO寄存器的值

2.5 如何解决新的数据相关问题

进一步考虑mfhi、mflo指令的处理过程,这两条指令会在流水线的执行阶段读取HI、LO寄存器的值,如果直接采用HILO模块给出的HI、LO寄存器的值,可能不会程序希望的正确值。因为此时处于访存、写回阶段的指令可能会修改HI、LO寄存器的值。

lui $1,0x0000		// $1 = 0x00000000
lui $2,0xffff		// $2 = 0xffff0000
mthi $0				// hi = 0x00000000
mthi $1				// hi = 0x00000000
mthi $2				// hi = 0xffff0000
mfhi $4				// $4 = 0xffff0000

在这里插入图片描述
指令3 4 5都要改变HI寄存器的值,当指令6处于执行阶段时,指令5处于访存阶段,指令4处于写回阶段,此时HI寄存器的值是指令3刚刚写入的0x00000000,HILO模块将该值前递给执行阶段,从而得到错误结果,正确的值应该是处于访存阶段的指令5要写入的数据。

似曾相识?这就是第5章介绍的数据相关问题,解决方法还是使用数据前递。将处于访存阶段、写回阶段的指令对HI、LO寄存器的操作信息反馈到执行阶段。

在这里插入图片描述

2.6 系统结构的修改

  • 在写回阶段添加了HILO模块,用于实现HI、LO寄存器
  • 执行阶段的EX模块中添加了与HI、LO寄存器有关的输入接口,包括为了解决HI、LO寄存器的数据相关问题而引入的接口。
  • 执行阶段的EX模块中添加了whilo_o、hi_o、lo_o输出接口,分别表示是否要写HILO、要写入HI寄存器的值、要写入LO寄存器的值。这三个接口传递出来的信息会一直通过后面的模块到达写回阶段的HILO模块。

在这里插入图片描述

3. 修改OpenMIPS

3.1 HILO寄存器的实现

`include "defines.v"
`timescale 1ns/1ps
module hilo_reg (
    input wire              rst,
    input wire              clk,

    input wire              we,     // HI、LO寄存器写使能
    input wire [`RegBus]    hi_i,
    input wire [`RegBus]    lo_i,

    output reg [`RegBus]    hi_o,
    output reg [`RegBus]    lo_o
);
    always @(*) begin
        if (rst == `RstEnable) begin
            hi_o <= `ZeroWord;
            lo_o <= `ZeroWord;
        end else if (we == `WriteEnable) begin 
            hi_o <= hi_i;
            lo_o <= lo_i;
        end
    end
endmodule

3.2 修改ID模块

这六条指令都是SPECIAL类指令,只需要功能码便可判断是哪条指令。
在这里插入图片描述
首先在宏定义中添加这六条指令的功能码。

`define EXE_MOVZ                6'b001010           // 指令movz的指令码
`define EXE_MOVN                6'b001011           // 指令movn的指令码
`define EXE_MFHI                6'b010000           // 指令mfhi的指令码
`define EXE_MTHI                6'b010001           // 指令mthi的指令码
`define EXE_MFLO                6'b010010           // 指令mflo的指令码
`define EXE_MTLO                6'b010011           // 指令mtlo的指令码
//AluOp
`define EXE_MOVZ_OP             8'b00001010
`define EXE_MOVN_OP             8'b00001011
`define EXE_MFHI_OP             8'b00010000
`define EXE_MTHI_OP             8'b00010001
`define EXE_MFLO_OP             8'b00010010
`define EXE_MTLO_OP             8'b00010011
//AluSel
`define EXE_RES_MOVE            3'b011	            // 移动操作

修改ID模块:

case (op3)
    ......
    `EXE_MFHI:  begin
        wreg_o      <= `WriteEnable;
        aluop_o     <= `EXE_MFHI_OP;
        alusel_o    <= `EXE_RES_MOVE;
        reg1_read_o <= `ReadDisable;
        reg2_read_o <= `ReadDisable;
        instvalid   <= `InstValid;
    end
    `EXE_MFLO:  begin
        wreg_o      <= `WriteEnable;
        aluop_o     <= `EXE_MFLO_OP;
        alusel_o    <= `EXE_RES_MOVE;
        reg1_read_o <= `ReadDisable;
        reg2_read_o <= `ReadDisable;
        instvalid   <= `InstValid;
    end
    `EXE_MTHI:  begin
        wreg_o      <= `WriteDisable;
        aluop_o     <= `EXE_MTHI_OP;
        reg1_read_o <= `ReadEnable;
        reg2_read_o <= `ReadDisable;
        instvalid   <= `InstValid;
    end
    `EXE_MTLO:  begin
        wreg_o      <= `WriteDisable;
        aluop_o     <= `EXE_MTLO_OP;
        reg1_read_o <= `ReadEnable;
        reg2_read_o <= `ReadDisable;
        instvalid   <= `InstValid;
    end
    `EXE_MOVN:  begin
        aluop_o     <= `EXE_MOVN_OP;
        alusel_o    <= `EXE_RES_MOVE;
        reg1_read_o <= `ReadEnable;
        reg2_read_o <= `ReadEnable;
        instvalid   <= `InstValid;
        // reg2_o的值就是地址为rt的通用寄存器的值
        if (reg2_o != `ZeroWord) begin
            wreg_o  <= `WriteEnable;
        end else begin
            wreg_o  <= `WriteDisable;
        end
    end
    `EXE_MOVZ_OP:  begin
        aluop_o     <= `EXE_MOVZ_OP;
        alusel_o    <= `EXE_RES_MOVE;
        reg1_read_o <= `ReadEnable;
        reg2_read_o <= `ReadEnable;
        instvalid   <= `InstValid;
        // reg2_o的值就是地址为rt的通用寄存器的值
        if (reg2_o == `ZeroWord) begin
            wreg_o  <= `WriteEnable;
        end else begin
            wreg_o  <= `WriteDisable;
        end
    end
    default:    begin
    end
endcase     //op3

几点说明:

  • 除mthi、mvlo指令外的其余4条移动操作指令的运算类型alusel_o均为EXE_RES_MOVE
  • mthi、mvlo指令需要修改HI、LO寄存器,但是不需要改变通用寄存器,所以在其译码阶段wreg_o为WriteDisable。设置reg1_read_o为1,表示需要通过Regfile模块读端口1读取通用寄存器的值,默认为指令第21~25bit,即rs
  • movz指令的译码阶段需要读取rs、rt寄存器的值,要设置reg1_read_o、reg2_read_o均为ReadEnable。

3.3 修改执行阶段

3.3.1 修改EX模块
module ex (
    ......
    // HILO模块给出的HI、LO寄存器的值
    input wire [`RegBus]        hi_i,
    input wire [`RegBus]        lo_i,

    // 访存阶段的指令是否要写HI、LO,用于检测HI、LO寄存器带来的数据相关问题
    input wire                  mem_whilo_i,
    input wire [`RegBus]        mem_hi_i,
    input wire [`RegBus]        mem_lo_i,

    // 写回阶段的指令是否要写HI、LO,用于检测HI、LO寄存器带来的数据相关问题
    input wire                  wb_whilo_i,
    input wire [`RegBus]        wb_hi_i,
    input wire [`RegBus]        wb_lo_i,

    // 处于执行阶段的指令对HI、LO寄存器的写操作请求
    output reg                  whilo_o,
    output reg [`RegBus]        hi_o,       // 执行阶段的指令要写入HI寄存器的值
    output reg [`RegBus]        lo_o,       // 执行阶段的指令要写入LO寄存器的值
    ......
);
    ......
/**************************  移动操作  **************************/
// 得到HILO寄存器的值,此处要解决数据相关问题
always @(*) begin
    if (rst == `RstEnable) begin
        {HI,LO} <= {`ZeroWord,`ZeroWord};
    end else if (mem_whilo_i == `WriteEnable) begin
        {HI,LO} <= {mem_hi_i,mem_lo_i};
    end else if (wb_whilo_i == `WriteEnable) begin
        {HI,LO} <= {wb_hi_i,wb_lo_i};
    end else begin
        {HI,LO} <= {hi_i,lo_i};
    end
end

// MFHI、MFLI、MOVZ、MOVN指令
always @(*) begin
    if (rst == `RstEnable) begin
        moveres <= `ZeroWord;
    end else begin
        moveres <= `ZeroWord;
        case (aluop_i) 
            `EXE_MFHI_OP:   begin
                moveres <= HI;
            end
            `EXE_MFLO_OP:   begin
                moveres <= LO;
            end
            `EXE_MOVZ_OP:   begin
                moveres <= reg1_i;      // reg1_i默认为标号rs的寄存器的值
            end
            `EXE_MOVN_OP:   begin
                moveres <= reg1_i;
            end
            default: begin
            end
        endcase
    end
end


/**************************  依据aluop_i选择最终的运算结果  **************************/
always @(*) begin
    wd_o    <= wd_i;
    wreg_o  <= wreg_i;
    case (alusel_i)
        `EXE_RES_LOGIC: begin
            wdata_o <= logicout;
        end
        `EXE_RES_SHIFT: begin
            wdata_o <= shiftres;
        end
        `EXE_RES_MOVE:  begin
            wdata_o <= moveres;
        end
        default: begin
            wdata_o <= `ZeroWord;
        end
    endcase
end

/**********  如果是MTHI、MTLO指令,那么需要给出whilo_o、hi_o和lo_o的值  **********/
always @(*) begin
    if (rst == `RstEnable) begin
        whilo_o <= `WriteDisable;
        hi_o    <= `ZeroWord;
        lo_o    <= `ZeroWord;
    end else if (aluop_i == `EXE_MTHI_OP) begin
        whilo_o <= `WriteEnable;
        hi_o    <= reg1_i;
        lo_o    <= LO;      // 写HI寄存器,LO寄存器不变
    end else if (aluop_i == `EXE_MTLO_OP) begin
        whilo_o <= `WriteEnable;
        hi_o    <= HI;      // 写LO寄存器,HI寄存器不变
        lo_o    <= reg1_i;  // 注意是reg1_i
    end else begin
        whilo_o <= `WriteDisable;
        hi_o    <= `ZeroWord;
        lo_o    <= `ZeroWord;
    end
end
.......

几点说明:

  • 在获取HILO寄存器值时,判断正处于访存阶段、写回阶段的指令是否要写HILO寄存器。如果要写,则就将要写入的值前递;如果不需要写入,就取HILO寄存器现存的值
  • movz、movn指令的结果movres都赋值reg1_i(rs标号寄存器的值),在译码阶段就已根据rt标号寄存器的值设置了wreg_o来选择是否将结果写入rd
  • 如果是MTHI、MTLO指令,那么需要给出whilo_o、hi_o和lo_o的值。写HI寄存器时,LO寄存器不变;写LO寄存器时,HI寄存器不变。两者都是利用reg1_i(rs标号寄存器的值)写入
3.3.2 修改EX/MEM模块
module ex_mem (
    input wire clk,
    input wire rst,

    // 来自执行阶段的信息
    input wire[`RegBus]         ex_wdata,
    input wire[`RegAddrBus]     ex_wd,
    input wire                  ex_wreg,
    input wire                  ex_whilo,
    input wire[`RegBus]         ex_hi,
    input wire[`RegBus]         ex_lo,              

    // 传递到访存阶段的信息
    output reg[`RegBus]         mem_wdata,
    output reg[`RegAddrBus]     mem_wd,
    output reg                  mem_wreg,
    output reg                  mem_whilo,
    output reg[`RegBus]         mem_hi,
    output reg[`RegBus]         mem_lo
);
    always @(posedge clk) begin
        if (rst == `RstEnable)  begin
            mem_wdata <= `ZeroWord;
            mem_wd    <= `NOPRegAddr;
            mem_wreg  <= `WriteDisable;
            mem_whilo <= `WriteDisable;
            mem_hi    <= `ZeroWord;
            mem_lo    <= `ZeroWord;
        end else begin
            mem_wdata <= ex_wdata;
            mem_wd    <= ex_wd;
            mem_wreg  <= ex_wreg;
            mem_whilo <= ex_whilo;
            mem_hi    <= ex_hi;
            mem_lo    <= ex_lo;
        end
    end
endmodule

3.4 修改访存阶段

3.4.1 修改MEM模块
`include "defines.v"
`timescale 1ns/1ps
module mem (
    input wire                  rst,

    // 来自执行阶段的信息
    input wire[`RegBus]         wdata_i,
    input wire[`RegAddrBus]     wd_i, 
    input wire                  wreg_i,
    input wire                  whilo_i,
    input wire[`RegBus]         hi_i,
    input wire[`RegBus]         lo_i, 

    // 访存阶段的结果
    output reg[`RegBus]         wdata_o,
    output reg[`RegAddrBus]     wd_o,
    output reg                  wreg_o,
    output reg                  whilo_o,
    output reg[`RegBus]         hi_o,
    output reg[`RegBus]         lo_o
);

    always @(*) begin
        if (rst == `RstEnable) begin
            wdata_o     <= `ZeroWord;
            wd_o        <= `NOPRegAddr;
            wreg_o      <= `WriteDisable;
            whilo_o     <= `WriteDisable;
            hi_o        <= `ZeroWord;
            lo_o        <= `ZeroWord;
        end else begin
            wdata_o     <= wdata_i;
            wd_o        <= wd_i;
            wreg_o      <= wreg_i;
            whilo_o     <= whilo_i;
            hi_o        <= hi_i;
            lo_o        <= lo_i;
        end
    end
endmodule
3.4.2 修改MEM/WB模块
`include "defines.v"
`timescale 1ns/1ps
module mem_wb(
    input wire rst,
    input wire clk,

    // 访存阶段的结果
    input wire[`RegBus]         mem_wdata,
    input wire[`RegAddrBus]     mem_wd,
    input wire                  mem_wreg,
    input wire                  mem_whilo,
    input wire[`RegBus]         mem_hi,
    input wire[`RegBus]         mem_lo, 

    // 传递到写回阶段的信息
    output reg[`RegBus]         wb_wdata,
    output reg[`RegAddrBus]     wb_wd,
    output reg                  wb_wreg,
    output reg                  wb_whilo,
    output reg[`RegBus]         wb_hi,
    output reg[`RegBus]         wb_lo
);

   always @(*) begin
        if (rst == `RstEnable) begin
            wb_wdata      <= `ZeroWord;
            wb_wd         <= `NOPRegAddr;
            wb_wreg       <= `WriteDisable;
            wb_whilo      <= `WriteDisable;
            wb_hi         <= `ZeroWord;
            wb_lo         <= `ZeroWord;
        end else begin
            wb_wdata      <= mem_wdata;
            wb_wd         <= mem_wd;
            wb_wreg       <= mem_wreg;
            wb_whilo      <= mem_whilo;
            wb_hi         <= mem_hi;
            wb_lo         <= mem_lo;
        end
    end
endmodule

3.5 修改OpenMIPS顶层结构

在原先的文件基础上,根据上面的修改过后的结构图将各接口新增的接口连接起来。

......
    // 连接执行阶段EX模块的输出与EX/MEM模块输入的变量
    wire                ex_whilo_o;
    wire[`RegBus]       ex_hi_o;
    wire[`RegBus]       ex_lo_o;

    // 连接EX/MEM模块的输出与访存阶段MEM模块的输入的变量
    wire                mem_whilo_i;
    wire[`RegBus]       mem_hi_i;
    wire[`RegBus]       mem_lo_i;

    // 连接访存阶段MEM模块输出与MEM/WB模块输入的变量
    wire                mem_whilo_o;
    wire[`RegBus]       mem_hi_o;
    wire[`RegBus]       mem_lo_o;

    // 连接MEM/WB模块输出与写回阶段的输入的变量
    wire                wb_whilo_i;
    wire[`RegBus]       wb_hi_i;
    wire[`RegBus]       wb_lo_i;

    // 连接HILO寄存器与EX模块
    wire[`RegBus]       hi;
    wire[`RegBus]       lo;
......
  
    // EX模块实例化
    ex ex0(
        ......
        .hi_i(hi),
        .lo_i(lo),
        .mem_whilo_i(mem_whilo_o),
        .mem_hi_i(mem_hi_o),
        .mem_lo_i(mem_lo_o),
        .wb_whilo_i(wb_whilo_i),
        .wb_hi_i(wb_hi_i),
        .wb_lo_i(wb_lo_i),

       
        .whilo_o(ex_whilo_o),
        .hi_o(ex_hi_o),
        .lo_o(ex_lo_o)
    );

    // EX/MEM模块实例化
    ex_mem ex_mem0(
        ......
        .ex_hi(ex_hi_o),
		.ex_lo(ex_lo_o),
		.ex_whilo(ex_whilo_o),

        .mem_hi(mem_hi_i),
		.mem_lo(mem_lo_i),
		.mem_whilo(mem_whilo_i)	
    );

    // MEM模块实例化
    mem mem0(
        ......
        .hi_i(mem_hi_i),
		.lo_i(mem_lo_i),
		.whilo_i(mem_whilo_i),
        
        .hi_o(mem_hi_o),
		.lo_o(mem_lo_o),
		.whilo_o(mem_whilo_o)
    );

    // MEM/WB模块实例化
    mem_wb mem_wb0(
        ......
        .mem_hi(mem_hi_o),
		.mem_lo(mem_lo_o),
		.mem_whilo(mem_whilo_o),

        .wb_hi(wb_hi_i),
		.wb_lo(wb_lo_i),
		.wb_whilo(wb_whilo_i)	
    );

    // HILO寄存器实例化
    hilo_reg hilo_reg0(
		.clk(clk),
		.rst(rst),
	
		// 来自MEM/WB模块的信息
		.we(wb_whilo_i),
		.hi_i(wb_hi_i),
		.lo_i(wb_lo_i),
	
		// 传递到EX模块的信息
		.hi_o(hi),
		.lo_o(lo)	
	);
......

4. 测试程序

   .org 0x0
   .set noat
   .global _start
_start:
   lui $1,0x0000          # $1 = 0x00000000
   lui $2,0xffff          # $2 = 0xffff0000
   lui $3,0x0505          # $3 = 0x05050000
   lui $4,0x0000          # $4 = 0x00000000 

   movz $4,$2,$1          # $4 = 0xffff0000
   movn $4,$3,$1          # $4 = 0xffff0000
   movn $4,$3,$2          # $4 = 0x05050000
   movz $4,$2,$3          # $4 = 0x05050000

   mthi $0                # hi = 0x00000000
   mthi $2                # hi = 0xffff0000
   mthi $3                # hi = 0x05050000
   mfhi $4                # $4 = 0x05050000

   mtlo $3                # li = 0x05050000
   mtlo $2                # li = 0xffff0000
   mtlo $1                # li = 0x00000000
   mflo $4                # $4 = 0x00000000        
3c010000
3c02ffff
3c030505
3c040000
0041200a
0061200b
0062200b
0043200a
00000011
00400011
00600011
00002010
00600013
00400013
00200013
00002012

在这里插入图片描述
在这里插入图片描述

参考资料

  1. 《自己动手写CPU》
  2. 《MIPS基准指令集手册_v1.00》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值