这里写自定义目录标题
正在学习计算机组成原理,以下为计组大作业,借鉴了很多其他人的代码加入了自己的更改。仅供学习使用。这里实现的5条操作指令为:
ADD R1 R2 R3 :R3=R1+R2;加和赋值操作
XOR R1 R2 R3 :R3=R1^R2;异或赋值操作
LOAD R1 R2 VAl1:R2=MEM[R1+VAL1]从memory载入数据
STORE R1 R2 VAL2: MEM[R1+VAL2]=R1;向memory写入数据
beq R1 R2 VAL3 : if(R1==R2) PC=PC+1+VAL3;条件跳转
这个是简易CPU的5级流水线流程图。关于各个部分具体的内容介绍我就不赘述了,可以去网上查找资料。关于流水线操作的数据冲突以及操作冲突的内容也不做过多的介绍。主要是放代码。
上图为32位操作指令的结构,【31-25】位没用上全部置零。【24-22】为指令的标志位,用来区分指令。【21-19】,【18-16】为两个寄存器的地址位,用来指示被操作数的地址,在指令被读取之后会到对应的寄存器中提取数据,然后进行操作。第一个指令类型使用于ADD和XOR,LOAD、STORE、beq适用于第二个指令类型。
简易CPU的结构:
1 PCPU 主要模块:用从指令内存得到的指令进行运算处理,并从数据内存中写入或读取数据。这是最核心的部分,其他模块都很好设计。
2 Instruction_Mem 指令模块: 为PCPU模块提供指令,使得PCPU中通过指令寄存器中的值,找到指令的内存地址,从而读取指令。
3 Data_Mem 数据模块: PCPU从中读取数据或写入数据
上图借鉴博客:https://blog.csdn.net/august717/article/details/46120163,感觉这个写的比我好,大家可以去看看。
上代码:
头文件 headfile.v
`ifndef HEADFILE_H_
`define idle 1'b0
`define exec 1'b1
`define LOAD 3'b010
`define STORE 3'b011
`define ADD 3'b000
`define XOR 3'b001
`define beq 3'b100
`define gr0 3'b000
`define gr1 3'b001
`define gr2 3'b010
`define gr3 3'b011
`define gr4 3'b100
`define gr5 3'b101
`define gr6 3'b110
`define gr7 3'b111
`endif
顶层cpu.v
module cpu( input wire clk, enable, reset, start);
wire [31:0] d_datain; //data from memory
wire [31:0] i_datain; //instruction
wire [7:0] d_addr; //address for data memory
wire [31:0] d_dataout;//output data to data memory
wire d_we; //write datamemory write enable
wire [7:0] i_addr; //pc
PCPU pcpu(
.clock(clk),
.enable(enable),
.reset(reset),
.start(start),
.d_datain(d_datain),
.i_datain(i_datain),
.d_addr(d_addr),
.d_dataout(d_dataout),
.d_we(d_we),
.i_addr(i_addr)
);
I_mem i_mem(
.addra(i_addr),
.douta(i_datain)
);
D_mem d_mem(
.wea(d_we),
.addra(d_addr),
.dina(d_dataout),
.douta(d_datain)
);
endmodule
底层:PCPU.v
`include "headfile.v"
module PCPU(input wire reset, enable, clock, start,
input wire [31:0] d_datain, //data from memory
i_datain, //instruction
output wire [7:0] d_addr, //address for data memory
output wire [7:0] i_addr, //pc
output wire [31:0] d_dataout,//output data to data memory
output wire d_we //write enable
);
reg state,next_state; //to control the CPU
reg [7:0] pc;
reg [31:0] id_ir,ex_ir,mem_ir,wb_ir;
reg [31:0] reg_A, reg_B, reg_C, reg_C1, smdr, smdr1, ALUo;
reg dw, zf, nf, cf, cin,flag;
reg [31:0] gr[0:7];
assign d_dataout = smdr1;
assign d_we = dw;
assign d_addr = reg_C[7:0];//*****
assign i_addr = pc;
//************* CPU control *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
state <= `idle;
else
state <= next_state;
end
always @(*)
begin
case (state)
`idle :
if ((enable == 1'b1) && (start == 1'b1)) // on
next_state <= `exec;
else
next_state <= `idle;
`exec :
if (enable == 1'b0)//pause
next_state <= `idle;
else
next_state <= `exec;
endcase
end
//************* IF *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
begin
id_ir <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
pc <= 8'b0000_0000;
end
else if (state ==`exec)
begin
//Load hazard Stall
if((id_ir[24:22] == `LOAD)&&(i_datain[24:22]!=`LOAD))
begin
//r2
if((id_ir[18:16]==i_datain[18:16])&&((i_datain[24:22]==`STORE)||(i_datain[24:22]==`ADD)||(i_datain[24:22]==`XOR)))
begin
pc <= pc;
id_ir <= 16'bx;
end
//r3
else if((id_ir[18:16]==i_datain[21:19])&&((i_datain[24:22]==`STORE)||(i_datain[24:22]==`ADD)||(i_datain[24:22]==`XOR)))
begin
pc <= pc;
id_ir <= 16'bx;
end
else
begin
pc <= pc + 1'b1;
id_ir <= i_datain;
end
end
else if(id_ir[24:22] == `beq || ex_ir[24:22] == `beq)
begin
pc <= pc;
id_ir <= 16'bx;
end
else if((mem_ir[24:22] == `beq)&& (flag == 1'b1))
begin
pc <= pc+mem_ir[7:0];
id_ir <= 16'bx;
end
else
begin
pc <= pc + 1'b1;
id_ir <= i_datain;
end
end
else if(state==`idle)
begin
id_ir <= id_ir;
pc <= pc;
end
end
//************* ID *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
begin
ex_ir <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
reg_A <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
reg_B <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
smdr <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
end
//flush
// else if(state == `exec && (ex_ir[15:11]==`BZ && zf==1'b1))
// ex_ir <= 16'bx;
else if (state == `exec)
begin
ex_ir <= id_ir;
//regA
//r1=r2#r3 or r1=r2#val
if(id_ir[24:22] == `LOAD || id_ir[24:22] == `STORE || id_ir[24:22] == `ADD || id_ir[24:22] == `XOR)
begin
//r2
if( (ex_ir[24:22] == `ADD || ex_ir[24:22] == `XOR||ex_ir[24:22] == `beq )&& ex_ir[2:0] == id_ir[21:19])
reg_A <= ALUo;
else if( (mem_ir[24:22] == `ADD || mem_ir[24:22] == `XOR ||mem_ir[24:22] == `beq )&& mem_ir[2:0] == id_ir[21:19])
reg_A <= reg_C;
else if( mem_ir[24:22] == `LOAD && mem_ir[18:16] == id_ir[21:19])
reg_A <= d_datain;
else if( (wb_ir[24:22] == `ADD || wb_ir[24:22] == `XOR ||wb_ir[24:22] == `beq)&& wb_ir[2:0] == id_ir[21:19])
reg_A <= reg_C1;
else if( wb_ir[24:22] == `LOAD && wb_ir[18:16] == id_ir[21:19])
reg_A <= reg_C1;
else
reg_A <= gr[id_ir[21:19]];//gr2
end
else
reg_A <= gr[id_ir[21:19]];//gr2
//regB
if (id_ir[24:22] == `LOAD || id_ir[24:22] == `STORE )
reg_B <= {28'b0000_0000_0000, id_ir[3:0]};//val3
//r1=r2#r3
else if(id_ir[24:22] == `ADD || id_ir[24:22] == `XOR||id_ir[24:22] == `beq)
begin
if( (ex_ir[24:22] == `ADD || ex_ir[24:22] == `XOR ||ex_ir[24:22] == `beq )&& ex_ir[2:0] == id_ir[18:16])
reg_B <= ALUo;
else if( (mem_ir[24:22] == `ADD || mem_ir[24:22] == `XOR ||mem_ir[24:22] == `beq )&& mem_ir[2:0] == id_ir[18:16])
reg_B <= reg_C;
else if( mem_ir[24:22] == `LOAD && mem_ir[18:16] == id_ir[18:16])
reg_B <= d_datain;
else if( (wb_ir[24:22] == `ADD || wb_ir[24:22] == `XOR ||wb_ir[24:22] == `beq)&& wb_ir[2:0] == id_ir[18:16])
reg_B <= reg_C1;
else if( wb_ir[24:22] == `LOAD && wb_ir[18:16] == id_ir[18:16])
reg_B <= reg_C1;
else
reg_B <= gr[id_ir[18:16]];//gr2
end
else
reg_B <= gr[id_ir[18:16]];//gr3
//STORE
if (id_ir[24:22] == `STORE)
begin
//r1???
if(id_ir[18:16] == ex_ir[2:0] && (ex_ir[24:22] == `ADD || ex_ir[24:22] == `XOR ))
smdr <= ALUo;
else if(id_ir[18:16] == mem_ir[2:0] && (mem_ir[24:22] == `ADD || mem_ir[24:22] == `XOR))
smdr <= reg_C;
else if(id_ir[18:16] == mem_ir[18:16] && mem_ir[24:22] == `LOAD)
smdr <= d_datain;
else if(id_ir[18:16] == wb_ir[2:0] && (wb_ir[24:22] == `ADD || wb_ir[24:22] == `XOR ))
smdr <= reg_C1;
else if(id_ir[18:16] == wb_ir[18:16] && wb_ir[24:22] == `LOAD )
smdr <= reg_C1;
else
smdr <= gr[id_ir[18:16]];
end
else
smdr <= gr[id_ir[18:16]];
end
end
//************* ALU *************//
reg signed [31:0] reg_A1;
always @(*)
begin
reg_A1 <= reg_A;
end
always @(*)
begin
if(ex_ir[24:22] == `ADD )
{cf, ALUo} <= reg_A + reg_B;
else if(ex_ir[24:22] == `XOR)
{cf, ALUo} <= reg_A ^ reg_B;
else if(ex_ir[24:22] == `LOAD || ex_ir[24:22] == `STORE)
{cf, ALUo} <= reg_A + reg_B;
else if(ex_ir[24:22] == `beq)
begin
if(reg_A ^ reg_B ==0)
flag = 1'b1;
else
flag =1'b0;
end
else
{cf, ALUo} <= 33'b0;
end
//************* EX *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
begin
mem_ir <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
reg_C <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
smdr1 <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
zf <= 1'b0;
nf <= 1'b0;
cin <= 1'b0;
dw <= 1'b0;
end
else if (state == `exec)
begin
mem_ir <= ex_ir;
reg_C <= ALUo;
if (ex_ir[24:22] == `ADD)
begin
if (ALUo == 32'b0000_0000_0000_0000_0000_0000_0000_0000)
zf <= 1'b1;//if zero
else
zf <= 1'b0;
if (ALUo[31] == 1'b1)//if negative
nf <= 1'b1;
else
nf <= 1'b0;
end
else
begin
zf <= zf;
nf <= nf;
end
//carry
if (ex_ir[24:22] == `ADD)
cin <= cf;
else
cin <= cin;
//STORE
if (ex_ir[24:22] == `STORE)
begin
dw <= 1'b1;//data wire enable
smdr1 <= smdr;
end
else
begin
dw <= 1'b0;
smdr1 <= 32'b0;
end
end
end
//************* MEM *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
begin
wb_ir <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
reg_C1 <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
end
else if (state == `exec)
begin
wb_ir <= mem_ir;
if (mem_ir[24:22] == `LOAD)
reg_C1 <= d_datain;
else
reg_C1 <= reg_C;
end
end
//************* WB *************//
always @(posedge clock or negedge reset)
begin
if (!reset)
begin
gr[7] <= 32'h00000001;
gr[6] <= 32'h00000001;
gr[5] <= 32'h00000001;
gr[4] <= 32'h00000002;
gr[3] <= 32'h00000001;
gr[2] <= 32'h00000001;
gr[1] <= 32'h00000001;
gr[0] <= 32'h00000001;
end
else if (state == `exec)
begin
if (wb_ir[24:22] == `LOAD)
gr[wb_ir[18:16]] <= reg_C1;//write back to gr1
else if((wb_ir[24:22] == `ADD) ||(wb_ir[24:22] == `XOR))
gr[wb_ir[2:0]] <= reg_C1;//write back to gr1
else
gr[wb_ir[18:16]] <= gr[wb_ir[18:16]];
end
end
endmodule
底层2 instraction-mem.v
`include "headfile.v"
module I_mem(input wire [7:0] addra, output reg [31:0] douta);
reg [31:0] i_mem[20:0];
always @(*)
begin
douta <= i_mem[addra];
end
initial begin
//gcm
i_mem[0] <= {7'b0000000, `ADD, `gr6, `gr1, 13'b0, `gr2}; //gr2=gr6+gr1
i_mem[1] <= {7'b0000000, `XOR, `gr4, `gr5, 13'b0, `gr6}; //gr6=gr4^gr5
i_mem[2] <= {7'b0000000,`LOAD, `gr3, `gr4, 16'h0001}; //gr4 = mem[gr3+1]
i_mem[3] <= {7'b0000000, `ADD, `gr2, `gr5, 13'b0, `gr5}; //gr5=gr2+gr5
i_mem[4] <= {7'b0000000,`STORE, `gr3, `gr7, 16'h0005}; //mem[gr3+5]=gr7
//???? test
i_mem[6] <= {7'b0000000, `ADD, `gr6, `gr1, 13'b0, `gr4}; //gr4=gr6+gr1
i_mem[7] <= {7'b0000000, `ADD, `gr4, `gr5, 13'b0, `gr1}; //gr1=gr4+gr5
//???? teat
i_mem[9] <= {7'b0000000,`beq, `gr5, `gr6, 12'h000,4'b0010};//if gr6==gr5,pc=0001+1;
i_mem[10] <= {7'b0000000, `ADD, `gr6, `gr1, 13'b0, `gr2}; //gr2=gr6+gr1
i_mem[11] <= {7'b0000000, `ADD, `gr3, `gr5, 13'b0, `gr5}; //gr5=gr3+gr5
i_mem[12] <= {7'b0000000, `ADD, `gr3, `gr5, 13'b0, `gr5}; //gr5=gr3+gr5
end
endmodule
底层3 data-mem.v
module D_mem(input wire wea, input wire [7:0] addra, input wire [31:0] dina, output wire [31:0] douta
);
reg [31:0] d_mem[255:0];
assign douta = d_mem[addra];
always @(*)
begin
if(wea == 1'b1)
d_mem[addra] <= dina;
end
initial begin
//data
d_mem[0] <= 32'h00000000;
d_mem[1] <= 32'h00000001; //1
d_mem[2] <= 32'h00000005; //5
d_mem[3] <= 32'h00000008;//8
end
endmodule
测试代码:test.v
module test;
// Inputs
reg clk;
reg enable;
reg reset;
reg start;
// Instantiate the Unit Under Test (UUT)
cpu uut (
.clk(clk),
.enable(enable),
.reset(reset),
.start(start)
);
always #5 clk = ~clk;
initial begin
// Initialize Inputs
clk = 0;
enable = 0;
reset = 0;
start = 0;
$monitor("%h:%b:%h:%h:%h:%h:%h:%h:%b:%h:%h:%h:%h:%h:%h:%h:%h:%b:%b:%b",
uut.pcpu.pc, uut.pcpu.id_ir, uut.pcpu.reg_A, uut.pcpu.reg_B, uut.pcpu.reg_C,
uut.pcpu.d_addr, uut.pcpu.d_dataout, uut.pcpu.ALUo, uut.pcpu.d_we, uut.pcpu.reg_C1, uut.pcpu.gr[1], uut.pcpu.gr[2], uut.pcpu.gr[3],
uut.pcpu.gr[4], uut.pcpu.gr[5], uut.pcpu.gr[6],
uut.pcpu.gr[7], uut.pcpu.cin, uut.pcpu.nf, uut.pcpu.zf);
// Wait 100 ns for global reset to finish
#100;
$display("pc: id_ir: regA:regB:regC:da:dout:ALUo:dwe:reC1: gr1:gr2:gr3: gr4: gr5: gr6: gr7: cf:nf:zf");
// Add stimulus here
#10 reset <= 0;
#10 reset <= 1;
#10 enable <= 1;
#10 start <=1;
#10 start <= 0;
#100;
end
endmodule
可以安装modelsim来跑跑这个代码,欢迎大家来交流更好的想法。有啥不懂的可以私聊我,虽然我不一定能及时回复。