指令(MIPS技术的指令集)

本文详细解析了MIPS指令结构,包括寄存器操作数、存储器操作数,以及寻址方式如伪直接寻址、PC相对寻址等。还介绍了决策指令、过程支持和内存布局,以及32位立即数处理和分支跳转的地址计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

指令是由操作码和地址码两部分组成的。 

一、引言

1.MIPS操作数:

2.硬件设计的设计原则:

3.常用指令:

4.加速大概率事件的例子:

二、计算机硬件操作

1.计算机硬件的操作数

2.存储器操作数

3.常数或立即数操作数

三、寻址方式

伪直接寻址:

PC相对寻址:

寄存器寻址型:

基址寻址型: 

立即数寻址型:

四、指令的表示

指令格式:二进制数 字段 组成的指令表示形式

1.R型MIPS指令(寄存器型):

2.I型MIPS指令(立即数和数据传送指令):

五、决策指令

1.条件分支指令

2.无条件分支指令

3.小于则置位

4.通过以上条件分支和小于则置位得到 小于则分支

六、计算机硬件对过程的支持(函数)

七、内存布局

八、MIPS中的32位立即数

九、分支和跳转中的寻址

跳转指令: jump

 条件分支指令:bne,beq


指令是由操作码和地址码两部分组成的。 

操作码的长度可以变化,也可以是固定的。 扩展操作码技术,不同的地址数的指令(如三地址指令、二地址指令) 可以具有不同的操作码长度。

指令集:一台计算机的全部指令

存储程序:指令和数据都以数字形式存储于存储器中

MIPS体系结构中 寄存器大小为32位,字的大小为32位与寄存器相同。

所有MIPS指令都是32位长。

MIPS是按字节编址的,字的起始地址必是4的倍数。

MIPS采用大端编址。

大端编址和小端编址的字地址都是字的最低字节地址。

a1b7f5b955e74d2297ce7a474536cb46.jpeg

一、引言

1.MIPS操作数:

32个寄存器:

$s0---$s7(16~23)  存放变量的寄存器

$t0---$t9 (8~15)  存放临时变量的寄存器

用于过程(函数)中:

$a0---$a3  用于传递参数的4个寄存器     

$v0---$v1  用于返回值的两个值寄存器  (val)

$ra   用于返回起始点的返回地址寄存器 (register address,向PC返回调用点的地址)

$zero  存放常数0的寄存器

$gp: 静态数据的全局指针寄存器(reg 28) global pointer for static data (reg 28)

$sp: 堆栈指针寄存器stack pointer (reg 29)

$fp: 帧指针寄存器(frame pointer) ,保存过程帧的第一个字 (reg 30)

存储器字:

保存数据结构、数组 和 溢出的寄存器 数据

 add $t0,$s0,$s1;  //一条运算指令, $t0、b、c为操作数

2.硬件设计的设计原则:

简单源于规整 : 每条指令只能执行一个操作,与加法类似的指令有且仅有三个操作数。

越小越快 : 寄存器只有32个。

优秀的设计需要适宜的折中方案:指令长度相同的情况下,不同类型的指令采用不同的指令格式。不同类型指令采用不同的解码方式,但都是32位相同的指令长度,尽可能保持相似的指令格式。

3.常用指令:

add (加法) : add $t0,$s0,$s1

sub  (减法) :sub $t0,$s0,$s1

addi (加立即数):addi $t0,$s0,4

sll (逻辑左移):sll $t0,$s0,2

srl  (逻辑右移): srl $t0,$s0,3

bne (不相等则分支):bne $s0,$s1,L1

beq (,相等则分支):beq $s0,$s1,L1

j(无条件跳转):j L1

slt(set on less than  小于则置位):slt $t0,$s0,$s1

slti(立即数版小于则置位):slti $t0,$s0,5

lw(数据传送指令,取数):lw $t0,120($s0)  //存入t0

sw(数据传送指令,存数):sw $t0,120($s0)  //存入120($s0)

jal(跳转和链接指令):jal fact //跳转到fact位置,并保存当前指令PC+4到$ra中

jr(寄存器跳转指令):jr $ra

lui(读取立即数高位指令):lui $s0,61 //将61的二进制代码放入s0的高六位

4.加速大概率事件的例子:

根据使用频率来确定要定义的常数:如将$zero恒置为0

寄存器约定:传递4个参数、2个寄存器返回结果、保存8个寄存器、10个暂存器对大多数过程调用来说足够使用

寻址附近的指令:PC+4

二、计算机硬件操作

                ————计算机必须能执行算术运算

                ————每条MIPS算术指令只执行一个操作。编译器将高级程序语言编译成汇编指令。

1.计算机硬件的操作数

即 MIPS操作数。

2.存储器操作数

由于MIPS的 算术运算指令只对寄存器进行操作而变量(包括数组的基址,对应成为基址寄存器)存放在寄存器中,数据结构 (如数组和结构)是存放在存储器中的。则必须要 数据传送指令 给出存储器中特定元素的地址进行读取操作(lw),放入临时寄存器%tx 中。(数组元素通过基址寄存器中的地址值和对应偏移量,可以在存储器中找到特定的数据)
PS:编译器会尽量将最常用的变量保持在寄存器中,而将其他的变量放在存储器中,方法是使用取数或存数指令在寄存吾和存储器之间传送变量。将不常使用的变量(或稍后才使用的变量)存
回到存储导中 的过程叫作寄存器溢出。

3.常数或立即数操作数

                ————直接将立即数的大小放在算术运算指令中,指令中就携带了该常数的信息。

                ————对应的指令 为 addi。由于 MIPS 支持负常数,所以 MIPS 中不需要设置减立即数的指令

                ————将$zero 的值置为0。//可将add变为赋值操作



三、寻址方式

伪直接寻址:

J型指令寻址方式

PC相对寻址:

beq指令寻址方式

寄存器寻址型

寄存器寻址(直接):在地址码字段直接指出寄存器的编号,操作数在寄存器内。

寄存器间接寻址:在地址码中指出寄存器的编号,寄存器内存储操作数在主存中的地址。

基址寻址型: 

基址(基址寄存器)

立即数寻址型

立即寻址:指令中带有立即数

e025e68566e84ca88e772e55fd1c47bc.png



四、指令的表示

                                                                                        ————所有MIPS指令都是32位长。

指令格式:二进制数 字段 组成的指令表示形式

OP的值决定后面的字段分割数

1.R型MIPS指令(寄存器型):

//add 、sub 、and 、sll 、srl

ce612082489d4e94adbd5abd5d09a885.png

必须有3个寄存器,两个用于运算,一个保存结果

op:操作码  //所有算术运算op是一样的,比如加、减、与、位移(addi与之不一样)

rs:第一个源操作数寄存器

rt:第二个源操作数寄存器

rd:存放结果的目的寄存器

shamt:(shift amount)位移量(不适用时,该字段内容为0)

funct:功能(码) //和op一起决定算术运算具体操作

执行过程:

        PC取出指令,放入IR,放入CU对OP和funct译码,同时取出寄存器rs、rt中的值送入ALU,然后对rs和rt执行相应的运算,运算结果放入rd中。

20ab3861945a49e8a026c7bebdc40b37.png

对于逻辑操作:

OP和和其他算术运算一样都是0,但是funct变为0,位移量shamt决定rt的位移位数。

而rs未被利用,置0。

sll $t2,$s0,4 //shift left左移,指令中有三个寄存器,但是只需要用到两个。将$s0中的数据取出后对其左移4位,结果保存至$t2。($s0中数据本身没变)


2.I型MIPS指令(立即数和数据传送指令):

//addi、lw、sw

cc17d7c519944b48b563a165f5c15340.png

当16bits为立即数时:addi

rs:源寄存器

rt:存放结果的目的寄存器

//constant+rs-> rt constant的范围为 -2^15~2^15-1

如: addi $t1,$s0,-300


当16bits为地址时:数据传送指令 lw/sw

rs: 基址寄存器

rt:存放结果的目的寄存器

//address 表示基址偏移量即offset,它的值是地址数值的增值

如: lw $t0,1200($s0)//其中1200表示的是 地址的值增加1200,而不是数组下标!且偏移量必须是常数。 

/*若不是常数 如 save[i]设i存放在$s0中 save基址存放在$s1中

sll $t0,$s0,4 //按字节寻址,真实地址值应该加是i*4

add $t1,$s1,$t0 //将save的值+i*4 放入 临时寄存器中

lw $t2,(0)$t1  //根据临时寄存器的基址偏移0找到 save[i]*/

/*lui指令、分支指令的格式和它一样*//

596875fa4dd3422fa5c4f5238b80b408.png



五、决策指令

1.条件分支指令

定义:先比较两个值,然后根据比较结果决定是否从程序中的一个新地址开始执行指令序列。

①beq(branch if equal):

如果相等则分支

beq register1,register 2,L1//如果相等则跳转至L1处

②bne(branch if not equal)

如果不相等则分支

bne register 1,register 2,L1//如果不相等则跳转至L1处

2.无条件分支指令

j(jump):

无条件跳转

j L1//无条件跳转至L1处

3.小于则置位

①slt(set on less than)

slt $t0,$s3,$s4 //if s3<s4 then t0=1

②slti (立即数版本)

slti $t0,$s3,4 //if s3<4 then t0=1;

4.通过以上条件分支和小于则置位得到 小于则分支

if(a<b)  cout<<1;

a存在$s0,b存于$s1

slt $t0,$s0,$s1// s0<s1时  t0置1,否则置0

bne $zero,$t0,L1  //当zero和t0内容不一样时,跳转至L1处,也就是当t0置1时跳转。

L1:

        cout<<1;

六、计算机硬件对过程的支持(函数)

        过程:根据提供的参数执行一定任务的存储的子程序。

过程运行的6个步骤

1.将参数放在过程可以访问的寄存器里

2.将控制转移给过程

3.获得过程所需要的存储资源

4.执行过程的操作(请求的任务)

5.将结果的值放在调用程序可以访问到的寄存器

6.将控制返回到调用点

翻译:

调用者调用函数时,先将PC+4的值(调用点下一条指令的值)存入$ra,将参数的值存入$a0~$a3中,将控制转移给过程,运行过程的代码,为获得$s0~$s7寄存器的使用,将原来的数值压栈,若要进行递归调用,$ra和$t0~$t9的值也要压栈保存,然后可以利用寄存器进行过程的操作,得到的结果存入$v0~$v1(它们能被主程序直接访问),弹栈回复原寄存器的值,将$ra的值送入PC,被调用者将控制返回到调用点。

$a0---$a3:用于传递参数的4个寄存器     

$v0---$v1:用于返回值的两个值寄存器  (val)

$ra:用于返回起始点的返回地址寄存器 (register address,向PC返回调用点的地址)

跳转和链接指令:

jal ProcedureAddress// jal Func();

跳转,跳转至过程处,链接是将PC+4的值存入$ra

寄存器跳转指令:

jr $ra// 无条件跳转至$ra中程序的指令处。

七、内存布局

注:下图并非MISPS体系结构的一部分,而是一种软件规定。

d7616d9220d144f2a71ee48e02dd1ec9.png

Text(正文):存放指令,PC的高四位为0000 

Stack:存放栈,从地址高位开始向下存放。

八、MIPS中的32位立即数

——根据I型寄存器指令知道,立即数最高只有16位。但有时候需要更大的常数。

84f0b3c173964d4c96667184d1927f61.png

读取立即数高位指令lui。

加载32位常数:

lui $s0,61

ori $s0,2304

先将61的二进制代码放入s0的高四位,ori 立即数逻辑或操作把0读到高16位,2304在低16位和s0进行或操作 就得到了61 2304 的二进制代码,即s0中的最终结果:

0000 0000 0011 1101 0000 1001 0000 0000。

九、分支和跳转中的寻址

跳转指令: jump

e0ddba4b26e94760a6e84c2cb1b16416.png

dee4e35723d54223ae0c4e3fcaeec718.png

该指令过后PC的值为:

PC(31~28):address*4.

PC前四位 31~28 为0000,后28位变化,刚好为文本范围。

address表示的是指令的第几个位置,乘以4表示指令的真实地址。

因为指令地址的低2位都为0。

使得整个address的范围 变成了 28位,实际上真实地址后两位为恒为00,所以address表示的并不是真实地址值,而是地址值的前26位。


 条件分支指令:bne,beq

分支寻址形式成为PC相对寻址,是在PC的基础上进行的。它们倾向于转到附近的指令,所以用PC的增值基本上很合适。

71d7788b27c9412c85dc79c6da64a3ec.png

 该指令过后PC的值为:

PC=PC+address*4

也就是说address本质上表示的是跳转了多少指令,而不是跳转了多少地址值,地址值体现在*4上。如果跳转的是地址值,address的后两位都是00,浪费空间。

注意:当PC取这条指令的时候,PC的值为先增加4。(具体与硬件有关)

所以图中80012的address值为2,表示跳转2个指令,因为此时PC值已经为80016了。

7714cf3f1ed149aeb2602048bdc2735a.png

afa5cce8c8f24c0985e06410411b466b.png

十、C语言转化成可执行文件

C语言程序------>汇编语言程序------>目标模块------>可执行文件:机器语言程序------>存储器

                编译器                   汇编器             链接器                                          加载器            

### 回答1: 很好,这是一项非常有挑战性的任务。实现一个MIPS单周期CPU需要一些基础知识,包括MIPS指令集体系结构、Verilog HDL编程语言以及数字电路设计等方面的技能。 下面是一个简单的MIPS单周期CPU的Verilog HDL实现,包括九条基本运算指令(add, sub, and, or, slt, addi, lui, lw, sw)以及三种控制指令(beq, bne, j)。 ``` module MIPS ( input clk, input rst, input [31:0] inst, input [31:0] data_in, output [31:0] data_out ); // 寄存器文件 reg [31:0] reg_file [31:0]; // ALU 控制信号 wire [3:0] alu_ctrl; // 数据存储器 reg [31:0] mem [1023:0]; // 控制信号 wire RegDst, Jump, Branch, MemRead, MemtoReg, ALUOp1, ALUOp0, MemWrite, ALUSrc; wire [1:0] JumpCtrl; wire [2:0] BranchCtrl; // 寄存器文件读写 reg [4:0] rs, rt, rd; wire [31:0] read_data1, read_data2; reg RegWrite; // ALU 输入 wire [31:0] alu_a, alu_b; // ALU 输出 wire [31:0] alu_out; // 内存访问 wire [31:0] mem_data; // 控制器 controller ctrl ( .opcode(inst[31:26]), .funct(inst[5:0]), .RegDst(RegDst), .Jump(Jump), .Branch(Branch), .MemRead(MemRead), .MemtoReg(MemtoReg), .ALUOp1(ALUOp1), .ALUOp0(ALUOp0), .MemWrite(MemWrite), .ALUSrc(ALUSrc), .rs(inst[25:21]), .rt(inst[20:16]), .rd(inst[15:11]), .JumpCtrl(JumpCtrl), .BranchCtrl(BranchCtrl) ); // ALU 控制器 alu_ctrl_unit alu_ctrl_unit ( .ALUOp1(ALUOp1), .ALUOp0(ALUOp0), .funct(inst[5:0]), .alu_ctrl(alu_ctrl) ); // 控制器输出线路 assign RegWrite = ctrl.RegWrite; assign Jump = ctrl.Jump; assign Branch = ctrl.Branch; assign MemRead = ctrl.MemRead; assign MemtoReg = ctrl.MemtoReg; assign MemWrite = ctrl.MemWrite; assign ALUSrc = ctrl.ALUSrc; assign RegDst = ctrl.RegDst; assign JumpCtrl = ctrl.JumpCtrl; assign BranchCtrl = ctrl.BranchCtrl; assign rs = ctrl.rs; assign rt = ctrl.rt; assign rd = ctrl.rd; // 读取寄存器文件 assign read_data1 = reg_file[rs]; assign read_data2 = reg_file[rt]; // 写入寄存器文件 always @ (posedge clk) begin if (rst) begin reg_file[0] <= 32'h00000000; reg_file[1] <= 32'h00000000; reg_file[2] <= 32'h00000000; reg_file[3] <= 32'h00000000; reg_file[4] <= 32'h00000000; reg_file[5] <= 32'h00000000; reg_file[6] <= 32'h00000000; reg_file[7] <= 32'h00000000; reg_file[8] <= 32'h00000000; reg_file[9] <= 32'h00000000; reg_file[10] <= 32'h00000000; reg_file[11] <= 32'h00000000; reg_file[12] <= 32'h00000000; reg_file[13] <= 32'h00000000; reg_file[14] <= 32'h00000000; reg_file[15] <= 32'h00000000; reg_file[16] <= 32'h00000000; reg_file[17] <= 32'h00000000; reg_file[18] <= 32'h00000000; reg_file[19] <= 32'h00000000; reg_file[20] <= 32'h00000000; reg_file[21] <= 32'h00000000; reg_file[22] <= 32'h00000000; reg_file[23] <= 32'h00000000; reg_file[24] <= 32'h00000000; reg_file[25] <= 32'h00000000; reg_file[26] <= 32'h00000000; reg_file[27] <= 32'h00000000; reg_file[28] <= 32'h00000000; reg_file[29] <= 32'h00000000; reg_file[30] <= 32'h00000000; reg_file[31] <= 32'h00000000; end else begin if (RegWrite) begin reg_file[rd] <= alu_out; end end end // ALU alu alu ( .a(alu_a), .b(alu_b), .ctrl(alu_ctrl), .out(alu_out) ); // 加法器 wire [31:0] adder_out; assign adder_out = read_data1 + read_data2; assign alu_a = read_data1; assign alu_b = read_data2; // 减法器 wire [31:0] sub_out; assign sub_out = read_data1 - read_data2; // 逻辑与门 wire [31:0] and_out; assign and_out = read_data1 & read_data2; // 逻辑或门 wire [31:0] or_out; assign or_out = read_data1 | read_data2; // 小于比较器 wire [31:0] slt_out; assign slt_out = (read_data1 < read_data2) ? 1 : 0; // 立即数扩展 wire [31:0] imm_ext; assign imm_ext = { {16{inst[15]}}, inst[15:0] }; // ALU 输入选择器 mux2x1 mux_alu_a ( .s(ALUSrc), .d0(read_data2), .d1(imm_ext), .q(alu_a) ); // 内存访问 always @ (posedge clk) begin if (rst) begin for (i = 0; i < 1024; i = i + 1) begin mem[i] <= 32'h00000000; end end else begin if (MemRead) begin mem_data <= mem[alu_out >> 2]; end else if (MemWrite) begin mem[alu_out >> 2] <= data_in; end end end // 数据存储器读写 mux2x1 mux_mem_data ( .s(MemtoReg), .d0(alu_out), .d1(mem_data), .q(data_out) ); // 立即数左移16位 wire [31:0] imm_left; assign imm_left = { {16{inst[15]}}, inst[15:0], {14'b0} }; // LUI 指令 assign alu_b = imm_left; // Jump 指令 assign alu_b = { inst[25:0], 2'b00 }; // Branch 指令 assign alu_b = imm_ext; // 九条基本运算指令 always @ (posedge clk) begin if (rst) begin // do nothing end else begin case (inst[31:26]) 6'b000000: begin case (inst[5:0]) 6'b100000: alu_b <= read_data2; // add 6'b100010: alu_b <= sub_out; // sub 6'b100100: alu_b <= and_out; // and 6'b100101: alu_b <= or_out; // or 6'b101010: alu_b <= slt_out; // slt endcase end 6'b001000: alu_b <= imm_ext; // addi 6'b001111: alu_b <= imm_left; // lui 6'b100011: MemRead <= 1; // lw 6'b101011: MemWrite <= 1; // sw default: alu_b <= 32'h00000000; endcase end end // beq 指令 assign Branch = (read_data1 == read_data2) ? 1 : 0; // bne 指令 assign Branch = (read_data1 != read_data2) ? 1 : 0; // j 指令 assign Jump = 1; endmodule ``` 这个Verilog HDL实现是一个简单的MIPS单周期CPU,它可以实现您所需的指令集。这个实现只是一个起点,您可以根据您的需求进行修改和扩展。 ### 回答2: MIPS(Microprocessor without Interlocked Pipeline Stages)是一种常用的指令集体系结构(ISA),用于设计和实现处理器。为了实现一个MIPS单周期CPU,可以使用Verilog HDL(硬件描述语言)进行编程。 首先,需要定义所需的寄存器和数据通路。这包括指令寄存器(Instruction Register,IR)、程序计数器(Program Counter,PC)、数据存储器(Data Memory)、立即数扩展单元、ALU(算术逻辑单元)等。可以使用Verilog代码定义这些模块,并建立相应的连接。 接下来,需要实现九条基本运算指令,包括lw(加载字)、sw(存储字)、lui(立即数装载高位)、beq(等于分支)、bne(不等于分支)、j(跳转)、addi(立即数加法)、和六个基本算术运算指令(如add、sub、and、or、slt、beq)。 对于lw指令,首先需要从指令中提取出目标寄存器和基地址寄存器,并将其送到地址计算器。地址计算器将基地址寄存器与立即数扩展单元输出的偏移量相加,然后将结果发送到数据存储器,读取存储器中的数据,并将其存储在目标寄存器中。 对于sw指令,类似地,需要从指令中提取目标寄存器和基地址寄存器,并将其发送到地址计算器。然后,将其输出与立即数扩展单元输出的偏移量相加,然后将目标寄存器的值存储在该地址处。 对于lui指令,需要从指令中提取出目标寄存器和立即数,并将该立即数的高16位扩展为32位,然后将其存储在目标寄存器中的高16位。 对于beq和bne指令,需要从指令中提取比较的两个寄存器,并将它们的值送入ALU执行相应的比较操作。根据比较结果,根据指令的偏移值分支到相应的地址。 对于j指令,从指令中提取跳转地址,并将其直接存储到程序计数器中,以实现无条件跳转。 对于addi以及其他算术运算指令,需要从指令中提取出源寄存器的值和立即数,并将它们传递给ALU。ALU将执行相应的算术或逻辑操作,并将结果存储在目标寄存器中。 通过实现上述指令,并在数据通路中建立相应的连线和控制信号,就可以实现一个基本的MIPS单周期CPU。然而,这只是一个基本的实现,仍然有许多改进空间,例如引入流水线以提高性能和加入异常处理等功能。 ### 回答3: MIPS单周期CPU是一种基于MIPS架构的中央处理单元,通过使用Verilog HDL编程语言可以实现其功能。在实现过程中,我们需要考虑指令的解析、逻辑电路的设计和控制信号的生成。 首先,我们需要设计一个指令解析模块,用于将指令按照不同的类型进行分类,并提取指令的操作码和操作数。在lw和sw指令中,我们需要进行内存的读写操作,因此需要设计一个内存模块。 对于lui指令,它是立即数装载高位指令,通过将16位的立即数左移16位后与0相加得到32位的结果,然后将结果存入对应的寄存器中。 对于beq和bne指令,它们是分支指令,需要根据条件判断是否进行跳转。我们需要设计一个分支控制电路来判断两个操作数是否相等,并生成相应的控制信号。 对于j指令,它是无条件跳转指令,直接将指令的目标地址存入程序计数器。 在实现九条基本运算指令时,我们需要设计一个算术逻辑单元(ALU)来进行运算,并将结果存入目标寄存器。同时,我们还需要设计一个寄存器堆来存储和读取寄存器中的数据。 除了上述指令外,我们还需要设计控制单元来生成各种控制信号,如读写控制信号、使能信号和时钟信号等,以确保指令按照正确的顺序执行。 综上所述,通过使用Verilog HDL编程语言,并结合指令解析模块、内存模块、分支控制电路、ALU和寄存器堆等,我们可以实现一个包含lw、sw、lui、beq、bne、j、addi等九条基本运算指令MIPS单周期CPU。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yorelee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值