You are currently viewing VHDL Testbench #1: Kiểm tra mạch tổ hợp

VHDL Testbench #1: Kiểm tra mạch tổ hợp

Source: https://vhdlguide.readthedocs.io/en/latest/vhdl/testbench.html

Để mô phỏng mạch điện chúng ta vừa thiết kế chúng ta có thể tạo ra các dạng sóng mô phỏng bằng ModelSim bằng cách cung cấp các giá trị đầu vào thủ công. Phương pháp này có một nhược điểm là nếu số lượng các tín hiệu đầu vào rất lớn và/hoặc chúng ta phải thực hiện quá trình mô phỏng lặp lại nhiều lần quá trình cung cấp giá trị đầu vào thủ công này có thể khá phức tạp và tốn thời gian. Ví dụ, giả sử đầu vào là 10 bit và chúng ta muốn kiểm tra tất cả các giá trị có thể có của đầu vào, tức là 210−1, thì không thể thực hiện thủ công. Trong những trường hợp như vậy, phương pháp mô phỏng sử dụng testbench tỏ ra hiệu quả hơn và đáng tin cậy hơn. Hơn nữa, testbench cho phép chúng ta có thể xuất kết quả mô phỏng dưới dạng các tệp csv (comma separated file – tệp được phân tách bằng dấu phẩy) để có thể phân tích thêm bằng các phần mềm khác như: Python, Excel và Matlab, v.v.

Do testbench chỉ được sử dụng cho mục đích mô phỏng (không phải để tổng hợp thành phần cứng), do đó toàn bộ cấu trúc lệnh VHDL chằng hạn “assert”, “report” và “for loop”, v.v. có thể được sử dụng để viết testbenches.

1.1.1.     Testbench cho các mạch tổ hợp

Bài viết này trình bày các cách viết testbench khác nhau cho các mạch tổ hợp. Để đơn giản và dế hiểu hiểu, một mạch cộng half adder đơn giản được dùng làm đối tượng kiểm tra bằng nhiều phương pháp mô phỏng khác nhau.

a)      Ví dụ thiết kế: Half adder

Bảng 1‑9 chỉ ra mã nguồn VHDL mô tả một bộ cộng half adder sẽ được dùng làm đối tượng mạch được kiểm tra bằng các phương pháp khác nhau.

Bảng 1‑9. Mã nguồn VHDL mô tả bộ cộng half adder

-- half_adder.vhd

library ieee;
use ieee.std_logic_1164.all;

entity half_adder is 
  port (a, b : in std_logic;
        sum, carry : out std_logic
    );
end half_adder;

architecture arch of half_adder is
begin
  sum <= a xor b;
  carry <= a and b;
end arch;

b)     Testbench đơn giản

Lưu ý rằng, testbench được viết trong các tệp VHDL riêng biệt như ví dụ trong Bảng 1‑10. Cách đơn giản nhất để viết testbench là khởi tạo một đơn vị thiết kế cần kiểm tra (design under test) trong testbench và cung cấp tất cả các giá trị đầu vào cùng một trong tệp VHDL như được giải thích dưới đây.

Trong Bảng 1‑10, một thực thể cho testbench có tên  “Half_adder_simple_tb“, được khai báo tại Dòng 7-8. Lưu ý rằng, phần khai báo thực thể của testbench luôn trống, tức là không có cổng nào được xác định trong thực thể (xem Dòng 7-8). Sau đó, 4 tín hiệu được khai báo là a, b, sum và carry (Dòng 11-12) bên trong thân kiến ​​trúc của testbench. Những tín hiệu này sau đó được kết nối với thiết kế bộ cộng half adder thực tế bằng cách sử dụng mô hình cấu trúc trong Dòng 15. Cuối cùng, các giá trị khác nhau được gán cho tín hiệu đầu vào như chỉ ra trong các dòng 22 và 23.

Trong Dòng 22, giá trị của a được khởi tạo là ‘0’ (ở 0 ns), sau đó nó thay đổi thành ‘1’ ở 20 ns và một lần nữa thay đổi thành ‘0’ ở 40 ns và ‘1’ ở 60 ns. Theo cách tương tự, giá trị của b ban đầu được khởi tạo là ‘0’ và thay đổi thành ‘1’ tại 40 ns tại Dòng 23. Theo cách này, 4 tổ hợp có thể được tạo ra và gán cho hai đầu vào (ab) là “00”, “01”, “10” và “11” như trong Hình 1‑1. Trong hình này cũng cho thấy các kết quả đầu ra tức là tổng và cờ nhớ tương ứng.

Bảng 1‑10. Testbench đơn giản cho bộ cộng half adder

-- half_adder_simple_tb.vhd

library ieee;
use ieee.std_logic_1164.all;


entity half_adder_simple_tb is
end half_adder_simple_tb;

architecture tb of half_adder_simple_tb is
    signal a, b : std_logic;  -- inputs 
    signal sum, carry : std_logic;  -- outputs
begin
    -- connecting testbench signals with half_adder.vhd
  UUT : entity work.half_adder port map (a => a, b => b, sum => sum, carry =>carry);

    -- inputs
    -- 00 at 0 ns
    -- 01 at 20 ns, as b is 0 at 20 ns and a is changed to 1 at 20 ns
    -- 10 at 40 ns
    -- 11 at 60 ns
    a <= '0', '1' after 20 ns, '0' after 40 ns, '1' after 60 ns;
    b <= '0', '1' after 40 ns;        
end tb ;

day hoc stem vhdl testbench

Hình 1‑1. Kết quả mô phỏng bộ cộng half adder bằng testbench trong Bảng 1‑10.

c)      Testbench với câu lệnh process

Trong Bảng 1‑11, câu lệnh process được sử dụng trong testbench để bao gồm các giá trị đầu vào cùng với các giá trị đầu ra tương ứng. Nếu các đầu ra được chỉ định không khớp với đầu ra được tạo bởi bộ cộng half adder, thì các lỗi sẽ được tạo ra. Lưu ý rằng, câu lệnh process dùng trong testbench không có danh sách cảm biến.

Giải thích testbench:

Testbench này giống như testbench trước cho đến Dòng 15, sau đó câu lệnh process được sử dụng để định nghĩa các mẫu đầu vào như có thể thấy ở các dòng 20-21 (“00”), 27-28 (“01”), 33-34 (“10”) và 39-40 (“11”). Hơn nữa, đầu ra dự kiến cũng được hiển thị bên dưới các dòng này, ví dụ dòng 23 cho thấy tổng sum là ‘0’ và cờ nhớ carry là ‘0’ ứng với đầu vào “00”. Hơn thế, nếu đầu ra được tạo bởi half adder khác với các giá trị dự kiến này thì testbench sẽ tạo ra một thông báo lỗi. Chẳng hạn, khi chạy mô phỏng một thông báo lỗi sẽ được tạo ra bởi dòng 50 cho mẫu đầu vào “01” (Hình 1‑3) (vì tổng được tạo bởi Half_adder cho a = ‘0’ và b = ‘1’ (xác định trong dòng 46-47) là 1, trong khi tổng dự kiến cho tổ hợp này là 0 (được xác định ở dòng 49)). Lưu ý rằng, thông báo lỗi này được tạo ra nhằm mục đích minh họa cho hoạt động tự kiểm tra của testbench; lỗi này do giá trị mong đợi được nhập không chính xác chứ không phải do bộ cộng được thiết kế không chính xác (đầu ra của bộ cộng là chính xác). Lỗi sẽ được hiển thị trên cửa sổ “transcript” của ModelSim như chỉ ra trong Hình 1‑3.

Ngoài ra, tham số “period” được định nghĩa là 20 ns tại Dòng 18, và sau đó được sử dụng sau mỗi lần gán giá trị đầu vào (ví dụ tại dòng 22) để quy định giá trị gán đến đầu vào sẽ được hiển thị trong 20 ns trước khi chuyển đến các giá trị đầu vào tiếp theo (Hình 1‑2).

Bảng 1‑11. Testbench với câu lệnh process cho bộ cộng half adder

-- half_adder_process_tb.vhd

library ieee;
use ieee.std_logic_1164.all;


entity half_adder_process_tb is
end half_adder_process_tb;

architecture tb of half_adder_process_tb is
    signal a, b : std_logic;
    signal sum, carry : std_logic;
begin
    -- connecting testbench signals with half_adder.vhd
 UUT : entity work.half_adder port map (a => a, b => b, sum => sum, carry => carry);

    tb1 : process
        constant period: time := 20 ns;
        begin
            a <= '0';
            b <= '0';
            wait for period;
            assert ((sum = '0') and (carry = '0'))  -- expected output
            -- error will be reported if sum or carry is not 0
            report "test failed for input combination 00" severity error;

            a <= '0';
            b <= '1';
            wait for period;
            assert ((sum = '1') and (carry = '0'))
            report "test failed for input combination 01" severity error;

            a <= '1';
            b <= '0';
            wait for period;
            assert ((sum = '1') and (carry = '0'))
            report "test failed for input combination 10" severity error;

            a <= '1';
            b <= '1';
            wait for period;
            assert ((sum = '0') and (carry = '1'))
            report "test failed for input combination 11" severity error;

            -- Fail test
            a <= '0';
            b <= '1';
            wait for period;
            assert ((sum = '0') and (carry = '1'))
           report "test failed for input combination 01 (fail test)" severity error;


            wait; -- indefinitely suspend process
        end process;
end tb;

day hoc stem VHDL testbench

Hình 1‑2. Kết quả mô phỏng bộ cộng với testbench trong Bảng 1‑11.

day hoc stem VHDL testbench

Hình 1‑3. Lỗi được tạo ra bởi testbench trong Bảng1‑11.

d)     Testbench với bảng look-up table

Các mẫu đầu vào có thể được định nghĩa dưới dạng bảng tra cứu như được chỉ ra trong testbench ở Bảng 1‑12, thay vì được định nghĩa tách biệt tại các vị trí khác nhau như được thực hiện trong Bảng 1‑11.

Giải thích testbench:

Nguyên tắc cơ bản của testbench trong Bảng 1‑12 là tương tự như trong Bảng 1‑11 nhưng được viết theo phong cách khác. Testbench với bảng tra cứu có thể được viết theo ba bước như dưới đây:

  • Định nghĩa bản ghi: Đầu tiên chúng ta cần định nghĩa một bản ghi chứa tất cả các cột có thể có trong bảng tra cứu. Trong ví dụ bộ cộng half adder, có bốn cột có thể có tương ứng với các tham số a, b, sum và carry, được xác định trong bản ghi tại Dòng 15-18.
  • Tạo bảng tra cứu: Tiếp theo, chúng ta cần xác định các giá trị của bảng tra cứu như được thực hiện tại Dòng 20-28. Ở đây phương thức liên kết theo vị trí được sử dụng để gán giá trị cho các cột (xem dòng 22-27). Ngoài ra, phương pháp liên kết theo tên cũng có thể được sử dụng như được mô tả trong nhận xét ở Dòng 23.
  • Gán giá trị cho các tín hiệu: Sau đó, các giá trị của bảng tra cứu cần được gán cho thực thể half_adder (từng giá trị một). Cụ thể trong testbench, một vòng lặp “for” bắt đầu ở dòng 35 được sử dụng để gán các giá trị a và b của “test-vector” tới các tín hiệu a và b (xem chú thích ở Dòng 36 để hiểu rõ hơn) . Tương tự, các giá trị kỳ vọng của tổng sum và giá trị nhớ carry được tạo ra ở Dòng 41-44. Cuối cùng, thông báo được tạo ra cho các đầu ra sai tại Dòng 46-50.

Bảng 1‑12. Testbench với bảng tra cứu cho bộ cộng half adder

-- half_adder_lookup_tb.vhd

library ieee;
use ieee.std_logic_1164.all;

entity half_adder_lookup_tb is
end half_adder_lookup_tb;

architecture tb of half_adder_lookup_tb is
    
    signal a, b : std_logic; -- input
    signal sum, carry : std_logic; -- output

    -- declare record type
    type test_vector is record
        a, b : std_logic;
        sum, carry : std_logic;
    end record; 

    type test_vector_array is array (natural range <>) of test_vector;
    constant test_vectors : test_vector_array := (
        -- a, b, sum , carry   -- positional method is used below
        ('0', '0', '0', '0'), -- or (a => '0', b => '0', sum => '0', carry => '0')
        ('0', '1', '1', '0'),
        ('1', '0', '1', '0'),
        ('1', '1', '0', '1'),
        ('0', '1', '0', '1')  -- fail test
        );

begin
 UUT : entity work.half_adder port map (a => a, b => b, sum => sum, carry => carry);

    tb1 : process
    begin
        for i in test_vectors'range loop
            a <= test_vectors(i).a;  -- signal a = i^th-row-value of test_vector's a
            b <= test_vectors(i).b;

            wait for 20 ns;

            assert ( 
                        (sum = test_vectors(i).sum) and 
                        (carry = test_vectors(i).carry) 
                    )

            -- image is used for string-representation of integer etc.
            report  "test_vector " & integer'image(i) & " failed " & 
                    " for input a = " & std_logic'image(a) & 
                    " and b = " & std_logic'image(b)
                    severity error;
        end loop;
        wait;
    end process; 

end tb;

day hoc stem VHDL testbench

Hình 1‑4. Kết quả mô phỏng của testbench trong Bảng 1‑12.

day hoc stem VHDL testbench

Hình 1‑5. Lỗi được tạo ra từ testbench trong Bảng 1‑12.

Nguyễn Kiêm Hùng

Hung K. Nguyen studied “Electronic Engineering” in both his bachelor’s and master’s degrees at the Vietnam National University, Hanoi, Vietnam. He received the bachelor’s degree in 2003. After receiving his bachelor’s degree, He worked as an internship in the Research Center of Electronics and Telecommunications. In 2006, He received the master’s degree in electronic engineering from VNU University of Engineering and Technology (VNU-UET). Before pursuing his Ph.D’s degree, He worked as a researcher at the Laboratory for Smart Integrated Systems in VNU University of Engineering and Technology for two years. In 2008, He went to Southeast University, Nanjing, China to get his Ph.D degree. He received the Ph.D. degree in Microelectronics and Solid State Electronics from Southeast University in 2013. After got his Ph.D’s degree, He returned to VNU University of Engineering and Technology to continue his research in VLSI design. He works currently as an assistant professor and senior researcher at VNU Key Laboratory for Smart Integrated Systems. His research interests mainly include multimedia processing, reconfigurable computing, and SoC designs.

Trả lời