I. Mục tiêu
Hình
1.1
Khối ALU 4 bit
Mục đích của bài thí nghiệm này là thiết kế một ALU đơn giản
như sau:
- Độ dài các toán hạng là 4-bit.
- Các ngõ nhập function-select gồm có: M, S0 và S1.
- Các tác vụ ALU thực hiện được cho trong bảng bên dưới:
Hình 1.2 Xử
lý các tác vụ về logic và toán học dựa trên input ngõ vào
II. Tóm tắt các bước tiến hành.
Trước tiên chúng ta tiến hành thiết kế cho các bộ cộng
half adder ( 2 ngõ vào 1 bit , 2 ngõ ra 1 bit), full adder ( 3 ngõ vào 1 bit
bao gồm thêm 1 biến nhớ c_in, 2 ngõ ra 1 bit). Hai bộ này giúp chúng ta tiến
hành thực hiện phép tính cộng cho các số hạng.
Thiết kế khối “alu_single” để tính toán các kết quả 1
bit dữ liệu. Từ 4 khối “alu_single” ta có được khối 1 khối “alu” 4 bit dữ liệu.
Khối “logic_unit” thực hiện phép toán “AND”, “OR”,
“XOR” và “NXOR”.
Khối “arithmetic_unit” thực hiện phép toán “+” các số
hạng đầu vào.
Khối “mux_2to1” lựa chọn tín hiệu output từ khối
“logic_unit” hoặc khối “arithmetic_unit” dựa vào ngõ vào “M”.
Kết quả output ngõ ra đi qua 1 FF để tín hiệu có thể
output đồng bộ với cạnh lên của clock.
Trong bài này, Chúng ta còn thực hiện viết test bench
self test. Môi trường tự dump kết quả pass hoặc fail dựa vào kết quả so sánh
input ngõ ra và kết quả dự đoán.
Bên cạnh đó chúng ta còn xây dựng 1 môi trường uvm
hoàn chỉnh để tiến hành random test. Môi trường UVM chỉ dành cho việc kiểm tra
Verilog code với nhiều input và output. Tuy nhiên, Trong bài này chúng ta tiến
hành build một môi trường hoàn chỉnh để từng bước làm quen với nó.
III. Lý thuyết hoạt động của mạch.
Mạch half adder và full adder được thiết kế tương tự
như lab 1 (Bộ add 4 bit).
Đối với mạch half adder ta có 2 giá trị ngõ vào 1 bit
và 2 giá trị ngõ ra. Half adder giúp chúng ta tính toán kết quả cộng 2 ngõ vào
1 bit và cho ra giá trị ngõ ra 1 bit và 1 bit nhớ.
Hình
3.1 Khối
half adder.
Bảng
3.1
Bảng giá trị vào/ra cho khối half adder.
in_1
|
in_2
|
sum
|
c_out
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
0
|
1
|
0
|
1
|
0
|
1
|
1
|
0
|
1
|
Khối full adder dùng để tính tổng giá trị 3 ngõ vào 1
bit (thêm 1 biến c_in) ở đây ta sử dụng 2 khối half adder để xây đựng khối full
adder.
Hình 3.2 Khối full adder.
Hình
3.3
Xây dựng khối full adder từ 2 khối half adder.
Bảng 3.2 bảng giá trị vào ra
cho khối full adder.
c_in
|
in_1
|
in_2
|
sum
|
c_out
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
0
|
0
|
1
|
1
|
0
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
0
|
1
|
0
|
1
|
1
|
1
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
Khối logic_unit được mô tả như bên dưới:
Hình
3.4
Khối logic_unit.
S1
|
S0
|
in_0
|
in_1
|
H
|
0
|
0
|
A
|
B
|
A&B
|
0
|
1
|
A
|
B
|
A|B
|
1
|
0
|
A
|
B
|
A^B
|
1
|
1
|
A
|
B
|
~(A^B)
|
Bảng
3.3
Hoạt động của bộ “logic_unit”.
Với khối này giá trị H không phụ thuộc vào giá trị M
ngõ vào. Giá trị M ngõ vào có chức năng lựa chọn giá trị ngõ ra từ khối
“logic_unit” hay từ khối “arithmetic_unit”.
S1
|
S0
|
in_0
|
in_1
|
Carry_in
|
G
|
0
|
0
|
A
|
B
|
C0
|
A&C0
|
0
|
1
|
A
|
B
|
C0
|
A+B+C0
|
1
|
0
|
A
|
B
|
C0
|
A+B'+C0
|
1
|
1
|
A
|
B
|
C0
|
A'+B+C0
|
Bảng
3.4
Hoạt động của khối “arithmetic_unit”.
Kết quả của bộ ALU 4 bit sẽ đi qua 1 FF để tín hiệu ngõ ra chạy cùng với tín hiệu clock.
Hình
3.5
Sơ đồ tổng quát của bộ alu 4 bit.
IV. Verilog code và
test bench, môi trường uvm.
Thật ra code cho khối này rất ngắn và đơn giản nhưng mình luôn lựa chọn cách viết phức tạp nhất để bản thân làm quen với những cấu trúc phức tạp và tăng khả năng readable của bản thân (Sau này gặp các code dễ hơn sẽ đọc nhanh hơn).
Verilog code:
//Author: TrongTran
//Date :
05/02/2019
//Module: ALU 4 bit data
module alu (clk, rst_n, in_1, in_2, carry_in, S0, S1, M,
carry_out, out);
parameter WIDTH
= 4;
input S0;
input S1;
input M;
input clk;
input rst_n;
input
[WIDTH-1:0] in_1;
input [WIDTH-1:0]
in_2;
input carry_in;
output
carry_out;
output
[WIDTH-1:0] out;
wire [WIDTH-2:0]
carry_tmp;
reg [WIDTH-1:0]
out;
reg carry_out;
wire [WIDTH-1:0]
out_tmp;
wire carry_out_tmp;
// Gọi module 4 bộ alu_single 1 bit để có được 1 bộ alu 4
bit.
alu_single
alu_single_0 (.in_1(in_1[0]), .in_2(in_2[0]), .carry_in(carry_in),
.S0(S0), .S1(S1),
.M(M), .carry_out(carry_tmp[0]), .out(out_tmp[0]));
generate
genvar index;
for (index = 1;
index < WIDTH - 1 ; index = index + 1) begin: re_peat
alu_single
alu_single_1 (.in_1(in_1[index]), .in_2(in_2[index]),
.carry_in(carry_tmp[index-1]),
.S0(S0), .S1(S1),
.M(M), .carry_out(carry_tmp[index]), .out(out_tmp[index]));
end
endgenerate
alu_single
alu_single_2 (.in_1(in_1[WIDTH - 1]), .in_2(in_2[WIDTH - 1]),
.carry_in(carry_tmp[WIDTH - 2]),
.S0(S0), .S1(S1),
.M(M), .carry_out(carry_out_tmp), .out(out_tmp[WIDTH - 1]));
// Output sẽ đi qua 1 FF để ngỏ ra được đồng bộ với clock.
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
begin
out <=
4'b0000;
carry_out
<= 1'b0;
end else begin
out <=
out_tmp;
carry_out
<= carry_out_tmp;
end
end
endmodule
// Thiết kế alu_single
1 bit từ các khối mux_2to1, logic_unit, arithmethic_unit.
module alu_single (in_1, in_2, carry_in, S0, S1, M,
carry_out, out);
input S0;
input S1;
input M;
input in_1;
input in_2;
input carry_in;
output
carry_out;
output out;
wire G;
wire H;
wire carry_out;
wire out;
wire
carry_out_sm;
logic_unit
logic_unit_0 (.S0(S0), .S1(S1), .in_1(in_1), .in_2(in_2), .H(H));
arithmetic_unit
arithmetic_unit_0 (.S0(S0), .S1(S1), .in_1(in_1), .in_2(in_2),
.carry_in(carry_in), .carry_out(carry_out_sm), .G(G));
mux_2to1
mux_2to1_0 (.G(G), .H(H), .M(M), .out(out));
mux_2to1
mux_2to1_1 (.G(carry_out_sm), .H(1'b0), .M(M), .out(carry_out));
endmodule
// Thiết kế khối mux_2to1
module mux_2to1 (G, H, M, out);
input G, H, M;
output out;
wire out;
assign out = (M
== 1'b0)? H: G;
endmodule
// Thiết kế khối
logic_unit
module logic_unit (S0, S1, in_1, in_2, H);
input S0, S1,
in_1, in_2;
output H;
wire H;
assign H = ({S1,
S0} == 2'b00) ? in_1&in_2:
({S1,
S0} == 2'b01) ? in_1|in_2:
({S1,
S0} == 2'b10) ? in_1^in_2:
({S1,
S0} == 2'b11) ? ~(in_1^in_2): 1'b0;
endmodule
// Thiết kế khối arithmetic_unit
module arithmetic_unit ( S0, S1, in_1, in_2, carry_in,
carry_out, G);
input S0, S1,
in_1, in_2, carry_in;
output
carry_out, G;
wire carry_out,
G;
wire out_case01,
out_case10, out_case11, carry_out_case01, carry_out_case10, carry_out_case11;
full_adder full_adder_01
(.in_1(in_1), .in_2(in_2), .c_in(carry_in), .out(out_case01),
.c_out(carry_out_case01));
full_adder full_adder_10
(.in_1(in_1), .in_2(~in_2), .c_in(carry_in), .out(out_case10),
.c_out(carry_out_case10));
full_adder full_adder_11
(.in_1(~in_1), .in_2(in_2), .c_in(carry_in), .out(out_case11),
.c_out(carry_out_case11));
assign
{carry_out, G} = ({S1, S0} == 2'b00) ? in_1+carry_in:
({S1, S0} == 2'b01) ? {carry_out_case01, out_case01}:
({S1, S0} == 2'b10) ?
{carry_out_case10, out_case10}:
({S1, S0} == 2'b11) ? {carry_out_case11, out_case11}: 2'b00;
endmodule
// Khối full adder
module full_adder (in_1, in_2, c_in, out, c_out);
// Define input/output
input in_1;
input in_2;
input c_in;
output out;
output c_out;
wire sum_tmp, c_tmp_0, c_tmp_1;
// Add 2 inputs
half_adder half_adder_00 (.in_1(in_1), .in_2(in_2),
.out(sum_tmp), .c_out(c_tmp_0));
// Add sum of 2 input with carry 1
half_adder half_adder_01 (.in_1(sum_tmp), .in_2(c_in),
.out(out), .c_out(c_tmp_1));
// Calculate carry out
assign c_out = c_tmp_1 | c_tmp_0;
endmodule
// Khối half adder
module half_adder (in_1, in_2, out, c_out);
// defien input/output
input in_1;
input in_2;
output out;
output c_out;
// Calculate sum of 2 inputs
assign out = in_1^in_2;
// Calculate carry out
assign c_out = in_1&in_2;
endmodule
Để cho việc dễ dàng re-use ở đây mình sử dụng vòng lập :
generate
…
endgenerate
Vòng lập này hoàn toàn có thể tổng hợp (Synthesis) được.
Nói về vòng lập generate thì việc này được sử dụng nhiều
khi muốn một tác vụ được lập lại nhiều lần. Với code C sẽ sử dụng vòng lặp for
trong Verilog cấu trúc generate cũng tương tự như for trong C.
Test bench
//Author: TrongTran
//Date : 05/09/2019
//Module: ALU 4 bit data testbench
`timescale 1ns/1ns
//`include "alu.v"
module alu_tb ();
parameter WIDTH = 4;
reg S0;
reg S1;
reg M;
reg [WIDTH-1:0] in_1;
reg [WIDTH-1:0] in_2;
reg carry_in;
reg clk;
reg rst_n;
wire carry_out;
wire [WIDTH-1:0] out;
integer i;
alu alu_00 (.clk(clk), .rst_n(rst_n), .in_1(in_1), .in_2(in_2), .carry_in(carry_in), .S0(S0), .S1(S1), .M(M), .carry_out(carry_out), .out(out));
//initial begin
// $monitor ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d, carry_out = %d", M, S1, S0, in_1, in_2, carry_in, out, carry_out);
//end
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial begin
#1;
rst_n = 0;
for ( i = 0 ; i < 3; i = i+1) begin
S0 = $urandom;
S1 = $urandom;
M = $urandom;
in_1 = $urandom;
in_2 = $urandom;
carry_in = $urandom;
#20;
end
rst_n = 1;
repeat (1000) begin
S0 = $urandom;
S1 = $urandom;
M = $urandom;
in_1 = $urandom;
in_2 = $urandom;
carry_in = $urandom;
#20;
end
//$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d", M, S1, S0, in_1, in_2, carry_in);
//$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d", M, S1, S0, in_1, in_2, carry_in, out);
//$finish;
end
// If after 55 cycle, Simulation will pass.
initial begin
repeat (25) begin
@ (posedge clk);
end
$display ("[%t]--------------- SIMULATION PASS ---------------", $time);
$finish;
end
initial begin
reg [3:0] out_tmp;
reg [3:0] out_cmp;
reg c_tmp;
reg c_cmp;
reg [3:0] out_1;
repeat (20) begin
@ (posedge clk) begin
$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d, out_tmp = %d, c = %d, c_tmp = %d", M, S1, S0, in_1, in_2, carry_in, out, out_tmp, carry_out, c_tmp);
if ((out != out_cmp)||(carry_out != c_tmp)) begin
$display ("[%t]--------------- SIMULATION FAIL ---------------", $time);
$finish;
end
if (rst_n == 1'b0) begin
out_tmp = 4'b0000;
c_cmp = 1'b0;
end else begin
case ({M, S1, S0})
3'b000: {c_cmp,out_tmp} = {1'b0,in_1&in_2};
3'b001: {c_cmp,out_tmp} = {1'b0,in_1|in_2};
3'b010: {c_cmp,out_tmp} = {1'b0,in_1^in_2};
3'b011: {c_cmp,out_tmp} = {1'b0,~(in_1^in_2)};
3'b100: {c_cmp,out_tmp} = in_1+carry_in;
3'b101: begin
out_1 = in_1;
{c_cmp, out_tmp} = out_1+in_2+carry_in;
end
3'b110: begin
out_1 = ~in_2;
{c_cmp, out_tmp} = out_1+in_1+carry_in;
$display("c_cmp = %d, out_1 = %d, in_1 = %d, in_2 = %d", c_cmp, out_1, in_1, in_2);
end
3'b111: begin
out_1 = ~in_1;
{c_cmp, out_tmp} = out_1+in_2+carry_in;
end
default: {c_cmp,out_tmp} = 5'b00000;
endcase
end
out_cmp = out_tmp;
c_tmp = c_cmp;
end
end
end
//initial begin
// $vcdplusfile("alu.vpd");
// $vcdpluson();
//end
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, alu_tb);
end
endmodule
Còn tiếp (+_+)
Test bench
//Author: TrongTran
//Date : 05/09/2019
//Module: ALU 4 bit data testbench
`timescale 1ns/1ns
//`include "alu.v"
module alu_tb ();
parameter WIDTH = 4;
reg S0;
reg S1;
reg M;
reg [WIDTH-1:0] in_1;
reg [WIDTH-1:0] in_2;
reg carry_in;
reg clk;
reg rst_n;
wire carry_out;
wire [WIDTH-1:0] out;
integer i;
alu alu_00 (.clk(clk), .rst_n(rst_n), .in_1(in_1), .in_2(in_2), .carry_in(carry_in), .S0(S0), .S1(S1), .M(M), .carry_out(carry_out), .out(out));
//initial begin
// $monitor ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d, carry_out = %d", M, S1, S0, in_1, in_2, carry_in, out, carry_out);
//end
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial begin
#1;
rst_n = 0;
for ( i = 0 ; i < 3; i = i+1) begin
S0 = $urandom;
S1 = $urandom;
M = $urandom;
in_1 = $urandom;
in_2 = $urandom;
carry_in = $urandom;
#20;
end
rst_n = 1;
repeat (1000) begin
S0 = $urandom;
S1 = $urandom;
M = $urandom;
in_1 = $urandom;
in_2 = $urandom;
carry_in = $urandom;
#20;
end
//$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d", M, S1, S0, in_1, in_2, carry_in);
//$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d", M, S1, S0, in_1, in_2, carry_in, out);
//$finish;
end
// If after 55 cycle, Simulation will pass.
initial begin
repeat (25) begin
@ (posedge clk);
end
$display ("[%t]--------------- SIMULATION PASS ---------------", $time);
$finish;
end
initial begin
reg [3:0] out_tmp;
reg [3:0] out_cmp;
reg c_tmp;
reg c_cmp;
reg [3:0] out_1;
repeat (20) begin
@ (posedge clk) begin
$display ("M_S1_S0 = %b%b%b, in_1 = %d, in_2 = %d, carry_in = %d, out = %d, out_tmp = %d, c = %d, c_tmp = %d", M, S1, S0, in_1, in_2, carry_in, out, out_tmp, carry_out, c_tmp);
if ((out != out_cmp)||(carry_out != c_tmp)) begin
$display ("[%t]--------------- SIMULATION FAIL ---------------", $time);
$finish;
end
if (rst_n == 1'b0) begin
out_tmp = 4'b0000;
c_cmp = 1'b0;
end else begin
case ({M, S1, S0})
3'b000: {c_cmp,out_tmp} = {1'b0,in_1&in_2};
3'b001: {c_cmp,out_tmp} = {1'b0,in_1|in_2};
3'b010: {c_cmp,out_tmp} = {1'b0,in_1^in_2};
3'b011: {c_cmp,out_tmp} = {1'b0,~(in_1^in_2)};
3'b100: {c_cmp,out_tmp} = in_1+carry_in;
3'b101: begin
out_1 = in_1;
{c_cmp, out_tmp} = out_1+in_2+carry_in;
end
3'b110: begin
out_1 = ~in_2;
{c_cmp, out_tmp} = out_1+in_1+carry_in;
$display("c_cmp = %d, out_1 = %d, in_1 = %d, in_2 = %d", c_cmp, out_1, in_1, in_2);
end
3'b111: begin
out_1 = ~in_1;
{c_cmp, out_tmp} = out_1+in_2+carry_in;
end
default: {c_cmp,out_tmp} = 5'b00000;
endcase
end
out_cmp = out_tmp;
c_tmp = c_cmp;
end
end
end
//initial begin
// $vcdplusfile("alu.vpd");
// $vcdpluson();
//end
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, alu_tb);
end
endmodule
Còn tiếp (+_+)