精华内容
下载资源
问答
  • MIPS指令 千次阅读
    2021-12-26 09:30:44

    MIPS有三种指令格式:(所有指令都是32 位长)

    • R格式
    功能操作码6bit1源操作数寄存器5bit2源操作数寄存器5bit目标操作数寄存器(放结果)5bit偏移量5bit函数码6bit说明
    标记/指令opcodersrtrdshamtfunct
    位数31-2625-2120-1615-1110-65-0
    例:add000000rsrtrd00000100000rd=rs+rt
    例: jr000000rs000000000000000001000PC=rs

    算数类指令
    逻辑类指令
    位移类指令
    跳转指令

    • I格式
    功能操作码6bit源操作数寄存器5bit目标寄存器(放结果)5bit地址相对基址偏移量(立即数)16bit说明
    标记/指令opcodersrdaddress
    位数31-2625-2120-1615-0
    算数 addi001000rsrdimrd=rs+im
    逻辑 andi001100rsrdimrd=rs&im
    载入 lui00111100000rdimrt=im*65536
    跳转 beq000100rsrdimPC=(rs==rt)?PC+4+im<<2:PC
    • J格式
    功能操作码6bit目标地址26bit说明
    标记opcodetarget address
    j000010addrPC={(PC+4)[31,28],addr,00}
    jal000011addr$31=PC;PC={(PC+4)[31,28],addr,00}

    MIPS指令

    指令
    功能
    应用实例
    .align n将以2的n次方的对齐方式编译代码,这个位置就基于开始位置偏移,实现对二进制的指定位置编译
    dla 用来加载程序中某些带标号的位置或者变量的地址的宏指令,双字
    la (load address)用来加载程序中某些带标号的位置或者变量的地址的宏指令la $t0, var1拷贝var1 内存地址到t0;
    dli 装入双字立即数常数
    li (load immediate)装入立即数常数li register_destination, value
    lui 把立即数加载到寄存器高位;

    jal 绝对跳转指令,直接寻址方式 MIPS文档中描述为not PC-relative,即PC无关寻址。基地址+偏移
    bal 相对跳转指令,相对寻址以程序计数器PC的当前值(R15中的值)为基地址,进行偏移;MIPS文档中描述为PC-relative,即PC相关寻址

    cache 操作指令
    I格式:cache op,offset(base)
    执行op制定的cace操作,16位的offset经符号位扩展,添加到base寄存器以形成有效地址;
    cache指令中的op字段低两位[17:16]表示操作的是哪个cache

    2‘b00 I-L1(primary instruction)
    2'b01 D-L1(primary data or unified primary)
    2'b10    L3Cache(Tertiary)
    2'b11 L2Cache(secondary)
    

    cache指令中的op字段高三位[20:18]表示的是cache操作模式

    addi 加立即数指令,支持溢出检测,具体表现为:遇到溢出时,溢出错误标志变为高电平,传送到控制器cu中,导致此时的寄存器写使能信号regwr无效,最终结果为不将运算结果写入目的寄存器
    addiu是加立即数指令,不受溢出限制,具体表现为:遇到溢出时,对溢出的结果进行32bit求模,将求模结果写入目的寄存器中,因而不受溢出限制。
    忽略溢出,addi与addiu等价
    Add是将rs与rt中的值相加,结果存储在rd寄存器中。检测溢出。如果不溢出,将结果存储在rd寄存器中,如果溢出,抛出溢出异常,不改变rd中的值。
    Addu是将rs与rt中的值相加,结果存储在rd寄存器中。不检测溢出,如果溢出,不会抛出异常。
    忽略溢出,add与addu等价
    add是有符号数加法,addu是"无符号数"加法,但是对于cpu来说,都是一样的,不管有没有符号位,都是从最低位加,进位,一直到最高位。

    LB ( load byte)从存储器中读取一个字节的数据到寄存器中LB R1, 0(R2)
    lb register_destination, RAM_source

    LH从存储器中读取半个字的数据到寄存器中LH R1, 0(R2)

    LW (Load Word)从存储器中读取一个字的数据到寄存器中LW R1, 0(R2)数字是偏移量,可以正负
    从内存中 复制 RAM_source 的内容到 对应的寄存器中,'l’加载‘Load’ ‘w’意为’word’,即该数据大小为4个字节
    lw register_destination, RAM_source

    LD从存储器中读取双字的数据到寄存器中LD R1, 0(R2)

    L.S从存储器中读取单精度浮点数到寄存器中L.S R1, 0(R2)

    L.D从存储器中读取双精度浮点数到寄存器中L.D R1, 0(R2)

    LBU
    功能与LB指令相同,但读出的是不带符号的数据
    LBU R1, 0(R2)

    LHU
    功能与LH指令相同,但读出的是不带符号的数据
    LHU R1, 0(R2)

    LWU
    功能与LW指令相同,但读出的是不带符号的数据
    LWU R1, 0(R2)

    SB (store byte (low-order))把一个字节的数据从寄存器存储到存储器中SB R1, 0(R2)
    sb register_source, RAM_destination
    SH
    把半个字节的数据从寄存器存储到存储器中
    SH R1,0(R2)

    SW (store word存储一个字) 把一个字的数据从寄存器存储到存储器中SW R1, 0(R2) 数字是偏移量,可以正负
    sw register_source, RAM_destination

    SD
    把两个字节的数据从寄存器存储到存储器中SD R1, 0(R2)

    S.S
    把单精度浮点数从寄存器存储到存储器中
    S.S R1, 0(R2)

    S.D
    把双精度数据从存储器存储到存储器中
    S.D R1, 0(R2)

    DADD
    把两个定点寄存器的内容相加,也就是定点加
    DADD R1,R2,R3

    DADDI
    把一个寄存器的内容加上一个立即数
    DADDI R1,R2,#3

    DADDU
    不带符号的加
    DADDU R1,R2,R3

    DADDIU
    把一个寄存器的内容加上一个无符号的立即数
    DADDIU R1,R2,#3

    ADD.S
    把一个单精度浮点数加上一个双精度浮点数,结果是单精度浮点数
    ADD.S F0,F1,F2

    ADD.D
    把一个双精度浮点数加上一个单精度浮点数,结果是双精度浮点数
    ADD.D F0,F1,F2

    ADD.PS
    两个单精度浮点数相加,结果是单精度浮点数
    ADD.PS F0,F1,F2

    DSUB
    两个寄存器的内容相减,也就是定点数的减
    DSUB R1,R2,R3

    DSUBU
    不带符号的减
    DSUBU R1,R2,R3

    SUB.S
    一个双精度浮点数减去一个单精度浮点数,结果为单精度
    SUB.S F1,F2,F3

    SUB.D
    一个双精度浮点数减去一个单精度浮点数,结果为双精度浮点数
    SUB.D F1,F2,F3

    SUB.PS
    两个单精度浮点数相减
    SUB.SP F1,F2,F3

    DDIV
    两个定点寄存器的内容相除,也就是定点除
    DDIV R1,R2,R3

    DDIVU
    不带符号的除法运算
    DDIVU R1,R2,R3

    DIV.S
    一个双精度浮点数除以一个单精度浮点数,结果为单精度浮点数
    DIV.S F1,F2,F3

    DIV.D
    一个双精度浮点数除以一个单精度浮点数,结果为双精度浮点数
    DIV.D F1,F2,F3

    DIV.PS
    两个单精度浮点数相除,结果为单精度
    DIV.PS F1,F2,F3

    DMUL
    两个定点寄存器的内容相乘,也就是定点乘
    DMUL R1,R2,R3

    DMULU
    不带符号的乘法运算
    DMULU R1,R2,R3

    MUL.S
    一个双精度浮点数乘以一个单精度浮点数,结果为单精度浮点数
    DMUL.S F1,F2,F3

    MUL.D
    一个双精度浮点数乘以一个单精度浮点数,结果为双精度浮点数
    DMUL.D F1,F2,F3

    MUL.PS
    两个单精度浮点数相乘,结果为单精度浮点数
    DMUL.PS F1,F2,F3

    AND
    与运算,两个寄存器中的内容相与
    ANDR1,R2,R3

    ANDI
    一个寄存器中的内容与一个立即数相与
    ANDIR1,R2,#3

    OR
    或运算,两个寄存器中的内容相或
    ORR1,R2,R3

    ORI
    一个寄存器中的内容与一个立即数相或
    ORIR1,R2,#3

    XOR
    异或运算,两个寄存器中的内容相异或
    XORR1,R2,R3

    XORI
    一个寄存器中的内容与一个立即数异或

    XORIR1,R2,#3

    ‘b’ (Branches) 流控制,分支(if else系列)

    b	target		#  unconditional branch to program label target
    beq	$t0,$t1,target	#  branch to target if  $t0 = $t1
    blt	$t0,$t1,target	#  branch to target if  $t0 < $t1
    ble	$t0,$t1,target	#  branch to target if  $t0 <= $t1
    bgt	$t0,$t1,target	#  branch to target if  $t0 > $t1
    bge	$t0,$t1,target	#  branch to target if  $t0 >= $t1
    bne	$t0,$t1,target	#  branch to target if  $t0 <> $t1
    

    BEQZ条件转移指令,当寄存器中内容为0时转移发生BEQZ R1,target
    BNEZ条件转移指令,当寄存器中内容不为0时转移发生BNEZ R1, target
    BEQ条件转移指令,当两个寄存器内容相等时转移发生BEQ R1,R2 target
    if(R1==R2) 跳转到target,target=PC <- PC+4 + (sign-extend)immediate<<2
    BNE 条件转移指令,当两个寄存器中内容不等时转移发生BNE R1,R2 target
    if(R1!=R2) 跳转到target,target=PC <- PC+4 + (sign-extend)immediate<<2

    汇编中的while1

    1:
     b 1b
     nop
    

    0~9的数字为局部标签。局部标签可以重复使用,语法为:
    xf:往前跳的意思,就是还未执行的程序,x代表0~9的某个标签,f代表forward的意思。
    xb:往后跳的意思,回到原来已经执行过的语句,x代表0~9的某个标签,b代表backward的意思。

    1: ;A
    #cmp r0, #0
    beqz r0, 1f ;    @  r0==0那么向前跳转到B处执行
    nop
    
    bnez r0,1b ;   @ 否则向后跳转到A处执行
    nop
    1: ;B
    

    J (Jumps)直接跳转指令,跳转的地址在指令中,跳转(while, for, goto系列)jtarget

    JR (jump and link)使用寄存器的跳转指令,跳转地址在寄存器中JR R1

    JAL

    直接跳转指令,并带有链接功能,指令的跳转地址在指令中,跳转发生时要把返回地址存放到R31这个寄存器中

    JAL R1 name

    JALR

    使用寄存器的跳转指令,并且带有链接功能,指令的跳转地址在寄存器中,跳转发生时指令的放回地址放在R31这个寄存器中

    JALR R1

    move 寄存器复制到另一个寄存器move t 2 , t2, t2,t3 --> $t2 = $t3

    MOV.S把一个单精度浮点数从一个浮点寄存器复制到另一个浮点寄存器MOV.S F0,F1

    MOV.D

    把一个双精度浮点数从一个浮点寄存器复制到另一个浮点寄存器
    MOV.D F0,F1

    • MFC0 把一个数据从特殊寄存器复制到通用寄存器 MFC0 R1,R2
      mfc0 rt, rd将CP0中的rd寄存器内容传输到rt通用寄存器;
    • MTC0把一个数据从通用寄存器复制到特殊寄存器 MTC0 R1,R2
      mtc0 rt, rd 将rt通用寄存器中内容传输到CP0中寄存器rd;
    • MFC1 把一个数据从定点寄存器复制到浮点寄存器 MFC1 R1,F1
    • MTC1 把一个数据从浮点寄存器复制到定点寄存器MTC1 R1,F1
      mfhi/mflo rt将CP0的hi/lo寄存器内容传输到rt通用寄存器中;
      mthi/mtlo rt将rt通用寄存器内容传输到CP0的hi/lo寄存器中;
      当MIPS体系结构演进到MIPS IV的64位架构后,新增了两条指令dmfc0和dmtc0,向CP0的寄存器中读/写一个64bit的数据。

    LUI

    把一个16位的立即数填入到寄存器的高16位,低16位补零

    LUI R1,#42

    DSLL

    双字逻辑左移

    DSLL R1,R2,#2

    DSRL

    双字逻辑右移

    DSRL R1,R2,#2

    DSRA

    双字算术右移

    DSRA R1,R2,#2

    DSLLV

    可变的双字逻辑左移

    DSLLV R1,R2,#2

    DSRLV可变的双字罗伊右移DSRLV R1,R2,#2

    DSRAV可变的双字算术右移DSRAV R1,R2,#2

    SLT如果R2的值小于R3,那么设置R1的值为1,否则设置R1的值为0SLT R1,R2,R3

    SLTI如果寄存器R2的值小于立即数,那么设置R1的值为1,否则设置寄存器R1的值为0SLTI R1,R2,#23

    SLTU功能与SLT一致,但是带符号的SLTU R1,R2,R3

    SLTUI功能与SLT一致,但不带符号SLTUI R1,R2,R3

    MOVN如果第三个寄存器的内容为负,那么复制一个寄存器的内容到另外一个寄存器MOVN R1,R2,R3

    MOVZ如果第三个寄存器的内容为0,那么复制一个寄存器的内容到另外一个寄存器MOVZ R1,R2,R3

    TRAP根据地址向量转入管态

    ERET从异常中返回到用户态

    MADD.S一个双精度浮点数与单精度浮点数相乘加,结果为单精度

    MADD.D一个双精度浮点数与单精度浮点数相乘加,结果为双精度

    MADD.PS两个单精度浮点数相乘加,结果为单精度

    更多相关内容
  • 【计组实验】P3 Verilog多周期处理器开发 MIPS指令

    万次阅读 多人点赞 2019-05-07 22:36:14
    a) MIPS-Lite2={MIPS-Lite1,lbsb}。 b) MIPS-Lite1={addu,subu,ori,lw,sw,beq,j,lui,addi,addiu,slt,jal,jr }。 c) 所有运算类指令均可以不支持溢出。 处理器为多周期设计。 控制信号 各控制信号...

    全部代码+测试指令:

    链接:CPUMultiCycle p3正确运行_百度网盘
    提取码:gmzb

    (实验P1P2)Logisim电路图以及Verilog单周期处理器设计,见以下博客:

    【计算机组成原理课设】logisim完成单周期处理器开发 MIPS指令集

    【完结】Modelsim Verilog单周期处理器开发 MIPS指令集

    实验要求

    1. 处理器应 MIPS-Lite2 指令集。
      a) MIPS-Lite2={MIPS-Lite1,lb,sb}。
      b) MIPS-Lite1={addu,subu,ori,lw,sw,beq,j,lui,addi,addiu,slt,jal,jr }。
      c) 所有运算类指令均可以不支持溢出。
    2. 处理器为多周期设计。

    笔记
    在这里插入图片描述

    控制信号

    各控制信号含义
    在这里插入图片描述
    控制信号真值表
    在这里插入图片描述

    状态转移图

    在这里插入图片描述

    测试指令

    34100001 34110003 34080001 340cabab 3c0d000a 00102021 00082821
    0c000c33 00028021 02288823 1211fffa 34080004 3c017fff 3421ffff
    0001c021 27090003 270a0005 23160006 ad09fffc ad010000 810e0003
    a10c0007 8d0f0004 a104fffd 8112ffff 00082021 00092821 0c000c33
    0148c82a 13200018 0184a02a 12800001 3c0cffff 34000001 3c13efef
    3c01abab 3421cdcd 00011821 24640002 20770005 0c000c33 00024021
    00082021 00092821 0c000c33 00024821 01004821 3c0a0069 11090001
    1000fff4 08000c37 00851021 21801234 03e00008 201a5678

    答案:mars运行结果
    在这里插入图片描述
    verilog波形
    在这里插入图片描述

    全部代码

    mips.v

    `timescale 1ns/1ns
    
    module mips(
    			 input clk,
    			 input reset
    			);
    	//以下是每个模块的输出
    	//Control控制单元
    	wire [1:0]RegDst;		//写寄存器的目标寄存器号来源:0-rt 1-rd
    	wire ALUSrc;			//第二个ALU操作数的来源 :0-读寄存器2 1-指令低16位的符号拓展
    	wire [1:0] MemToReg;	//写入寄存器的数据来源
    	wire RegWrite;			//寄存器写使能有效
    	wire MemWrite;			//将数据写入到指定存储器单元中
    	wire PCSrc;				//判断是否执行分支(PC+4 or PC+4+offset)
    	wire [1:0]ExtOp;		//控制Extender的拓展方式
    	wire [2:0]ALUctr;		//3bit 控制ALU的运算
    	wire j_ctr;				//控制PC是否转移到J指令指示的地址
    	wire jr_ctr;			//jr指令
    	wire DMSrc;
    	wire [15:0]immediate16;//address
    	wire [25:0]immediate26;//J指令 26位跳转地址
    	wire NPC_ctr;
    	wire sb_ctr;
    	
    	//IM指令存储器
    	wire [31:0] imout;
    	wire [5:0] opcode;
    	wire [4:0] rs;
    	wire [4:0] rt;
    	wire [4:0] rd;
    	wire [5:0] shamt;
    	wire [5:0] func;
    	
    	//GPR寄存器堆
    	wire [31:0] rd1;		//读出的数据1
    	wire [31:0] rd2;		//读出的数据2
    	
    	//DM数据存储器
    	wire [31:0] dout;		//读出的数据
    	wire [31:0] lbout;		//lb读出的数据
    	
    	//ALU算数逻辑单元
    	wire [31:0] Result;
    	wire zero;
    	wire overflow;
    	
    	//Ext拓展单元
    	wire [31:0] EXTout;
    	
    	//MUX多路选择器
    	wire [31:0] DataToDM;
    	wire [31:0] DataToALU;
    	wire [31:0] RegWriteData;
    	wire [4:0] RegWriteAddress;
    	
    	//NPC
    	wire [31:0] NPC;		//下一条指令的地址
    	wire [31:0] jalPC;		//jal跳转的地址
    	
    	//PC
    	wire [31:0] PC;			//PC输出下一条指令的地址
    	
    	//DataLate延迟模块
    	wire [31:0] rd1_late;		//寄存器读数据1
    	wire [31:0] rd2_late;		//寄存器读数据2
    	wire [31:0] Result_late;	//ALU结果
    	wire overflow_late;			//ALU溢出标志位
    	
    	wire[31:0]imout_late;		//取出的整条指令
    	wire[5:0]opcode_late;		//输出 6位操作码op
    	wire[4:0]rs_late;			//输出 源1
    	wire[4:0]rt_late;			//输出 源2
    	wire[4:0]rd_late;			//输出 目的
    	wire[4:0]shamt_late;		//只用于移位指令sll
    	wire[5:0]func_late;			//指出ALU的功能
    	wire[15:0]immediate16_late;	//beq的低16位分支地址
    	wire[25:0]immediate26_late;	//J指令的低26位跳转地址
    	
    	wire j_ctr_late;
    	wire jr_ctr_late;
    	wire PCSrc_late;
    	wire [31:0] dout_late;		//读出的数据
    	wire [31:0] lbout_late;		//读出的数据
    	
    	//以下是各个模块的实例化 就是用导线把他们接在一起 注意函数参数要与上方的输出相同 否则会出现变量未定义的问题
    	control Control(clk,opcode[5:0],zero,func[5:0],overflow_late,RegDst[1:0],ALUSrc,MemToReg[1:0],RegWrite,MemWrite,PCSrc,ExtOp[1:0],ALUctr[2:0],j_ctr,jr_ctr,sb_ctr,DMSrc,NPC_ctr);
    						/*opcode[5:0],
    						zero,				//beq指令
    						func[5:0],
    						overflow,
    						
    						RegDst[1:0],		//写寄存器的目标寄存器号来源:0-rt 1-rd
    						ALUSrc,		//第二个ALU操作数的来源
    						MemToReg[1:0],	//写入寄存器的数据来源
    						RegWrite,	//寄存器写使能有效
    						MemWrite,	//将数据写入到指定存储器单元中
    						PCSrc,		//判断是否执行分支(PC+4 or PC+4+offset)
    						ExtOp[1:0],		//控制Extender的拓展方式
    						ALUctr[2:0],		//3bit 控制ALU的运算
    						j_ctr,		//控制PC是否转移到J指令指示的地址
    						jr_ctr,		//jr指令
    						DMSrc
    						*/
    	DataLate_J_JR late_J(j_ctr, clk, j_ctr_late);
    	DataLate_J_JR late_JR(jr_ctr, clk, jr_ctr_late);
    	DataLate_J_JR late_PCSrc(PCSrc, clk, PCSrc_late);
    					
    	im_4k im		(PC[11:2],imout[31:0],opcode[5:0],rs[4:0],rt[4:0],rd[4:0],shamt[4:0],func[5:0],immediate16[15:0],immediate26[25:0]);
    						/*
    						input [11:2]addr,//与上一个模块以导线相连 10bit 要取出的指令地址
    			
    						output reg[31:0]dout,//取出的整条指令
    						output reg[5:0]op,//输出 6位操作码op
    						output reg[4:0]rs,//输出 源1
    						output reg[4:0]rt,//输出 源2
    						output reg[4:0]rd,//输出 目的
    						output reg[4:0]shamt,//只用于移位指令sll
    						output reg[5:0]func,//指出ALU的功能
    						output reg[15:0]immediate16,//beq的低16位分支地址
    						output reg[25:0]immediate26//J指令的低26位跳转地址
    						*/
    	DataLateIM dlIM	(clk,imout[31:0],opcode[5:0],rs[4:0],rt[4:0],rd[4:0],shamt[4:0],func[5:0],immediate16[15:0],immediate26[25:0],imout_late[31:0],opcode_late[5:0],rs_late[4:0],rt_late[4:0],rd_late[4:0],shamt_late[4:0],func_late[5:0],immediate16_late[15:0],immediate26_late[25:0]);
    						/*
    						input clk,
    
    						input[31:0]dout,//取出的整条指令
    						input[5:0]op,//输出 6位操作码op
    						input[4:0]rs,//输出 源1
    						input[4:0]rt,//输出 源2
    						input[4:0]rd,//输出 目的
    						input[4:0]shamt,//只用于移位指令sll
    						input[5:0]func,//指出ALU的功能
    						input[15:0]immediate16,//beq的低16位分支地址
    						input[25:0]immediate26//J指令的低26位跳转地址
    
    						
    						output reg[31:0]imout_late,//取出的整条指令
    						output reg[5:0]op_late,//输出 6位操作码op
    						output reg[4:0]rs_late,//输出 源1
    						output reg[4:0]rt_late,//输出 源2
    						output reg[4:0]rd_late,//输出 目的
    						output reg[4:0]shamt_late,//只用于移位指令sll
    						output reg[5:0]func_late,//指出ALU的功能
    						output reg[15:0]immediate16_late,//beq的低16位分支地址
    						output reg[25:0]immediate26_late//J指令的低26位跳转地址
    						*/
    			
    	GPR gpr			(PC,clk,reset,RegWrite,rs[4:0],rt[4:0],RegWriteAddress,RegWriteData,rd1[31:0],rd2[31:0]);
    						/*
    						WPC[31:0],		//writePC
    						clk,
    						reset,
    						RegWrite,		//寄存器写使能
    						ra1[4:0],		//ReadAddress1
    						ra2[4:0],		//ReadAddress2
    						wa[4:0],			//RegWriteAddress
    						wd[31:0],			//WriteData
    						
    						rd1[31:0],		//ReadData1
    						rd2[31:0]			//ReadData2
    						*/
    					
    	DataLate32A ADR(rd1[31:0], clk, rd1_late[31:0]);
    	DataLate32B BDR(rd2[31:0], clk, rd2_late[31:0]);
    					
    	dm_4k dm		(Result_late[11:0],Result_late[11:2],rd2[31:0],MemWrite,sb_ctr,clk,dout[31:0],lbout[31:0]);
    						/*
    						input [11:0]addr12,	//lb指令专用
    						input [11:2]addr,	//(sw指令)由Result[11:2](ALU基地址+偏移的计算结果)输入,表示要写入的数据单元的地址
    						input [31:0]din,	//datain
    						input we,			//MemWrite数据存储器写使能
    						input clk,
    									
    						output [31:0] dout,			//dataout 取出的数据
    						output reg [31:0] lbout		//dataout lb专用
    						*/
    	DataLate32RAM dlRAM(dout[31:0], clk, dout_late[31:0]);
    	DataLate32RAMLB dlRAMLB(lbout[31:0], clk, lbout_late[31:0]);
    					
    					
    	ALU Alu			(rd1[31:0],DataToALU,5'b0,ALUctr[2:0],Result[31:0],zero,overflow);
    						/*
    						A[31:0],				//输入32位A
    						B[31:0],				//输入32位B
    						sl[4:0],				//左移的位数 sl是移位指令 没用
    						ALUctr[2:0],			//3位ALU控制信号
    						
    						Result[31:0],			//因为要在always里面赋值,所以定义为reg
    						zero			//零标志位
    						*/
    						
    	DataLate32ALU ALUout(Result[31:0], clk, Result_late[31:0]);	
    	DataLate1 OverFlowLate(overflow, clk, overflow_late);	
    					
    	EXT ext			(immediate16[15:0],ExtOp[1:0],EXTout[31:0]);
    						/*
    						EXTin[15:0],			//输入EXT的数据
    						ExtOp[1:0],
    						
    						EXTout[31:0]			//拓展后输出的数据
    						*/
    	
    	//计算参与运算的ALU数据2
    	mutiplexer32_1 MuxDataToALU(ALUSrc,rd2[31:0],EXTout,DataToALU);
    						/*
    						control,//ALU数据2来源:0-读寄存器2 1-指令低16位的符号拓展
    						din0 [31:0],	//读寄存器2
    						din1 [31:0],	//指令低16位的符号拓展
    						
    						out32_1 [31:0]
    						*/
    					
    	
    	//RegWriteData的计算
    	mutiplexer32_2 mux32_2(MemToReg,Result_late,dout_late,jalPC,lbout_late,RegWriteData);//MUX RegWriteData
    					/*
    						control [1:0], //MemToReg: 00ALU运算结果(包含高16拓展\普通运算) 01数据存储器的输出 10jalPC JAL跳转的PC+4 用于RegWriteData的计算
    						din0 [31:0],	//00 ALU运算结果(包含高16拓展\普通运算)
    						din1 [31:0],	//01 DM输出
    						din2 [31:0],	//10 jalPC JAL跳转的PC+4
    						din3 [31:0],	//11 lb
    							
    						out32_2 [31:0]
    					*/
    	
    	//RegWriteAddress的计算	
    	mutiplexer5	mux5(RegDst,rt,rd,5'b11111,RegWriteAddress);
    						/*
    						control [1:0],//Regdst写寄存器的目标寄存器号来源 0-rt 1-rd
    						din0 [4:0],	//rt lui应该写到rt
    						din1 [4:0],	//rd
    						din2 [4:0],	//5'b11111 31号寄存器
    						
    						out5 [4:0]
    						*/
    	
    	NextPC npc		(NPC_ctr,PC[31:0],immediate16[15:0],immediate26[25:0],rd1[31:0],PCSrc_late,j_ctr_late,jr_ctr_late,NPC[31:0],jalPC[31:0]);
    						/*
    						PC[31:0],
    						imm16[15:0],			//beq
    						imm26[25:0],			//j,jal
    						rsd[31:0],				//jr跳转至寄存器 对应接口是rd1[31:0] 是从寄存器读出的第一个数据
    						branch_ctr,
    						j_ctr,
    						jr_ctr,
    						
    						NPC[31:0],			//下一条指令的地址
    						jalPC[31:0]			//jal跳转的地址
    						*/
    					
    				
    	 Pc mypc		(NPC_ctr,NPC[31:0],clk,reset,PC[31:0]);
    						/*
    						NPC[31:0],			//NextPC计算单元
    						clk,			//时钟信号
    						reset,			//复位
    						PC[31:0]				//输出下一条指令的地址
    						*/
    endmodule
    	
    

    gpr.v

    //GPR寄存器堆
    `timescale 1ns / 1ns
    //注意组合逻辑(always敏感列表)用=赋值,时序逻辑(clk控制)用<=赋值
    module GPR(
    			input [31:0] WPC,	//由PC输入 writePC 仅用于控制台输出
    			input clk,
    			input reset,
    			input RegWrite,		//写使能
    			
    			input [4:0] ra1,	//ReadAddress1
    			input [4:0] ra2,	//ReadAddress2
    			input [4:0] wa,		//WriteAddress
    			
    			input [31:0]wd,		//WriteData
    			
    			output [31:0] rd1,	//ReadData1
    			output [31:0] rd2	//ReadData2
    		);
    		
    	reg[31:0] regfile[31:0];	//定义32个 32-bit寄存器
    	
    	assign rd1 = regfile[ra1];	//第一个读出的数据
    	assign rd2 = regfile[ra2];	//第二个读出的数据
    	
    	integer i;
    	
    	initial
    		begin
    			for(i = 0; i < 32; i = i + 1) regfile[i] <= 0;
    		end
    		
    	//时序逻辑
    	always@(negedge clk, posedge reset)
    		begin
    			if(reset)
    				begin
    					for(i = 0; i < 32; i = i + 1) regfile[i] <= 0;
    				end
    			else if(RegWrite==1 && wa != 0)//wa就是WriteAddress 不能写0号寄存器
    				begin
    					regfile[wa] <= wd;//写入数据
    					$display($time,,"(Write GPR)PC%b: WriteAddress%b <= Data%b", WPC, wa, wd);
    					#1
    					begin
    						$display($time,,"(Show GPR)");
    						for(i = 0; i < 32; i = i + 1)
    						begin
    							$display($time,,"GPR%d = %h", i, regfile[i]);
    						end
    					end
    				end
    			else if(RegWrite==0)//写使能无效
    				begin
    					$display($time,,"(GPR)Write:false");
    				end
    			else//写使能有效但是寄存器地址为0
    				begin
    					$display($time,,"(GPR)DEFAULT:You CANNOT Write To GPR Address 0x00000000");
    				end
    		end		
    endmodule
    

    im.v

    `timescale 1ns/1ns
    //IM 指令存储器 与上一个模块以导线相连wire输入 将本模块数据输出到下一个模块reg输出
    module im_4k(
    			input [11:2]addr,//与上一个模块以导线相连 10bit 要取出的指令地址
    			
    			output reg[31:0]dout,//取出的整条指令
    			
    			output reg[5:0]op,//输出 6位操作码op
    			output reg[4:0]rs,//输出 源1
    			output reg[4:0]rt,//输出 源2
    			output reg[4:0]rd,//输出 目的
    			output reg[4:0]shamt,//只用于移位指令sll
    			output reg[5:0]func,//指出ALU的功能
    			
    			output reg[15:0]immediate16,//beq的低16位分支地址
    			output reg[25:0]immediate26//J指令的低26位跳转地址
    		);
    
    	reg [31:0]im[1023:0];//im存储读取的所有指令 2^10
    	
    	wire [11:2]address;//因为im一共只有2^10,所以address的位宽为10
    	assign address=addr;//按字节寻址,address自动取addr的左移两位 实际超出去没关系 im一共只有2^10,地址只取[11:2]这段 虽然PC复位后初值为0x0000_3000与MARS的Memory Configuration相配合 但是实际寻址时,超出部分被忽略
    	
    	integer i;
    	
    	initial begin
    		$readmemh("p3-test.txt",im);//从文件中读取指令 格式:$readmemb("<数据文件名>",<存贮器名>,<起始地址>);
    		begin
    			$display("(IM)Finish Read File",im);
    			for(i=0;i<128;i=i+1)$write("%h ", im[i]);
    			$write("\n");//换行专用
    		end
    		
    	end
    	
    	always @(*)//分离输出指令的各个部分
    		begin
    			op=im[address][31:26];
    			rs=im[address][25:21];
    			rt=im[address][20:16];
    			rd=im[address][15:11];
    			shamt=im[address][10:6];
    			func=im[address][5:0];
    			
    			immediate16=im[address][15:0];
    			immediate26=im[address][25:0];
    			
    			$display($time,,"(IM)Current Code:%b %b %b %b %b %b ; TheAddrOfCurCode = %b ",op,rs,rt,rd,shamt,func,addr);
    		end
    	
    endmodule	
    
    			
    

    mips_tb.v

    //用于测试整个处理器
    `timescale 1ns/1ns		//仿真时间单位/时间精度
    `include "mips.v"
    
    module mips_tb;
    	reg clk;
    	reg reset;
    	
    	mips mymips(clk, reset);
    	
    	always #10 clk = ~clk;
    	
    	initial clk = 0;
    	
    	initial
    		begin
    		$monitor($time,,"(mips_tb)clk = %b",clk);
    		$display("display is Valid");
    		#4500
    		$finish;
    		end
    		
    endmodule
    

    multiplexer

    //多路选择器
    
    `timescale 1ns / 1ns
     //注意组合逻辑(敏感列表)用=赋值,时序逻辑(clk控制)用<=赋值
     
    //二选一 32bit
    module mutiplexer32_1(
    						input control,
    							
    						input [31:0] din0,
    						input [31:0] din1,
    							
    						output reg [31:0] out32_1
    					);
    	always @(*)
    		begin
    			if(control==0) out32_1 = din0;
    			else out32_1 = din1;
    			$display($time,,"(MUX DataTo DM/ALU)control = %b, din0 = %b, din1 = %b, out = %b", control, din0, din1, out32_1);
    		end
    endmodule
    
    
    //四选一 32bit  00ALU运算结果 01数据存储器的输出 10JAL跳转的目的地址 11没用
    module mutiplexer32_2(
    						input [1:0]control,	//MemToReg
    							
    						input [31:0] din0,	//00 ALU运算结果(包含高16拓展\普通运算)
    						input [31:0] din1, 	//01 数据存储器的输出
    						input [31:0] din2,	//10 jalPC JAL跳转的PC+4
    						input [31:0] din3,	//11 将lb指令从RAM中取出的数据存储到Reg
    							
    						output reg [31:0] out32_2
    					);
       always @(*)
    		begin
    			if(control==0) 			out32_2=din0;
    			else if(control==2'b01) out32_2=din1;	//lw
    			else if(control==2'b10) out32_2=din2;
    			else if(control==2'b11) out32_2=din3;	//lb
    			$display($time,,"(MUX RegWriteData)control = %b, din0 = %b, din1 = %b, din2 = %b, din3 = %b, out = %b", control, din0, din1, din2, din3, out32_2);
    		end
    endmodule
    
    
    //3选1 5bit
    module mutiplexer5	(
    						input [1:0] control, //Regdst写寄存器的目标寄存器号来源 00:rt (lui) 01:rd 10:5'b11111 reg31
    						
    						input [4:0] din0,	//00:rt (lui)
    						input [4:0] din1,	//01:rd
    						input [4:0] din2,	//10:5'b11111 31号寄存器
    						
    						output reg [4:0] out5
    					);
    	 always @(*)
    		begin
    			if(control==0) 			out5=din0;
    			else if(control==2'b01) out5=din1;
    			else if(control==2'b10) out5=din2;
    			$display($time,,"(MUX RegWriteAddr)control = %b, din0 = %b, din1 = %b, out = %b", control, din0, din1, out5);
    		end
    endmodule
    
    

    npc.v

    `timescale 1ns / 1ns
    //计算下一条指令的地址
    //输入的PC是当前指令的地址
    //输出的NPC是下一条指令的地址
    //计算机组成原理与设计P47:MIPS是按字节编址的,所以字的起始地址必须是4的倍数 lw sw的偏移地址单位是字节,即字*4
    module NextPC
    		(
    			input NPC_ctr,			//控制PC是否更改
    			input [31:0] PC,	//当前指令的地址
    			input [15:0] imm16,	//当前指令的16位立即数,用于beq指令的分支地址
    			input [25:0] imm26,	//j,jal
    			input [31:0] rsd,	//(jr指令)由rd1[31:0]输入,通常是从31号寄存器中读出的数据
    			input branch_ctr,	//PCSrc 判断是否执行分支(PC+4 or PC+4+offset)
    			input j_ctr,		//判断当前指令是否是J指令
    			input jr_ctr,
    			
    			output reg [31:0] NPC,	//下一条指令的地址
    			output [31:0] jalPC		//jal跳转的地址 JAL跳转的下一条地址是PC+4将要存在reg31z中 用于RegWriteData的计算
    		);
    	reg [31:0]imm16ex;
    	assign jalPC = PC + 4;
    		
    	always @(NPC_ctr or j_ctr or jr_ctr)
    	begin
    		if(NPC_ctr)
    			begin
    				begin
    					if(j_ctr) NPC = {PC[31:28], imm26, 2'b0};	//j jal指令 需要将imm左移两位才是要跳转的地址
    					else if(jr_ctr) NPC = rsd;					//jr指令
    					else if(branch_ctr)							//beq指令,branch判断是否执行分支 拓展时需要将imm16左移两位
    						begin
    							if(imm16[15]==0)		imm16ex={14'b00000000000000,imm16[15:0],2'b0};  //正数手动符号拓展
    							else if(imm16[15]==1)	imm16ex={14'b11111111111111,imm16[15:0],2'b0};	//负数手动符号拓展
    							NPC = PC + 4 + imm16ex;
    							$display($time,,"(NPC beq)imm16:%b imm16ex:%b PC+4:%b",imm16,imm16ex,PC + 4);
    						end
    				else NPC = PC  + 4;
    				$display($time,,"(NPC)CurPCAddr:%b NextPcAddr:%b j_ctr=%b jr_ctr=%b ",PC,NPC,j_ctr,jr_ctr);
    			end
    		end
    	end
    		
    endmodule
    

    pc.v

    //输入:NPC代表的下一条指令的地址
    //输出:"下一条指令" 即 "当前指令"。如果不reset,则根据clk输出当前指令PC的地址
    `timescale 1ns / 1ns
    module Pc(	
    			input NPC_ctr,			//控制PC是否更改
    			input [31:0] NPC,		//NextPC计算单元
    			input clk,				//时钟信号
    			input reset,			//复位
    			output reg [31:0] PC	//输出下一条指令的地址
    		);
    		
    	initial
    		begin
    			PC <= 32'h0000_3000;		//PC复位后初值为0x0000_3000,目的是与MARS的Memory Configuration相配合
    		end
    		
    	always@(posedge clk, posedge reset)//任何一个变动都可以触发
    		begin
    			if(NPC_ctr)
    				begin
    					if(reset) PC <= 32'h0000_3000;//PC复位后初值为0x0000_3000,目的是与MARS的Memory Configuration相配合
    					else PC <= NPC;
    					$display($time,,"(PC)NextPcAddr%b",PC);
    				end
    		end
    endmodule
    

    alu.v

    `timescale 1ns / 1ns
    /*
     *	Module Name:	ALU
     *	File Created:	2019-4-4 16:11:12
     *	Notes: overflow是溢出标志位
     *  
     ALUOp
    3'b000: Result=($signed(A)<$signed(B))?32'h0000_0001:32'h0000_0000;//若A<B,则result=1
    3'b001: Result=A|B;//或
    3'b010: Result=A+B;//加
    3'b011: Result={B[15:0],16'h0000};//拓展到高16位
    3'b101: Result=B<<sl;//逻辑左移
    3'b110: Result=A-B;//减
    3'b111: Result = 32'h0000_0000//default
     */
     
     //注意组合逻辑(敏感列表)用=赋值,时序逻辑(clk控制)用<=赋值
     
     module ALU(	input [31:0] A,				//输入32位A
    				input [31:0] B,				//输入32位B
    				input [4:0]	sl,				//左移的位数
    				input [2:0] ALUctr,			//3位ALU控制信号
    				output reg[31:0] Result,	//因为要在always里面赋值,所以定义为reg
    				output reg zero,			//零标志位
    				
    				output reg overflow			//溢出标志位 addi加立即数支持溢出,如果溢出,(CTRL)让寄存器写使能无效
    			);
    			
    	initial overflow = 0;
    	
    	always@(*)			//(*)表示自动添加敏感列表
    		begin
    			case(ALUctr)//决定ALU操作
    				3'b000: //若A<B,则result=1
    					begin
    						Result=($signed(A)<$signed(B))?32'h0000_0001:32'h0000_0000;
    						$display($time,,"(ALU)(if a<b,res=1)A = %b, B = %b, Result = %b", A, B, Result);
    					end
    				
    				3'b001:	//或
    					begin
    						Result=A|B;
    						$display($time,,"(ALU)(or)A = %b, B = %b, Result = %b", A, B, Result);
    					end
    				
    				3'b010:	//加
    					begin
    						Result=A+B;
    						if((A[31]==1'h0&&B[31]==1'h0&&Result[31]==1'h1) || (A[31]==1'h1&&B[31]==1'h1&&Result[31]==1'h0))
    							begin
    								overflow=1'h1;//正溢/负溢
    								$write("OVERFLOW!\n");
    							end
    						else overflow=1'h0;//如果没有这一句 会产生锁存器?反正会有明明不溢出却被判断溢出的bug
    						$display($time,,"(ALU)(add)A = %b, B = %b, Result = %b A[31]=%b B[31]=%b overflow=%b", A, B, Result,A[31],B[31],overflow);
    					end
    				
    				3'b011:	//拓展到高16位 lui
    					begin
    						Result={B[15:0],16'h0000};
    						$display($time,,"(ALU)(16toUp32)A = %b, B = %b, Result = %b", A, B, Result);
    					end
    				
    				3'b101: //逻辑左移
    					begin
    						Result=B<<sl;
    						$display($time,,"(ALU)(<<)A = %b, B = %b, Result = %b", A, B, Result);
    					end
    				
    				3'b110:	//减
    					begin
    						Result=A-B;
    						$display($time,,"(ALU)(sub)A = %b, B = %b, Result = %b", A, B, Result);
    					end
    				
    				default:
    					begin
    						Result<=32'h0000_0000;
    						$display($time,,"(ALU)(DEFAULT)");
    					end
    				
    			endcase
    			
    			if(A==B) zero=1;//计算零标志位
    			else zero=0;
    			
    		end
    	
    endmodule
    			
    

    crtl

    //控制单元controller
    `timescale 1ns/1ns
    /*
    ALUOp
    3'b000: Result=($signed(A)<$signed(B))?32'h0000_0001:32'h0000_0000;//若A<B,则result=1
    3'b001: Result=A|B;//或
    3'b010: Result=A+B;//加
    3'b011: Result={B[15:0],16'h0000};//拓展到高16位
    3'b101: Result=B<<sl;//逻辑左移
    3'b110: Result=A-B;//减
    3'b111: Result = 32'h0000_0000//default
    
    EXTOp
    2'b00:无符号16to32
    2'b01:有符号16to32
    2'b10:拓展到高16位
    */
    module control(
    				input clk,
    				input [5:0] opcode,		//6位操作码
    				input zero,				//beq指令
    				input [5:0] func,
    				input overflow,			//溢出标志位 addi专用
    				
    				output reg [1:0]RegDst,	//写寄存器的目标寄存器号来源:00:rt (lui) 01:rd 10:5'b11111 reg31
    				output reg ALUSrc,		//第二个ALU操作数的来源
    				output reg [1:0] MemToReg,	//写入寄存器的数据来源 00ALU运算结果 01数据存储器的输出 10JAL跳转的目的地址
    				output reg RegWrite,	//寄存器写使能有效
    				output reg MemWrite,	//将数据写入到指定存储器单元中
    				output reg PCSrc,		//判断是否执行分支(PC+4 or PC+4+offset)
    				output reg [1:0]ExtOp,		//控制Extender的拓展方式
    				output reg [2:0]ALUctr,	//3bit 控制ALU的运算
    				output reg j_ctr,		//控制PC是否转移到J指令指示的地址
    				output reg jr_ctr,		//jr指令
    				output reg sb_ctr,		//sb指令
    				output reg DMSrc,		//(冗余)输入到DM中的数据来源
    				output reg NPC_ctr			//控制PC是否更改
    			);
    			
    	parameter [2:0] sif = 3'b000,
    	                sid = 3'b001,
    					exe = 3'b010,
    					mem = 3'b011,
    					wb = 3'b100;
    
    	reg [2:0] state;
    	reg [2:0] next_state;
    	
    	parameter [5:0] j_op = 6'b000010;
    	parameter [5:0] jal_op = 6'b000011;
    	parameter [5:0] jr_op = 6'b000000		,jr_func = 6'b001000;
    	parameter [5:0] addu_op = 6'b000000		,addu_func = 6'b100001;
    	parameter [5:0] subu_op = 6'b000000		,subu_func = 6'b100011;
    	parameter [5:0] slt_op = 6'b000000		,slt_func = 6'b101010;
    	parameter [5:0] ori_op = 6'b001101;
    	parameter [5:0] lw_op = 6'b100011;
    	parameter [5:0] sw_op = 6'b101011;
    	parameter [5:0] beq_op = 6'b000100;
    	parameter [5:0] lui_op = 6'b001111;
    	parameter [5:0] addi_op = 6'b001000;
    	parameter [5:0] addiu_op = 6'b001001;
    	parameter [5:0] lb_op = 6'b100000;
    	parameter [5:0] sb_op = 6'b101000;
    	
    	reg [2:0]defaulter = 0;		//为了不让第一个指令被跳过
    	
    	//初始化
    	initial begin
    		RegDst 		= 2'b00;	//写寄存器的目标寄存器号来源:00:rt(lui) 01:rd 10:5'b11111 reg31
    		ALUSrc 		= 0;		//第二个ALU操作数的来源0-读寄存器2 1-指令低16位的符号拓展
    		MemToReg 	= 2'b0;		//写入寄存器的数据来源 00ALU运算结果 01数据存储器的输出 10JAL跳转的目的地址
    		RegWrite 	= 0;		//寄存器写使能有效
    		MemWrite 	= 0;		//将数据写入到指定存储器单元中
    		PCSrc 		= 0;		//判断是否执行分支(PC+4 or PC+4+offset)
    		ExtOp 		= 2'b0;		//控制Extender的拓展方式00无符号16to32 01:有符号16to32 10拓展到高16
    		ALUctr		= 3'b111;	//3bit 控制ALU的运算
    		j_ctr 		= 0;		//控制PC是否转移到J指令指示的地址
    		jr_ctr 		= 0;		//jr指令
    		sb_ctr		= 0;		//sb指令 除了sb_ctr以外sb指令完全模仿sw 
    		DMSrc 		= 0;
    		NPC_ctr		= 0;
    	end
    
    	//状态根据时钟转移
    	always @(posedge clk) 
    		begin
    			state <= next_state;
    		end
    
    	//状态机 此部分根据当前状态确定下一个状态 状态转移过程
    	always @(state or opcode or func) begin
    		case(state)
    			sif: 
    				begin
    					next_state = sid;
    					$display("(CTRL)sif next:sid");
    				end
    				
    			
    			sid: 
    				begin
    					case (opcode)
    						j_op:
    							begin
    								next_state = sif;						// j
    								$display("(CTRL)sid next:sif");
    							end
    						jal_op:
    							begin
    								next_state = wb;					// jal
    								$display("(CTRL)sid next:wb");
    							end
    						6'b000000:
    							begin
    								if(func == jr_func) 
    									begin
    										next_state = sif;	//jr
    										$display("(CTRL)sid next:sif");
    									end
    								else 
    									begin
    										next_state = exe;				//除了 j jal以外的所有op=000000指令
    										$display("(CTRL)sid next:exe");
    									end
    							end
    						default: 
    							begin
    								next_state = exe; 					//上述以外所有指令
    								$display("(CTRL)sid next:exe");
    							end
    					endcase
    				end
    				
    			exe: 
    				begin
    					case (opcode)
    						beq_op: 
    							begin
    								next_state = sif;				// beq
    								$display("(CTRL)exe next:sif");
    							end
    						lw_op: 
    							begin
    								next_state = mem;			 	// lw
    								$display("(CTRL)exe next:mem");
    							end
    						sw_op: 
    							begin
    								next_state = mem;				// sw
    								$display("(CTRL)exe next:mem");
    							end
    						lb_op: 
    							begin
    								next_state = mem;			 	// lb
    								$display("(CTRL)exe next:mem");
    							end
    						sb_op: 
    							begin
    								next_state = mem;				// sb
    								$display("(CTRL)exe next:mem");
    							end
    						default:
    							begin
    								next_state = wb; 					//上述以外所有指令
    								$display("(CTRL)exe next:wb");
    							end
    					endcase
    				end
    			
    			mem:
    				begin
    					if(opcode == lw_op || opcode == lb_op)
    						begin
    							next_state = wb;						//lw lb
    							$display("(CTRL)mem next:wb");
    						end
    					else 
    						begin
    							next_state = sif;						//sw
    							$display("(CTRL)mem next:sif");
    						end			
    				end
    				
    			wb: 
    				begin
    					next_state = sif;
    					$display("(CTRL)wb next:sif");
    				end
    
    			default: 
    				begin
    					next_state = sif;								//理论上不应该存在此项
    					defaulter = 2;
    					$display("(CTRL)default next:sif defaulter=%d",defaulter);
    				end
    		endcase
    		
    		//输出
    		begin
    			
    			if(opcode==j_op)							$display($time,"(CTRL)J OpCode = %b func = %b",opcode,func);
    			else if(opcode==jal_op)						$display($time,"(CTRL)JAL OpCode = %b func = %b",opcode,func);
    			else if(opcode==jr_op && func==jr_func)		$display($time,"(CTRL)JR OpCode = %b func = %b",opcode,func);
    			else if(opcode==addu_op && func==addu_func)	$display($time,"(CTRL)ADDU OpCode = %b func = %b",opcode,func);
    			else if(opcode==subu_op && func==subu_func)	$display($time,"(CTRL)SUBU OpCode = %b func = %b",opcode,func);
    			else if(opcode==slt_op && func==slt_func)	$display($time,"(CTRL)SLT OpCode = %b func = %b",opcode,func);
    			else if(opcode==ori_op)						$display($time,"(CTRL)ORI OpCode = %b func = %b",opcode,func);
    			else if(opcode==lw_op)						$display($time,"(CTRL)LW OpCode = %b func = %b",opcode,func);
    			else if(opcode==sw_op)						$display($time,"(CTRL)SW OpCode = %b func = %b",opcode,func);
    			else if(opcode==beq_op)						$display($time,"(CTRL)BEQ OpCode = %b func = %b",opcode,func);
    			else if(opcode==lui_op)						$display($time,"(CTRL)LUI OpCode = %b func = %b",opcode,func);
    			else if(opcode==addi_op)					$display($time,"(CTRL)ADDI OpCode = %b func = %b",opcode,func);
    			else if(opcode==addiu_op)					$display($time,"(CTRL)ADDIU OpCode = %b func = %b",opcode,func);
    			else if(opcode==lb_op)						$display($time,"(CTRL)LB OpCode = %b func = %b",opcode,func);
    			else if(opcode==sb_op)						$display($time,"(CTRL)SB OpCode = %b func = %b",opcode,func);
    			else $display($time,"(CTRL)NO SUCH INSTRUCT!");
    
    		end
    	end
    	
    	
    	
    	//状态改变->信号量改变 此部分根据当前状态确定信号量
    	always @(state or opcode or func) 
    	begin
            // 确定RegDst的值
            if (state == wb)
    			begin
    				if(opcode == jal_op) RegDst = 2'b10;	// jal
    				else if(opcode == 6'b000000 && (func == addu_func || func == subu_func || func == slt_func))RegDst = 2'b01; //addu subu slt
    				else RegDst = 2'b00;
    			end
    		else RegDst = 2'b00;
    
    		  
            // 确定ALUSrc的值
            if (state == exe)
    			begin
    				if(opcode == 6'b000000 && (func == addu_func || func == subu_func || func == slt_func)) ALUSrc = 0;
    				else if(opcode == ori_op) ALUSrc = 1;
    				else if(opcode == lw_op || opcode == sw_op || opcode == lui_op || opcode == addi_op || opcode == addiu_op || opcode == lb_op || opcode == sb_op) ALUSrc = 1;
    				else ALUSrc = 0;
    			end
    		else if(state == wb && func == ori_op) ALUSrc = 1;
    		else ALUSrc = 0;
    		  
            // 确定MemToReg的值
            if (state == sid && opcode == j_op) MemToReg = 2'b00;
    		else if(state == wb)
    			begin
    				if(opcode == jal_op) MemToReg = 2'b10;
    				else if(opcode == lw_op) MemToReg = 2'b01;
    				else if(opcode == lb_op) MemToReg = 2'b11;
    				else MemToReg = 2'b00;
    			end
            else MemToReg = 2'b00;
    		  
            // 确定RegWrite的值:wb状态下 只有在addi才检测溢出,其余情况直接 =1
            if (state == wb)
    			begin
    				if(opcode == addi_op && overflow == 1)RegWrite = 0;//addi溢出的情况 不能写reg
    				else RegWrite = 1;
    			end
            else RegWrite = 0;
    		  
            // 确定MemWrite的值
            if (state == mem && (opcode == sw_op || opcode == sb_op)) MemWrite = 1;
            else MemWrite = 0;
            
    		  // 确定PCSrc的值
            if (state == exe && opcode == beq_op && zero == 1) PCSrc = 1;
            else PCSrc = 0;
            
    		  // 确定ExtOp的值
            if (state == exe &&(opcode == lw_op || opcode == sw_op || opcode == beq_op || opcode == addi_op || opcode == addiu_op || opcode == lb_op || opcode == sb_op)) ExtOp = 2'b01;
            else ExtOp = 2'b00;
            
    		  // 确定ALUctr的值
            if (state == exe)
    			begin
    				if (opcode == addu_op && func == addu_func) ALUctr = 3'b010;
    				else if (opcode == subu_op && func == subu_func) ALUctr = 3'b110;
    				else if (opcode == slt_op && func == slt_func) ALUctr = 3'b000;
    				else if (opcode == ori_op) ALUctr = 3'b001;
    				else if (opcode == lw_op || opcode == sw_op || opcode == addi_op || opcode == addiu_op || opcode == lb_op || opcode == sb_op)  ALUctr = 3'b010;
    				else if (opcode == lui_op)  ALUctr = 3'b011;
    				else ALUctr = 3'b111;
    			end
            else ALUctr = 3'b111;
            
    		  // 确定j_ctr的值 这样写行吗 我是说真值表填的对不对
            if (opcode == j_op && state == sid) j_ctr = 1;
            else if (opcode == jal_op && state == wb) j_ctr = 1;
            else j_ctr = 0;
            
    		  // 确定jr_ctr的值
            if (opcode == jr_op && func == jr_func && state == sid) jr_ctr = 1;
            else jr_ctr = 0;
            
    		  // 确定DMSrc的值 没用
            DMSrc = 0;
            
    		  // 确定NPC_ctr的值
            if (state == sif)NPC_ctr = 1;
    		else NPC_ctr = 0;
    		
    		  //确定sb_ctr的值
    		if (state == mem && opcode == sb_op) sb_ctr = 1;
    		else sb_ctr = 0;
    		
    		//保护第一条指令不被跳过
    		if(defaulter > 0 && defaulter != 3)
    			begin
    				$display("defaulter=%d",defaulter);
    				defaulter = defaulter - 1;
    				$display("defaulter=%d",defaulter);
    				if(defaulter == 0)
    					begin
    						NPC_ctr = 0;
    						defaulter = 3;
    					end
    			end
    			
    		$display($time,"(CTRL)OpCode=%b func=%b RegDst=%b ALUSrc=%b MemToReg=%b RegWrite=%b MemWrite=%b PCSrc=%b ExtOp=%b ALUctr=%b j_ctr=%b jr_ctr=%b DMSrc=%b NPC_ctr=%b defaulter=%d",opcode,func,RegDst,ALUSrc,MemToReg,RegWrite,MemWrite,PCSrc,ExtOp,ALUctr,j_ctr,jr_ctr,DMSrc,NPC_ctr,defaulter);
        end
    endmodule
    				
    

    dm.v

    `timescale 1ns / 1ns
    //DM数据寄存器
    module dm_4k(
    				input [11:0]addr12,	//lb sb指令专用
    				input [11:2]addr,	//(sw指令)由Result[11:2](ALU基地址+偏移的计算结果)输入,表示要写入的数据单元的地址
    				input [31:0]din,	//datain sw sb指令
    				input we,			//MemWrite数据存储器写使能
    				input sb_ctr,
    				input clk,
    							
    				output [31:0] dout,			//dataout 取出的数据
    				output reg [31:0] lbout		//dataout lb专用
    			);
    			
    	wire [11:2] address;			//以字为单位的地址,因为寄存器是1024"字"
    	assign address = addr;
    	
    	reg[31:0] dm[1023:0];		//32bit*1024字的数据存储器
    	
    	//普通的读数据
    	assign dout = dm[address];//始终输出
    	
    	integer i;
    	initial
    		begin
    			for(i = 0; i < 1024; i = i + 1) dm[i] <= 0;//初始化数据存储器为0
    		end
    		
    	//给lb专用的数据输出
    	always@(*)
    		begin
    			case(addr12[1:0])
    				2'b00:
    					begin
    						if(dm[address][7]==0)lbout = {24'b0000_0000_0000_0000_0000_0000,dm[address][7:0]};
    						else lbout = 				{24'b1111_1111_1111_1111_1111_1111,dm[address][7:0]};
    					end
    				2'b01:
    					begin
    						if(dm[address][15]==0)lbout = {24'b0000_0000_0000_0000_0000_0000,dm[address][15:8]};
    						else lbout = 				{24'b1111_1111_1111_1111_1111_1111,dm[address][15:8]};
    					end
    				2'b10:
    					begin
    						if(dm[address][23]==0)lbout = {24'b0000_0000_0000_0000_0000_0000,dm[address][23:16]};
    						else lbout = 				{24'b1111_1111_1111_1111_1111_1111,dm[address][23:16]};
    					end
    				2'b11:
    					begin
    						if(dm[address][31]==0)lbout = {24'b0000_0000_0000_0000_0000_0000,dm[address][31:24]};
    						else lbout = 				{24'b1111_1111_1111_1111_1111_1111,dm[address][31:24]};
    					end
    			endcase
    			
    			$display($time,,"(DM LBread)addr12[1:0]=%b lbout=%b", addr12[1:0], lbout);//lb专用送出
    		end
    		
    	always@(negedge clk)
    		begin			
    			//写寄存器
    					if(we)
    						begin
    						
    							if(sb_ctr)	//sb指令只写一个字节 不能影响旁边
    								begin
    									$display($time,,"(DM)Before MemWrite:");
    									begin
    										for(i=0;i<128;i=i+1)$write("%h ", dm[i]);
    										$write("\n");//换行专用
    									end
    									
    									case(addr12[1:0])
    										2'b00:dm[address][7:0] <= din[7:0];
    										
    										2'b01:dm[address][15:8] <= din[7:0];
    										
    										2'b10:dm[address][23:16] <= din[7:0];
    										
    										2'b11:dm[address][31:24] <= din[7:0];
    									endcase
    									
    									#1
    									begin
    										$display($time,,"(DM MemWrite)dmAddr%b <= Data%b", addr, din);
    										$display($time,,"(DM)Finish MemWrite:");
    										begin
    											for(i=0;i<128;i=i+1)$write("%h ", dm[i]);
    											$write("\n");//换行专用
    										end
    									end
    								end
    								
    							else		//sw指令
    								begin
    									$display($time,,"(DM)Before MemWrite:");
    									begin
    										for(i=0;i<128;i=i+1)$write("%h ", dm[i]);
    										$write("\n");//换行专用
    									end
    								
    									dm[address] <= din;
    								
    									#1
    									begin
    										$display($time,,"(DM MemWrite)dmAddr%b <= Data%b", addr, din);
    										$display($time,,"(DM)Finish MemWrite:");
    										begin
    											for(i=0;i<128;i=i+1)$write("%h ", dm[i]);
    											$write("\n");//换行专用
    										end
    									end
    								end
    						end
    		end
    endmodule
    

    ext.v

    //Extender位拓展
    /*
    EXTOp
    2'b00:无符号16to32
    2'b01:有符号16to32
    2'b10:拓展到高16位
    */
     //注意组合逻辑(always敏感列表)用=赋值,时序逻辑(clk控制)用<=赋值
    `timescale 1ns / 1ns
    module EXT(
    			input [15:0] EXTin,			//输入EXT的数据
    			input [1:0]ExtOp,
    			output reg [31:0] EXTout	//拓展后输出的数据
    			);
    			
    	always@(*)
    		begin
    			case(ExtOp)
    				2'b00://无符号16to32
    					begin
    						EXTout = {16'b0,EXTin[15:0]};
    						$display($time,,"(EXT)EXTOp = 00; EXTin = %b EXTout = %b",EXTin, EXTout);
    					end
    					
    				2'b01://有符号16to32
    					begin
    						if(EXTin[15]==1)
    							EXTout = {16'b1111_1111_1111_1111,EXTin[15:0]};
    						else EXTout = {16'b0000_0000_0000_0000,EXTin[15:0]};
    						$display($time,,"(EXT)EXTOp = 01; EXTin = %b EXTout = %b",EXTin, EXTout);
    					end
    					
    				2'b10://拓展至高16
    					begin
    						EXTout = {EXTin[15:0],16'b0000_0000_0000_0000};
    						$display($time,,"(EXT)EXTOp = 10; EXTin = %b EXTout = %b",EXTin, EXTout);
    					end
    				
    				default: 
    					begin
    						$display($time,,"(EXT)EXTOp = default");
    						EXTout = 32'b0;
    					end
    			endcase
    		end
    endmodule
    

    datalate

    `timescale 1ns / 1ns
    
    //32位
    module DataLate32A(
    					input [31:0] i_data,		//in
    					input clk,
    					
    					output reg [31:0] o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_A)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    module DataLate32B(
    					input [31:0] i_data,		//in
    					input clk,
    					
    					output reg [31:0] o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_B)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    module DataLate32ALU(
    					input [31:0] i_data,		//in
    					input clk,
    					
    					output reg [31:0] o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_ALU)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    module DataLate32RAM(
    					input [31:0] i_data,		//in
    					input clk,
    					
    					output reg [31:0] o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_RAM)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    module DataLate32RAMLB(
    					input [31:0] i_data,		//in
    					input clk,
    					
    					output reg [31:0] o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_RAM_LB)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    //1位
    module DataLate1(
    					input i_data,		//in
    					input clk,
    					
    					output reg o_data	//out
    				);
     
        always @(negedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLateOverFlow)out:%b",o_data);
        end
    endmodule
    
    
    //j+jr_ctr
    module DataLate_J_JR(
    					input i_data,		//in
    					input clk,
    					
    					output reg o_data	//out
    				);
     
        always @(posedge clk) begin
            o_data <= i_data;
    		$display($time,,"(DataLate_J/JR/PCsrc)in:%b out:%b",i_data,o_data);
        end
    endmodule
    
    

    部分控制台输出结果

    # (CTRL)wb next:sif
    #                 5030(CTRL)ADDU OpCode = 000000 func = 100001
    #                 5030(CTRL)OpCode=000000 func=100001 RegDst=01 ALUSrc=0 MemToReg=00 RegWrite=1 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=111 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=0 defaulter=3
    #                 5030 (MUX RegWriteAddr)control = 01, din0 = 00000, din1 = 01001, out = 01001
    #                 5030 (ALU)(DEFAULT)
    #                 5030 (ALU)(DEFAULT)
    #                 5030 (mips_tb)clk = 1
    #                 5040 (Write GPR)PC00000000000000000011000010111000: WriteAddress01001 <= Data00101011101010111100110111010001
    #                 5040 (DataLate_A)in:00101011101010111100110111010001 out:00101011101010111100110111010001
    #                 5040 (DataLate_B)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5040 (DataLate_RAM)in:00000000000000000000000000000000 out:10000000000000000000001000000010
    #                 5040 (DataLate_RAM_LB)in:00000000000000000000000000000000 out:00000000000000000000000000000010
    #                 5040 (DataLate_ALU)in:00000000000000000000000000000000 out:00101011101010111100110111010001
    #                 5040 (DataLateOverFlow)out:0
    #                 5040 (DM LBread)addr12[1:0]=00 lbout=00000000000000000000000000000010
    #                 5040 (MUX RegWriteData)control = 00, din0 = 00000000000000000000000000000000, din1 = 00000000000000000000000000000000, din2 = 00000000000000000011000010111100, din3 = 00000000000000000000000000000000, out = 00000000000000000000000000000000
    #                 5040 (mips_tb)clk = 0
    #                 5041 (Show GPR)
    #                 5041 GPR          0 = 00000000
    #                 5041 GPR          1 = ababcdcd
    #                 5041 GPR          2 = ababcdd3
    #                 5041 GPR          3 = ababcdcd
    #                 5041 GPR          4 = 2babcdd1
    #                 5041 GPR          5 = 80000002
    #                 5041 GPR          6 = 00000000
    #                 5041 GPR          7 = 00000000
    #                 5041 GPR          8 = 2babcdd1
    #                 5041 GPR          9 = 2babcdd1
    #                 5041 GPR         10 = 80000004
    #                 5041 GPR         11 = 00000000
    #                 5041 GPR         12 = 0000abab
    #                 5041 GPR         13 = 000a0000
    #                 5041 GPR         14 = 0000007f
    #                 5041 GPR         15 = ab000000
    #                 5041 GPR         16 = 00000003
    #                 5041 GPR         17 = 00000001
    #                 5041 GPR         18 = ffffff80
    #                 5041 GPR         19 = efef0000
    #                 5041 GPR         20 = 00000000
    #                 5041 GPR         21 = 00000000
    #                 5041 GPR         22 = 00000000
    #                 5041 GPR         23 = ababcdd2
    #                 5041 GPR         24 = 7fffffff
    #                 5041 GPR         25 = 00000001
    #                 5041 GPR         26 = 00000000
    #                 5041 GPR         27 = 00000000
    #                 5041 GPR         28 = 00000000
    #                 5041 GPR         29 = 00000000
    #                 5041 GPR         30 = 00000000
    #                 5041 GPR         31 = 000030b4
    #                 5050 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5050 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5050 (DataLate_J/JR/PCsrc)in:0 out:0
    # (CTRL)sif next:sid
    #                 5050(CTRL)ADDU OpCode = 000000 func = 100001
    #                 5050(CTRL)OpCode=000000 func=100001 RegDst=00 ALUSrc=0 MemToReg=00 RegWrite=0 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=111 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=1 defaulter=3
    #                 5050 (NPC)CurPCAddr:00000000000000000011000010111000 NextPcAddr:00000000000000000011000010111100 j_ctr=0 jr_ctr=0 
    #                 5050 (MUX RegWriteAddr)control = 00, din0 = 00000, din1 = 01001, out = 00000
    #                 5050 (mips_tb)clk = 1
    #                 5060 (GPR)Write:false
    #                 5060 (DataLate_A)in:00101011101010111100110111010001 out:00101011101010111100110111010001
    #                 5060 (DataLate_B)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5060 (DataLate_RAM)in:10000000000000000000001000000010 out:00000000000000000000000000000000
    #                 5060 (DataLate_RAM_LB)in:00000000000000000000000000000010 out:00000000000000000000000000000000
    #                 5060 (DataLate_ALU)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5060 (DataLateOverFlow)out:0
    #                 5060 (MUX RegWriteData)control = 00, din0 = 00000000000000000000000000000000, din1 = 10000000000000000000001000000010, din2 = 00000000000000000011000010111100, din3 = 00000000000000000000000000000010, out = 00000000000000000000000000000000
    #                 5060 (mips_tb)clk = 0
    #                 5070 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5070 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5070 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5070 (PC)NextPcAddr00000000000000000011000010111000
    #                 5070 (MUX RegWriteData)control = 00, din0 = 00000000000000000000000000000000, din1 = 10000000000000000000001000000010, din2 = 00000000000000000011000011000000, din3 = 00000000000000000000000000000010, out = 00000000000000000000000000000000
    #                 5070 (IM)Current Code:001111 00000 01010 00000 00001 101001 ; TheAddrOfCurCode = 0000101111 
    # (CTRL)sid next:exe
    #                 5070(CTRL)ADDU OpCode = 000000 func = 100001
    #                 5070(CTRL)OpCode=000000 func=100001 RegDst=00 ALUSrc=0 MemToReg=00 RegWrite=0 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=111 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=0 defaulter=3
    #                 5070 (MUX DataTo DM/ALU)control = 0, din0 = 10000000000000000000000000000100, din1 = 00000000000000000100100000100001, out = 10000000000000000000000000000100
    #                 5070 (MUX RegWriteAddr)control = 00, din0 = 01010, din1 = 00000, out = 01010
    # (CTRL)sid next:exe
    #                 5070(CTRL)LUI OpCode = 001111 func = 101001
    #                 5070(CTRL)OpCode=001111 func=101001 RegDst=00 ALUSrc=0 MemToReg=00 RegWrite=0 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=111 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=0 defaulter=3
    #                 5070 (EXT)EXTOp = 00; EXTin = 0000000001101001 EXTout = 00000000000000000000000001101001
    #                 5070 (ALU)(DEFAULT)
    #                 5070 (ALU)(DEFAULT)
    #                 5070 (MUX DataTo DM/ALU)control = 0, din0 = 10000000000000000000000000000100, din1 = 00000000000000000000000001101001, out = 10000000000000000000000000000100
    #                 5070 (mips_tb)clk = 1
    #                 5080 (GPR)Write:false
    #                 5080 (DataLate_A)in:00000000000000000000000000000000 out:00101011101010111100110111010001
    #                 5080 (DataLate_B)in:10000000000000000000000000000100 out:00000000000000000000000000000000
    #                 5080 (DataLate_RAM)in:10000000000000000000001000000010 out:10000000000000000000001000000010
    #                 5080 (DataLate_RAM_LB)in:00000000000000000000000000000010 out:00000000000000000000000000000010
    #                 5080 (DataLate_ALU)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5080 (DataLateOverFlow)out:0
    #                 5080 (mips_tb)clk = 0
    #                 5090 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5090 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5090 (DataLate_J/JR/PCsrc)in:0 out:0
    # (CTRL)exe next:wb
    #                 5090(CTRL)LUI OpCode = 001111 func = 101001
    #                 5090(CTRL)OpCode=001111 func=101001 RegDst=00 ALUSrc=1 MemToReg=00 RegWrite=0 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=011 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=0 defaulter=3
    #                 5090 (ALU)(16toUp32)A = 00000000000000000000000000000000, B = 10000000000000000000000000000100, Result = 00000000000001000000000000000000
    #                 5090 (MUX DataTo DM/ALU)control = 1, din0 = 10000000000000000000000000000100, din1 = 00000000000000000000000001101001, out = 00000000000000000000000001101001
    #                 5090 (ALU)(16toUp32)A = 00000000000000000000000000000000, B = 00000000000000000000000001101001, Result = 00000000011010010000000000000000
    #                 5090 (mips_tb)clk = 1
    #                 5100 (GPR)Write:false
    #                 5100 (DataLate_A)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5100 (DataLate_B)in:10000000000000000000000000000100 out:10000000000000000000000000000100
    #                 5100 (DataLate_RAM)in:10000000000000000000001000000010 out:10000000000000000000001000000010
    #                 5100 (DataLate_RAM_LB)in:00000000000000000000000000000010 out:00000000000000000000000000000010
    #                 5100 (DataLate_ALU)in:00000000011010010000000000000000 out:00000000000000000000000000000000
    #                 5100 (DataLateOverFlow)out:0
    #                 5100 (MUX RegWriteData)control = 00, din0 = 00000000011010010000000000000000, din1 = 10000000000000000000001000000010, din2 = 00000000000000000011000011000000, din3 = 00000000000000000000000000000010, out = 00000000011010010000000000000000
    #                 5100 (mips_tb)clk = 0
    #                 5110 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5110 (DataLate_J/JR/PCsrc)in:0 out:0
    #                 5110 (DataLate_J/JR/PCsrc)in:0 out:0
    # (CTRL)wb next:sif
    #                 5110(CTRL)LUI OpCode = 001111 func = 101001
    #                 5110(CTRL)OpCode=001111 func=101001 RegDst=00 ALUSrc=0 MemToReg=00 RegWrite=1 MemWrite=0 PCSrc=0 ExtOp=00 ALUctr=111 j_ctr=0 jr_ctr=0 DMSrc=0 NPC_ctr=0 defaulter=3
    #                 5110 (ALU)(DEFAULT)
    #                 5110 (MUX DataTo DM/ALU)control = 0, din0 = 10000000000000000000000000000100, din1 = 00000000000000000000000001101001, out = 10000000000000000000000000000100
    #                 5110 (ALU)(DEFAULT)
    #                 5110 (ALU)(DEFAULT)
    #                 5110 (mips_tb)clk = 1
    #                 5120 (Write GPR)PC00000000000000000011000010111100: WriteAddress01010 <= Data00000000011010010000000000000000
    #                 5120 (DataLate_A)in:00000000000000000000000000000000 out:00000000000000000000000000000000
    #                 5120 (DataLate_B)in:10000000000000000000000000000100 out:10000000000000000000000000000100
    #                 5120 (DataLate_RAM)in:10000000000000000000001000000010 out:10000000000000000000001000000010
    #                 5120 (DataLate_RAM_LB)in:00000000000000000000000000000010 out:00000000000000000000000000000010
    #                 5120 (DataLate_ALU)in:00000000000000000000000000000000 out:00000000011010010000000000000000
    #                 5120 (DataLateOverFlow)out:0
    #                 5120 (MUX DataTo DM/ALU)control = 0, din0 = 00000000011010010000000000000000, din1 = 00000000000000000000000001101001, out = 00000000011010010000000000000000
    #                 5120 (MUX RegWriteData)control = 00, din0 = 00000000000000000000000000000000, din1 = 10000000000000000000001000000010, din2 = 00000000000000000011000011000000, din3 = 00000000000000000000000000000010, out = 00000000000000000000000000000000
    #                 5120 (ALU)(DEFAULT)
    #                 5120 (mips_tb)clk = 0
    #                 5121 (Show GPR)
    #                 5121 GPR          0 = 00000000
    #                 5121 GPR          1 = ababcdcd
    #                 5121 GPR          2 = ababcdd3
    #                 5121 GPR          3 = ababcdcd
    #                 5121 GPR          4 = 2babcdd1
    #                 5121 GPR          5 = 80000002
    #                 5121 GPR          6 = 00000000
    #                 5121 GPR          7 = 00000000
    #                 5121 GPR          8 = 2babcdd1
    #                 5121 GPR          9 = 2babcdd1
    #                 5121 GPR         10 = 00690000
    #                 5121 GPR         11 = 00000000
    #                 5121 GPR         12 = 0000abab
    #                 5121 GPR         13 = 000a0000
    #                 5121 GPR         14 = 0000007f
    #                 5121 GPR         15 = ab000000
    #                 5121 GPR         16 = 00000003
    #                 5121 GPR         17 = 00000001
    #                 5121 GPR         18 = ffffff80
    #                 5121 GPR         19 = efef0000
    #                 5121 GPR         20 = 00000000
    #                 5121 GPR         21 = 00000000
    #                 5121 GPR         22 = 00000000
    #                 5121 GPR         23 = ababcdd2
    #                 5121 GPR         24 = 7fffffff
    #                 5121 GPR         25 = 00000001
    #                 5121 GPR         26 = 00000000
    #                 5121 GPR         27 = 00000000
    #                 5121 GPR         28 = 00000000
    #                 5121 GPR         29 = 00000000
    #                 5121 GPR         30 = 00000000
    #                 5121 GPR         31 = 000030b4
    
    展开全文
  • 基于MIPS指令集的单周期处理器设计

    千次阅读 2020-11-17 13:33:38
    基于MIPS指令集的单周期处理器设计 一、项目概述 设计题目 设数组存有8个任意字符,将其按顺序拼接得到一个双字(64位),对此双字进行循环左移4位。计算新得到的8个字符中,每个字符中1的个数,并对应存储成新的...

    基于MIPS指令集的单周期处理器设计

    (完整程序获取见文章末尾)

    一、项目概述
    1. 设计题目
      设数组存有8个任意字符,将其按顺序拼接得到一个双字(64位),对此双字进行循环左移4位。计算新得到的8个字符中,每个字符中1的个数,并对应存储成新的数组。
    2. 设计内容
      (1)按设计题目采用C语言实现并验证,给出验证结果。
      (2)将验证过的C程序转化为MIPS汇编程序,并转化为机器码。
      (3)面向该汇编程序采用的指令集,采用Verilog分别实现为单周期处理器。
      (4)采用Verilog设计上述处理器的验证环境,并在仿真器上验证,最终给出验证波形图。
      (5)每种处理器的实现要求按照top-down的设计方法,进行模块划分。
      (6)设计结果要求充分考虑成本、性能及存储器存储空间优化。
    二、C语言代码及测试结果

    根据设计题目的要求设计可C语言代码如下:

    //预处理命令
    #include <stdio.h>
    //主函数
    int main(void){ 
      int i,j ;
      char array[8] = "abcdefgh"; 
      char new_array[8];
      char count[8];   //为了编汇编程序时统一使用lb/sb指令,将count也定义位char型
    
      //进行数组的循环移位操作
      for(i = 0;i<7;i++){   
        new_array[i] = array[i]<<4 | array[i+1]>>4;  
        }//由于字符型是8位的,因此左移四位时,高4位被舍去,低4位补0,右移4位时也类似
      new_array[7] = array[7]<<4 | array[0]>>4;    //对于循环左移,最右侧的字符需要特殊处理
    //计算循环移位后的新字符中每个字符中“1”的个数
      for(i = 0;i<8;i++){
        //依次判断每个字符的1的个数
            count[i] = 0;
            for(j = 0;j < 8;j++){
            if((new_array[i] & 1<<j) != 0)  //依次判断字符的每个位是否为1
               count[i]++;
            }  
            printf("array[%d] = %x, new_array[%d] = %x, count[%d] = %d\n",i,
            array[i],i,(unsigned char)new_array[i],i,count[i]);  //不希望编译这条语句时可以使用条件编译指令     
      }
      return 0;
    }
    
    
    
    

    程序运行结果如下:
    在这里插入图片描述
    一个字符为8位,对字符数组进行4位的循环左移,若将字符表示为16进制,则可看为循环左移1位,根据上述运行结果可知,C语言程序实现了题目的要求。

    三、汇编代码及机器码

    根据C语言编写MIPS汇编程序如下所示:

    add $t0,$zero,$zero  #寄存器$t0代表变量i,并初始化为0
    add $t1,$zero,$zero   #寄存器$t1代表变量j,并初始化为0
    addi $t2,$zero,0#寄存器$t2存储着字符数组array的基地址0
    addi $t3,$zero,8  #寄存器$t3存储着字符数组new_array的基地址8
    addi $t4,$zero,16 #寄存器$t4存储着int数组count的基地址16
    addi $s0, $zero, 1    #$s0存放常数1
    addi $s1, $zero, 4    #$s1存放常数4
    
    
    #对数组array进行循环移位得到新的数组new_array
    loop1:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
    	addi $t6, $t5, 1  #将array[i+1]的地址放入$t6中
    	add $t7, $t0,$t3 #将new_array[i]的地址放入$t7中
    	lb $t8, 0($t5)  #将array[i]存入$t8的低8位
    	lb $t5, 0($t6) #将array[i+1]存入$t5的低8位
    	sllv $t8, $t8, $s1 #将array[i]逻辑左移4位后放入$t8中
    	srlv $t5, $t5, $s1 #将array[i+1]逻辑右移4位后放入$t5中
    	or $t5, $t5, $t8 #将移位后的array[i]和array[i+1]按位或后放入$t5中
    	sb $t5, 0($t7)  #将$t5的低8位放入内存中new_array[i]位置上
    	addi $t0, $t0, 1 # i++
    	addi $a3, $zero, 6 
    	slt $t5, $a3,$t0 #若6 < i即i > =7,则$t5置1
    	beq  $t5, $zero,loop1  #若i <7,则继续循环
    lb $t5, 7($t2)  #将array[7]从内存中取出,放入$t5的低8位
    lb $t6, 0($t2)  #将array[0]从内存中取出,放入$t6的低8位
    sllv $t5, $t5, $s1
    srlv $t6, $t6, $s1
    or $t5, $t5, $t6
    sb $t5, 7($t3) #存入new_array[7]的值
    
    #计算新数组new_array中每个字符的1的个数,并存入数组count
    add $t0,$zero,$zero  #前一次循环中i的值被改变,现在要重新置0
    loop2:	add $t5, $t0, $t4 #将count[i]的地址放入$t5中
    	add $t6, $t0, $t3 #将new_array[i]的地址放入$t6中
    	add $s2, $zero, $zero #$s2寄存器暂时存放计数结果
    	add $t1, $zero, $zero  #每次进入循环前将j置0
    	loop3: sllv $t7, $s0, $t1  #$t7存放1左移j位的结果
    		lb $t8, 0($t6) #将new_array[i]存入$t8的低8位
    		and $t7, $t7, $t8  
    		beq $t7, $zero, else
    		addi $s2, $s2, 1   #count++
    		else:   sb $s2,0($t5)  #将计数结果存入内存的count[i]对应的位置上
    			addi $t1, $t1, 1 #j++
    			addi $a3, $zero, 7
    			slt $t9, $a3, $t1#若j > 7,则$t9置1
    			beq $t9, $zero, loop3 
          addi $t0, $t0, 1  #i++
          addi $a3, $zero, 7
          slt $t5, $a3, $t0 #若i<8,则$t5置1
          beq $t5, $zero, loop2
    

    为了能在MARS仿真器上运行上述MIPS汇编代码,需对汇编代码进行更改:将数组首地址更改为可访问的数据段,并加入将”a,b,c,d,e,f,g,h”存入内存中的汇编语言,具体修改内容如下:

    add $t0,$zero,$zero  #寄存器$t0代表变量i,并初始化为0
    add $t1,$zero,$zero   #寄存器$t1代表变量j,并初始化为0
    addi $t2,$zero,0x10010000  #寄存器$t2存储着字符数组array的基地址0
    addi $t3,$zero,0x10010008  #寄存器$t3存储着字符数组new_array的基地址8
    addi $t4,$zero,0x10010010   #寄存器$t4存储着int数组count的基地址16
    addi $s0, $zero, 1    #$s0存放常数1
    addi $s1, $zero, 4    #$s1存放常数4
    
    #依次将"a,b,c,d,e,f,g,h"存入数组array中
    addi $a0, $zero, 0x61 #"a"的ASCII码
    loop:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
    	add $t6, $a0, $t0 #要存入array[i]的字符
    	sb $t6, 0($t5) #依次将"a,b,c,d,e,f,g,h"存入array[i]中
    	addi $t0, $t0, 1  #i++
    	addi $a3, $zero, 7
           slt $t5, $a3, $t0  #若i<8,则$t5置1
            beq $t5, $zero, loop
    
    #对数组array进行循环移位得到新的数组new_array
    add $t0,$zero,$zero   #前一次循环改变了i的值,再次进入循环后重新将i置0	
    loop1:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
    	addi $t6, $t5, 1  #将array[i+1]的地址放入$t6中
    	add $t7, $t0,$t3 #将new_array[i]的地址放入$t7中
    	lb $t8, 0($t5)  #将array[i]存入$t8的低8位
    	lb $t5, 0($t6) #将array[i+1]存入$t5的低8位
    	sllv $t8, $t8, $s1 #将array[i]逻辑左移4位后放入$t8中
    	srlv $t5, $t5, $s1 #将array[i+1]逻辑右移4位后放入$t5中
    	or $t5, $t5, $t8 #将移位后的array[i]和array[i+1]按位或后放入$t5中
    	sb $t5, 0($t7)  #将$t5的低8位放入内存中new_array[i]位置上
    	addi $t0, $t0, 1 # i++
    	addi $a3, $zero, 6 
    	slt $t5, $a3,$t0 #若6 < i即i > =7,则$t5置1
    	beq  $t5, $zero,loop1  #若i <7,则继续循环
    lb $t5, 7($t2)  #将array[7]从内存中取出,放入$t5的低8位
    lb $t6, 0($t2)  #将array[0]从内存中取出,放入$t6的低8位
    sllv $t5, $t5, $s1
    srlv $t6, $t6, $s1
    or $t5, $t5, $t6
    sb $t5, 7($t3) #存入new_array[7]的值
    
    #计算新数组new_array中每个字符的1的个数,并存入数组count
    add $t0,$zero,$zero  #前一次循环中i的值被改变,现在要重新置0
    loop2:	add $t5, $t0, $t4 #将count[i]的地址放入$t5中
    	add $t6, $t0, $t3 #将new_array[i]的地址放入$t6中
    	add $s2, $zero, $zero #$s2寄存器暂时存放计数结果
    	add $t1, $zero, $zero  #每次进入循环前将j置0
    	loop3: sllv $t7, $s0, $t1  #$t7存放1左移j位的结果
    		lb $t8, 0($t6) #将new_array[i]存入$t8的低8位
    		and $t7, $t7, $t8  
    		beq $t7, $zero, else
    		addi $s2, $s2, 1   #count++
    		else:   sb $s2,0($t5)  #将计数结果存入内存的count[i]对应的位置上
    			addi $t1, $t1, 1 #j++
    			addi $a3, $zero, 7
    			slt $t9, $a3, $t1#若j > 7,则$t9置1
    			beq $t9, $zero, loop3 
          addi $t0, $t0, 1  #i++
          addi $a3, $zero, 7
          slt $t5, $a3, $t0 #若i<8,则$t5置1
          beq $t5, $zero, loop2
    

    在Mars中运行汇编代码结果如下:
    在这里插入图片描述
    根据以上运行结果可知,编写的汇编程序实现了要求的功能。再将汇编语言转化位机器码,如下图所示:

    在这里插入图片描述
    存入的最后一条指令是ffff_ffff,是自定义的停机指令,当遇到停机指令时pc不再更新,数据存储器和寄存器堆也不能写入数据。

    四、指令集描述

    根据汇编语言文件可知,包含的指令有:
    (1)数据传输指令:
    指令 特点
    sb I型,opcode:101000(从高位到低位),将寄存器的低八位存入内存
    lb I型,opcode:100000,将内存中一个字节放入寄存器低8位
    (2)逻辑指令:
    指令 含义
    sllv R型,funct:000100,sllv $t1, $t2, $t3  $t1 = $t2 << $t3
    srlv R型,funct:000110,srlv $t1, $t2, $t3  $t1 = $t2 >> $t3
    or R型,funct:100101, 两个源寄存器按位或,结果存入目标
    寄存器
    and R型,funct:100100,两个源寄存器按位与,结果存入目标寄存器
    (3)算术指令:
    指令 含义
    add R型,funct:100000,两个源寄存器相加,结果放入目标寄存器
    addi I型,opcode:001000,源寄存器与立即数相加,结果放入目标寄存器
    (4)跳转/转移/比较指令:
    指令 含义
    beq I型,opcode:000100,比较两个寄存器的值若相等则PC指针跳转到相应位置
    slt R型,funct:101010,slt $1, $2, $3,if ($2 < $3),$1 = 1;else $1 = 0;

    五、单周期处理器设计

    5.1模块示意图与模块代码
    5.1.1程序计数器pc
    pc中存储着当前指令地址,在本指令集中除了遇到BEQ指令有效的情况是,pc都是每过一个周期加1,模块示意图如下:
    在这里插入图片描述
    adress_in[31:0]为输入pc的新地址
    adress_out[31:0]为当前pc输出
    clk为时钟
    rst为复位信号
    PC模块的Verilog代码如下:

    `include "define.v"
    //程序计数器模块
    module pc(input clk,rst,pc_en,
              input [`BUS_WIDTH-1:0] address_in,
              output reg [`BUS_WIDTH-1:0] address_out); 
    always@(posedge clk or negedge rst)  //rst为异步低电平复位信号
     begin
        if(!rst) address_out <= 32'b0;
        else if(!pc_en) ;   //运行到停机指令时,pc_en=0,pc不再更新
        else address_out <= address_in;
     end
    endmodule
    
    

    5.1.2寄存器堆模块
    本寄存器组拥有32位寄存器,每一个寄存器为32位,寄存器中存放着数据,通过输入的指令取出指定数据进行相应的操作,模块示意图如下:
    在这里插入图片描述
    ra[5:0]为第一个读出寄存器的地址
    rb[5:0]为第二个读出寄存器的地址
    rw[5:0]为写入寄存器的地址
    reg_wr为寄存器写使能信号
    wr_data[31:0]为写入寄存器的数据
    bus_a[31:0]为寄存器的第一个输出数据
    bus_b[31:0]为寄存器的第二个输出数据
    寄存器堆模块的Verilog代码如下:

    `include "define.v"
    //寄存器堆模块
    module reg_file (input clk,reg_wr,input [`BUS_WIDTH-1:0] wr_data, 
    input [`REG_WIDTH-1:0] ra, rb, rw,
    output  [`BUS_WIDTH-1:0] bus_a, bus_b);
    reg [`BUS_WIDTH-1:0] rom [`BUS_WIDTH-1:0];  //寄存器堆存储结构32x32
    assign bus_a = rom[ra];
    assign bus_b = rom[rb];
    initial
      $readmemh("0.txt",rom,0,31);  //寄存器堆初始化为0,便于仿真,无法综合   
    always@(posedge clk)
      if(reg_wr)
      rom[rw] <= wr_data;
      else ; 
    endmodule
    

    5.1.3数据存储器模块
    数据存储器模块存储数据,以字节为存储单位。其模块示意图如下:
    在这里插入图片描述
    数据存储器模块存储数据,以字节为存储单位。其模块示意图如下:
    data_in[31:0]为输入数据
    clk为时钟信号
    wr_en为写使能信号
    data_out[31:0]为数据输出信号

    数据存储器模块的Verilog代码如下:

    `include "define.v"
    module data_mem (
      input clk, wr_en, 
      input [`BUS_WIDTH-1:0] adr,instr,
      input [`BUS_WIDTH-1:0] data_in,   
      output [`BUS_WIDTH-1:0] data_out  
    );
    reg [`BYTE-1:0] rom [`DATA_MEM-1:0]; 
    /*当停机时在控制台输出数据存储器中的数据
    来观察是否实现功能*/
    always@(*)
    	begin
    		if(instr == 32'hffff_ffff)
    			begin:for_loop
    			integer i ;
    			for(i=0;i< 24;i = i+4)
    			$display("data_mem[%2d]=%h,data_mem[%2d]=%h,data_mem[%2d]=%h,data_mem[%2d]=%h\n"
    			,i,rom[i],i+1,rom[i+1],i+2,rom[i+2],i+3,rom[i+3]);
    			end
    		else ;
    	end 
    initial
    begin
      $readmemh("ascii.txt",rom,0,7);  
    end
    assign data_out = {{24{1'b0}},rom[adr]};  
    /*由于用到的数据传输指令只有lb和sb
    因此在读出数据时输出的高24位置0
    写入数据时只取输入数据的低8位*/
    always@(posedge clk)  
      begin
        if(wr_en)
        rom[adr] <= data_in[`BYTE-1:0]; 
        else ; 
      end
    endmodule
    

    5.1.4指令存储器
    指令存储器存放指令,以字为储存单位。其模块示意图如下:
    在这里插入图片描述

    address_in[31:0]为输入指令11存储器的地址
    instr[31:0]输出的指令
    指令存储器模块的Verilog代码如下:

    `include "define.v"
    //指令存储器模块
    module instr_mem( input [`BUS_WIDTH-1:0] address_in, 
     output   [`BUS_WIDTH-1:0] instr); 
    //指令寄存器按字寻址,最小单元是一个字
     reg [`BUS_WIDTH-1:0] rom [`INSTR-1:0];   
    initial
      $readmemh("instru_hex.txt",rom); //将指令存入指令寄存器
    assign instr = rom[address_in[`BUS_WIDTH-1:2]];  
    //输入的地址是按字节编址时的地址,而指令存储器实质上以字为单位,因此输入地址低两位要舍去
    endmodule
    
    

    5.1.5符号扩展模块
    符号扩展模块的功能是将一个16位立即数进行有符号扩展,其模块示意图如下:
    在这里插入图片描述

    imm16[15:0]为输入的16位立即数
    imm32[31:0]为输出的32为立即数
    符号扩展模块的Verilog代码如下:

    `include "define.v"
    //符号扩展模块
    module sign_ext(input [15:0] imm16,
                    output [`BUS_WIDTH-1:0] imm32);  
    assign imm32 = {{16{imm16[15]}},imm16};  //将16位立即数进行有符号扩展到32位
    endmodule
    

    5.1.6控制模块
    控制模块实现的功能是根据指令中的opcode和funct位得到各个部件的控制信号。其模块示意图如下:
    在这里插入图片描述
    opcode是MIPS指令的操作码
    funct是MIPS的R型指令的功能码
    alu_ctr是alu的控制信号
    reg_dst是决定写入寄存器是rd还是rt的信号
    reg_wr是寄存器写使能信号
    alu_src是决定alu第二个操作数是来源于寄存器还是立即数的信号
    mem_wr是数据存储器写使能信号
    mem_to_reg是决定写入寄存器的数据是来源于alu还是存储器的信号
    控制模块的Verilog代码如下:

    `include "define.v"
    //控制器模块
    module control(input [5:0] opcode,funct,output reg [2:0] alu_ctr,
                  output  pc_en,reg_dst,reg_wr,alu_src,mem_wr,mem_to_reg);
    //当遇到停机指令ffff_ffff时,pc_en = 0
    assign pc_en = !((opcode == 6'b111111) && (funct == 6'b111111));
    //reg_dst代表的是往寄存器堆中写入数据是写入rd还是rt寄存器的控制信号,对于R型指令写入rd,对于I型指令写入rt 
    assign reg_dst = (opcode == 6'b0); 
    //reg_wr代表的是寄存器堆的写使能信号,在设计的指令集中所有R型指令均可写入寄存器堆,I型指令中lb和addi可写入
    assign reg_wr = (opcode == 6'b0) | (opcode == `LB) | (opcode == `ADDI); 
    //alu_src代表控制ALU第二个操作数来源的信号,对于R型指令,BEQ指令,第二个操作数为busb,对于I型指令,第二个操作数为立即数
    assign alu_src = (opcode == 6'b0 | opcode == `BEQ);
    //mem_wr代表数据存储器写入使能信号,在设计的指令集中只有sb指令可以写入存储器
    assign mem_wr = (opcode == `SB);
    //mem_to_reg决定写入寄存器的数据是来源与数据存储器还是alu,只有在lb指令时数据来源于存储器
    assign mem_to_reg = (opcode != `LB);
    //alu_ctr是决定alu做何种运算的控制信号
    always@(*)
    begin
      if(opcode == 6'b0) //R型指令
      begin
        case(funct)
        `SLLV: alu_ctr = 3'd1;
        `SRLV: alu_ctr = 3'd2;
        `OR:   alu_ctr = 3'd4;
        `AND:  alu_ctr = 3'd3;
        `ADD:  alu_ctr = 3'd0;
        `SLT:  alu_ctr = 3'd6;
        default: alu_ctr = 3'd0;
        endcase 
      end
      else if(opcode == `BEQ) alu_ctr = 3'd5;  //在I型指令中只有BEQ不是作加法运算
      else alu_ctr = 3'd0;
    end
    endmodule
    

    5.1.7 ALU模块
    ALU模块是运算单元,基于本指令集的处理器的ALU至少要实现加法,逻辑左、右移,按位与、或,“是否相等比较(beq)”这6种运算。其模块示意图如下:
    在这里插入图片描述
    in_a[31:0]为alu的第一个操作数
    in_b[31:0]为alu的第二个操作数
    alu_ctr[2:0]决定alu做何种运算
    branch为分支跳转语句生效的标志信号
    result[31:0]是alu的运算结果
    ALU模块的Verilog代码如下:

    `include "define.v"
    //运算器模块
    module alu(input [`BUS_WIDTH-1:0] in_a,in_b,
               input [2:0] alu_ctr, //至少要实现6种运算
               output  branch,  //分支跳转标志
               output reg [`BUS_WIDTH-1:0] result);
          parameter ADD = 3'd0, SLL = 3'd1, SRL = 3'd2, AND = 3'd3, OR = 3'd4, EQ = 3'd5, SLT = 3'd6;
          assign branch = (alu_ctr == EQ) & (in_a == in_b); 
          //当alu_ctr=EQ,且两个操作数相等时,说明beq指令生效
               always@(*)
               begin
                 case(alu_ctr)
                   ADD: result = in_a + in_b;
                   SLL: result = in_b << in_a; //在仿真时发现汇编指令sllv $1,$2,$3分别对应于rd,rt,rs 
                   SRL: result = in_b >> in_a; //而一般的R型指令的对应关系为rd,rs,rt
                   AND: result = in_a & in_b;
                   OR:  result = in_a | in_b;
                   SLT: result = (in_a < in_b) ? 32'b1 : 32'b0;
                   default: result = 32'b0;
                 endcase              
               end
    endmodule
    

    5.1.8 其他模块
    除了上述主要模块以外,还有一些简单的小模块,其Verilog代码如下:

    //对一些常用的常量进行宏定义
    `define BUS_WIDTH 32  //定义总线位宽位32
    `define INSTR 50  //定义指令存储器大小为50,可存储50条32位指令
    `define DATA_MEM 24 //定义数据存储期大小为24,可存储24个字节
    `define REG_WIDTH 5  //寄存器堆共有32个,需要的地址线为5位
    `define BYTE 8 
    `define SB 6'b101000
    `define LB 6'b100000
    `define SLLV 6'b000100
    `define SRLV 6'b000110
    `define OR 6'b100101
    `define AND 6'b101000
    `define ADD 6'b100000
    `define ADDI 6'b001000
    `define BEQ 6'b000100
    `define SLT 6'b101010
    
    //输入数据位宽为5的2选1选择器
    module mux_5(input [4:0] rd, rt,
                input sel,output [4:0] rw);
          assign rw = sel ? rd : rt;
    endmodule
    
    //位宽为32的2选1选择器
    module mux_32(input [31:0] rd, rt,
                input sel,output [31:0] rw);
          assign rw = sel ? rd : rt;
    endmodule
    

    各个模块的连接示意图如下所示:
    在这里插入图片描述
    根据模块连接图,可写出顶层模块如下所示:

    `include "define.v"
    `include "mux_32.v"
    `include "pc.v"
    `include "instr_mem.v"
    `include "mux_5.v"
    `include "alu.v"
    `include "sign_ext.v"
    `include "reg_file.v"
    `include "data_mem.v"
    `include "control.v"
    module top(input clk,rst);
    wire branch,reg_dst,alu_src,reg_wr,mem_to_reg,mem_wr,pc_en;
    wire [`BUS_WIDTH-1:0] new_pc,pc,instr,operand2,imm32,result,bus_a,bus_b,reg_data,mem_out;
    wire [2:0] alu_ctr;
    wire [4:0] rw;
    pc pc_i(.clk(clk),.rst(rst),.address_in(new_pc),.address_out(pc),.pc_en(pc_en)); //输出pc
    instr_mem inst_mem_i(.address_in(pc),.instr(instr));  //取值
    sign_ext sign_ext_i(.imm16(instr[15:0]),.imm32(imm32)); //立即数符号扩展
    mux_32 mux_1(.rd(pc+32'd4+imm32*4),.rt(pc+32'd4),.sel(branch),.rw(new_pc)); //得到下一pc
    mux_5 mux_2(.rd(instr[15:11]),.rt(instr[20:16]),.rw(rw),.sel(reg_dst)); //确定写寄存器
    mux_32 mux_4(.rd(result),.rt(mem_out),.rw(reg_data),.sel(mem_to_reg));  //确定寄存器堆写入数据的来源
    reg_file reg_file_i(.bus_a(bus_a),.bus_b(bus_b),.clk(clk),.reg_wr(reg_wr),
    .wr_data(reg_data),.rw(rw),.ra(instr[25:21]),.rb(instr[20:16])); //寄存器堆写入与读出 
    mux_32 mux_3(.rd(bus_b),.rt(imm32),.rw(operand2),.sel(alu_src));  //确定ALU第二个操作数
    alu alu_i(.in_a(bus_a),.in_b(operand2),.alu_ctr(alu_ctr),.branch(branch),.result(result)); //alu计算
    data_mem  data_mem_i(.data_in(bus_b),.clk(clk),.wr_en(mem_wr),.adr(result),.data_out(mem_out),.instr(instr)); 
    //数据寄存器的写入与读出
    control control_i(.opcode(instr[31:26]),.funct(instr[5:0]),.alu_ctr(alu_ctr),.pc_en(pc_en), //控制信号产生
    .reg_dst(reg_dst),.reg_wr(reg_wr),.alu_src(alu_src),.mem_wr(mem_wr),.mem_to_reg(mem_to_reg));  
    endmodule
    

    5.2 仿真验证
    由于在编写数据存储器和指令存储器模块时,已经利用$readmemh系统函数将其初始化,所以testbench中只需要加入时钟信号即可编写testbench如下:

    `timescale 100ns/1ns
    `include "top.v"
    module test;
    reg clk = 1'b0, rst = 1'b1; //信号定义
    top top_i(.clk(clk),.rst(rst));//待测模块实例化
    initial
    begin
     #1 rst = 1'b0;
     #1 rst = 1'b1;
     #10000 $finish;
    end
    initial
      forever #5 clk = !clk;
    initial
    begin
      $dumpfile("test.vcd");
      $dumpvars(0,test);
    end
    endmodule
    

    在ISE中进行仿真,得到的结果如下图所示:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    根据仿真结果可知,数据存储的的[7:0]存储着原字符数组,而[15:8]被写入了经过循环移位后的新字符数组,[23:16]被写入了新字符数组中每个字符中的1的个数,且数据与C语言程序运行结果一致,因此可认为设计的单周期处理器实现了要求的功能。

    六、易出错的地方
    1. 为了便于仿真时观察实验结果,在许多模块中加入了不可综合的语句,若要进行综合,并将程序烧录进FPGA中则需要删除这些不可综合的语句。
    2. 一般的设计处理器的流程是先确定需要哪些指令,在根据分析每条指令的数据通路、控制通路,如下图所示
      在这里插入图片描述
      而在本实验中是根据具体的问题要求来设计指令集,汇编程序要用到哪些指令,在微处理器中就考虑哪些指令。
    3. 使用对内存初始化函数readmemh(16进制数据)时,要注意的是数据文件(txt)需要在项目文件夹中,否则就要写出文件的全路径,还需注意的是全路径要用"/“符号,而windows下用的是”"。
    4. 可能要用到的MIPS指令集知识
      在这里插入图片描述
      在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    一般来说,R型指令的汇编语言和机器码的对应关系为:
    add $1, $2, $3 其中$1,$2,$3分别对应机器码的rd,rs,rt位置,
    而sllv与srlv指令有些特殊,其对应关系为:
    sllv $1, $2, $2 其中$1,$2,$3分别对应机器码的rd,rt,rs位置。

    七、完整程序下载

    https://download.csdn.net/download/qq_45439159/13119543

    展开全文
  • 基于RISCV指令集的单周期处理器,实现了RV32I中的lui,auipc,jal,jalr,beq,bne,blt,bge,bltu,bgeu,lb,lh,lw,lbu,lhu,sb,sh,sw,addi,slti,sltiu,xori,ori,andi,slli,srli,srai,add,sub,sll,slt,sltu,xor,srl,sra,or,...
  • sb指令(将地址为rt的通用寄存器的最低字节存储到内存中的指定地址 sw 使用sw指令在0x58处存储0x84838281。 sw指令(将地址为rt的通用寄存器的值存储到内存中的指定地址。该指令有地址对齐要求,要求计算出来的存储...

    指令说明

    加载指令

    31-2625-2120-1615-0useagefunction
    LB(100000)basertoffsetlb rt,offset(base)从内存中指定加载处,读取一个字节,然后符号扩展到32位,保存到地址为rt的寄存器中
    LBU(100100)basertoffsetlbu rt,offset(base)同上,除有符号变无符号外
    LH(100001)basertoffsetlh rt,offset(base)从内存中指定加载处,读取一个半字,然后无符号扩展到32位,保存到地址为rt的寄存器中,该指令有地址对齐要求,要求加载地址的最低位为0
    LHU(100101)basertoffsetlhu rt,offset(base)同上,除有符号变为无符号外
    LW(100011)basertoffsetlw rt,offset(base)从内存中指定的加载地址处,读取一个字,保存到地址为rt的通用寄存器中。该指令有地址对齐要求,要求加载地址的最低位为00
    LWL(100010)basertoffsetlwl rt,offset(base)从内存中指定的加载地址处,加载一个字的最高有效部分lwl指令对加载地址没有要求,允许地址非对齐加载
    LWR(100110)basertoffsetlwr rt,offset(base)从内存中指定的加载地址处,加载一个字的最低有效部分非对齐加载指令,向右加载
    • lwl
      加载地址=offset符号扩展到32位的值+地址为base的通用寄存器的值
      假设计算出来的加载地址是loadaddr,loadaddr的最低两位是n,将loadaddr最低两位设为0后的值为loadaddr_align。
      loadaddr=signed_extended(offset)+GPR(base)
      n=loadaddr[1:0]
      loadaddr_align=loadaddr-n
    • lwr
      假设计算出来的加载地址是loadaddr,loadaddr的最低两位的值为n,将loadaddr最低两位设为0后的值称为loadaddr_align。
      loadaddr=signed_extended(offset)+GPR(base)
      n=loadaddr[1:0]
      loadaddr_align=loadaddr-n

    存储指令

    31-2625-2120-1615-0useagefunction
    SB(101000)basertoffsetsb rt,offset(base)将地址为rt的通用寄存器的最低字节存储到内存中的指定地址
    SH(101001)basertoffsetsh rt offset(base)将地址为rt的通用寄存器的最低两个字节存储到内存中的指定地址。该指令有地址对齐要求,要求计算出来的存储地址最低位为0
    SW(101011)basertoffsetsw rt,offset(base)将地址为rt的通用寄存器的值存储到内存中的指定地址。该指令有地址对齐要求,要求计算出来的存储地址的最低位为00
    SWL(101010)basertoffsetswl rt,offset(base)将地址为rt的通用寄存器的高位部分存储到内存中指定的地址处,存储地址的最后两位确定了要存储rt通用寄存器的哪几个字节。swl指令对存储地址没有对齐要求
    SWR(101110)basertoffsetswr rt,offset(base)将地址为rt的通用寄存器的低位部分存储到内存中的指定地址处,存储地址的最后两位确定了要存储rt通用寄存器的哪几个字节。swr指令对存储地址没有对齐要求

    存储地址=offset符号扩展到32位的值+地址为base的通用寄存器的值

    • swl
      storeaddr=signed_extended(offset)+GPR(base)
      n=storeaddr[1:0]
      storeaddr_align=storeaddr-n
    • swr
      storeaddr=signed_extended(offset)+GPR(base)
      n=storeaddr[1:0]
      storeaddr_align=storeaddr-n

    =============================================================
    好烦啊 用法真烦。。。真是坠了

    加载指令用法示例

    OpenMIPS处理器是按字节寻址,且是大端模式。数据的高位保存在存储器的低地址中,而数据的低位保存在存储器的高地址中。

    sh

    使用sh指令在0x54处存储0x8281
    sh指令(将地址为rt的通用寄存器的最低两个字节存储到内存中的指定地址。该指令有地址对齐要求,要求计算出来的存储地址最低位为0)
    在这里插入图片描述0x8281高位0x82在低地址0x54处存储,低位0x81在低地址0x55处存储。

    sb

    使用sb指令在0x50处存储0x81。
    sb指令(将地址为rt的通用寄存器的最低字节存储到内存中的指定地址
    在这里插入图片描述

    sw

    使用sw指令在0x58处存储0x84838281。
    sw指令(将地址为rt的通用寄存器的值存储到内存中的指定地址。该指令有地址对齐要求,要求计算出来的存储地址的最低位为00)
    在这里插入图片描述

    lbu

    此时使用加载指令lbu从0x58处加载一个字节。
    lbu(从内存中指定加载处,读取一个字节,然后无符号扩展到32位,保存到地址为rt的寄存器中)。
    读出的字节是0x84,经无符号扩展至32位是0x00000084。

    lb

    使用指令lb从0x58处加载一个字节。
    读出来的字节是0x84,经有符号扩展是0xffffff84。

    lhu

    使用指令lhu从0x58处加载一个半字。读出来的半字就是0x8483,经无符号扩展至32位就是0x00008483。

    lh

    使用指令lh从0x58处加载一个半字。读出来的半字就是0x8483,经有符号扩展至32位就是0xffff8483。
    字节与半字(32位处理器)
    所以一个半字是两个字节。
    lh(从内存中指定加载处,读取一个半字,然后无符号扩展到32位,保存到地址为rt的寄存器中,该指令有地址对齐要求,要求加载地址的最低位为0)
    使用lh从0x59处加载一个半字,如果不考虑对齐要求,就是0x8382,(32’b00001111_00000011_00001111_00000010)。考虑到加载地址的最低位是0x59(32’b0000_0000_0000_0101_0000_0000_0000_1001)最低位不是0舍去。
    使用lh从0x5a处加载一个半字,就是0x8281,无符号扩展至32位就是0x00008281

    lw

    使用lw从0x58处加载一个字,读出来的字就是4个字节。0x84838281

    lwl

    假设计算出来的加载地址是5,lwl指令要从地址5加载数据,那么loadaddr=5(0101),n=2’b01=1,loadaddr_align=5-1=4。从4处加载一个字,即4,5,6,7的字节,因为n=1所以将加载到的字的最低3个字节保存到地址为rt的通用寄存器的最高三个字节。从地址为loadaddr_align处加载一个字,4个字节,然后将这个字的最低4-n个字节保存到地址为rt的通用寄存器的高位,低位保持不变。
    在这里插入图片描述

    lwr

    假设计算出来的加载地址是9,lwr指令要从地址9加载数据,那么loadaddr=9,n=1,loadaddr_align=8。从地址为loadaddr_align处加载一个字,4个字节,然后将这个字的最高n+1个字节保存到地址为rt的通用寄存器的低位,并保持高位不变。(和lwl相反)从地址为8处加载一个字,对应的地址是8,9,10,11的字节,n=1,将加载到的字的最高两个字节保存到地址为rt的通用寄存器的低2字节。
    在这里插入图片描述

    swl

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

    swr

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

    浅记一下。

    展开全文
  • 第九章 加载存储指令的实现

    千次阅读 2022-01-30 14:00:57
    9.1.1 加载指令lb,lbu,lh,lhu,lw说明 加载指令lb,lbu,lh,lhu,lw说明 从图中可知,这5条加载指令可以根据指令中26-31bit的指令码加以区分,另外,加载指令的第0-15bit是offset,第21-15bit是base,加载地址的计算方
  • 章将实现MIPS32指令集架构中定义的加载存储指令,分两步:...--6条存储指令sb、sc、sh、sw、swl、swr 9.1.1加载指令lb、lbu、lh、lhu、lw说明 这5条加载指令可以根据指令中26~31bit的指令码加以区分,另外,加载指.
  • RISC-V 指令格式和6种基本整数指令

    万次阅读 多人点赞 2021-09-07 09:22:41
    指令格式是指令使用二进制编码表示的结构形式,一般一条指令分为操作码和地 址码两部分: 一、操作码 操作码表示指令的属性功能和执行的指令类型。操作数对应的二进制位数决定了计算机能够实现的最大指令数目。例如...
  • 汇编器执行如果如下: 有符号表示: 无符号表示: 11.SB:sb指令 将操作数寄存器rs2中的低8位数据,写回存储器 如: addi x1,x0,0xf1 addi x2,x0,0xf2 addi x3,x0,0xf3 addi x4,x0,0xf4 sb x1,0,x0 sb x2,1,x0 sb ...
  • SLT/SLTU/ADDI/ADDIU/ANDI/ORI/XORI/LUI/SLTI/SLTIU/LB/LBU/LH/LH U/LW/SB/SH/SW/BEQ/BNE/BGEZ/BGTZ/BLEZ/BLTZ/J/JAL/JR/JALR} 42条 • ISA2 = {add, sub, addu, subu, addi, ori, lui, and, andi, or, nor,slt, ...
  • 9.1.1 加载指令lb、lbu、lh、lhu、lw说明 9.1.2 存储指令sb、sh、sw说明 9.1.3 加载存储指令用法示例 OpenMIPS处理器是按照字节寻址,并且是大端模式,在这种模式下,数据的高位保存在存储器的低地址中,而数据的...
  • MBUS_MSG指令

    2021-01-20 16:54:31
    MBUS_MSG指令输入/输出操作数数据类型首次布尔I,Q,M,S,SM,T,C,V,L(以上升沿检测元素为条件的功率流)从站字节VB,IB,QB,MB,SB,SMB,LB,AC,Constant*V
  • lb(指令码6’b100000) : 字节 加载指令,用法:lb,rt,offset(base),作用:从内存中指定的加载地址处,读取 一个字节 ,然后 符号扩展 至32位,保存到地址为rt的通用寄存器中 - lbu(指令码6’b100100) : 无符号...
  • 本文是RVSC-V处理器学习的第一篇文章,对RV32IMAFDC指令集的学习笔记。 运算指令 立即数运算指令 addi rd,rs1,imm[11:0] 功能 1.将rs1与12位立即数(进行符号位扩展)相加,并将结果存到rd寄存器中去,如果结果溢出...
  • 例如,一个MIPS CPU支持的部分汇编指令和操作数如下: 计算机中,大多数指令都是无操作数(operand)(参与一个运算(操作,operation)的元素个数)、一个操作数、两个操作数或三个操作数的指令(一些指令集中的...
  • MIPS32指令集架构简介

    千次阅读 2020-12-09 21:47:51
    1.4 MIPS32指令集架构简介 本书设计的处理器遵循MIPS32 Release 1架构,所以本节介绍的MIPS32指令集架构指的就是MIPS32 Release 1。 1.4.1 数据类型 指令的主要任务就是对操作数进行运算,操作数有不同的类型和...
  • 使用Verilog搭建一个单周期CPU

    千次阅读 2020-12-04 12:10:36
    使用Verilog搭建一个单周期CPU 搭建篇 总体结构 其实跟使用logisim搭建CPU基本一致,甚至更简单,因为完全可以照着logisim的电路图来写,各个模块和模块间的连接在...目前支持指令集{addu、subu、ori、lw、sw、be
  • risc-v指令

    千次阅读 2021-07-27 17:05:22
    RISC-V指令集 R TYPE ADD ADD rd, rs1, rs2 //x[rd] = x[rs1] + x[rs2] SUB SUB rd, rs1, rs2 //x[rd] = x[rs1] - x[rs2] SLL SLL rd, rs1, rs2 //x[rd] = x[rs1] << x[rs2] SLT SLT rd, rs1, rs2 //x[rd]...
  • 加载存储指令说明加载指令(7) lb、lbu、lh、lhu、lw、lwl、lwr存储指令(5) sb、sh、sw、swl、swr补充 加载指令(7) lb、lbu、lh、lhu、lw、lwl、lwr op[31:26] base[25:21] rt[20:16] offset[15:0] ...
  • 代码结构 分为数据部分——数据定义初始化、程序部分——操作 数据部分由.data 开始 数据的定义形式: name: .byte 12 //分别是变量名 变量类型,变量值,注意形式 变量类型具有很多种,举几个栗子: ...lb //
  • 计算机组成与设计 第二章 指令:计算机的语言
  • 加载指令 指令名称 功能 ISA type 用法 含义 LA 地址加载,伪指令 RV32I/RV64I 伪指令 la rd, ...
  • 但是 ARM 比较特殊,因为它作为一个 RISC 架构竟然有 push/pop 指令……ARMv7ARM 指令集从旧到新有 ARMv6、ARMv7、ARMv7s、ARMv8,使用新指令集的芯片也能支持旧的指令集,但是无法发挥出较好的性能。同时要注意,...
  • MIPS汇编总结

    千次阅读 2021-05-06 08:59:34
    一、汇编指令 1.为了简化处理器的设计,MIPS处理器采用了固定结构的汇编指令:每个指令由4个部分组成:1个操作符+3个操作数 指令格式:指令代码 操作数1,操作数2,操作数3 2.在汇编指令中,寄存器没有数据类型...
  • 浅谈指令流水线

    千次阅读 2020-08-05 14:41:38
      指令流水线作为计算机组成原理中一个重要的组成部分,弄清指令流水线的操作步骤对制作CPU有着很大的帮助。本文主要讲述下指令流水线的相关知识。   指令流水线是较单周期指令和多周期指令更有效率的一种方式。...
  • 读字节:lb(Load Byte)与lbu(Load Byte Unsigned) 从主存中读取一个字节并且将该字节写入指定的寄存器 格式: lb rt,offset(base) 2.读半字: lh(Load Halfword)与lhu(Load Halfword Unsigned) 3.读字:lw(Load ...
  • mips指令与寄存器详解

    万次阅读 多人点赞 2015-03-28 21:21:36
    功能与LB指令相同,但读出的是不带符号的数据 LBU R1, 0(R2) LHU 功能与LH指令相同,但读出的是不带符号的数据 LHU R1, 0(R2) LWU ...
  • 精简指令集(MIPS为例)常用指令特点及其格式一、指令指令集是指一台计算机的全部指令;不同的计算机有不同的指令集,但是在许多方面都有共同之处。早期的计算机有非常简单的指令集:实现简单,许多现代计算机也有...
  • 32位mips指令说明

    万次阅读 多人点赞 2019-04-27 21:45:32
    指令(1条) nop指令 逻辑运算指令(8条) and、or、xor、nor指令 当功能码是6b100100时,表示是and指令,逻辑“与”运算。 指令用法为:and rd,rs,rt。 指令作用为:rd ← rs AND rt,将地址为rs的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 739
精华内容 295
关键字:

lb sb指令