数电课程模型机 CPU 设计:译码器和 ALU(Verilog 实现)

译码器

译码器的指令系统表如下图所示。

指令系统表

译码器做起来其实不难,当编码符合要求的时候,把相应的汇编符号输出设为 1 就行了。

R1、R2 代表操作寄存器的地址,可以为 A、B 或 C 寄存器中的任何一个,地址分别为 00、01 和 10。

所谓” 符合要求 “,可以举个例子。比如 NOT 的编码是 0101 R1XX,R1 代表寄存器 1 的地址,XX 是通配符,所以可以做匹配 IR[7-:4]==4'b0101,而且应为寄存器地址的地方不能是 11。(话说也没多少人会在这个位置传进 11 吧…… 除了喜欢点一杯炒饭的测试工程师)

特别注意的是,有三种 MOV 指令。这里定义第一种 MOV 指令为 MOVA,第二种为 MOVB,第三种为 MOVC。我先使用 1100 匹配进 MOV,然后再区分 MOVA、MOVB 和 MOVC。

verilog
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
module decoder (EN,IR,MOVA,MOVB,MOVC,ADD,SUB,AND,NOT,RSR,RSL,JMP,JZ,JC,IN,OUT,NOP,HALT);
    input EN;
    input[7:0] IR;
    output MOVA,MOVB,MOVC,ADD,SUB,AND,NOT,RSR,RSL,JMP,JZ,JC,IN,OUT,NOP,HALT;
    reg MOVA,MOVB,MOVC,ADD,SUB,AND,NOT,RSR,RSL,JMP,JZ,JC,IN,OUT,NOP,HALT;
always @(IR,EN) begin
    if(EN==1'b1) begin
        if (IR[7-:4]==4'b1100) begin //MOV
            if(IR[3-:2]==2'b11) begin //MOVB 11R2
                MOVA=1'b1;
                MOVB=1'b0;
                MOVC=1'b0;
            end
            else if (IR[1-:2]==2'b11) begin //MOVC R1111
                MOVA=1'b0;
                MOVB=1'b0;
                MOVC=1'b1;
            end
            else begin //MOVA R1R2
                MOVA=1'b1;
                MOVB=1'b0;
                MOVC=1'b0;
            end
        end
        else begin
            MOVA=1'b0;
            MOVB=1'b0;
            MOVC=1'b0;
        end
        if (IR[7-:4]==4'b1001 && IR[3-:2]!=2'b11 && IR[1-:2]!=2'b11) begin //ADD
            ADD=1'b1;
        end
        else ADD=1'b0;
        if (IR[7-:4]==4'b0110 && IR[3-:2]!=2'b11 && IR[1-:2]!=2'b11) begin //SUB
            SUB=1'b1;
        end
        else SUB=1'b0;
        if (IR[7-:4]==4'b1011 && IR[3-:2]!=2'b11 && IR[1-:2]!=2'b11) begin //AND
            AND=1'b1;
        end
        else AND=1'b0;
        if (IR[7-:4]==4'b0101 && IR[3-:2]!=2'b11) begin //NOT
            NOT=1'b1;
        end
        else NOT=1'b0;
        if (IR[7-:4]==4'b1010 && IR[1-:2]==2'b00 && IR[3-:2]!=2'b11) begin //RSR
            RSR=1'b1;
        end
        else RSR=1'b0;
        if (IR[7-:4]==4'b1010 && IR[1-:2]==2'b11 && IR[3-:2]!=2'b11) begin //RSL
            RSL=1'b1;
        end
        else RSL=1'b0;
        if (IR[7:0]==8'b0011_0000) begin //JMP
            JMP=1'b1;
        end
        else JMP=1'b0;
        if (IR[7:0]==8'b0011_0001) begin //JZ
            JZ=1'b1;
        end
        else JZ=1'b0;
        if (IR[7:0]==8'b0011_0010) begin //JC
            JC=1'b1;
        end
        else JC=1'b0;
        if (IR[7-:4]==4'b0010 && IR[3-:2]!=2'b11) begin //IN
            IN=1'b1;
        end
        else IN=1'b0;
        if (IR[7-:4]==4'b0100 && IR[3-:2]!=2'b11) begin //OUT
            OUT=1'b1;
        end
        else OUT=1'b0;
        if (IR[7:0]==8'b0111_0000) begin //NOP
            NOP=1'b1;
        end
        else NOP=1'b0;
        if (IR[7:0]==8'b1000_0000) begin //HALT
            HALT=1'b1;
        end
        else HALT=1'b0;
    end
    else begin
        MOVA=1'b0;
        MOVB=1'b0;
        MOVC=1'b0;
        ADD=1'b0;
        SUB=1'b0;
        AND=1'b0;
        NOT=1'b0;
        RSR=1'b0;
        RSL=1'b0;
        JMP=1'b0;
        JZ=1'b0;
        JC=1'b0;
        IN=1'b0;
        OUT=1'b0;
        NOP=1'b0;
        HALT=1'b0;
    end

end

endmodule //decoder
      

ALU

我之前想复杂了,完全不需要设计 RCA,因为 Cf 指的 是最高位的进位或借位(即溢出),而不是逐位取进位或借位,所以可以直接使用拼接解决。 怪不得从网上找了一堆资料没发现如何找借位

ALU 引脚关系

这样就没有之前那么难了,把 ppt 里的东西直接翻译出来就行了, 源代码如下。

verilog
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
module ALU2 (M,S,A,B,t,Cf,Zf);
    input M;// 算术运算指示
    input [3:0] S;// 运算类型
    input [7:0] A,B;// 参与运算的数字

    output [7:0] t;// 输出结果
    output Cf,Zf;// 是否进位,是否为 0

    reg [7:0] t;
    reg Cf,Zf;
    reg [7:0] temp1;

    always @(M,S,A,B) begin
        if (M==1'b0) begin // 不进行算术运算
            if(S==4'b1100) t[7:0]=A[7:0];
            else t=8'b0;
            Cf=1'b0;
            Zf=1'b0;
        end
        else begin// 进行算术运算
            case (S)
                4'b1001: begin //ADD
                    {Cf,t}=A+B;
                    if (t==8'b0) begin
                        Zf=1'b1;
                    end
                    else Zf=1'b0;
                end
                4'b0110: begin //SUB
                    {Cf,t}=B-A;
                    if(t==8'b0) Zf=1'b1;
                    else Zf=1'b0;
                end
                4'b1011: begin //AND
                    // t=A&&B;
                    t[0]=A[0]&&B[0];
                    t[1]=A[1]&&B[1];
                    t[2]=A[2]&&B[2];
                    t[3]=A[3]&&B[3];
                    t[4]=A[4]&&B[4];
                    t[5]=A[5]&&B[5];
                    t[6]=A[6]&&B[6];
                    t[7]=A[7]&&B[7];
                    Cf=0;
                    Zf=0;
                end
                4'b0101: begin
                    t=~B; //NOT
                    Cf=0;
                    Zf=0;
                end
                4'b1010: begin
                    t=B;
                    Cf=0;
                    Zf=0;
                end
                4'b0100: begin
                    t=B;
                    Cf=0;
                    Zf=0;
                end
                4'b1100: begin
                    t=A;
                    Cf=0;
                    Zf=0;
                end
                default: begin
                    Cf=0;
                    Zf=0;
                    t=8'b0;
                end
            endcase
        end
    end
endmodule //ALU2

本文代码仿真所使用的波形文件可从 此处 获取,使用 Quartus II 13.0 SP1 生成。2021/12/06 已更新本人验收用波形,数据较多。