单周期十条指令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. 规定

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

  2. 指令格式为:

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

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

2. 原理图

image-20201203143332188



四、实验步骤

1. CPU各部件实现


pc

输入:

clkrststopctuct
时钟时钟停机条件转移无条件转移

输出:

offsetpc
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

输入:

clkacc_wrdata_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

输入:

in1in2alu_op
操作数1操作数2操作选择信号

输出:

ctZ
条件转移结果
//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位指令操作码

输出:

stopuctacc_wrdataMem_wralu_op
停机信号非条件转移uctacc读写控制数据存储器读写控制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_wrclkaddrdata_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. 仿真结果及分析

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

image-20201203143332188

1. 清除累加器指令CL

image-20201205134717180
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=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


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进行写操作

此时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

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=111111111111in2不变


4. 循环左移一位指令CSL

image-20201205135859815

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=111111111111in2不变


5. 加法指令ADD

image-20201205140301744

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=0000000000000001in2不变;Z=in1+in2=0000000000000011


6. 存数指令STA

image-20201205140444486

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=0000000000000001in1不变


7. 取数指令LDA

image-20201205141129047
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=0000000000000001in2不变


8. 无条件转移指令JMP

image-20201205141227746
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执行无条件转移指令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

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不再增加,保持不变

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baret-H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值