逻辑、移位操作与空指令的实现

article/2025/10/7 8:06:42

逻辑、移位操作和空指令的实现

1. 流水线数据相关的问题

流水线上经常会有一些被称为“相关”的情况发生,它使得指令序列中下一条指令无法按照设计的时钟周期执行,这些“相关”会降低流水线的性能。

1.1 流水线相关

流水线中的相关可分为:

  • 结构相关:指令在执行时,由于硬件资源满足不了指令执行的要求,发生硬件资源冲突而产生的相关。如:指令和数据都共享一个寄存器,在某个时钟周期,流水线既要完成某条指令对寄存器中数据的访问操作,又要完成后续的取指令操作,这样就会发生存储器访问冲突,产生结构相关。
  • 数据相关:在流水线中执行的几条指令中,一条指令依赖于前面指令的执行结果。
  • 控制相关:流水线中的分支指令或则其他需要改写PC的指令造成的相关。

1.2 数据相关

本节只讨论数据相关的问题:

  • RAW,即Read After Write。假设指令j在指令i后执行,RAW表示指令i将数据写入寄存器后,指令j才能从这个寄存器读取数据。如果指令j在指令i写入前读取数据,则指令j得到不正确的数据。
  • WAR,即Write After Read。假设指令j在指令i后执行,WAR表示在指令i读出寄存器数据后,指令j才能向这个寄存器写入数据。如果指令j在指令i读取前写入数据,则指令i得到不正确的数据。
  • WAW,即Write After Write。假设指令j在指令i后执行,RAW表示指令i将数据写入寄存器后,指令j才能向这个寄存器写入数据。如果指令j在指令i写入前写入数据,则该寄存器的值不是最新的值。

对目前的OpenMIPS结构而言,只有流水线写回阶段才会写寄存器,因此不存在WAW。又因为只能在流水线译码阶段读寄存器、写回阶段写寄存器,不存在WAR相关。

RAW相关有三种情况:

  • 相邻指令存在数据相关
    在这里插入图片描述
  • 相隔1条指令的指令间存在数据相关
    在这里插入图片描述
  • 相隔2条指令的指令间存在数据相关
    在这里插入图片描述

1.3 如何避免数据冒险

对于相隔2条指令的指令间存在数据相关(译码、写回阶段存在数据相关),在Regfile模块中已经得到了解决:

	...if((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin   rdata1 <= wdata;  ...if((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin rdata2 <= wdata;...

对于相邻指令间存在数据相关、相隔1条指令的指令间存在数据相关的情况:

  • 用暂停来避免数据相关(数据冒险)
    在这里插入图片描述

    暂停时,处理器会停止流水线上一条或多条指令,直到冒险不再满足。

    让一条指令停在译码阶段,直到产生它的源操作数的指令进入写回阶段,这样就可以避免数据冒险。

    弊端:使流水线暂停了多个周期,降低了整体的吞吐量。

  • 用数据前递(转发)来避免数据冒险
    在这里插入图片描述
    实现数据前递需要在基本的硬件结构中添加一些额外的数据连接和控制逻辑。
    将新计算的结果从执行阶段转发到译码阶段。当访存阶段有对寄存器为进行的写使,也可以使用数据转发。

  • 加载互锁:用暂停来处理加载/使用冒险
    在这里插入图片描述
    在周期4,mrmovq指令访存,addq指令要进行计算,即便是数据前递也来不及。这就需要addq指令至少暂停一个周期,再进行数据前递。

  • 编译器调度:编译器检测到相关后,可以改变部分指令的执行顺序
    在这里插入图片描述

2. OpenMIPS对数据相关问题的解决措施

采用数据前递的方法处理数据相关。将执行阶段的结果、访存阶段的结果前递到译码阶段,使其参与译码阶段选择源操作数的过程。
在这里插入图片描述
在原有的基础上改动:
​ 将执行阶段的结果 ex_wreg_o、ex_wd_o 和 ex_wdata_o,访存阶段的结果 mem_wreg_o、mem_wd_o 和 mem_wdata_o 前递到译码阶段ID模块。
在这里插入图片描述

2.1 ID模块添加输入接口和控制逻辑

module id(...// 处于执行阶段的指令的运算结果// 数据转发 data forwarding 来避免数据冒险input wire              ex_wreg_i,      // 处于执行阶段的指令是否要写寄存器input wire[`RegAddrBus] ex_wd_i,       // 处于执行阶段的指令写的目的寄存器的地址input wire[`RegBus]     ex_wdata_i,     // 处于执行阶段的指令写的目的寄存器的数据// 处于访存阶段的指令的运算结果// 数据转发 data forwarding 来避免数据冒险input wire              mem_wreg_i,      // 处于访存阶段的指令是否要写寄存器input wire[`RegAddrBus] mem_wd_i,        // 处于访存阶段的指令写的目的寄存器的地址input wire[`RegBus]     mem_wdata_i,     // 处于访存阶段的指令写的目的寄存器的数据...
);...
/****************  第二段:确定进行运算的源操作数1  ****************/
/* 给reg1_o的赋值的过程增加了两种情况1. 如果Regfile模块读端口1要读取的寄存器就是要执行阶段要写的目的寄存器,那么直接把执行阶段的结果ex_wdata_i作为reg1_o的值;2. 如果Regfile模块读端口1要读取的寄存器就是要访存阶段要写的目的寄存器,那么直接把访存阶段的结果mem_wdata_i作为reg1_o的值;
*/
always @(*) beginif (rst == `RstEnable) beginreg1_o <= `ZeroWord;end else if((reg1_read_o == `ReadEnable) && (ex_wreg_i == `WriteEnable)&& (reg1_addr_o == ex_wd_i)) begin      reg1_o <= ex_wdata_i;       // 读入执行阶段的结果end else if((reg1_read_o == `ReadEnable) && (mem_wreg_i == `WriteEnable)&& (reg1_addr_o == mem_wd_i)) begin      reg1_o <= mem_wdata_i;       // 读入访存阶段的结果end else if (reg1_read_o == `ReadEnable) beginreg1_o <= reg1_data_i;      // Regfile读端口1的输出值end else if (reg1_read_o == `ReadDisable) beginreg1_o <= imm;              // 读立即数end else beginreg1_o <= `ZeroWord;end
end/****************  第三阶段:确定进行运算的源操作数2 ****************/
always @(*) beginif (rst == `RstEnable) beginreg2_o <= `ZeroWord;end else if((reg2_read_o == `ReadEnable) && (ex_wreg_i == `WriteEnable)&& (reg2_addr_o == ex_wd_i)) begin      reg2_o <= ex_wdata_i;       // 读入执行阶段的结果end else if((reg2_read_o == `ReadEnable) && (mem_wreg_i == `WriteEnable)&& (reg2_addr_o == mem_wd_i)) begin      reg2_o <= mem_wdata_i;       // 读入访存阶段的结果end else if (reg2_read_o == `ReadEnable) beginreg2_o <= reg2_data_i;      // Regfile读端口2的输出值end else if (reg2_read_o == `ReadDisable) beginreg2_o <= imm;              // 读立即数end else beginreg2_o <= `ZeroWord;end
endendmodule

2.2 在OpenMIPS顶层模块中添加数据连接关系

...
// 译码阶段ID模块实例化id id0(...// 来自正处于执行阶段的指令的结果.ex_wreg_i(ex_wreg_o),.ex_wd_i(ex_wd_o),.ex_wdata_i(ex_wdata_o),// 来自正处于访存阶段的指令的结果.mem_wreg_i(mem_wreg_o),.mem_wd_i(mem_wd_o),.mem_wdata_i(mem_wdata_o),...);
...

3. 测试数据相关问题的解决效果

更改指令存储器内容:

34011100    // ori $1,$0,0x1100
34210020	// ori $1,$1,0x0020
34214400	// ori $1,$1,0x4400
34210044	// ori $1,$1,0x0044

创建工程,仿真:
在这里插入图片描述
$1的变化符合预期,修改后的OpenMIPS结构正确解决了数据相关问题。

4. 逻辑、移位操作和空指令的说明

OpenMIPS中所有指令的格式均为立即数型(I-Type)、跳转型(J-Type)和寄存器型(R-Type)三种类型中的一种。
在这里插入图片描述
MIPS32指令集架构中定义:

  • 逻辑操作:and、andi、or、ori、xor、xori、nor、lui
  • 移位操作:sll、sllv、sra、srav、srl、srlv
  • 空指令:nop、ssnop
    ssnop是一种特殊类型的空操作,在每个周期发射多条指令的CPU中,使用ssnop指令可以确保单独占用一个周期。OpenMIPS为标量处理器,每个周期只发射一条指令,所以在OpenMIPS中可以按照 nop 指令的处理方式来处理ssnop指令。
    sync(用于保证加载、存储操作的顺序)、pref(用于缓存预取),对于OpenMIPS而言,是顺序执行、加载和存储的,没有实现缓存,故将上述两条指令当成 nop 指令处理。

4.1 and、or、xor、nor指令

这四条指令都是R类型指令,指令码都是6’b000000,也就是MIPS32指令集架构中定义的SPECIAL类。第6~10bit都为0,需要根据功能码判断是哪种指令。
在这里插入图片描述

  • function:100100,AND
    汇编格式:AND rd, rs, rt
    功能描述:寄存器 rs 中的值与寄存器 rt 中的值按位逻辑与,结果写入寄存器 rd 中。
    操作定义:GPR[rd] ← GPR[rs] & GPR[rt]

  • function:100101,OR
    汇编格式:OR rd, rs, rt
    功能描述:寄存器 rs 中的值与寄存器 rt 中的值按位逻辑或,结果写入寄存器 rd 中。
    操作定义:GPR[rd] ← GPR[rs] or GPR[rt]

  • function:100110,XOR
    汇编格式:XOR rd, rs, rt
    功能描述:寄存器 rs 中的值与寄存器 rt 中的值按位逻辑异或,结果写入寄存器 rd 中。
    操作定义:GPR[rd] ← GPR[rs] xor GPR[rt]

  • function:100111,NOR
    汇编格式:NOR rd, rs, rt
    功能描述:寄存器 rs 中的值与寄存器 rt 中的值按位逻辑或非,结果写入寄存器 rd 中。
    操作定义:GPR[rd] ← GPR[rs] nor GPR[rt]

4.2 andi、xori指令

andi、xori都是I类型指令,依据第26~31bit指令码判断是哪种指令。
在这里插入图片描述

  • opcode:001100,ANDI
    汇编格式:ANDI rt, rs, imm
    功能描述:寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑与,结果写入寄存器 rt 中。
    操作定义:GPR[rt] ← GPR[rs] and Zero_extend(imm)

  • opcode:001110,XORI
    汇编格式:XORI rt, rs, imm
    功能描述:寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑异或,结果写入寄存器 rt 中。
    操作定义:GPR[rt] ← GPR[rs] xor Z ero_ext end(imm)

4.3 lui指令

lui指令是I类型指令,依据第26~31bit指令码是否为6’b001111判断是否是lui指令。
在这里插入图片描述
汇编格式:LUI rt, imm
功能描述:将 16 位立即数 imm 写入寄存器 rt 的高 16 位,寄存器 rt 的低 16 位置 0 。

4.4 sll、sllv、sra、srav、stlv、stlv指令

这六条指令都是R类型指令,指令码都为6’b000000,SPECIAL类指令,需要根据0~5bit的功能码判断是那种指令。
在这里插入图片描述

  • function:000000,SLL
    汇编格式:SLL rd, rt, sa
    功能描述:由立即数 sa 指定移位量,对寄存器 rt 的值进行逻辑左移,结果写入寄存器 rd 中。

  • function:000010,SRL
    汇编格式:SRL rd, rt, sa
    功能描述:由立即数 sa 指定移位量,对寄存器 rt 的值进行逻辑右移,结果写入寄存器 rd 中。

  • function:000011,SRA
    汇编格式:SRA rd, rt, sa
    功能描述:由立即数 sa 指定移位量,对寄存器 rt 的值进行算术右移,结果写入寄存器 rd 中。

  • function:000100,SLLV
    汇编格式:SLLV rd, rt, rs
    功能描述:由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行逻辑左移,结果写入寄存器 rd 中。

  • function:000110,SRLV
    汇编格式:SRLV rd, rt, rs
    功能描述:由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行逻辑右移,结果写入寄存器 rd 中。

  • function:000111,SRAV
    汇编格式:SRAV rd, rt, rs
    功能描述:由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行算术右移,结果写入寄存器 rd 中。

总结一下,指令的助记符最后有“v",表示移位位数由寄存器确定;指令的助记符最后没有“v",表示移位位数由指令中6~10Bit的sa确定

4.5 nop、ssnop、sync、pref指令

nop、ssnop指令的指令码、功能码与SLL指令一致,这样译码的时候会造成冲突吗?

nop = sll $0,$0,0
ssnop = sll $0,$0,1

其实两者是等价的,sll指令向$0寄存器保存移位结果,实际上不会由任何效果,无论向$0写任何数,$0始终为0,等同于什么都没做,这正是空指令的效果。所以nop、ssnop指令不用特意实现,完全可以当作特殊的逻辑左移指令。
在这里插入图片描述

5. 修改OpenMIPS以实现移位、逻辑操作与空指令

5.1 修改译码阶段的ID模块

首先在defines.v文件中添加宏定义:

`define EXE_AND                 6'b100100           // 指令and的功能码
`define EXE_OR                  6'b100101           // 指令or的功能码
`define EXE_XOR                 6'b100110           // 指令xor的功能码
`define EXE_NOR                 6'b100111           // 指令nor的功能码
`define EXE_ANDI                6'b001100           // 指令andi的指令码
`define EXE_ORI                 6'b001101           // 指令ori的指令码
`define EXE_XORI                6'b001110           // 指令xori的指令码
`define EXE_LUI                 6'b001111           // 指令lui的指令码`define EXE_SLL                 6'b000000           // 指令sll的功能码
`define EXE_SLLV                6'b000100           // 指令sllv的功能码
`define EXE_SRL                 6'b000010           // 指令srl的功能码
`define EXE_SRLV                6'b000110           // 指令srlv的功能码
`define EXE_SRA                 6'b000011           // 指令sra的功能码
`define EXE_SRAV                6'b000111           // 指令srav的功能码`define EXE_SYNC                6'b001111           // 指令sync的功能码
`define EXE_PREF                6'b110011           // 指令pref的功能码`define EXE_NOP                 6'b000000           // 指令nop的功能码
`define SSNOP                   32'b00000000000000000000000001000000`define EXE_SPECIAL_INST        6'b000000           //SPECIAL类指令的指令码

对指令进行译码的前提是能判断出指令种类,如下图:

在这里插入图片描述

修改后的ID模块:

`include "defines.v"
`timescale 1ns/1ps
module id(
......
);// 取得指令的指令码,功能码
// 对于ori指令只需判断26~31bit的值,即可判断是否是ori指令
wire[5:0] op  = inst_i[31:26];               // 取指令码opcode
wire[4:0] op2 = inst_i[10:6];               // 用来判断指令种类
wire[5:0] op3 = inst_i[5:0];                // 取功能码function
wire[4:0] op4 = inst_i[20:16];              // 取 rt// 保存指令执行需要的立即数
reg[`RegBus]    imm;// 指示指令是否有效
reg     instvalid;/***************************  第一段:对指令进行译码  ********************************/
always @(*) beginif (rst == `RstEnable) beginaluop_o     <= `EXE_NOP_OP;         alusel_o    <= `EXE_RES_NOP;wd_o        <= `NOPRegAddr;wreg_o      <= `WriteDisable;instvalid   <= `InstValid;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadDisable;reg1_addr_o <= `NOPRegAddr;reg2_addr_o <= `NOPRegAddr;imm         <= `ZeroWord;end else beginaluop_o     <= `EXE_NOP_OP;alusel_o    <= `EXE_RES_NOP;wd_o        <= inst_i[15:11];       // 默认目的寄存器地址wd_owreg_o      <= `WriteDisable;instvalid   <= `InstInvalid;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadDisable;reg1_addr_o <= inst_i[25:21];       // 默认的reg1_addr_oreg2_addr_o <= inst_i[20:16];       // 默认的reg2_addr_oimm         <= `ZeroWord;case (op)`EXE_SPECIAL_INST:  begincase (op2)5'b000000:  begincase (op3)`EXE_OR:    beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_OR_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_AND:   beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_AND_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_XOR:   beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_XOR_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_NOR:   beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_NOR_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_SLLV:  beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SLL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_SRLV:  beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SRL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_SRAV:  beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SRA_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;end`EXE_SYNC:  beginwreg_o      <= `WriteDisable;aluop_o     <= `EXE_NOP_OP;alusel_o    <= `EXE_RES_NOP;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadEnable;instvalid   <= `InstValid;enddefault:    beginendendcase     //op3enddefault:    beginendendcase         // op2end`EXE_ORI:   beginwreg_o      <= `WriteEnable;            aluop_o     <= `EXE_OR_OP;             alusel_o    <= `EXE_RES_LOGIC;         reg1_read_o <= `ReadEnable;             reg2_read_o <= `ReadDisable;           imm         <= {16'h0, inst_i[15:0]};   wd_o        <= inst_i[20:16];           instvalid   <= `InstValid;              end `EXE_ANDI:  beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_AND_OP;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadDisable;imm         <= {16'h0,inst_i[15:0]};wd_o        <= inst_i[20:16];instvalid   <= `InstValid;end`EXE_XORI: beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_XOR;alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadDisable;imm         <= {16'h0,inst_i[15:0]};wd_o        <= inst_i[20:16];instvalid   <= `InstValid;end`EXE_LUI:   beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_OR_OP;		// 将lui指令转为ori指令执行alusel_o    <= `EXE_RES_LOGIC;reg1_read_o <= `ReadEnable;reg2_read_o <= `ReadDisable;imm         <= {inst_i[15:0],16'h0};wd_o        <= inst_i[20:16];instvalid   <= `InstValid;end`EXE_PREF:  beginwreg_o      <= `WriteDisable;aluop_o     <= `EXE_NOP_OP;alusel_o    <= `EXE_RES_NOP;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadDisable;instvalid   <= `InstValid;enddefault: beginendendcase         // case opif (inst_i[31:21] == 11'b0) beginif (op3 == `EXE_SLL)    beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SLL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadEnable;imm[4:0]    <= inst_i[10:6];wd_o        <= inst_i[15:11];instvalid   <= `InstValid;end  else if (op3 == `EXE_SRL)  beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SRL_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `ReadDisable;reg2_read_o <= `ReadEnable;imm[4:0]    <= inst_i[10:6];wd_o        <= inst_i[15:11];instvalid   <= `InstValid;end else if (op3 == `EXE_SRA)   beginwreg_o      <= `WriteEnable;aluop_o     <= `EXE_SRA_OP;alusel_o    <= `EXE_RES_SHIFT;reg1_read_o <= `WriteDisable;reg2_read_o <= `WriteEnable;imm         <= inst_i[10:6];wd_o        <= inst_i[15:11];instvalid   <= `InstValid;endendend         // else
end     // always/**************  第二段:确定进行运算的源操作数1  **************/
/* 给reg1_o的赋值的过程增加了两种情况1. 如果Regfile模块读端口1要读取的寄存器就是要执行阶段要写的目的寄存器,那么直接把执行阶段的结果ex_wdata_i作为reg1_o的值;2. 如果Regfile模块读端口1要读取的寄存器就是要访存阶段要写的目的寄存器,那么直接把访存阶段的结果mem_wdata_i作为reg1_o的值;
*/
always @(*) beginif (rst == `RstEnable) beginreg1_o <= `ZeroWord;end else if((reg1_read_o == `ReadEnable) && (ex_wreg_i == `WriteEnable)&& (reg1_addr_o == ex_wd_i)) begin      reg1_o <= ex_wdata_i;       // 读入执行阶段的结果end else if((reg1_read_o == `ReadEnable) && (mem_wreg_i == `WriteEnable)&& (reg1_addr_o == mem_wd_i)) begin      reg1_o <= mem_wdata_i;       // 读入访存阶段的结果end else if (reg1_read_o == `ReadEnable) beginreg1_o <= reg1_data_i;      // Regfile读端口1的输出值end else if (reg1_read_o == `ReadDisable) beginreg1_o <= imm;              // 读立即数end else beginreg1_o <= `ZeroWord;end
end/**************  第三阶段:确定进行运算的源操作数2 **************/
always @(*) beginif (rst == `RstEnable) beginreg2_o <= `ZeroWord;end else if((reg2_read_o == `ReadEnable) && (ex_wreg_i == `WriteEnable)&& (reg2_addr_o == ex_wd_i)) begin      reg2_o <= ex_wdata_i;       // 读入执行阶段的结果end else if((reg2_read_o == `ReadEnable) && (mem_wreg_i == `WriteEnable)&& (reg2_addr_o == mem_wd_i)) begin      reg2_o <= mem_wdata_i;       // 读入访存阶段的结果end else if (reg2_read_o == `ReadEnable) beginreg2_o <= reg2_data_i;      // Regfile读端口2的输出值end else if (reg2_read_o == `ReadDisable) beginreg2_o <= imm;              // 读立即数end else beginreg2_o <= `ZeroWord;end
endendmodule

5.2 修改执行阶段的EX模块

`include "defines.v"
`timescale 1ns/1ps
module ex(
......     
);reg[`RegBus]    logicout;       // 保存逻辑运算的结果
reg[`RegBus]    shiftres;      // 保存移位运算的结果// 逻辑运算
always @(*) beginif (rst == `RstEnable)  beginlogicout <= `ZeroWord;end else begincase (aluop_i)`EXE_OR_OP: beginlogicout <= reg1_i | reg2_i;end`EXE_AND_OP: beginlogicout <= reg1_i & reg2_i;end`EXE_NOR_OP: beginlogicout <= ~(reg1_i | reg2_i);end`EXE_XOR_OP: beginlogicout <= reg1_i ^ reg2_i;enddefault: beginlogicout <= `ZeroWord;endendcaseend     // else
end     // always// 移位运算
always @(*) beginif (rst == `RstEnable) beginshiftres <= `ZeroWord;end else begincase (aluop_i)`EXE_SLL_OP: beginshiftres <= reg2_i << reg1_i[4:0];end`EXE_SRL_OP: beginshiftres <= reg2_i >> reg1_i[4:0];end`EXE_SRA_OP: beginshiftres <= ({32{reg2_i[31]}} << (6'd32-{1'b0,reg1_i[4:0]})) | reg2_i >> reg1_i[4:0];enddefault: beginshiftres <= `ZeroWord;endendcaseend
end// 依据aluop_i选择最终的运算结果
always @(*) beginwd_o    <= wd_i;wreg_o  <= wreg_i;case (alusel_i)`EXE_RES_LOGIC: beginwdata_o <= logicout;end`EXE_RES_SHIFT: beginwdata_o <= shiftres;enddefault: beginwdata_o <= `ZeroWord;endendcase
endendmodule

6. 测试程序

6.1 逻辑运算功能测试

	.org 0x0
.global _start.set noat
_start:lui  $1,0x0101ori  $1,$1,0x0101ori  $2,$1,0x1100        # $2 = $1 | 0x1100 = 0x01011101or   $1,$1,$2            # $1 = $1 | $2 = 0x01011101andi $3,$1,0x00fe        # $3 = $1 & 0x00fe = 0x00000000and  $1,$3,$1            # $1 = $3 & $1 = 0x00000000xori $4,$1,0xff00        # $4 = $1 ^ 0xff00 = 0x0000ff00xor  $1,$4,$1            # $1 = $4 ^ $1 = 0x0000ff00nor  $1,$4,$1            # $1 = $4 ~^ $1 = 0xffff00ff   nor is "not or"

编译得到:

3c010101
34210101
34221100
00220825
302300fe
00610824
3824ff00
00810826
00810827

在这里插入图片描述

6.2 移位运算和空指令功能测试

   .org 0x0.set noat.global _start
_start:lui   $2,0x0404ori   $2,$2,0x0404ori   $7,$0,0x7ori   $5,$0,0x5ori   $8,$0,0x8syncsll   $2,$2,8    # $2 = 0x40404040 sll 8  = 0x04040400sllv  $2,$2,$7   # $2 = 0x04040400 sll 7  = 0x02020000srl   $2,$2,8    # $2 = 0x02020000 srl 8  = 0x00020200srlv  $2,$2,$5   # $2 = 0x00020200 srl 5  = 0x00001010nopsll   $2,$2,19   # $2 = 0x00001010 sll 19 = 0x80800000ssnopsra   $2,$2,16   # $2 = 0x80800000 sra 16 = 0xffff8080srav  $2,$2,$8   # $2 = 0xffff8080 sra 8  = 0xffffff80 

编译得到:

3c020404
34420404
34070007
34050005
34080008
0000000f
00021200
00e21004
00021202
00a21006
00000000
000214c0
00000040
00021403
01021007

建立工程,仿真:
在这里插入图片描述
在这里插入图片描述

参考资料

  1. 《自己动手写CPU》
  2. 《MIPS基准指令集手册_v1.00》
  3. 《深入理解计算机系统》

http://chatgpt.dhexx.cn/article/BUYNM9qL.shtml

相关文章

汇编移位指令SHR,SAR,SAL/SHL,ROR,ROL,RCR,RCL

目录 逻辑右移SHR 算数右移SAR&#xff08;重点&#xff09; 算数/逻辑左移SAL/SHL(完成的操作都一样) 循环右移ROR 循环左移ROL 带进位循环右移RCR 带进位循环左移RCL 总结 例题 一 二 移位指令为双操作数指令&#xff0c;用于将目的的操作数中的二进制数移位。 目…

位移指令实现乘法、除法计算

前言 大家都知道51单片机是有乘法、除法指令的&#xff0c;不管是用C语言还是汇编语言&#xff0c;都是可以直接计算乘法、除法的&#xff0c;我以为&#xff0c;-&#xff0c;*&#xff0c;/ 这些算术运算是单片机的标配&#xff0c;而我公司使用的应广单片机居然没有乘法、除…

微机原理——移位指令

例题 思路 选择移位语句&#xff0c;右移&#xff0c;将AL移出的送入DX左端&#xff0c;将BL移出的送入DX左端。循环八次 MOV AL,01100101B; MOV BL,11011010B; XOR DX,DX;两个值相同&#xff0c;异或结果为0。等效&#xff1a;MOV DX,0 MOV CX,8;count L1: SHR AL,1;逻辑右…

汇编语言---移位指令

移位指令是一组经常使用的指令,包括:算数移位、逻辑移位、双精度移位、循环移位、带进位的循环移位; 移位指令都有一个指定需要移动的二进制位数的操作数,该操作数可以是立即数,也可以是CL的值;在8086中,该操作数只能是1,但是在其后的CPU中,该立即数可以是定义域[1,31]之内的数…

汇编语言——移位指令

基本概念 移位操作指令&#xff1a;移位操作指令是一组经常使用的指令&#xff0c;属于汇编语言逻辑指令中的一部分&#xff0c;它包括移位指令&#xff08;含算术移位指令、逻辑移位指令&#xff09;&#xff0c;循环移位指令&#xff08;含带进位的循环移位指令&#xff09;&…

汇编指令之移位指令

移位指令包括了 算术移位指令、逻辑移位指令、循环移位指令。 格式为:xxx oper1,CL/1 ;移位次数只能是1或者存放在CL里面。 一、算术移位指令 1、算术左移指令SAL 功能&#xff1a;左移一次&#xff0c;最低位补0&#xff0c;最高位送入CF标志位&#xff0c;如图&am…

汇编指令(四)移位指令

学习概要 格式 移位指令主要分四种 一、逻辑移位指令 1.逻辑左移指令SHL 2.逻辑右移指令SHR 3.逻辑移位指令的功能 二、算术移位指令 1.算术左移指令SAL 2.算术右移指令SAR 最高位不变的意思就是&#xff0c;最高位原来是1&#xff08;0&#xff09;&#xff0c;右移过后…

【大学生软件测试基础】白盒测试 - 语句覆盖 - 03

任务1、依据源代码画出程序流程图&#xff1b; 任务2、根据程序流程图&#xff0c;找出程序的所有执行路径&#xff1b; 任务3、找出能覆盖所有语句的最少路径&#xff1b; 任务4、根据最少路径设计语句覆盖用例&#xff1b; 流程图&#xff1a; 任务2、根据程序流程图&…

修正的判定条件覆盖例题_语句覆盖、判断覆盖、条件覆盖、条件判定组合覆盖、多条件覆盖、修正条件覆盖...

int function(bool a,bool b,boolc){intx; x=0;if(a&&(b||c)){x=1;returnx; } } 1、语句覆盖(SC) 选择足够多的测试数据,使得被测程序中的每条语句至少执行一次。 测试用例:a=T,b=T,c=T 2、判断覆盖(DC) 设计足够的测试用例,使得程序中的每个判定至少都获得一次真值…

语句覆盖,判定覆盖,条件覆盖,条件/判定覆盖,条件组合覆盖,路径覆盖

最近在复习软件测试的考试&#xff0c;每次到白盒测试这里都要为这几种逻辑覆盖方法感到头疼&#xff0c;这次终于决定好好整理出来。 逻辑覆盖是通过对程序逻辑结构的遍历实现程序的覆盖。它是一系列测试过程的总称&#xff0c;这组测试过程逐渐进行越来越完整的通路测试。 根…

软件测试培训之:白盒测试的语句覆盖法和判定覆盖法

白盒测试是相对于黑盒测试而言的&#xff0c;黑盒测试不关注程序内部的实现结构&#xff0c;仅仅是通过向程序进行输入来观察程序的输出对不对;白盒测试就需要关注程序内部的实现结构&#xff0c;对程序的逻辑结构实施相关的测试;那么下面来谈谈语句覆盖法、判定覆盖法和条件覆…

语句覆盖、判定覆盖、条件覆盖 - 白盒笔试题解析

1. 定义 黑盒测试 - 不关注内部结构的测试/基于输入输出的测试 白盒测试 - 基于内部结构的测试 白盒测试的关键&#xff1a; 覆盖度 2. 白盒相关概念 语句覆盖 - 设计一套测试 让被测对象中所有语句得到测试覆盖 判定覆盖 - 设计一套测试 让被测对象中所有判定得到测试覆…

白盒测试用例设计方法(语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、组合覆盖、路径覆盖、基本路径覆盖)

语句覆盖&#xff1a;每条语句至少执行一次。 判定覆盖&#xff1a;每个判定的所有可能结果至少出现一次。&#xff08;又称“分支覆盖”&#xff09; 条件覆盖&#xff1a;每个条件的所有可能结果至少执行一次。 判定/条件覆盖&#xff1a;一个判定中的每个条件的所有可能结果…

白盒测试的逻辑覆盖辨析(语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖)

白盒测试逻辑覆盖&#xff08;语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖&#xff09; 逻辑覆盖测试&#xff1a; 语句覆盖&#xff1a;每条语句至少执行一次判定覆盖&#xff1a;每一判定的每个分支至少执行一次条件覆盖&#xff1a;每一判定中的每个条件&…

白盒测试技术——语句覆盖、判定覆盖、条件覆盖

结合一个例子说&#xff1a; 画出相应的程序流程图&#xff0c;更方便分析&#xff1a; 首先我们要明确一点&#xff1a;测试用例要尽可能少。 1、语句覆盖&#xff1a;被测程序中每个语句至少执行一次 路径&#xff1a;s->a->b->c->d->e A2&#xff0c;B0&a…

语句覆盖、分支覆盖、谓词测试和路径覆盖

目录 语句覆盖分支覆盖&#xff08;判定覆盖&#xff09;谓词测试原子谓词覆盖&#xff08;条件覆盖&#xff09;分支-谓词覆盖(判定条件覆盖或分支条件覆盖)复合谓词覆盖&#xff08;条件组合覆盖&#xff09; 路径覆盖包含关系 语句覆盖 “语句覆盖”是一个比较弱的测试标准…

白盒测试 | 用例设计方法之语句覆盖

语句覆盖&#xff0c;顾名思义就是针对代码语句的嘛。它的含义是我们设计出来的测试用例要保证程序中的每一个语句至少被执行一次。通常语句覆盖被认为是“最弱的覆盖”&#xff0c;原因是它仅仅考虑对代码中的执行语句进行覆盖而没有考虑各种条件和分支&#xff0c;因此在实际…

逻辑覆盖测试(一)语句覆盖

语句覆盖&#xff1a; 设计测试用例时保证程序的每条语句至少执行一次。 简单来说&#xff0c;就是每个语句都覆盖一遍。 例子&#xff1a; 流程图如下&#xff1a; 测试用例如下&#xff1a; x4,z9&#xff0c;第一个if语句执行到了&#xff1b; x4,y7,第二个if语句为true…

设计测试用例实现语句覆盖,判定覆盖,条件覆盖,判定/条件覆盖,条件组合覆盖,路径覆盖.

第一题&#xff1a; 设计测试用例实现语句覆盖,判定覆盖,条件覆盖&#xff0c;判定/条件覆盖,条件组合覆盖,路径覆盖. 【1】语句覆盖&#xff08;设计若干个测试用例&#xff0c;使程序中的每个可执行语句至少执行一次&#xff09; &#xff08;x>3&#xff09;&&&…

语句覆盖、条件覆盖、判定覆盖、条件-判定覆盖、组合覆盖、路径覆盖

文章目录 语句覆盖判定覆盖条件覆盖条件--判定覆盖组合覆盖路径覆盖 逻辑覆盖率&#xff1a;语句覆盖<条件覆盖<判定覆盖<条件-判定覆盖<组合覆盖<路径覆盖 语句覆盖 基本思想&#xff1a;设计用例&#xff0c;使程序中的每个可执行语句至少执行一次。 每个可执…