基本情况
本多周期CPU支持RV32I指令集(fence,ecall,ebreak除外),已通过部分指令的测试,如代码有bug,欢迎指出。
模块划分
代码将CPU划分为以下几个模块: 1.控制单元:ControlUnit 用于产生控制信号,是CPU的大脑,指挥中枢。 2.分支决策单元:BranchUnit 用于决定B型指令是否发生跳转 3.立即数单元:ImmUnit 用于从指令中解析出立即数. 4.寄存器堆:RegisterFile RISC-V的32个寄存器,其中x0寄存器恒为0,不可更改。 5.指令存储器:InstrMem 用于存储指令,目前仅支持32位指令,不支持16位指令,且地址是4字节对齐的。 6.数据存储器:DataMem 数据存储器,用于存储数据,可进行字、半字、字节的读写。 7.Data_Ext 用于load指令,从读出的字中获取需要的部分,并进行符号/无符号扩展。例如LH,LBU指令选取哪个半字、哪个字节,以及如何扩展为32bit。 8.PC生成单元:NPC_Generator 根据Jal,Jalr以及Branch生成下一条指令的地址(PCF),若上述三个信号均为无效电平,则更新为PCF+4 9.计算部件:ALU 用于算数逻辑运算。
代码
控制单元
`timescale 1ns / 1ps
`include"Parameters.v"
import Params::*;
module ControlUnit(
input logic [6:0] Op,
input logic [2:0] Fn3,
input logic [6:0] Fn7,
output logic JalD,
output logic JalrD,
output LoadType RegWriteD,
output logic MemToRegD,
output logic [3:0] MemWriteD,
output logic LoadNpcD,
output BType BranchTypeD,
output AluOp AluControlD,
output logic [1:0] AluSrc2D,
output logic AluSrc1D,
output Type ImmType
);
assign JalD=(Op==7'b1101111)?1'b1:1'b0;
assign JalrD=(Op==7'b1100111)?1'b1:1'b0;
always_comb
begin
if(Op==7'b0000011)
begin
case(Fn3)
3'b000:RegWriteD=LB;
3'b001:RegWriteD=LH;
3'b010:RegWriteD=LW;
3'b100:RegWriteD=LBU;
3'b101:RegWriteD=LHU;
default:RegWriteD=NOREGWRITE;
endcase
end
else if(Op==7'b0010011||Op==7'b0110011||Op==7'b0110111||Op==7'b0010111||Op==7'b1101111||Op==7'b1100111)
begin
RegWriteD=LW;
end
else
begin
RegWriteD=NOREGWRITE;
end
end
always_comb
begin
if(Op==7'b0000011)
MemToRegD=1;
else
MemToRegD=0;
end
always_comb
begin
if(Op==7'b0100011)
case(Fn3)
3'b000:MemWriteD=4'b0001;
3'b001:MemWriteD=4'b0011;
3'b010:MemWriteD=4'b1111;
default:MemWriteD=4'b0000;
endcase
else
MemWriteD=4'b0000;
end
assign LoadNpcD=(JalD|JalrD)?1'b1:1'b0;
always_comb
begin
if(Op==7'b1100011)
case(Fn3)
3'b000:BranchTypeD=BEQ;
3'b001:BranchTypeD=BNE;
3'b100:BranchTypeD=BLT;
3'b101:BranchTypeD=BGE;
3'b110:BranchTypeD=BLTU;
3'b111:BranchTypeD=BGEU;
default:BranchTypeD=NOBRANCH;
endcase
else
BranchTypeD=NOBRANCH;
end
always_comb
begin
case(Op)
7'b0000011:AluControlD=ADD;
7'b0100011:AluControlD=ADD;
7'b0110111:AluControlD=LUI;
7'b0010111:AluControlD=ADD;
7'b1100011:AluControlD=ADD;
7'b1101111:AluControlD=ADD;
7'b1100111:AluControlD=ADD;
7'b0110011:
begin
case(Fn3)
3'b000:if(Fn7[5]==0)
AluControlD=ADD;
else
AluControlD=SUB;
3'b001:AluControlD=SLL;
3'b010:AluControlD=SLT;
3'b011:AluControlD=SLTU;
3'b100:AluControlD=XOR;
3'b101:if(Fn7[5]==0)
AluControlD=SRL;
else
AluControlD=SRA;
3'b110:AluControlD=OR;
3'b111:AluControlD=AND;
default:AluControlD=ADD;
endcase
end
7'b0010011:
begin
case(Fn3)
3'b000:AluControlD=ADD;
3'b010:AluControlD=SLT;
3'b011:AluControlD=SLTU;
3'b100:AluControlD=XOR;
3'b110:AluControlD=OR;
3'b111:AluControlD=AND;
3'b001:AluControlD=SLL;
3'b101:if(Fn7[5]==0)
AluControlD=SRL;
else
AluControlD=SRA;
default:AluControlD=ADD;
endcase
end
default:AluControlD=ADD;
endcase
end
always_comb
if(Op==7'b0010111)
AluSrc1D=1;
else
AluSrc1D=0;
always_comb
begin
if(Op==7'b0010011&&(Fn3==3'b001||Fn3==3'b101))
AluSrc2D=2'b00;
else if(Op==7'b1100011||Op==7'b0110011)
AluSrc2D=2'b01;
else
AluSrc2D=2'b10;
end
always_comb
begin
case(Op)
7'b0110011:ImmType=RTYPE;
7'b0010011:ImmType=ITYPE;
7'b0000011:ImmType=ITYPE;
7'b1100111:ImmType=ITYPE;
7'b1100011:ImmType=BTYPE;
7'b0100011:ImmType=STYPE;
7'b1101111:ImmType=JTYPE;
7'b0110111:ImmType=UTYPE;
7'b0010111:ImmType=UTYPE;
default:ImmType=RTYPE;
endcase
end
endmodule
计算单元
`timescale 1ns / 1ps
import Params::*;
`include "Parameters.v"
module ALU(
input logic [31:0] Operand1,
input logic [31:0] Operand2,
input AluOp AluControl,
output logic [31:0] AluOut
);
always_comb
begin
case(AluControl)
SLL:AluOut=Operand1<<(Operand2[4:0]);
SRL:AluOut=Operand1>>(Operand2[4:0]);
SRA:AluOut=$signed(Operand1)>>>(Operand2[4:0]);
ADD:AluOut=Operand1+Operand2;
SUB:AluOut=Operand1-Operand2;
XOR:AluOut=Operand1^Operand2;
OR:AluOut=Operand1|Operand2;
AND:AluOut=Operand1&Operand2;
SLT:AluOut=($signed(Operand1)<$signed(Operand2))?32'd1:32'd0;
SLTU:AluOut=(Operand1<Operand2)?32'd1:32'd0;
LUI:AluOut=Operand2;
default:AluOut=0;
endcase
end
endmodule
立即数生成单元
`timescale 1ns / 1ps
import Params::*;
`include "Parameters.v"
module ImmUnit(
input Type ImmType,
input logic [31:0] In,
output logic [31:0] Out
);
always_comb
begin
case(ImmType)
RTYPE:Out=32'd0;
ITYPE:Out={{20{In[31]}},In[31:20]};
UTYPE:Out={In[31:12],12'd0};
BTYPE:Out={{19{In[31]}},In[31],In[7],In[30:25],In[11:8],1'b0};
JTYPE:Out={{11{In[31]}},In[31],In[19:12],In[20],In[30:21],1'b0};
STYPE:Out={{20{In[31]}},In[31:25],In[11:7]};
default:Out=32'd0;
endcase
end
endmodule
分支决策单元
`timescale 1ns / 1ps
`include "Parameters.v"
import Params::*;
module BranchUnit(
input logic [31:0] Reg1,
input logic [31:0] Reg2,
input BType BrType,
output logic BranchE
);
always_comb
begin
case(BrType)
BEQ:if(Reg1==Reg2)
BranchE=1;
else
BranchE=0;
BNE:if(Reg1!=Reg2)
BranchE=1;
else
BranchE=0;
BLT:if($signed(Reg1)<$signed(Reg1))
BranchE=1;
else
BranchE=0;
BLTU:if(Reg1<Reg2)
BranchE=1;
else
BranchE=0;
BGE:if($signed(Reg1)>=$signed(Reg2))
BranchE=1;
else
BranchE=0;
BGEU:if(Reg1>=Reg2)
BranchE=1;
else
BranchE=0;
default:BranchE=0;
endcase
end
endmodule
寄存器文件
`timescale 1ns / 1ps
module RegisterFile(
input logic clk,
input logic [4:0] RS1,
input logic [4:0] RS2,
output logic [31:0] RegOut1,
output logic [31:0] RegOut2,
input logic [31:0] WD,
input logic [4:0] RD,
input logic WE
);
logic [31:0] RegFile [0:31];
initial
begin
for(int i=0;i<32;i++)
RegFile[i]=0;
end
always_ff@(negedge clk)
begin
if(WE)
begin
if(RD!=0)
RegFile[RD]<=WD;
end
end
always_comb
begin
RegOut1=RegFile[RS1];
RegOut2=RegFile[RS2];
end
endmodule
数据存储器
`timescale 1ns / 1ps
module DataMem(
input logic clk,
input logic [31:0] A,
input logic [31:0] WD,
input logic [3:0] WE,
output logic [31:0] RD
);
parameter N = 4096;
logic [31:0] DMEM [0:N-1];
initial begin
for(int i=0;i<N;i++)
DMEM[i]=i;
end
assign RD=DMEM[A[31:2]];
always_ff@(negedge clk)
begin
case(WE)
4'b0001:DMEM[A[31:2]][7:0]<=WD[7:0];
4'b0011:DMEM[A[31:2]][15:0]<=WD[15:0];
4'b1111:DMEM[A[31:2]]<=WD;
default:;
endcase
end
endmodule
指令存储器
`timescale 1ns / 1ps
module InstrMem(
input logic [31:0] InstrAddr,
output logic [31:0] Instr
);
parameter N = 64;
logic [31:0] IMEM [0:N-1];
initial begin
IMEM[0]=32'h00410093;
IMEM[1]=32'h00102423;
IMEM[2]=32'h0040a283;
IMEM[3]=32'h0000c437;
IMEM[4]=32'h0000c317;
IMEM[5]=32'h00508133;
IMEM[6]=32'h00508463;
IMEM[7]=32'h005080b3;
IMEM[8]=32'h00c000ef;
IMEM[9]=32'h005080b3;
IMEM[10]=32'h005080b3;
IMEM[11]=32'h40508433;
IMEM[12]=32'h00241513;
IMEM[13]=32'h40155633;
IMEM[14]=32'h401003b3;
IMEM[15]=32'h4013d393;
IMEM[16]=32'h04428867;
IMEM[17]=32'h001080b3;
IMEM[18]=32'h00187813;
end
assign Instr=IMEM[InstrAddr[31:2]];
endmodule
PC生成单元
`timescale 1ns / 1ps
module NPC_Generator(
input logic Jal,
input logic Jalr,
input logic Branch,
input logic [31:0] JalT,
input logic [31:0] JalrT,
input logic [31:0] BrT,
input logic [31:0] PC,
output logic [31:0] NPC
);
always_comb
begin
if(Jalr)
NPC=JalrT;
else if(Branch)
NPC=BrT;
else if(Jal)
NPC=JalT;
else
NPC=PC+4;
end
endmodule
Data_Ext单元
`timescale 1ns / 1ps
`include "Parameters.v"
import Params::*;
module Data_Ext(
input logic [1:0] LoadByteSelect,
input LoadType RegWrite,
input logic [31:0] In,
output logic [31:0] Out
);
always_comb
begin
case(RegWrite)
LW:Out=In;
LH:begin
case(LoadByteSelect)
2'b00:Out={{16{In[15]}},In[15:0]};
2'b01:Out={{16{In[23]}},In[23:8]};
2'b10:Out={{16{In[31]}},In[31:16]};
2'b11:Out=0;
endcase
end
LHU:begin
case(LoadByteSelect)
2'b00:Out={16'd0,In[15:0]};
2'b01:Out={16'd0,In[23:8]};
2'b10:Out={16'd0,In[31:16]};
2'b11:Out=0;
endcase
end
LB:begin
case(LoadByteSelect)
2'b00:Out={{24{In[7]}},In[7:0]};
2'b01:Out={{24{In[15]}},In[15:8]};
2'b10:Out={{24{In[23]}},In[23:16]};
2'b11:Out={{24{In[31]}},In[31:24]};
endcase
end
LBU:begin
case(LoadByteSelect)
2'b00:Out={24'd0,In[7:0]};
2'b01:Out={24'd0,In[15:8]};
2'b10:Out={24'd0,In[23:16]};
2'b11:Out={24'd0,In[31:24]};
endcase
end
default:Out=In;
endcase
end
endmodule
顶层模块
`timescale 1ns / 1ps
`include "Parameters.v"
import Params::*;
module MC_RV32Core(
input logic clk,
input logic rst_n
);
typedef enum bit [2:0] {
IDLE,
IF,ID,EXE,MEM,WB
} State;
State cur_state,next_state;
logic [4:0] RS1;
logic [4:0] RS2;
logic [4:0] RD;
logic [31:0] Imm;
logic [6:0] Op;
logic [2:0] Fn3;
logic [6:0] Fn7;
logic Jal;
logic Jalr;
LoadType RegWrite;
logic MemToReg;
logic [3:0] MemWrite;
logic LoadNpc;
BType BranchType;
AluOp AluControl;
logic AluSrc1;
logic [1:0] AluSrc2;
Type ImmType;
logic [31:0] RegOut1;
logic [31:0] RegOut2;
logic [31:0] JalT;
logic [31:0] PCF;
logic [31:0]Instr;
logic [31:0] NPC;
logic [31:0] Operand1;
logic [31:0] Operand2;
logic [31:0] AluOut;
logic [31:0] BrT;
logic [31:0] JalrT;
logic [31:0] StoreData;
logic [31:0] DM_RD;
logic [31:0] Result;
logic [31:0] RegWriteData;
logic [31:0] DM_RD_EXT;
logic RegWriteEn;
assign RS1=Instr[19:15];
assign RS2=Instr[24:20];
assign RD=Instr[11:7];
assign Op=Instr[6:0];
assign Fn3=Instr[14:12];
assign Fn7=Instr[31:25];
always_comb
begin
if(cur_state==WB)
begin
if(RegWrite==NOREGWRITE)
RegWriteEn=0;
else
RegWriteEn=1;
end
else
RegWriteEn=0;
end
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
PCF<=0;
else if(cur_state==WB)
PCF<=NPC;
always_comb
begin
if(AluSrc1==1'b1)
Operand1=PCF;
else
Operand1=RegOut1;
end
always_comb
begin
if(AluSrc2==2'b00)
Operand2=RS2;
else if(AluSrc2==2'b01)
Operand2=RegOut2;
else
Operand2=Imm;
end
assign StoreData=RegOut2;
always_comb
begin
if(LoadNpc)
Result=PCF+4;
else
Result=AluOut;
end
always_comb
begin
if(MemToReg)
RegWriteData=DM_RD_EXT;
else
RegWriteData=Result;
end
assign JalT=Imm+PCF;
assign JalrT=AluOut;
assign BrT=Imm+PCF;
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
cur_state<=IDLE;
else
cur_state<=next_state;
always_comb begin
case(cur_state)
IDLE:if(~rst_n)
next_state=IDLE;
else
next_state=IF;
IF:next_state=ID;
ID:next_state=EXE;
EXE:next_state=MEM;
MEM:next_state=WB;
WB:next_state=IF;
default:next_state=IDLE;
endcase
end
ControlUnit U1(
.Op(Op),
.Fn3(Fn3),
.Fn7(Fn7),
.JalD(Jal),
.JalrD(Jalr),
.RegWriteD(RegWrite),
.MemToRegD(MemToReg),
.MemWriteD(MemWrite),
.LoadNpcD(LoadNpc),
.BranchTypeD(BranchType),
.AluControlD(AluControl),
.AluSrc2D(AluSrc2),
.AluSrc1D(AluSrc1),
.ImmType(ImmType)
);
ALU U2(
.Operand1(Operand1),
.Operand2(Operand2),
.AluControl(AluControl),
.AluOut(AluOut)
);
BranchUnit U3(
.Reg1(Operand1),
.Reg2(Operand2),
.BrType(BranchType),
.BranchE(Branch)
);
DataMem U4(
.clk(clk),
.A(AluOut),
.WD(StoreData),
.WE(MemWrite),
.RD(DM_RD)
);
Data_Ext U5(
.LoadByteSelect(AluOut[1:0]),
.RegWrite(RegWrite),
.In(DM_RD),
.Out(DM_RD_EXT)
);
ImmUnit U6(
.ImmType(ImmType),
.In(Instr),
.Out(Imm)
);
InstrMem U7(
.InstrAddr(PCF),
.Instr(Instr)
);
NPC_Generator U8(
.Jal(Jal),
.Jalr(Jalr),
.Branch(Branch),
.JalT(JalT),
.JalrT(JalrT),
.BrT(BrT),
.PC(PCF),
.NPC(NPC)
);
RegisterFile U9(
.clk(clk),
.RS1(RS1),
.RS2(RS2),
.RegOut1(RegOut1),
.RegOut2(RegOut2),
.WD(RegWriteData),
.RD(RD),
.WE(RegWriteEn)
);
endmodule
完整工程见gitee 指令存储器中的测试指令(涵盖了所有类型,包括R型,I型(移位、寄存器计立即数和load),U型(auipc和lui),J型(jal),B型,S型),其相应的汇编代码如下所示(这些指令已经通过测试),读者可以根据汇编代码验证CPU的正确性,同时加深对CPU执行指令的理解(更多的指令测试还未进行,如发现有运行错误的指令,欢迎提出)
|