Lab2 计算机组成与结构 16位简易ALU

Step by Step, Case by Case

Posted by R1NG on October 27, 2020 Viewed Times

Lab2: COMP12111 计算机组成与结构: 16位简易ALU电路设计

在本文中, 我们将基于 Ch1, Ch2 所学知识, 搭建完成 16 位简易 ALU的电路设计, 并对其进行测试.

1. ALU 设计目标

在本次实验中, 我们将设计一个 16 位的运算逻辑单元 (ALU). 其电路拓扑图如下:

20201107080602 其中, 指令输入 M 的输入与功能定义表如下:

20201107080625

2. ALU 逻辑设计

在本次实验中, 由于 ALU 执行的运算受指令输入所控制, 我们使用阻塞赋值和 case 语句对该组合逻辑结构加以定义. 其主要逻辑结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
// behavioural description

always @ (*)
begin
    case (M)
        2'b00: Q = Y;
        2'b01: Q = X+Y;
        2'b10: Q = X+16'h0001;     //represent 1 in hexadecimal way to avoid confusions
        2'b11: Q = X-Y;
        default: Q = 'bx;          //default: send NULL to alert
    endcase
end

注意:

  1. 在定义 case 语句时, 必须注意是否合理定义 default 语句, 否则当 case 的判断信号为高阻或未定义时, 条件体的执行就会出现问题. 在本实例中, 我们列举所有可能的合理输入并对其输出进行依次定义, 并指定当接收非法输入时输出无效信号 'bx 以起到警告作用.
  2. 在本实例中我们需要特别注意敏感度列表的定义. ALU 执行的运算类型受到控制变量的影响, 那么是否意味着 always 程序体的执行与其他输入变量的变化值无关呢? 我们考虑这个情形: 如果我们希望多次连续地输入不同的数值, 但执行同一种运算, 那么如果敏感度列表中不包含输入数据值的话, 将会导致无法连续执行同一种运算, 并且在执行一种运算之后必须改变指令变量输入值才能进行新的运算. 因此, 合理的敏感度列表应当包含所有的输入值.

阻塞赋值语句的被赋值变量必须被声明为 reg 数据类型. 而 Verilog 规定, 电路定义时, 输入变量必须被定义为 wire. 故电路输入/输出定义如下:

1
2
3
4
5
6
// module header

module mu0_alu(input  wire [15:0]  X, 
               input  wire [15:0]  Y, 
               input  wire [1:0]   M, 
               output reg  [15:0]  Q);

对电路定义和设计的总结如下:

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
// MU0 ALU design 


// The following line is required for simulation 
`timescale 1ns/100ps

// for simulation purposes
`default_nettype none

// module header

module mu0_alu(input  wire [15:0]  X, 
               input  wire [15:0]  Y, 
               input  wire [1:0]   M, 
               output reg  [15:0]  Q);

// behavioural description

always @ (*)
begin
    case (M)
        2'b00: Q = Y;
        2'b01: Q = X+Y;
        2'b10: Q = X+16'h0001;   //represent 1 in hexadecimal way to avoid confusions
        2'b11: Q = X-Y;
        default: Q = 'bx;        //default: send NULL to alert
    endcase
end

endmodule 

// for simulation purposes
`default_nettype wire


3. ALU 电路测试

要对电路进行测试, 我们需要先后依序定义被测试电路的输入/输出, 将被测试电路模块实例化为被测试对象, 最后指定测试内容.

  1. 定义被测电路的输入/输出: 由于在实验中, 我们要依序变换输入信号的值, 因此输入信号应被定义为 reg 数据类型. 相应的, 我们并不关心输出信号值的变化历史, 故它可以被定义为 wire 数据类型.

    在本实验中, 被测电路的输入/输出定义如下:

    1
    2
    3
    4
    5
    
     // Internal connections
     wire [15:0] Q;  //output Q
     reg [15:0] X;   //input X
     reg [15:0] Y;   //input Y
     reg [1:0] M;    //input M
    
  2. 将被测模块实例化被测对象:
    1
    2
    
     // Instantiate mu0_alu as dut (device under test)
     mu0_alu dut(X, Y, M, Q);
    
  3. 指定测试内容:
    在本实例中, 由于变量的运算是由编译器所提供的标准运算符所执行的, 因此我们可以不去考虑运算符可能输出结果的正确与否问题, 只需要确定当我们输入特定指令变量值时是否执行了设计要求所指定的运算类型.

    此外, 为了更好地在波形图上显示输出内容, 我们可以在最后一次测试项目后定义一个长为 100ns 的延迟.

    并且, 我们还可以刻意令指令变量输入无效值, 以此测试特殊情况下 ALU 的稳健性. 测试内容整理如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     // Test vectors
     initial
     begin
     #100 X = 16'h0003; Y = 16'h0002; M = 2'b00;    //initialize X=2, Y=1, M=0
     #100 M = 2'b00;         //Test1: M=0, Q = Y = 2
     #100 M = 2'b01;         //Test2: M=1, Q = X + Y = 5
     #100 M = 2'b10;         //Test3: M=2, Q= X+1 = 4
     #100 M = 2'b11;         //Test4: M=3, Q = X - Y = 1
     #100 M = 'bx;           //Test5: Invalid input
     #100
    
     $finish;   // end the simulation
     end
    

    对电路测试的总结如下:

    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
    
     // MU0 ALU testbench 
     // #1 = 1ns
     `timescale 1ns/100ps 
    
     module mu0_alu_tb();
    
     // Internal connections
     wire [15:0] Q;  //output Q
     reg [15:0] X;   //input X
     reg [15:0] Y;   //input Y
     reg [1:0] M;    //input M
    
    
    
     // Instantiate mu0_alu as dut (device under test)
     mu0_alu dut(X, Y, M, Q);
    
    
     // Test vectors
     initial
     begin
     #100 X = 16'h0003; Y = 16'h0002; M = 2'b00;    //initialize X=2, Y=1, M=0
     #100 M = 2'b00;         //Test1: M=0, Q = Y = 2
     #100 M = 2'b01;         //Test2: M=1, Q = X + Y = 5
     #100 M = 2'b10;         //Test3: M=2, Q= X+1 = 4
     #100 M = 2'b11;         //Test4: M=3, Q = X - Y = 1
     #100 M = 'bx;           //Test5: Invalid input
     #100
    
     $finish;   // end the simulation
     end
        
     // Save results as VCD file 
     initial
     begin
     $dumpfile("mu0_alu_tb_results.vcd");  // Save simulation waveforms in this file
     $dumpvars; // Capture all simulation waveforms
     end
    
     endmodule