1.框图
对指令进行分离,op为指令码,rs为要读的第一个寄存器的号,rt为要读的第二个寄存器的号或i-form指令要写的寄存器的号,rd为r-form指令要写的寄存器的号,immediate为指令中的立即数。
对寄存器组进行读操作。
目标寄存器的指定,其中Regdst和JAL都来自于控制单元,JAL说明该指令是否为JAL指令,rt为要读的第二个寄存器的号或i-form指令要写的寄存器的号,rd为r-form指令要写的寄存器的号,31为寄存器组的最大序号。
read_data为从 DATA RAM 取出的数据ALU_result为从执行单元来的运算的结果opcplus4来自取指单元,JAL 中用,Jal和MemtoReg来着控制单元,write_data用于储存要写寄存器的数据。
2.子模块设计
对于分离指令变量的模块,只需简单的将从取指单元送来的指令Instruction按对应位数分配给不同的变量即可,包括31-26交给opcode即OP信号,25-21交给read_register_1_address即rs信号,20-16交给read_register_2_address即rt信号,15-11交给write_register_address_1即rd(r-form)信号,20-16交给write_register_address_0即rt(i-form)信号,15-0交给Instruction_immediate_value即立即数。
定义寄存器组模块,只是定义,无需初始化,只需要简单的定义一个由32个32位寄存器组成的寄存器组。
对于目标寄存器的指定模块则需要根据RegDst和Jal信号的不同来给write_register_address信号附上不同的值,若RegDst信号为1,Jal信号为0,则赋值为rd,若RegDst信号为0,Jal信号为1,则赋值为31(5’b11111),其他情况,即RegDst和Jal信号均为0时,则赋值为rt,由于RegDst和Jal不同时为1忽略其赋值。
第四模块,即对需要写的数据进行准备,需根据MemtoReg和Jal信号的不同,对write_data写入不同的数据,若MemtoReg和Jal均为0则写入执行单元的运算结果,若MemtoReg为0,Jal为1,则写入pc+4,只要MemtoReg为1,则写入从DATA RAM or I/O port取出的数据。
第五模块实现对寄存器的写操作,在写入之前或者reset信号为1时需要对寄存器组进行初始化,这里是全赋值为0,当RegWrite信号为1时,根据write_register_address信号的值,即根据要写的寄存器的号,对寄存器组进行写入,其中需注意寄存器0恒等于0。
第六个模块实现的是16位立即数的32位扩展,首先取出立即数的最高位,即符号位,然后运用三目运算符,在andi,ori,xori,sltiu四个指令下执行无符号扩展,在不足的位数上补零,在其他指令下执行有符号位扩展,在不足的为位数上补符号位的值。
五、程序清单
1.设计文件
module Idecode32 (
input reset,
input clock,
output [31:0] read_data_1, // 输出的第一操作数
output [31:0] read_data_2, // 输出的第二操作数
input [31:0] Instruction, // 取指单元来的指令
input [31:0] read_data, // 从DATA RAM or I/O port取出的数据
input [31:0] ALU_result, // 从执行单元来的运算的结果,需要扩展立即数到32位
input Jal, // 来自控制单元,说明是JAL指令
input RegWrite, // 来自控制单元
input MemtoReg, // 来自控制单元
input RegDst, // 来自控制单元
output [31:0] Sign_extend, // 译码单元输出的扩展后的32位立即数
input [31:0] opcplus4 // 来自取指单元,JAL中用
);
reg[31:0] register[0:31]; //寄存器组共32个32位寄存器
reg[4:0] write_register_address; // 要写的寄存器的号
reg[31:0] write_data; // 要写寄存器的数据放这里
wire[4:0] read_register_1_address; // 要读的第一个寄存器的号(rs)
wire[4:0] read_register_2_address; // 要读的第二个寄存器的号(rt)
wire[4:0] write_register_address_1; // r-form指令要写的寄存器的号(rd)
wire[4:0] write_register_address_0; // i-form指令要写的寄存器的号(rt)
wire[15:0] Instruction_immediate_value; // 指令中的立即数
wire[5:0] opcode; // 指令码
assign opcode = Instruction[31:26]; //OP
assign read_register_1_address = Instruction[25:21];//rs
assign read_register_2_address = Instruction[20:16];//rt
assign write_register_address_1 = Instruction[15:11];// rd(r-form)
assign write_register_address_0 = Instruction[20:16];//rt(i-form)
assign Instruction_immediate_value = Instruction[15:0];//data,rladr(i-form)
wire sign; // 取符号位的值
assign sign = Instruction_immediate_value[15];
assign Sign_extend[31:0] = ((opcode == 6'b001100) || //andi
(opcode == 6'b001101) || //ori
(opcode == 6'b001110) || //xori
(opcode == 6'b001011))? //sltiu
{16'h0000,Instruction_immediate_value[15:0]}: //无符号扩展,补零
{sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,sign,Instruction_immediate_value[15:0]}; //有符号位扩展,补符号位
assign read_data_1 = register[read_register_1_address]; //取register[rs]
assign read_data_2 = register[read_register_2_address]; //取register[rd]
always @* begin //这个进程指定不同指令下的目标寄存器
if((RegDst == 1) && (Jal == 0)) //r-form jal (pc+1 > sp[$Rlast])
write_register_address = write_register_address_1; //RD(15-11)
else if((RegDst == 0) && (Jal == 1))
write_register_address = 5'b11111; //JAL
else write_register_address = write_register_address_0; // rt(20-16)
end
always @* begin
//这个进程基本上是实现结构图中右下的多路选择器,准备要写的数据
if((MemtoReg == 0) && (Jal == 0)) begin
write_data = ALU_result[31:0]; //写入执行单元的运算结果
end else if ((MemtoReg == 0) && (Jal == 1)) begin
write_data = opcplus4; //写入pc+4
end else begin
write_data = read_data; //写入从DATA RAM or I/O port取出的数据
end
end
integer i;
always @(posedge clock) begin // 本进程写目标寄存器
if(reset==1) begin // 初始化寄存器组
for(i=0;i<32;i=i+1) register[i] <= 0;
end else if(RegWrite==1) begin // 注意寄存器0恒等于0
case(write_register_address[4:0]) //根据要写的寄存器的号,对寄存器组进行写入
5'd0:register[0] <= 32'd0; //寄存器0恒等于0
5'd1:register[1] <= write_data; //寄存器1赋值
5'd2:register[2] <= write_data; //寄存器2赋值
5'd3:register[3] <= write_data; //寄存器3赋值
5'd4:register[4] <= write_data; //寄存器4赋值
5'd5:register[5] <= write_data; //寄存器5赋值
5'd6:register[6] <= write_data; //寄存器6赋值
5'd7:register[7] <= write_data; //寄存器7赋值
5'd8:register[8] <= write_data; //寄存器8赋值
5'd9:register[9] <= write_data; //寄存器9赋值
5'd10:register[10] <= write_data;//寄存器10赋值
5'd11:register[11] <= write_data;//寄存器11赋值
5'd12:register[12] <= write_data;//寄存器12赋值
5'd13:register[13] <= write_data;//寄存器13赋值
5'd14:register[14] <= write_data;//寄存器14赋值
5'd15:register[15] <= write_data;//寄存器15赋值
5'd16:register[16] <= write_data;//寄存器16赋值
5'd17:register[17] <= write_data;//寄存器17赋值
5'd18:register[18] <= write_data;//寄存器18赋值
5'd19:register[19] <= write_data;//寄存器19赋值
5'd20:register[20] <= write_data;//寄存器20赋值
5'd21:register[21] <= write_data;//寄存器21赋值
5'd22:register[22] <= write_data;//寄存器22赋值
5'd23:register[23] <= write_data;//寄存器23赋值
5'd24:register[24] <= write_data;//寄存器24赋值
5'd25:register[25] <= write_data;//寄存器25赋值
5'd26:register[26] <= write_data;//寄存器26赋值
5'd27:register[27] <= write_data;//寄存器27赋值
5'd28:register[28] <= write_data;//寄存器28赋值
5'd29:register[29] <= write_data;//寄存器29赋值
5'd30:register[30] <= write_data;//寄存器30赋值
5'd31:register[31] <= write_data;//寄存器31赋值
default:register[0] <= 32'd0;
endcase
end
end
endmodule
2.仿真文件
`timescale 1ns / 1ps
module idcode32_sim ();
// input
reg[31:0] Instruction = 32'b000000_00010_00011_00111_00000_100000; //add $7,$2,$3
reg[31:0] read_data = 32'h00000000; // 从DATA RAM or I/O port取出的数据
reg[31:0] ALU_result = 32'h00000005; // 需要扩展立即数到32位
reg Jal = 1'b0;
reg RegWrite = 1'b1;
reg MemtoReg = 1'b0;
reg RegDst = 1'b1;
reg clock = 1'b0 ,reset = 1'b1;
reg[31:0] opcplus4 = 32'h00000004; // 来自取指单元,JAL中用
// output
wire[31:0] read_data_1;
wire[31:0] read_data_2;
wire[31:0] Sign_extend;
Idecode32 Uid (
.reset (reset), // 复位(高电平有效)
.clock (clock), // CPU时钟
.read_data_1 (read_data_1), // 输出的第一操作数
.read_data_2 (read_data_2), // 输出的第二操作数
.Instruction (Instruction), // 取指单元来的指令
.read_data (read_data), // 从DATA RAM or I/O port取出的数据
.ALU_result (ALU_result), // 从执行单元来的运算的结果,需要扩展立即数到32位
.Jal (Jal), // 来自控制单元,说明是JAL指令
.RegWrite (RegWrite), // 来自控制单元
.MemtoReg (MemtoReg), // 来自控制单元
.RegDst (RegDst), // 来自控制单元
.Sign_extend (Sign_extend), // 扩展后的32位立即数
.opcplus4 (opcplus4) // 来自取指单元,JAL中用
);
initial begin
#200 reset = 1'b0;
#200 begin Instruction = 32'b001000_00111_00011_1000000000110111; //addi $3,$7,0X8037
read_data = 32'h00000000;
ALU_result = 32'hFFFF803C;
Jal = 1'b0;
RegWrite = 1'b1;
MemtoReg = 1'b0;
RegDst = 1'b0;
opcplus4 = 32'h00000008;
end
#200 begin Instruction = 32'b001100_00010_00100_1000000010010111; //andi $4,$2,0X8097
read_data = 32'h00000000;
ALU_result = 32'h00000002;
Jal = 1'b0;
RegWrite = 1'b1;
MemtoReg = 1'b0;
RegDst = 1'b0;
opcplus4 = 32'h0000000c;
end
#200 begin Instruction = 32'b000000_00000_00001_00101_00010_000000; //sll $5,$1,2
read_data = 32'h00000000;
ALU_result = 32'h00000004;
Jal = 1'b0;
RegWrite = 1'b1;
MemtoReg = 1'b0;
RegDst = 1'b1;
opcplus4 = 32'h00000010;
end
#200 begin Instruction = 32'b100011_00000_00110_0000000100000000; //LW $6,0(0X100)
read_data = 32'h0000007B;
ALU_result = 32'h00000054;
Jal = 1'b0;
RegWrite = 1'b1;
MemtoReg = 1'b1;
RegDst = 1'b0;
opcplus4 = 32'h00000014;
end
#200 begin Instruction = 32'b000011_00000000000000000000000000; //JAL 0000
read_data = 32'h00000000;
ALU_result = 32'h00000004;
Jal = 1'b1;
RegWrite = 1'b1;
MemtoReg = 1'b0;
RegDst = 1'b0;
opcplus4 = 32'h00000018;
end
end
always #50 clock = ~clock;
endmodule
六、时序仿真结果及分析和硬件测试结果及分析
1.程序仿真图:
2.管脚分配图: