单周期CPU处理器的Verilog设计
中央处理器,即CPU,作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。在CPU内部,电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。
文章目录
一、实验原理
1. 单周期CPU概述
中央处理器,即CPU,作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。在CPU内部,电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。
2. 单周期CPU指令模块
单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。CPU在处理指令时,一般需要经过以下几个步骤:
(1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
3. MIPS指令格式
MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。MIPS的指令格式为32位。下图给出了MIPS指令的3种格式。
4. 指令处理流程
5. 单周期CPU数据通路
CPU的电路包括数据路径(Data path)和控制部件(Control Unit)两大部分,下面给出了单周期CPU的总体设计图。
二、单周期CPU的设计
1.pcadd4
pcadd4用来作为PC寄存器的更新信号。
由于每条指令32位,所以增加一个32位加法器,固定与32位的立即数4进行相加,且得到的结果在当前时钟信号的上升沿更新进PC寄存器。
其中,In为输入端口,输入当前指令地址;Out为下一条指令地址,为输出端口。
module PCadd4(PC_o,PCadd4);
input [31:0] PC_o;
output [31:0] PCadd4;
CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
endmodule
2. PC
PC寄存器用来给出指令在指令储存器中的地址。
为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器,即全部置零。
其中,Clk为输入信号,时钟周期;Reset为输入信号,输入控制信号;Result为输入信号,输入目标地址,可能是跳转地址或者是下一条指令的地址;Address为输出信号,输出指令地址。
module PC(Clk,Reset,Result,Address);
input Clk;//时钟
input Reset;//是否重置地址。0-初始化PC,否则接受新地址
input[31:0] Result;
output reg[31:0] Address;
initial begin
Address <= 0;
end
always @(posedge Clk or negedge Reset)
begin
if (Reset==0) //如果为0则初始化PC,否则接受新地址
begin
Address <= 0;
end
else
begin
Address = Result;
end
end
endmodule
3.INSTMEM
依据当前pc,读取指令寄存器中相对应地址Addr[6:2]的指令。
将pc的输入作为敏感变量,当pc发生改变的时候,则进行指令的读取,根据相关的地址,输出指令寄存器中相对应的指令,且在设计指令的时候,要用到12条给出的指令且尽量合理。
Addr为输入信号,输入指令地址;Inst为输出信号,输出指令编码。
module INSTMEM(Addr,Inst);//指令存储器
input[31:0]Addr;
//状态为'0',写指令寄存器,否则为读指令寄存器
output[31:0]Inst;
wire[31:0]Rom[31:0];
assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20
assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
assign Rom[5'h04]=32'h00222824;//and $5,$1,$2 $5=8
assign Rom[5'h05]=32'h00223025;//or $6,$1,$2 $6=12
assign Rom[5'h06]=32'h14220002;//bne $1,$2,2
assign Rom[5'h07]=32'hXXXXXXXX;
assign Rom[5'h08]=32'hXXXXXXXX;
assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
assign Rom[5'h0A]=32'h0800000D;// J 0D
assign Rom[5'h0B]=32'hXXXXXXXX;
assign Rom[5'h0C]=32'hXXXXXXXX;
assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=10
assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
assign Rom[5'h0F]=32'h00221826;//xor $3,$1,$2
assign Rom[5'h10]=32'h00021900;//sll $3,$2,4
assign Rom[5'h11]=32'h00021902;//srl $3,$2,4
assign Rom[5'h12]=32'h00021903;//sra $3,$2,4
assign Rom[5'h13]=32'h30470009;//andi $7,$2,9
assign Rom[5'h14]=32'h382300EF;//xori $3,$1,0xef
assign Rom[5'h15]=32'h3C011234;//lui $1,0x1234
assign Rom[5'h16]=32'h0C00001A;//Jal 1A
assign Rom[5'h17]=32'h0800001A;// J 1A
assign Rom[5'h18]=32'hXXXXXXXX;
assign Rom[5'h19]=32'hXXXXXXXX;
assign Rom[5'h1A]=32'h03E00008;//Jr 16
assign Rom[5'h1B]=32'hXXXXXXXX;
assign Rom[5'h1C]=32'hXXXXXXXX;
assign Rom[5'h1D]=32'hXXXXXXXX;
assign Rom[5'h1E]=32'hXXXXXXXX;
assign Rom[5'h1F]=32'hXXXXXXXX;
assign Inst=Rom[Addr[6:2]];
endmodule
4. DATAMEM
数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。
由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。
module DATAMEM(Addr,Din,Clk,We,Dout);
input [31:0]Addr,Din;
input Clk,We;
output [31:0]Dout;
reg [31:0]ram[0:31];
integer i;
initial begin
for ( i = 0 ; i <= 31 ; i = i + 1)
ram [i] = i;
end
always @ (posedge Clk) begin
if (We) ram[Addr[6:2]] <= Din;
end
assign Dout = ram[Addr[6:2]];
endmodule
5. ALU
算数逻辑部件,需要实现加,减,按位与,按位或。
需要2位控制信号控制运算类型,核心部件是32位加法器ADDSUB_32。
Aluc:控制信号。
X:寄存器1的值。
Y:寄存器2的值或立即数。
R:输入寄存器端口D的计算结果,输出信号。
Z:当值为1时代表两个输入信号值相等,当值为0时代表两个输入信号不等,输出信号。
module ALU(X,Y,Aluc,R,Z);
input[31:0]X,Y;
input[3:0]Aluc;
output[31:0]R;
output Z;
wire[31:0]d_as,d_and,d_or,d_xor,d_lui,d_sh,d;
ADDSUB_32 as32(X,Y,Aluc[0],d_as);
assign d_and=X&Y;
assign d_or=X|Y;
assign d_xor=X^Y;
assign d_lui={Y[15:0],16'h0};
SHIFTER shift(Y,X[10:6],Aluc[3],Aluc[1],d_sh);
MUX6X32 select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc[3:0],R);
assign Z=~|R;
endmodule
6.CPU
实现CPU的封装,设计输出信号使得在方正时便于观察其波形图。
调用各个下层模块并将他们的输入和输出连接到一起。
CLk:时钟周期,外部输入信号。
Reset:清零信号,外部输入信号。
module CPU(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
input Clk,Reset;
output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D1,X,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
wire [3:0]Aluc;
wire [1:0]Pcsrc;
wire [4:0]Wr,Wr1;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
INSTMEM instmem(Addr,Inst);
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,shift,j);
MUX2X5 mux2x5_1(Inst[15:11],Inst[20:16],Regrt,Wr1);
MUX2X5 mux2x5_2(Wr1,31,j,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_COMBINATION shifter1(Inst[25:0],PCadd4,InstL2);
SHIFTER shifter2(EXTIMM,2,0,0,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x32_1(EXTIMM,Qb,Aluqb,Y);
MUX2X32 mux2x32_2(Qa,Inst,shift,X);
ALU alu(X,Y,Aluc,R,Z);
DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x32_3(Dout,R,Reg2reg,D1);
MUX2X32 mux2x32_4(D1,PCadd4,j,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2,Cout);
MUX4X32 mux4x32(PCadd4,mux4x32_2,Qa,InstL2,Pcsrc,Result);
assign NEXTADDR=Result;
assign ALU_R=R;
endmodule
三、设计运行结果
四、引用
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)