目录
一、实验目的
通过设计并实现支持 10 条指令的CPU,进一步理解和掌握CPU设计的基本原理和过程。
二、实验内容
设计和实现一个支持如下十条指令的单周期CPU。
非访存指令
- 清除累加器指令CLA
- 累加器取反指令COM
- 算术右移一位指令SHR:将累加器 ACC中的数右移一位,结果放回 ACC
- 循环左移一位指令CSL:对累加器中的数据进行操作
- 停机指令STP
访存指令
- 加法指令ADD X:[X] + [ACC] –>ACC,X为存储器地址,直接寻址
- 存数指令STA X,采用直接寻址方式
- 取数指令LDA X,采用直接寻址
转移类指令
- 无条件转移指令JMP imm:signExt(imm)->PC
- 有条件转移(负则转)指令BAN X:ACC 最高位为 1 则(PC)+X->PC,否则 PC 不变
三、实验原理
1. 规定
-
机器字长、指令字长和存储字长均为16位
-
指令格式为:
-
指令及其操作码对照表
非访存指令 访存指令 转移类指令 清除累加器指令CLA 0000
加法指令ADD 0100
无条件转移指令JMP 0111
累加器取反指令COM 0001
存数指令STA 0101
有条件转移指令BAN 1000
算术右移一位指令SHR 0010
取数指令LDA 0110
循环左移一位指令CSL 0011
停机指令STP: 1001
2. 原理图
四、实验步骤
1. CPU各部件实现
pc
输入:
clk | rst | stop | ct | uct |
---|---|---|---|---|
时钟 | 时钟 | 停机 | 条件转移 | 无条件转移 |
输出:
offset | pc |
---|---|
12位转移指令偏移量 | 12位指令地址码 |
//pc
module pc(
input wire clk, rst, stop, ct, uct, //时钟、重置、停机、条件转移、无条件转移
input wire [11:0] offset, //12位转移指令偏移量
output reg [11:0] pc //12位指令地址码
);
assign stop = 0; //初始化开机状态
// assign clk = (stop==1)?1'bz:0; //停机则将clk置0
always@(posedge clk) begin
if(rst == 1)
pc = 0;
else
pc = pc + 1;
end
always@(negedge clk) begin
if(uct == 1) //无条件转移
pc = offset-1;
if(ct == 1) //条件转移
pc = pc+offset-1;
end
endmodule
insMem
输入:
addr |
---|
12位指令地址码 |
输出:
Ins |
---|
16位指令 |
//insMem
module insMem(
input wire [11:0] addr, //12位指令地址码
output wire [15:0] Ins //16位指令
);
reg[15:0] insMem[4096:0]; //2^12个存储单元,每个存储单元16位
initial begin
insMem[0] = 16'b0000000000000000; //0000 0000 0000 0000 清除累加器指令CLA
insMem[1] = 16'b0001000000000000; //0001 0000 0000 0000 累加器取反指令COM
insMem[2] = 16'b0010000000000000; //0010 0000 0000 0000 算术右移一位指令SHR
insMem[3] = 16'b0011000000000000; //0011 0000 0000 0000 循环左移一位指令CSL
insMem[4] = 16'b0100000000000001 ; //0100 0000 0000 0001 加法指令ADD
insMem[5] = 16'b0101000000000000; //0101 0000 0000 0000 存数指令STA
insMem[6] = 16'b0110000000000000; //0110 0000 0000 0000 取数指令LDA
insMem[7] = 16'b0111000000001001; //0111 0000 0000 1001 无条件转移指令JMP
insMem[9] = 16'b1000000000001001; //1000 0000 0000 1001 有条件转移BAN
insMem[10] = 16'b100100000000000; //1111 0000 0000 0000 停机指令STP
end
assign Ins = insMem[addr];
endmodule
acc
输入:
clk | acc_wr | data_in |
---|---|---|
时钟 | acc读写控制 | 16位输入数据 |
输出:
data_out |
---|
16位输出数据 |
//acc
module acc(
input wire clk, acc_wr, //时钟、acc读写控制
input wire [15:0] data_in, //16位输入数据
output wire [15:0] data_out //16位输出数据
);
reg [15:0] acc; //16位acc
initial begin
acc = 1; //acc初始化1
end
assign data_out = acc;
always@(negedge clk) begin
if(acc_wr == 1)
acc = data_in;
end
endmodule
alu
输入:
in1 | in2 | alu_op |
---|---|---|
操作数1 | 操作数2 | 操作选择信号 |
输出:
ct | Z |
---|---|
条件转移 | 结果 |
//alu
module alu(
input wire [15:0] in1, in2, //操作数in1和in2
input wire [3:0] alu_op, //操作选择信号alu_op
output reg ct, //条件转移ct
output reg [15:0] Z //Z
);
initial begin
ct = 0;
Z = 0; //初始化结果Z为0
end
always@* begin
case (alu_op)
4'b0000: Z = 0; //清除累加器指令CLA
4'b0001: Z = ~in1; //累加器取反指令COM
4'b0010: Z = in1[15] == 1 ? {1'b1, in1[15:1]} : {1'b0, in1[15:1]}; //算术右移一位指令SHR
4'b0011: Z = {in1[14:0], in1[15]}; //循环左移一位指令CSL
4'b0100: Z = in1 + in2; //加法指令ADD
4'b0101: Z = in1; //存数指令STA
4'b0110: Z = in2; //取数指令LDA
4'b1000: ct = in1[15]==1?1:0; //有条件转移BAN
4'b1001: ; //停机指令
endcase
end
endmodule
cu
输入:
operate |
---|
4位指令操作码 |
输出:
stop | uct | acc_wr | dataMem_wr | alu_op |
---|---|---|---|---|
停机信号 | 非条件转移uct | acc读写控制 | 数据存储器读写控制 | alu操作选择 |
//cu
module cu(
input wire [3:0] operate, //4位指令操作码
output reg stop, uct, acc_wr, dataMem_wr, //停机信号、非条件转移uct、acc读写控制、数据存储器读写控制
output reg [3:0] alu_op //alu操作选择
);
initial begin
stop = 0;
uct = 0;
acc_wr = 0;
dataMem_wr = 0;
alu_op = 4'b1111;
end
always @(operate) begin
case(operate)
4'b0000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100000; //清除累加器指令CLA
4'b0001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100001; //累加器取反指令COM
4'b0010: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100010; //算术右移一位指令SHR
4'b0011: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100011; //循环左移一位指令CSL
4'b0100: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100100; //加法指令ADD
4'b0101: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00010101; //存数指令STA
4'b0110: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100110; //取数指令LDA
4'b0111: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b01000111; //无条件转移指令JMP
4'b1000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00001000; //有条件转移BAN
4'b1001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b10000000; //停机STOP
endcase
end
endmodule
dataMem
输入:
dataMem_wr | clk | addr | data_in |
---|---|---|---|
dataMem使能 | 时钟 | 12位指令地址 | 16位输入数据 |
输出:
data_out |
---|
16位输出数据 |
//dataMem
module dataMem(
input wire dataMem_wr, clk, //dataMem使能、时钟
input wire [11:0] addr, //12位指令地址
input wire [15:0] data_in, //16位输入数据
output wire [15:0] data_out //16位输出数据
);
reg [15:0] dataMem[4096:0]; //2^12个存储单元,每个存储单元16位
assign data_out = dataMem[addr];
initial begin
dataMem[0] = 16'b0000000000000001; //初始化1
dataMem[1] = 16'b0000000000000010; //初始化2
end
always@(negedge clk) begin
if(dataMem_wr == 1)
dataMem[addr] = data_in;
end
endmodule
2. CPU例化实现
调用以上各个模块
module cpu(
input wire clk, rst
);
wire stop, ct, uct, acc_wr, dataMem_wr;
wire [11:0] pc_addr;
wire [3:0] alu_op;
wire [15:0] ins, in1, in2, Z;
//pc实例化
pc pc(
.clk(clk), .rst(rst), .stop(stop), .ct(ct), .uct(uct),
.offset(ins[11:0]),
.pc(pc_addr)
);
//指令存储器实例化
insMem insMem(
.addr(pc_addr),
.Ins(ins)
);
//acc实例化
acc acc(
.clk(clk), .acc_wr(acc_wr),
.data_in(Z),
.data_out(in1)
);
//数据存储器实例化
dataMem dataMem(
.dataMem_wr(dataMem_wr), .clk(clk),
.addr(ins[11:0]),
.data_in(Z),
.data_out(in2)
);
//cu实例化
cu cu(
.operate(ins[15:12]),
.stop(stop), .uct(uct), .acc_wr(acc_wr), .dataMem_wr(dataMem_wr),
.alu_op(alu_op)
);
//alu实例化
alu alu(
.in1(in1), .in2(in2),
.alu_op(alu_op),
.ct(ct),
.Z(Z)
);
endmodule
3. 编写测试文件
module cpu_test;
reg clk, rst;
initial begin
clk = 1;
rst = 1;
#5 rst = 0;
#80 $stop;
end
always #5 clk = ~clk;
cpu cpu(
.clk(clk), .rst(rst)
);
endmodule
4. 仿真结果及分析
根据以下原理图及实验结果波形图进行分析
1. 清除累加器指令CL
0~5ns时:rst=1
,pc中指令地址pc_addr=000000000000
,到insMem中取出指令ins=0000000000000000
,其中操作码operation=0000
传给CU
4'b0000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100000;
CU发出以上各种控制信号
-
alu_op=0000
,然后alu执行清除累加器指令CLA,结果Z=0
//清除累加器指令CLA 4'b0000: Z = 0;
-
dataMem_wr=0
,不能对dataMem进行写操作; -
acc_wr=1
,可以对acc进行写操作
初始化时acc=1
、dataMem[0]=0000000000000001
;因此ACC的data_out端口输出1,传给ALU的in1口,in1=0000000000000001
;dataMem[0]的数据输出给ACC的in2,因此in2=0000000000000001
5~10ns时:5ns时rst置0,时钟下降沿;ALU将Z=0
写入ACC中,同时ACC输出数据到in1,使得in1=000000000000
;in2
不变
2. 累加器取反指令COM
10ns~15ns时:10ns时钟上升沿,pc_addr加1得pc_addr=000000000001
,到insMem中取出指令ins=0001000000000000
,其中的操作码operation=0001
传给CU
4'b0001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100001;
CU发出以上各种控制信号
-
alu_op=0001
:alu执行清除累加器指令CLA,结果Z=~in1=111111111111
//累加器取反指令COM 4'b0001: Z = ~in1;
-
dataMem_wr=0
,不能对dataMem进行写操作 -
acc_wr=1
,可以对acc进行写操作
此时in1
、in2
均不变
15~20ns时:15ns时钟下降沿,ALU将Z=111111111111
写入ACC中,同时ACC输出数据到in1,使得in1=111111111111
;
3. 算术右移一位指令SHR
20ns~25ns时:20ns时钟上升沿,pc_addr加1得pc_addr=000000000010
,到insMem中取出指令ins=0010000000000000
,其中的操作码operation=0010
传给CU
4'b0010: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100010;
CU发出以上各种控制信号
-
alu_op=0010
,然后alu执行算术右移一位指令SHR,结果Z=111111111111
//算术右移一位指令SHR 4'b0010: Z = in1[15] == 1 ? {1'b1, in1[15:1]} : {1'b0, in1[15:1]};
-
dataMem_wr=0
,不能对dataMem进行写操作;acc_wr=1
,可以对acc进行写操作
in2
不变
25~30ns时:25ns时钟下降沿,ALU将Z=111111111111
写入ACC中,同时ACC输出数据,使得in1=111111111111
;in2
不变
4. 循环左移一位指令CSL
30ns~35ns时:30ns时钟上升沿,pc_addr加1得pc_addr=00000011
,到insMem中取出指令ins=0011000000000000
,其中的操作码operation=0011
传给CU
4'b0011: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100011;
CU发出以上各种控制信号
-
alu_op=0011
,alu执行循环左移一位指令CSL,结果Z=111111111111
//循环左移一位指令 CSL4'b0011: Z = {in1[14:0], in1[15]};
-
dataMem_wr=0
,不能对dataMem进行写操作;acc_wr=1
,可以对acc进行写操作
in2
不变
35~40ns时:35ns时钟下降沿,ALU将Z=111111111111
写入ACC中,同时ACC输出数据,使得in1=111111111111
;in2
不变
5. 加法指令ADD
40ns~45ns时:40ns时钟上升沿,pc_addr加1得pc_addr=00000100
,到insMem中取出指令ins=0100000000000001
,其中的操作码operation=0100
传给CU
4'b0100: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100100;
CU发出以上各种控制信号
-
alu_op=0100
,然后alu执行加法指令ADD,in2从dataMem[1]读入数据,与ACC的数据相加得结果Z=1111111111111111+b0000000000000010=0000000000000001
//加法指令ADD 4'b0100: Z = in1 + in2;
-
dataMem_wr=0
,不能对dataMem进行写操作; -
acc_wr=1
,可以对acc进行写操作
45~50ns时:45ns时钟下降沿,ALU将Z=0000000000000001
写入ACC中,同时ACC输出数据,使得in1=0000000000000001
;in2
不变;Z=in1+in2=0000000000000011
6. 存数指令STA
50ns~55ns时:50ns时钟上升沿,pc_addr加1得pc_addr=000000000101
,到insMem中取出指令ins=0101000000000000
,其中的操作码operation=0101
传给CU
4'b0101: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00010101;
CU发出以上各种控制信号
-
alu_op=0101
,然后alu执行存数指令STA,结果Z=in1=0000000000000001
//存数指令STA 4'b0101: Z = in1;
-
dataMem_wr=1
,可以对dataMem进行写操作 -
acc_wr=0
,不能对acc进行写操作
55~60ns时:55ns时钟下降沿,ALU将Z=0000000000000001
写入dataMem[000000000101]中,输出数据,使得in2=0000000000000001
;in1
不变
7. 取数指令LDA
60ns~65ns时:60ns时钟上升沿,pc_addr加1得pc_addr=00000110
,到insMem中取出指令ins=0110000000000000
,其中的操作码operation=0110
传给CU
4'b0110: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100110;
CU发出以上各种控制信号
-
alu_op=0110
,然后alu执行取数指令LDA,结果Z=in2=0000000000000001
-
dataMem_wr=0
,不能对dataMem进行写操作;acc_wr=1
,可以对acc进行写操作
65~70ns时:65ns时钟下降沿,ALU将Z=0000000000000001
写入ACC中,同时输出数据,使得in1=0000000000000001
;in2
不变
8. 无条件转移指令JMP
70ns~75ns时:70ns时钟上升沿,pc_addr加1得pc_addr=000000000111
,到insMem中取出指令ins=0111000000001001
,其中的操作码operation=0111
传给CU
4'b0111: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00000111;
CU发出以上各种控制信号
alu_op=0111
,然后alu执行无条件转移指令JMPdataMem_wr=0
,不能对dataMem进行写操作;acc_wr=0
,不能对acc进行写操作uct=1
,进行无条件转移
75~80ns时:75ns时钟下降沿,pc被修改,转移偏移量为9,pc_addr=000000001001-1=000000001000
;
9. 有条件转移指令BAN
80ns~85ns时:80ns时钟上升沿,pc_addr加1得pc_addr=00001001
,到insMem中取出指令ins=1000000000001001
,其中的操作码operation=1000
传给CU
4'b1000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00001000;
CU发出以上各种控制信号
-
alu_op=1001
,然后alu执行有条件转移指令BAN,结果ct=0
//有条件转移BAN 4'b1000: ct = in1[15]==1?1:0;
-
dataMem_wr=0
,不能对dataMem进行写操作; -
acc_wr=0
,不能对acc进行写操作
85~90ns时:75ns时钟下降沿,由于ct,所以pc不被修改,保持不变
10. 停机指令STP
90ns后:90ns时钟上升沿,pc_addr加1得pc_addr=00001001
,到insMem中取出指令ins=100100000000000
,其中的操作码operation=1001
传给CU
4'b1001: {stop,uct,ct,acc_wr,dataMem_wr,alu_op} = 8'b100000000;
CU使stop=1
,发出停机控制信号,执行停机指令
//停机指令STP
4'b1001: ;
从此pc不再增加,保持不变