单周期十条指令CPU设计与verilog实现(Modelsim)
目录
一、实验目的
通过设计并实现支持 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不再增加,保持不变
更多推荐
所有评论(0)