设计和实现一个支持加法指令的单周期 CPU。要求该加法指令(表示为 add r1,r2,
r3)格式约定如下:
采用寄存器寻址,r1,r2,r3 为寄存器编号,r1 和 r2 存放两个源操作数,r3
为目标寄存器,其功能为[r1] + [r2] -> r3;
指令字长 16 位,操作码和地址码字段分配如下所示:
OpCode | r1 | r2 | r3 |
PC模块
module PC(clk,rst,y);
input clk,rst;
output y;
reg[7:0] y;
initial y = 0;
always@(posedge clk or negedge rst)begin
if(!rst)
y = 0;
else
y = y + 1;
end
endmodule
InsMemory
module InsMemory(Addr,Ins);
input[7:0] Addr;
output Ins;
reg[15:0] Ins;
reg [15:0]unit[8'd255:0];//256个16位存储单元
integer i,j;
initial begin
for(i=0;i<256;i=i+1)begin
j = i % 5;
unit[i][2:0] = j+2;
unit[i][5:3] = j+1;
unit[i][8:6] = j;
unit[i][15:9] = i%3;
end
end
always@(*)begin
Ins = unit[Addr];
end
endmodule
RegFile
module RegFile(
clk, // 时钟信号
RegRW, //读写信号,为1时写入(上升沿有效),为0时,读数据,
read_reg1,//要读出数据1的地址
read_reg2,//要读出数据2的地址
write_reg,//要写入数据的地址
write_data,//要写入的数据
reg1,//要读出的数据1
reg2//要读出的数据2
);
input clk,RegRW;
input[2:0] read_reg1,read_reg2,write_reg;
input[15:0] write_data;
output[15:0] reg1,reg2;
integer i;
reg[15:0] regfile[7:0];//有8个16位的寄存器
initial begin
for(i=0;i<8;i=i+1)
regfile[i] = 2*i+1;
end
always@(posedge clk)begin
if(RegRW == 1)
regfile[write_reg] = write_data;
end
assign reg1 = regfile[read_reg1];
assign reg2 = regfile[read_reg2];
endmodule
DataMemory
module DataMemory(
DataRW,//读写信号,当为1时,写入;为0时,读出
DAddr, //读或写的地址
DataIn,//要写入的数据
DataOut,//要读出的数据
);
input DataRW;
input[5: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;
end
always@(*)begin
if(DataRW)
memory[DAddr] = DataIn;
else
DataOut = memory[DAddr];
end
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
ControlUnit
module ControlUnit(
input[5:0] op, //op操作符
output reg RegOut,//二路选择器,为1时,输出r1,否则输出r3
output reg M2Reg,//二路选择器,为1时,输出Mem[addr],否则输出[r1]+[r2]
output reg RegRW,//寄存器堆读写信号,为1时写入,为0时,读出
output reg DataMemRW,//数据存储器读写信号,为1时写入,为0时,读出
output reg[2:0] alu_op//ALU的操作控制信号
);
initial begin
RegOut = 0;
M2Reg = 0;
RegRW = 0;
DataMemRW = 0;
alu_op = 0;
end
always@(*)begin
case(op)
//LDA 取数
6'b000000:begin
RegOut = 1;
M2Reg = 1;
RegRW = 1;
DataMemRW = 0;
alu_op = 0;
end
//ADD 加法
6'b000001:begin
RegOut = 0;
M2Reg = 0;
RegRW = 1;
DataMemRW = 0;
alu_op = 0;
end
//STA 存数
6'b000010:begin
RegOut = 0;
M2Reg = 0;
RegRW = 0;
DataMemRW = 1;
alu_op = 0;
end
endcase
end
endmodule
二路选择器
由于不受时钟信号控制,始终会将r1,r2寄存器中的数据作为ALU的操作数输入,将r1寄存器的数据作为DataMemory的要写入的数据,将DataMemory中地址为addr中的数据读出。当要将数据写入寄存器堆时,要有个3线二路选择器,决定是将从数据存储器中读出的数据写入r1,还是将[r1]+[r2]的结果送入r3。由于要写入的数据有可能是从数据寄存器中读出的数据,也有可能是[r1]+[r2]的结果,所以要有个16线二路选择器决定要写入的数据
module Multiplexer3(
control,//选择信号,当为1时,输出in1,当为0时,输出in0
in1,
in0,
out
);
input control;
input[2:0] in1,in0;
output[2:0]out;
assign out = control == 1 ? in1 : in0;
endmodule
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
顶层模块
module SignleCPU(
input clk,
input rst,
output[5:0]op,
output[2:0] r1,
output[2:0] r2,
output[2:0] r3,
output[5:0]addr,
output[15:0]ReadData1,
output[15:0]ReadData2,
output[15:0]WriteData
);
wire[15:0] Ins,DataOut,result;
wire[7:0]InsAddress;
wire[2:0] WriteReg,alu_op;
wire RegOut,M2Reg,RegRW,DataMemRW;
PC pc(clk,rst,InsAddress);
InsMemory insmemory(InsAddress,Ins);
assign op = Ins[15:9];
assign r1 = Ins[8:6];
assign r2 = Ins[5:3];
assign r3 = Ins[2:0];
assign addr = Ins[5:0];
RegFile regfile(clk,RegRW,r1,r2,WriteReg,WriteData,ReadData1,ReadData2);
ControlUnit cu(op,RegOut,M2Reg,RegRW,DataMemRW,alu_op);
DataMemory datamem(DataMemRW,addr,ReadData1,DataOut);
ALU alu(alu_op,ReadData1,ReadData2,result);
Multiplexer3 mul3(RegOut,r1,r3,WriteReg);
Multiplexer16 mul16(M2Reg,DataOut,result,WriteData);
endmodule
Test模块
module Test();
reg clk,rst;
wire[2:0] r1,r2,r3;
wire[5:0]addr;
wire[15:0] ReadData1,ReadData2,WriteData;
wire[5:0] op;
SignleCPU f(clk,rst,op,r1,r2,r3,addr,ReadData1,ReadData2,WriteData);
initial begin
clk = 0;rst = 1;
#40 $stop;
end
always #5 clk = ~clk;
endmodule
代码
Github