杭电计算机组成原理课程设计-实验十三-实现R-I-J型指令的CPU设计实验

实验内容

设计一个MIPS单周期R-I-J-CPU

实验原理

实验流程图
在这里插入图片描述

R-I-J CPU 指令大全
在这里插入图片描述
控制信号大全
在这里插入图片描述

汇编指令与.coe文件

汇编指令设计表格(beq_j)
地址机器代码汇编指令
[0x00400000]0x00004020add $8, $0, $0
[0x00400004]0x20090003addi $9, $0, 3
[0x00400008]0x200a0005addi $10, $0, 5
[0x0040000c]0x200b000aaddi $11, $0, a
[0x00400010]0x010b4020add $8, $8, $11
[0x00400014]0x2129ffffaddi $9, $9, -1
[0x00400018]0x016a5820add $11, $11, $10
[0x0040001c]0x11200001beq $9, $0, 4 [Loop2-0x0040001c]
[0x00400020]0x08000004j 0x00400010 [Loop1]
[0x00400024]0xac080020sw $8, 32($0)
[0x00400028]0x08000000j 0x00400000 [main]

在这里插入图片描述
.coe文件内容(beq_j)

memory_initialization_radix=16;
memory_initialization_vector=00004020,20090003,200a0005,200b000a,010b4020,2129ffff,016a5820,11200001,08000004,ac080020,08000000,01d89024,02299820,0253a025,01b1a804,02b1b004,016eb820,0009f880,00000820,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,3456789a,456789ab,56789abc,6789abcd;
汇编指令设计表格(bne_jal)
地址机器代码汇编指令
[0x00400000]0x00002020add $4, $0, $0
[0x00400004]0x20090003addi $9, $0, 3
[0x00400008]0x200a0005addi $10, $0, 5
[0x0040000c]0x200b000aaddi $11, $0, 10
[0x00400010]0x0c00000ajal 0x00400028 [addD]
[0x00400014]0x2129ffffaddi $9, $9, -1
[0x00400018]0x016a5820add $11, $11, $10
[0x0040001c]0x1520fffcbne $9, $0, -16 [Loop1-0x0040001c]
[0x00400020]0xac040020sw $4, 32($0)
[0x00400024]0x08000000j 0x00400000 [main]
[0x00400028]0x008b2020add $4, $4, $11
[0x0040002c]0x03e00008jr $31

在这里插入图片描述

.coe文件内容(bne_jal)

memory_initialization_radix=16;
memory_initialization_vector=00002020,20090003,200a0005,200b000a,0c00000a,2129ffff,016a5820,1520fffc,ac080020,08000000,008b2020,03e00008,01798826,01d89024,02299820,0253a025,01b1a804,02b1b004,016eb820,0009f880,00000820,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,3456789a,456789ab;

mem.coe文件内容 (Mem存储器)

memory_initialization_radix=16;
memory_initialization_vector=00000010,00632020,00010fff,00000008,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd,00000820,00632020,00010fff,20006789,ffff0000,0000ffff,88888888,99999999,aaaaaaaa,bbbbbbbb,12345678,23456789,3456789a,456789ab,56789abc,6789abcd;

.coe文件内容由汇编语言翻译而来,详情见 MIPS汇编器与模拟器实验
根据.coe文件创建IP核,IP核的创建详情见 ISE IP核创建教程

功能模块说明

module R_I_J_CPU(
	clk,rst,clk_m,
    Inst_code,PC,
    opcode,rs,rt,rd,shamt,func,imm,offset,
	ALU_F,ZF,OF,ALU_OP,
	imm_s,rt_imm_s,
	ALU_B,R_Data_A,W_Addr,W_Data,imm_kz,R_Data_B,M_R_Data,Write_Reg,Mem_Write,Mem_Addr,
	 PC_s,PC_next,w_r_s,wr_data_s
    );
	input clk;//时钟
    input rst;//清零
	input clk_m;//Mem时钟
	output reg [31:0]PC;//当前PC地址
	output reg [31:0]PC_next;//PC转移地址
    wire [31:0]PC_new; //PC+4
    output [31:0]Inst_code;//取出的指令
    output [5:0]opcode,func;//指令分段
    output [4:0]rs,rt,rd,shamt;//指令分段
	output [15:0]imm,offset;//指令分段
	wire [25:0]address;//指令分段
    output [31:0] ALU_F;//ALU结果
	output reg [2:0] ALU_OP;// ALU运算的OP
	output ZF,OF;    //零标志位和溢出标志位
	output reg Write_Reg;	//寄存器写入信号
	output reg Mem_Write; //MEM写入信号
	output [31:0]R_Data_A;	//REGS R_Data_A
	output [31:0]R_Data_B; //REGS R_Data_B
	output [31:0]M_R_Data; //MEM读出数据 M_R_Data
	output [7:0]Mem_Addr; //Mem地址
	output reg imm_s;		//控制立即数扩展方式的信号
	output reg rt_imm_s; //控制ALU_B口输入数据的信号
	output [4:0]W_Addr;	//REGS写入地址
	output [31:0]ALU_B;	//ALU_B口数据
	output [31:0]W_Data;	//REGS写入数据
	output reg [31:0]imm_kz;	//扩展后的立即数
	output reg [1:0]PC_s; //控制PC下一次转移为何种方式的信号
	output reg[1:0]w_r_s; //控制w_addr采用何种地址的信号
	output reg[1:0]wr_data_s;	//控制W_Data采用哪种数据的信号

逻辑引脚图

在这里插入图片描述

仿真时序波形图(以beq-J组合为例)

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

R-I CPU 完整代码

module R_I_J_CPU(
	clk,rst,clk_m,
    Inst_code,PC,
    opcode,rs,rt,rd,shamt,func,imm,offset,
	ALU_F,ZF,OF,ALU_OP,
	imm_s,rt_imm_s,
	 ALU_B,R_Data_A,W_Addr,W_Data,imm_kz,R_Data_B,M_R_Data,Write_Reg,Mem_Write,Mem_Addr,
	 PC_s,PC_next,w_r_s,wr_data_s
    );
	input clk;//时钟
    input rst;//清零
	input clk_m;
	output reg [31:0]PC;//地址
	output reg [31:0]PC_next;
    wire [31:0]PC_new;
    output [31:0]Inst_code;//取出的指令
    output [5:0]opcode,func;//指令分段
    output [4:0]rs,rt,rd,shamt;//指令分段
	output [15:0]imm,offset;//指令分段
	wire [25:0]address;//指令分段
    output [31:0] ALU_F;//ALU结果
	output reg [2:0] ALU_OP;//ALU结果
	output ZF,OF;
	output reg Write_Reg;
	output reg Mem_Write;
	output [31:0]R_Data_A;
	output [31:0]R_Data_B;
	output [31:0]M_R_Data;
	output [7:0]Mem_Addr;
	output reg imm_s;
	output reg rt_imm_s;
	output [4:0]W_Addr;
	output [31:0]ALU_B;
	output [31:0]W_Data;
	output reg [31:0]imm_kz;
	output reg [1:0]PC_s;
	output reg[1:0]w_r_s;
	output reg[1:0]wr_data_s;
	
	 initial PC = 32'h00000000;
	 assign PC_new = PC + 4;
	 

Rom_J ROM1 (
  .clka(clk), // input clka
  .addra(PC[7:2]), // input [5 : 0] addra
  .douta(Inst_code) // output [31 : 0] douta
);
	
	always @(*)
    case (PC_s)   //根据PC_s ,赋值PC_next
	2'b00:	PC_next = PC_new;	
	2'b01:	PC_next = R_Data_A;	
	2'b10:	PC_next = PC_new + (imm_kz<<2);	
	2'b11:	PC_next = {PC_new[31:28],address,2'b00};	
   endcase
	
	always @(negedge clk or posedge rst)
  begin
	if (rst)
		PC = 32'h00000000; //PC复位;
	else
		PC = PC_next; //PC更新为PC+4;
end;

	assign opcode =  Inst_code[31:26];
	assign rs =  Inst_code[25:21];
	assign rt =  Inst_code[20:16];
	assign rd=  Inst_code[15:11];
	assign shamt = Inst_code[10:6];
	assign func =  Inst_code[5:0];
	assign imm= Inst_code[15:0];
	assign offset= Inst_code[15:0];
	assign address = Inst_code[25:0];

	 always @(*)
		begin
		
		ALU_OP = 3'b100;	//默认做加法
		imm_s = 1'b0;		//默认对立即数/偏移量进行0扩展
		rt_imm_s = 1'b0;	//默认读出rt寄存器的数据送ALU_B	
		Write_Reg = 1'b1;	//默认写寄存器      
      Mem_Write = 1'b0;	//默认不写存储器	
		PC_s = 2'b00;
		w_r_s = 2'b00;
		wr_data_s = 2'b00;
		
    if (opcode==6'b000000)    //R指令
     begin
		case (func) 
		6'b100000:begin ALU_OP=3'b100;end   //add
		6'b100010:begin ALU_OP=3'b101;end	//sub
		6'b100100:begin ALU_OP=3'b000;end	//and
		6'b100101:begin ALU_OP=3'b001;end	//or
		6'b100110:begin ALU_OP=3'b010;end	//xor
		6'b100111:begin ALU_OP=3'b011;end	//nor
		6'b101011:begin ALU_OP=3'b110;end	//stlu
		6'b000100:begin ALU_OP=3'b111;end	//sllv
		6'b001000:begin Write_Reg=0;Mem_Write=0;PC_s = 2'b01; end   //jr
		endcase
    end
	 else
		begin
		case(opcode)
		6'b001000:begin w_r_s=2'b01;imm_s=1;rt_imm_s=1;ALU_OP=3'b100;end  //addi
		6'b001100:begin w_r_s=2'b01;rt_imm_s=1;ALU_OP=3'b000; end  //andi
		6'b001110:begin w_r_s=2'b01;rt_imm_s=1;ALU_OP=3'b010;end  //xori
		6'b001011:begin w_r_s=2'b01;rt_imm_s=1;ALU_OP=3'b110; end  //sltiu
		6'b100011:begin w_r_s=2'b01;imm_s=1;rt_imm_s=1;wr_data_s=2'b01;ALU_OP=3'b100; end  //lw
		6'b101011:begin imm_s=1;rt_imm_s=1;ALU_OP=3'b100;Write_Reg=0;Mem_Write=1; end  //sw
		6'b000100:begin ALU_OP=3'b101;PC_s = (ZF)?2'b10:2'b00; Write_Reg = 1'b0;end  //beq
		6'b000101:begin ALU_OP=3'b101;PC_s = (ZF)?2'b00:2'b10; Write_Reg = 1'b0;end  //bne
		6'b000010:begin Write_Reg=0;Mem_Write=0;PC_s = 2'b11; end  //j 
		6'b000011:begin w_r_s=2'b10;wr_data_s=2'b10;Write_Reg=1;Mem_Write=0;PC_s = 2'b11; end  //jal
		endcase
		end
	end;
	 
	always @(*)
		begin
		if(imm_s==1'b0)
			begin
			imm_kz={{16{1'b0}},imm};
			end
		if(imm_s==1'b1)
			begin
			case(imm[15])
			1'b1:imm_kz={{16{1'b1}},imm};
			1'b0:imm_kz={{16{1'b0}},imm};
			endcase
		end
	end;
	
REGS REGS_1(R_Data_A,R_Data_B,W_Data,rs,rt,W_Addr,Write_Reg,rst,~clk);
	 ALU ALU_1(ALU_OP,R_Data_A,ALU_B,ALU_F,ZF,OF);
	 	 RAM_B RAM1 (
  .clka(clk_m), // input clka
  .wea(Mem_Write), // input [0 : 0] wea
  .addra(Mem_Addr[7:2]), // input [5 : 0] addra
  .dina(R_Data_B), // input [31 : 0] dina
  .douta(M_R_Data) // output [31 : 0] douta
);
    assign W_Addr=(w_r_s[1]) ? 5'b11111 : ((w_r_s[0])?rt:rd);
	 assign ALU_B=(rt_imm_s)?imm_kz:R_Data_B;
    assign Mem_Addr=ALU_F[7:0];
	 assign W_Data = (wr_data_s[1])?PC_new :((wr_data_s[0])? M_R_Data:ALU_F);
endmodule

module REGS(R_Data_A,R_Data_B,W_Data,R_Addr_A,R_Addr_B,W_Addr,Write_Reg,rst,clk);
	input clk;//写入时钟信号
    input rst;//清零信号
    input Write_Reg;//写控制信号
    input [4:0]R_Addr_A;//A端口读寄存器地址
    input [4:0]R_Addr_B;//B端口读寄存器地址
    input [4:0]W_Addr;//写寄存器地址
    input [31:0]W_Data;//写入数据
	output [31:0]R_Data_A;//A端口读出数据
    output [31:0]R_Data_B;//B端口读出数据
	 
	 integer i;
	 reg [31:0] REG_Files[0:31];  
    initial
        for(i=0;i<32;i=i+1) REG_Files[i]<=0;
    always@(posedge clk or posedge rst)
    begin
        if(rst)
                for(i=0;i<32;i=i+1) REG_Files[i]<=0;
        else
                if(Write_Reg&&W_Addr!=32'd0) REG_Files[W_Addr]<=W_Data;
    end
    assign R_Data_A=REG_Files[R_Addr_A];
    assign R_Data_B=REG_Files[R_Addr_B];
endmodule


module ALU(ALU_OP,A,B,F,ZF,OF);
	  input  [2:0] ALU_OP;
	  input  [31:0] A;
	  input  [31:0] B;
	  output [31:0] F;
	  output  ZF;
	  output  OF;
	  reg [31:0] F;
	  reg    C,ZF;
	  
	 always@(*)
	  begin
		C=0;
		case(ALU_OP)
			3'b000:begin F=A&B; end
			3'b001:begin F=A|B; end
			3'b010:begin F=A^B; end
			3'b011:begin F=~(A|B); end 
			3'b100:begin {C,F}=A+B; end 
			3'b101:begin {C,F}=A-B; end 
			3'b110:begin F=A<B; end
			3'b111:begin F=B<<A; end
		endcase
		ZF = F==0;
		end
		assign OF = ((ALU_OP==3'b100)||(ALU_OP==3'b101))&&(A[31] ^ B[31] ^ F[31] ^ C); 
endmodule

测试用例代码

	always #13 clk_m=~clk_m;
	always #47 clk=~clk;
	
	initial begin
	
		clk = 0;
		rst = 1;
		clk_m = 0;
		#3;
      rst=0;
	end
      
endmodule

探索与思考(非标准答案)

  1. 谈谈你是如何产生测试程序的机器码的?仔细分析测试程序中,转移指令的 offset字段和 address 字段的编码,计算出转移地址,观察是否和你的转移目标地址一致。
    在这里插入图片描述
    答:首先编写如图所示汇编指令,
        之后用pcspim工具翻译机器代码,
        将涉及J与jal转移指令的机器代码第6位的1改为0,即可得到相应机器代码。测试程序中,转移指令的 offset字段和 address 字段的编码,转移地址,和我的转移目标地址一致。

  2. I 型指令 bltzal rs, label(branch if less than zero,and link)的功能是:若寄存器 rs 小于0,则转移并链接,相对当前指令(PC+4)转移的指令数由 offset 来决定。它的 OP编码为 6’b000001,rt 字段为 5’b10000,试着实现该指令,谈谈你的实现方法。
    答:将rt_imm_s信号更改为2位信号,只有执行bltzal rs指令时改信号为2,当信号为2时,ALU_B送入数据为0,且R-I cpu执行sltiu运算,即将RS地址数据与0进行比较。当ALU_F为1时,PC_s=2,即采用PC+4与offset决定PC转移地址,否则PC_s为0,即PC=PC+4。

  3. 基于图 11-60 ,添加一个输入设备(逻辑开关)和一个输出设备(LED 灯),假如
    直接用 I 型指令实现输入输出功能,输入指令 in 和输出指令 out 的格式也是 I 型指
    令。请画出修改后的系统结构图,写出这两条指令对应的控制信号,并编程实现该结构图。

在这里插入图片描述
如图所示,指令设备号由rs低位控制。
当IO-R为1时,输入指令。IO-W为1时,输出指令(需CLK)。
扩展wr_data_s,执行输入指令时其为3,即W_Data写入输入指令。
在这里插入图片描述

  1. 假设有一条指令 hlt,功能是停机,请修改图 11-60 所示的系统结构图,使得 PC_new 4→PC,从而实现 hlt 指令,写出 hlt 指令对应的控制信号,并编程实现该结构图。
    在这里插入图片描述
	always @(*)
    case (PC_s)   //根据PC_s ,赋值PC_next
	2'b000:	PC_next = PC_new;	
	2'b001:	PC_next = R_Data_A;	
	2'b010:	PC_next = PC_new + (imm_kz<<2);	
	2'b011:	PC_next = {PC_new[31:28],address,2'b00};	
	2'b100:	PC_next = PC_new-4;
   endcase

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值