Lab2: COMP12111 计算机组成与结构: 16位简易ALU电路设计
在本文中, 我们将基于 Ch1, Ch2
所学知识, 搭建完成 16
位简易 ALU
的电路设计, 并对其进行测试.
1. ALU
设计目标
在本次实验中, 我们将设计一个 16
位的运算逻辑单元 (ALU
). 其电路拓扑图如下:
其中, 指令输入 M
的输入与功能定义表如下:
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
注意:
- 在定义
case
语句时, 必须注意是否合理定义default
语句, 否则当case
的判断信号为高阻或未定义时, 条件体的执行就会出现问题. 在本实例中, 我们列举所有可能的合理输入并对其输出进行依次定义, 并指定当接收非法输入时输出无效信号'bx
以起到警告作用. - 在本实例中我们需要特别注意敏感度列表的定义.
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
电路测试
要对电路进行测试, 我们需要先后依序定义被测试电路的输入/输出, 将被测试电路模块实例化为被测试对象, 最后指定测试内容.
-
定义被测电路的输入/输出: 由于在实验中, 我们要依序变换输入信号的值, 因此输入信号应被定义为
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
- 将被测模块实例化被测对象:
1 2
// Instantiate mu0_alu as dut (device under test) mu0_alu dut(X, Y, M, Q);
-
指定测试内容:
在本实例中, 由于变量的运算是由编译器所提供的标准运算符所执行的, 因此我们可以不去考虑运算符可能输出结果的正确与否问题, 只需要确定当我们输入特定指令变量值时是否执行了设计要求所指定的运算类型.此外, 为了更好地在波形图上显示输出内容, 我们可以在最后一次测试项目后定义一个长为
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