基本要求:利用Verilog HDL语言,基于Xilinx FPGA nexys4实验平台,设计一个能够执行以下MIPS指令集的单周期类MIPS处理器,要求完成所有支持指令的功能仿真,验证指令执行的正确性;
支持基本的算术逻辑运算如add,sub,and,or,slt, a n d i \textcolor{red}{andi} andi指令;
支持基本的内存操作如lw,sw指令;
支持基本的程序控制如beq,j指令。
根据单周期mipsCPU的结构分析知,可以将子模块分为数据存储器dram、指令存储器irom、寄存器文件RegFile、主控制译码MainCtr、ALU控制信号译码ALUCtr以及算术逻辑单元ALU,分别实现各个模块的功能;
再将控制信号等在mips的顶层模块中使用assign语句进行赋值,同时在顶层模块中进行其余子模块的实例化,使之与控制信号以及32位Instr等进行关联,对于不同的指令都有对应于各自的控制信号;
使用vivado的IPcore实现指令存储器irom的逻辑功能。在时钟上升沿到来时,读入PC信号的高五位,作为地址,输出相应的32位Instr指令,此处我们在vivado中导入text.coe到irom_IP中,其本质为16进制的32位机器码,机器码对照表如下:
ps:此处的起始地址应该为0x00000000,j指令也应该相应地跳回0x00000000.
使用vivado的IPcore实现数据存储器dram的逻辑功能。在时钟下降沿到来时,根据写数据的使能信号MenWr决定是否在输入的地址addr处写入输入的数据DataIn,除此之外还会输出存储在输入的地址addr处的数据到DataOut;
使用Verilog语言编写实现寄存器文件RegFile的逻辑功能。在时钟下降沿到来时,倘若写使能信号RegWr为高电平且复位信号reset为低电平,则将输入的数据DataIn写到地址为输入写地址WrAddr处的寄存器regs[WrAddr];倘若复位信号reset为高电平,则我们将regs的值设定为初始默认值regs[i]=i(按照要求也可将其写为学号的ASCII码)。模块中同时使用assign语句决定RsData和RtData的值是0或者地址为RsAddr或RtAddr的寄存器的值。
使用Verilog语言编写实现主控制译码MainCtr的逻辑功能。输入Instr的高六位,输出控制信号和ALUOp,根据每个指令调用的模块得到各个控制信号的状态,再将ALUOp输入到ALU控制信号译码ALUCtr中去。
使用Verilog语言编写实现ALU控制信号译码ALUCtr的逻辑功能。输入ALUOp和Instr的低六位Funct,输出各个指令相应的ALUCtr信号,并且输入到算术逻辑单元ALU中去,其中andi和and的ALUOp和Funct虽然不同,但ALUCtr是一致的。
使用Verilog语言编写算术逻辑单元ALU。输入符号数字In1和符号数In2,由输入的ALUCtr信号觉得要做的运算,事实上我们采用的编码让sw和lw在算术逻辑单元ALU中所做的运算为add,andi所做的为and,beq所做的为sub。并且Zero信号默认为0,只有在执行sub运算时有机会为1,当其为1时,BranchZ = B & Zero才有机会为1,让PC跳转到BranchPC = ImmL2 + SquencePC,即左移两位的立即数与PC加4后的和。
顶层模块的输入信号仅有时钟信号clk和复位信号reset,声明一系列中间信号,使用assign语句和子模块实例化实现指令功能的完成以及控制信号的传递,数据的读写运算等。除此之外,使用always语句实现PC信号的选择和PC的复位。
由于不同的指令需求,对于ALUCtr和MainCtr的编码不同,笔者在此处给出部分源码:
`timescale 1ns / 1ps
module mips (
input clk,
input reset
);
wire [31:0] TempPC, JumpPC, MuxPC, SquencePC, BranchPC, Imm32, ImmL2, regWriteData, RsData, RtData, ALUin2, ALUres, memReadData, Instr;
wire [4:0] regWriteAddr;
wire [27:0] PsudeoPC;
wire [1:0] ALUop;
wire [3:0] ALUctr;
wire BranchZ, regDst, ALUsrc, M2R, regWriteEn, zero, memWriteEn, B, J;
reg [31:0] PC;
assign PsudeoPC = {Instr[25:0], 2'b00};
assign JumpPC = {SquencePC[31:28], PsudeoPC};
assign TempPC = J ? JumpPC : MuxPC;
assign MuxPC = BranchZ ? BranchPC : SquencePC;
assign BranchPC = ImmL2 + SquencePC;
assign ImmL2 = {Imm32[29:0], 2'b00};
assign Imm32 = {Instr[15] ? 16'hffff : 16'h0000, Instr[15:0]};
assign BranchZ = B & zero;
assign regWriteAddr = regDst ? Instr[15:11] : Instr[20:16];
assign ALUin2 = ALUsrc ? Imm32 : RtData;
assign regWriteData = M2R ? memReadData : ALUres;
assign SquencePC = PC + 4;
always @(negedge clk) begin
if(reset)
PC <= 32'h0;
else
PC <= TempPC;
end
ALU U0(RsData,ALUin2,ALUctr,ALUres,zero);
//dram U1(memWriteEn,ALUres[6:2],memReadData,RtData);
//irom U2(PC[6:2],Instr);
dram_IP U1(~clk,memWriteEn,ALUres[6:2],RtData,memReadData);
irom_IP U2(clk,PC[6:2],Instr);
RegFile U3(~clk,reset,regWriteEn,Instr[25:21],Instr[20:16],regWriteAddr,regWriteData,RsData,RtData);
MainCtr U4(Instr[31:26],regDst,ALUsrc,M2R,regWriteEn,memWriteEn,B,J,ALUop);
ALUCtr U5(ALUop,Instr[5:0],ALUctr);
endmodule
`timescale 1ns / 1ps
module ALU (
input signed [31:0] In1,
input signed [31:0] In2,
input [3:0] ALUCtr,
output reg [31:0] Res,
output reg Zero
);
always @(In1 or In2 or ALUCtr) begin
case (ALUCtr)
4'b0110://sub
begin
Res = In1 - In2;
Zero = (Res == 0) ? 1 : 0;
// only sub needs Zero be 1
end
4'b0010://add
begin
Res = In1 + In2;
Zero = 0;
end
4'b0000://and
begin
Res = In1 & In2;
Zero = 0;
end
4'b0001://or
begin
Res = In1 | In2;
Zero = 0;
end
4'b0111://slt
begin
Res = (In1 < In2) ? 1 : 0;
Zero = 0;
end
default:
begin
Res = 0;
Zero = 0;
end
endcase
end
endmodule
`timescale 1ns / 1ps
module RegFile (
input clk,
input reset,
input RegWr,//regWriteEn
input [4:0] RsAddr,
input [4:0] RtAddr,
input [4:0] WrAddr,//regWriteAddr
input [31:0] DataIn,//regWriteData
output [31:0] RsData,
output [31:0] RtData
);
// 32 regs, for each one is 32
reg [31:0] regs[0:31];
assign RsData = (RsAddr == 5'b0) ? 32'b0 : regs[RsAddr];
assign RtData = (RtAddr == 5'b0) ? 32'b0 : regs[RtAddr];
integer i;
always @(posedge clk) begin
if(!reset & RegWr) begin
regs[WrAddr] = DataIn;
end
else if(reset) begin
for (i = 0; i < 32; i = i + 1) begin
regs[i] = i;
end
end
end
endmodule
`timescale 1ns / 1ps
module MainCtr (
input [5:0] OpCode,// Instr[31:26]
output RegDst,
output ALUSrc,
output Mem2Reg,
output RegWr,
output MemWr,
output B,
output J,
output [1:0] ALUOp
);
reg [8:0] outputtemp;
assign RegDst = outputtemp[8];
assign ALUSrc = outputtemp[7];
assign Mem2Reg = outputtemp[6];
assign RegWr = outputtemp[5];
assign MemWr = outputtemp[4];
assign B = outputtemp[3];
assign J = outputtemp[2];
assign ALUOp = outputtemp[1:0];
always @(OpCode) begin
case (OpCode)
6'b000010: outputtemp = 9'bxxx0001xx;// j
6'b000000: outputtemp = 9'b100100010;// R
6'b100011: outputtemp = 9'b011100000;// lw
6'b101011: outputtemp = 9'bx1x010000;// sw
6'b000100: outputtemp = 9'bx0x001001;// beq
6'b001100: outputtemp = 9'b010100011;// andi
default: outputtemp = 9'b000000000;// default
endcase
end
endmodule
`timescale 1ns / 1ps
module ALUCtr (
input [1:0] ALUOp,
input [5:0] Funct,
output reg [3:0] ALUCtr
);
always @(ALUOp or Funct) begin
casex ({ALUOp, Funct})
8'b00xxxxxx: ALUCtr[3:0] = 4'b0010;// lw, sw
8'b01xxxxxx: ALUCtr[3:0] = 4'b0110;// beq
8'b10xx0000: ALUCtr[3:0] = 4'b0010;// add
8'b10xx0010: ALUCtr[3:0] = 4'b0110;// sub
8'b10xx0100: ALUCtr[3:0] = 4'b0000;// and
8'b10xx0101: ALUCtr[3:0] = 4'b0001;// or
8'b10xx1010: ALUCtr[3:0] = 4'b0111;// slt
8'b11xxxxxx: ALUCtr[3:0] = 4'b0000;// andi->and
default: ALUCtr[3:0] = 4'b0000;// initial
endcase
end
endmodule
`timescale 1ns / 1ps
module ALU_sim(
output [31:0] Res,
output Zero
);
reg [31:0] In1,In2;
reg [3:0] ALUCtr;
ALU S1(In1,In2,ALUCtr,Res, Zero);
initial begin
In1 = 32'hffff0000;
In2 = 32'h00ffff00;
ALUCtr=4'h2;
#10
ALUCtr=4'h6;
#10
ALUCtr=4'h0;
#10
ALUCtr=4'h1;
#10
ALUCtr=4'h7;
end
endmodule
`timescale 1ns/1ps
module RegFile_sim (
output [31:0] RsData,
output [31:0] RtData
);
reg clk;
reg reset;
reg regWriteEn;
reg [4:0] RsAddr;
reg [4:0] RtAddr;
reg [4:0] regWriteAddr;
reg [31:0] regWriteData;
parameter PERIOD = 10;
always begin
clk = 1'b0;
#(PERIOD/2) clk = 1'b1;
#(PERIOD/2) ;
end
initial begin
reset = 1;
RsAddr = 5'h0;
RtAddr = 5'h0;
#15
reset = 0;
#30
regWriteEn = 1;
regWriteAddr = 5'h03;
regWriteData = 32'h5aa5;
#20
RsAddr = 5'h03;
RtAddr = 5'h03;
end
RegFile S2(clk,reset,regWriteEn,RsAddr,RtAddr,regWriteAddr,regWriteData,RsData,RtData);
endmodule
module mips_sim (
);
reg clk;
reg reset;
mips U0(clk, reset);
parameter PERIOD = 10;
always begin
clk = 1'b0;
# (PERIOD/2) clk = ~clk;
# (PERIOD/2)
end
initial begin
reset = 1;
#20
reset = 0;
end
endmodule