多周期CPU设计与实现
有一个问题就是我只能做到在译码的上升沿获取指令的操作码,但是取数、存数指令,老师给的图要求取指阶段下一个是执行阶段。但就很矛盾,我在取指阶段根本无法获取指令操作码,所以不能根据指令操作码进行状态转移。
所以我的取数、存数就要加上译码阶段、什么也不干,当然,有获取指令操作码的作用。
关于vivado不显示寄存器和存储器的波形图
PC模块
module PC(
input clk,
input rst,
input PCWre,//当为1时,pc等于新地址,否则不变
input[15:0] newAddress,//新地址
output reg[15:0] currentAddress//输出地址
);
initial begin
currentAddress = 0;
end
always@(posedge clk or negedge rst)begin
if(!rst)
currentAddress = 0;
else if(PCWre)
currentAddress = newAddress;
else
currentAddress = currentAddress;
end
endmodule
InsMemory模块
module InsMemory(
input[15:0] addr,
output reg[15:0] Ins
);
reg[15:0] unit[31:0];//32个16位存储单元
initial begin
unit[0] = 16'b0000_000_000000001;//取数,将数据存储器地址为1的数据送入寄存器堆地址为1的地方
unit[1] = 16'b0001_000_001_000000;//加法,R[0] + R[1] -> R[0]
unit[2] = 16'b0001_000_010_000000;//加法,R[0] + R[2] -> R[0]
unit[3] = 16'b0001_000_011_000000;//加法,R[0] + R[3] -> R[0]
unit[4] = 16'b0010_000000000111;//跳转,跳到第七条指令
unit[5] = 16'b0001_000_100_000000;//加法,R[0] + R[4] -> R[0]
unit[6] = 16'b0001_000_101_000000;//加法,R[0] + R[5] -> R[0]
unit[7] = 16'b0001_000_110_000000;//加法,R[0] + R[6] -> R[0]
unit[8] = 16'b0011_000_000000000;//存数,R[0]->Mem[0]
end
always@(*)begin
Ins = unit[addr];
end
endmodule
IR模块
//为了使指令代码保持稳定,并且有指令分割功能
module IR(
input clk,
input IRWre,
input[15:0] Ins,
output reg[3:0]op,
output reg[2:0]r1,
output reg[2:0]r2,
output reg[8:0]addr,
output reg[11:0]imm
);
initial begin
op = 0;
r1 = 0;
r2 = 0;
addr = 0;
imm = 0;
end
always@(posedge clk)begin
if(IRWre)begin
op = Ins[15:12];
r1 = Ins[11:9];
r2 = Ins[8:6];
addr = Ins[8:0];
imm = Ins[11:0];
end
end
endmodule
RegFile模块
module RegFile(
input clk,
input RegRW,
input[2:0] read_reg1,
input[2:0] read_reg2,
input[15:0] write_data,//要写入的地址为read_reg1
output[15:0] reg1,
output[15:0] reg2
);
integer i;
reg[15:0] regfile[7:0];//有8个16位的寄存器
initial begin
for(i=0;i<8;i=i+1)
regfile[i] = i;
end
always@(posedge clk)begin
if(RegRW == 1)
regfile[read_reg1] = write_data;
end
assign reg1 = regfile[read_reg1];
assign reg2 = regfile[read_reg2];
endmodule
ALU模块
module ALU(
alu_op, //ALU操作控制
A, //操作数A
B, //操作数B
result //运算结果
);
input[2:0] alu_op;
input[15:0] A,B;
output reg[15:0]result;
always@(*)begin
case(alu_op)
3'b000 : result = A+B;
3'b001 : result = A-B;
3'b010 : result = A * B;
3'b011 : result = A / B;
3'b100 : result = A & B;
3'b101 : result = A | B;
3'b110 : result = A ^ B;
3'b111 : result = A << B;
default:result = 0;
endcase
end
endmodule
DataMemory模块
module DataMemory(
input DataMemRW,
input[8:0]DAddr,
input[15:0]DataIn,
output reg[15:0]DataOut
);
reg [15:0] memory[6'd63:0];//64个16位存储单元
integer i;
initial begin
for(i=0;i<64;i=i+1)
memory[i] = i*i;
memory[1] = 16'd100;
end
always@(*)begin
if(DataMemRW)
memory[DAddr] = DataIn;
else
DataOut = memory[DAddr];
end
endmodule
符号扩展模块
module SignZeroExtend(
input[11:0]imm,
output[15:0]extendImm
);
assign extendImm[11:0] = imm;
assign extendImm[15:12] = imm[11] ?4'hf :4'h0;
endmodule
TempReg模块
//主要用于从RegFile读出的数据等过一个周期再送入ALU
module TempReg(
input clk,
input ALUSrc,
input[15:0]inData,
output reg[15:0]outData
);
initial begin
outData = 0;
end
always@(posedge clk)begin
if(ALUSrc)
outData = inData;
end
endmodule
Multiplexer16模块
module Multiplexer16(
control, //选择信号,当为1时,输出in1,当为0时,输出in0
in1,
in0,
out
);
input control;
input[15:0] in1,in0;
output[15:0] out;
assign out = control ? in1:in0;
endmodule
ControlUnit模块
module ControlUnit(
input clk,
input rst,
input[3:0] op,//指令操作码
output reg PCWre,//为0时,pc值不变,否则为新地址
output reg PCSrc,//为1时,新地址为跳转地址,为0时,新地址为pc+1
output reg IRWre,
output reg ALUSrcA,
output reg ALUSrcB,
output reg Mem_Alu,//当为1时,选择输出mem_out,否则输出alu_result
output reg RegRW,
output reg DataMemRW,
output reg[2:0] alu_op
);
parameter[1:0]initState = 2'b11,
sIF = 2'b00,
sID = 2'b01,
sEXE = 2'b10;
reg[1:0] state,nextState;
initial begin
state = initState;
PCWre = 0;
PCSrc = 0;
IRWre = 0;
Mem_Alu = 0;
ALUSrcA = 0;
ALUSrcB = 0;
RegRW = 0;
DataMemRW = 0;
alu_op = 0;
end
always@(posedge clk)begin
if(!rst)
state = sIF;
else begin
state = nextState;
end
end
always@(state or op)begin
case(state)
initState:nextState = sIF;
sIF:nextState = sID;
sID:begin
if(op == 4'b0010)//当为跳转指令,下一阶段是取指
nextState = sIF;
else
nextState = sEXE;
end
sEXE:nextState = sIF;
endcase
if(nextState == sIF && state != initState)begin
PCWre = 1;//PC更换为下一条指令地址
end else
PCWre = 0;
if(state == sIF || nextState == sID)
IRWre = 1;
else
IRWre = 0;
if(op == 4'b0001)begin//加法指令
ALUSrcA = 1;
ALUSrcB = 1;
alu_op = 0;
end
else begin
ALUSrcA = 0;
ALUSrcB = 0;
alu_op = 0;
end
if(op == 4'b0010)begin
PCSrc = 1;
end
else
PCSrc = 0;
RegRW = 0;
DataMemRW = 0;
Mem_Alu = 0;
if(state == sEXE)begin
if(op == 4'b0000 || op == 4'b0001)//取数或加法
RegRW = 1;
else
RegRW = 0;
if(op == 4'b0011)//存数
DataMemRW = 1;
else
DataMemRW = 0;
if(op == 4'b0000)//取数
Mem_Alu = 1;
else
Mem_Alu = 0;
end
end
endmodule
顶层模块
module Main(
input clk,
input rst,
output [3:0]op,
output [2:0]r1,
output [2:0]r2,
output [8:0]addr,
output [11:0]imm,
output [15:0]Regdata1,
output [15:0]Regdata2,
output [15:0]write_data
);
wire[15:0]newAddress,currentAddress,pc_address,
Ins,extendImm,
ALUA,ALUB,result,memData;
wire PCWre,PCSrc,IRWre,RegRW,ALUSrcA,ALUSrcB,
Mem_Alu,DataMemRW;
wire[2:0]alu_op;
PC pc(clk,rst,PCWre,newAddress,currentAddress);
InsMemory insmemory(currentAddress,Ins);
IR ir(clk,IRWre,Ins,op,r1,r2,addr,imm);
RegFile regfile(clk,RegRW,r1,r2,write_data,Regdata1,Regdata2);
SignZeroExtend signExtend(imm,extendImm);
TempReg A(clk,ALUSrcA,Regdata1,ALUA);
TempReg B(clk,ALUSrcB,Regdata2,ALUB);
ALU alu(alu_op,ALUA,ALUB,result);
DataMemory dataMemory(DataMemRW,addr,Regdata1,memData);
Multiplexer16 mul1(Mem_Alu,memData,result,write_data);//用于选择输入RegFile的数据
assign pc_address = currentAddress + 1;
Multiplexer16 mu12(PCSrc,extendImm,pc_address,newAddress);//用于选择PC的newAddress
ControlUnit cu(clk,rst,op,PCWre,PCSrc,IRWre,ALUSrcA,ALUSrcB,
Mem_Alu,RegRW,DataMemRW,alu_op);
endmodule
测试模块
module Test();
reg clk,rst;
wire[3:0]op;
wire[2:0]r1,r2;
wire[8:0]addr;
wire[11:0]imm;
wire[15:0]Regdata1,Regdata2,write_data;
Main main(clk,rst,op,r1,r2,addr,imm,Regdata1,Regdata2,write_data);
initial begin
clk = 0;rst=1;
#40 $stop;
end
always #1 clk = ~clk;
endmodule
展示
状态0是取指阶段,状态1是译码阶段,状态2是执行阶段。下张图打错了,就不改了。