单周期十条指令CPU设计与verilog实现(Modelsim)

ゞ 浴缸里的玫瑰 2023-01-11 09:29 373阅读 0赞

目录

  • 一、实验目的
  • 二、实验内容
  • 三、实验原理
      1. 规定
      1. 原理图
  • 四、实验步骤
      1. CPU各部件实现
      • pc
      • insMem
      • acc
      • alu
      • cu
      • dataMem
      1. CPU例化实现
      1. 编写测试文件
      1. 仿真结果及分析
        1. 清除累加器指令CL
        1. 累加器取反指令COM
        1. 算术右移一位指令SHR
        1. 循环左移一位指令CSL
        1. 加法指令ADD
        1. 存数指令STA
        1. 取数指令LDA
        1. 无条件转移指令JMP
        1. 有条件转移指令BAN
        1. 停机指令STP

一、实验目的

通过设计并实现支持 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. 规定

  1. 机器字长、指令字长和存储字长均为16位
  2. 指令格式为:

    image-20210126212557470

  3. 指令及其操作码对照表




































    非访存指令 访存指令 转移类指令
    清除累加器指令CLA0000 加法指令ADD0100 无条件转移指令JMP0111
    累加器取反指令COM0001 存数指令STA0101 有条件转移指令BAN 1000
    算术右移一位指令SHR0010 取数指令LDA0110
    循环左移一位指令CSL0011
    停机指令STP: 1001

2. 原理图

image-20201203143332188


四、实验步骤

1. CPU各部件实现


pc

输入:




















clk rst stop ct uct
时钟 时钟 停机 条件转移 无条件转移

输出:














offset pc
12位转移指令偏移量 12位指令地址码
  1. //pc
  2. module pc(
  3. input wire clk, rst, stop, ct, uct, //时钟、重置、停机、条件转移、无条件转移
  4. input wire [11:0] offset, //12位转移指令偏移量
  5. output reg [11:0] pc //12位指令地址码
  6. );
  7. assign stop = 0; //初始化开机状态
  8. // assign clk = (stop==1)?1'bz:0; //停机则将clk置0
  9. always@(posedge clk) begin
  10. if(rst == 1)
  11. pc = 0;
  12. else
  13. pc = pc + 1;
  14. end
  15. always@(negedge clk) begin
  16. if(uct == 1) //无条件转移
  17. pc = offset-1;
  18. if(ct == 1) //条件转移
  19. pc = pc+offset-1;
  20. end
  21. endmodule

insMem

输入:












addr
12位指令地址码

输出:












Ins
16位指令
  1. //insMem
  2. module insMem(
  3. input wire [11:0] addr, //12位指令地址码
  4. output wire [15:0] Ins //16位指令
  5. );
  6. reg[15:0] insMem[4096:0]; //2^12个存储单元,每个存储单元16位
  7. initial begin
  8. insMem[0] = 16'b0000000000000000; //0000 0000 0000 0000 清除累加器指令CLA
  9. insMem[1] = 16'b0001000000000000; //0001 0000 0000 0000 累加器取反指令COM
  10. insMem[2] = 16'b0010000000000000; //0010 0000 0000 0000 算术右移一位指令SHR
  11. insMem[3] = 16'b0011000000000000; //0011 0000 0000 0000 循环左移一位指令CSL
  12. insMem[4] = 16'b0100000000000001 ; //0100 0000 0000 0001 加法指令ADD
  13. insMem[5] = 16'b0101000000000000; //0101 0000 0000 0000 存数指令STA
  14. insMem[6] = 16'b0110000000000000; //0110 0000 0000 0000 取数指令LDA
  15. insMem[7] = 16'b0111000000001001; //0111 0000 0000 1001 无条件转移指令JMP
  16. insMem[9] = 16'b1000000000001001; //1000 0000 0000 1001 有条件转移BAN
  17. insMem[10] = 16'b100100000000000; //1111 0000 0000 0000 停机指令STP
  18. end
  19. assign Ins = insMem[addr];
  20. endmodule

acc

输入:
















clk acc_wr data_in
时钟 acc读写控制 16位输入数据

输出:












data_out
16位输出数据
  1. //acc
  2. module acc(
  3. input wire clk, acc_wr, //时钟、acc读写控制
  4. input wire [15:0] data_in, //16位输入数据
  5. output wire [15:0] data_out //16位输出数据
  6. );
  7. reg [15:0] acc; //16位acc
  8. initial begin
  9. acc = 1; //acc初始化1
  10. end
  11. assign data_out = acc;
  12. always@(negedge clk) begin
  13. if(acc_wr == 1)
  14. acc = data_in;
  15. end
  16. endmodule

alu

输入:
















in1 in2 alu_op
操作数1 操作数2 操作选择信号

输出:














ct Z
条件转移 结果
  1. //alu
  2. module alu(
  3. input wire [15:0] in1, in2, //操作数in1和in2
  4. input wire [3:0] alu_op, //操作选择信号alu_op
  5. output reg ct, //条件转移ct
  6. output reg [15:0] Z //Z
  7. );
  8. initial begin
  9. ct = 0;
  10. Z = 0; //初始化结果Z为0
  11. end
  12. always@* begin
  13. case (alu_op)
  14. 4'b0000: Z = 0; //清除累加器指令CLA
  15. 4'b0001: Z = ~in1; //累加器取反指令COM
  16. 4'b0010: Z = in1[15] == 1 ? {1'b1, in1[15:1]} : {1'b0, in1[15:1]}; //算术右移一位指令SHR
  17. 4'b0011: Z = {in1[14:0], in1[15]}; //循环左移一位指令CSL
  18. 4'b0100: Z = in1 + in2; //加法指令ADD
  19. 4'b0101: Z = in1; //存数指令STA
  20. 4'b0110: Z = in2; //取数指令LDA
  21. 4'b1000: ct = in1[15]==1?1:0; //有条件转移BAN
  22. 4'b1001: ; //停机指令
  23. endcase
  24. end
  25. endmodule

cu

输入:












operate
4位指令操作码

输出:




















stop uct acc_wr dataMem_wr alu_op
停机信号 非条件转移uct acc读写控制 数据存储器读写控制 alu操作选择
  1. //cu
  2. module cu(
  3. input wire [3:0] operate, //4位指令操作码
  4. output reg stop, uct, acc_wr, dataMem_wr, //停机信号、非条件转移uct、acc读写控制、数据存储器读写控制
  5. output reg [3:0] alu_op //alu操作选择
  6. );
  7. initial begin
  8. stop = 0;
  9. uct = 0;
  10. acc_wr = 0;
  11. dataMem_wr = 0;
  12. alu_op = 4'b1111;
  13. end
  14. always @(operate) begin
  15. case(operate)
  16. 4'b0000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100000; //清除累加器指令CLA
  17. 4'b0001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100001; //累加器取反指令COM
  18. 4'b0010: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100010; //算术右移一位指令SHR
  19. 4'b0011: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100011; //循环左移一位指令CSL
  20. 4'b0100: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100100; //加法指令ADD
  21. 4'b0101: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00010101; //存数指令STA
  22. 4'b0110: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100110; //取数指令LDA
  23. 4'b0111: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b01000111; //无条件转移指令JMP
  24. 4'b1000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00001000; //有条件转移BAN
  25. 4'b1001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b10000000; //停机STOP
  26. endcase
  27. end
  28. endmodule

dataMem

输入:


















dataMem_wr clk addr data_in
dataMem使能 时钟 12位指令地址 16位输入数据

输出:












data_out
16位输出数据
  1. //dataMem
  2. module dataMem(
  3. input wire dataMem_wr, clk, //dataMem使能、时钟
  4. input wire [11:0] addr, //12位指令地址
  5. input wire [15:0] data_in, //16位输入数据
  6. output wire [15:0] data_out //16位输出数据
  7. );
  8. reg [15:0] dataMem[4096:0]; //2^12个存储单元,每个存储单元16位
  9. assign data_out = dataMem[addr];
  10. initial begin
  11. dataMem[0] = 16'b0000000000000001; //初始化1
  12. dataMem[1] = 16'b0000000000000010; //初始化2
  13. end
  14. always@(negedge clk) begin
  15. if(dataMem_wr == 1)
  16. dataMem[addr] = data_in;
  17. end
  18. endmodule

2. CPU例化实现

调用以上各个模块

  1. module cpu(
  2. input wire clk, rst
  3. );
  4. wire stop, ct, uct, acc_wr, dataMem_wr;
  5. wire [11:0] pc_addr;
  6. wire [3:0] alu_op;
  7. wire [15:0] ins, in1, in2, Z;
  8. //pc实例化
  9. pc pc(
  10. .clk(clk), .rst(rst), .stop(stop), .ct(ct), .uct(uct),
  11. .offset(ins[11:0]),
  12. .pc(pc_addr)
  13. );
  14. //指令存储器实例化
  15. insMem insMem(
  16. .addr(pc_addr),
  17. .Ins(ins)
  18. );
  19. //acc实例化
  20. acc acc(
  21. .clk(clk), .acc_wr(acc_wr),
  22. .data_in(Z),
  23. .data_out(in1)
  24. );
  25. //数据存储器实例化
  26. dataMem dataMem(
  27. .dataMem_wr(dataMem_wr), .clk(clk),
  28. .addr(ins[11:0]),
  29. .data_in(Z),
  30. .data_out(in2)
  31. );
  32. //cu实例化
  33. cu cu(
  34. .operate(ins[15:12]),
  35. .stop(stop), .uct(uct), .acc_wr(acc_wr), .dataMem_wr(dataMem_wr),
  36. .alu_op(alu_op)
  37. );
  38. //alu实例化
  39. alu alu(
  40. .in1(in1), .in2(in2),
  41. .alu_op(alu_op),
  42. .ct(ct),
  43. .Z(Z)
  44. );
  45. endmodule

3. 编写测试文件

  1. module cpu_test;
  2. reg clk, rst;
  3. initial begin
  4. clk = 1;
  5. rst = 1;
  6. #5 rst = 0;
  7. #80 $stop;
  8. end
  9. always #5 clk = ~clk;
  10. cpu cpu(
  11. .clk(clk), .rst(rst)
  12. );
  13. endmodule

4. 仿真结果及分析

根据以下原理图及实验结果波形图进行分析

image-20201203143332188

1. 清除累加器指令CL

image-20201205134717180
0~5ns时rst=1,pc中指令地址pc_addr=000000000000,到insMem中取出指令ins=0000000000000000,其中操作码operation=0000传给CU

  1. 4'b0000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100000;

CU发出以上各种控制信号

  • alu_op=0000,然后alu执行清除累加器指令CLA,结果Z=0

    1. //清除累加器指令CLA
    2. 4'b0000: Z = 0;
  • dataMem_wr=0,不能对dataMem进行写操作;
  • acc_wr=1,可以对acc进行写操作

初始化时acc=1dataMem[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=000000000000in2不变


2. 累加器取反指令COM

image-20201205134717180
10ns~15ns时:10ns时钟上升沿,pc_addr加1得pc_addr=000000000001,到insMem中取出指令ins=0001000000000000,其中的操作码operation=0001传给CU

  1. 4'b0001: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100001;

CU发出以上各种控制信号

  • alu_op=0001:alu执行清除累加器指令CLA,结果Z=~in1=111111111111

    1. //累加器取反指令COM
    2. 4'b0001: Z = ~in1;
  • dataMem_wr=0,不能对dataMem进行写操作
  • acc_wr=1,可以对acc进行写操作

此时in1in2均不变

15~20ns时:15ns时钟下降沿,ALU将Z=111111111111写入ACC中,同时ACC输出数据到in1,使得in1=111111111111


3. 算术右移一位指令SHR

image-20201205134754701

20ns~25ns时:20ns时钟上升沿,pc_addr加1得pc_addr=000000000010,到insMem中取出指令ins=0010000000000000,其中的操作码operation=0010传给CU

  1. 4'b0010: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100010;

CU发出以上各种控制信号

  • alu_op=0010,然后alu执行算术右移一位指令SHR,结果Z=111111111111

    1. //算术右移一位指令SHR
    2. 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=111111111111in2不变


4. 循环左移一位指令CSL

image-20201205135859815

30ns~35ns时:30ns时钟上升沿,pc_addr加1得pc_addr=00000011,到insMem中取出指令ins=0011000000000000,其中的操作码operation=0011传给CU

  1. 4'b0011: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00100011;

CU发出以上各种控制信号

  • alu_op=0011,alu执行循环左移一位指令CSL,结果Z=111111111111

    1. //循环左移一位指令
    2. 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=111111111111in2不变


5. 加法指令ADD

image-20201205140301744

40ns~45ns时:40ns时钟上升沿,pc_addr加1得pc_addr=00000100,到insMem中取出指令ins=0100000000000001,其中的操作码operation=0100传给CU

  1. 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

    1. //加法指令ADD
    2. 4'b0100: Z = in1 + in2;
  • dataMem_wr=0,不能对dataMem进行写操作;
  • acc_wr=1,可以对acc进行写操作

45~50ns时:45ns时钟下降沿,ALU将Z=0000000000000001写入ACC中,同时ACC输出数据,使得in1=0000000000000001in2不变;Z=in1+in2=0000000000000011


6. 存数指令STA

image-20201205140444486

50ns~55ns时:50ns时钟上升沿,pc_addr加1得pc_addr=000000000101,到insMem中取出指令ins=0101000000000000,其中的操作码operation=0101传给CU

  1. 4'b0101: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00010101;

CU发出以上各种控制信号

  • alu_op=0101,然后alu执行存数指令STA,结果Z=in1=0000000000000001

    1. //存数指令STA
    2. 4'b0101: Z = in1;
  • dataMem_wr=1,可以对dataMem进行写操作
  • acc_wr=0,不能对acc进行写操作

55~60ns时:55ns时钟下降沿,ALU将Z=0000000000000001写入dataMem[000000000101]中,输出数据,使得in2=0000000000000001in1不变


7. 取数指令LDA

image-20201205141129047
60ns~65ns时:60ns时钟上升沿,pc_addr加1得pc_addr=00000110,到insMem中取出指令ins=0110000000000000,其中的操作码operation=0110传给CU

  1. 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=0000000000000001in2不变


8. 无条件转移指令JMP

image-20201205141227746
70ns~75ns时:70ns时钟上升沿,pc_addr加1得pc_addr=000000000111,到insMem中取出指令ins=0111000000001001,其中的操作码operation=0111传给CU

  1. 4'b0111: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00000111;

CU发出以上各种控制信号

  • alu_op=0111,然后alu执行无条件转移指令JMP
  • dataMem_wr=0,不能对dataMem进行写操作;acc_wr=0,不能对acc进行写操作
  • uct=1,进行无条件转移

75~80ns时:75ns时钟下降沿,pc被修改,转移偏移量为9,pc_addr=000000001001-1=000000001000


9. 有条件转移指令BAN

image-20201205141333825
80ns~85ns时:80ns时钟上升沿,pc_addr加1得pc_addr=00001001,到insMem中取出指令ins=1000000000001001,其中的操作码operation=1000传给CU

  1. 4'b1000: {stop,uct,acc_wr,dataMem_wr,alu_op} = 8'b00001000;

CU发出以上各种控制信号

  • alu_op=1001,然后alu执行有条件转移指令BAN,结果ct=0

    1. //有条件转移BAN
    2. 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

  1. 4'b1001: {stop,uct,ct,acc_wr,dataMem_wr,alu_op} = 8'b100000000;

CU使stop=1,发出停机控制信号,执行停机指令

  1. //停机指令STP
  2. 4'b1001: ;

从此pc不再增加,保持不变

发表评论

表情:
评论列表 (有 0 条评论,373人围观)

还没有评论,来说两句吧...

相关阅读