You are currently viewing VHDL Testbench #3: Kiểm tra mạch tuần tự

VHDL Testbench #3: Kiểm tra mạch tuần tự

Trong các bài trước, chúng ta đã thấy việc sử dụng câu lệnh Process để viết testbench cho các mạch tổ hợp. Nhưng, trong trường hợp mạch tuần tự, chúng ta cần tín hiệu xung nhịp Clock và tín hiệu reset, do đó cần có thêm hai khối lệnh bổ sung. Vì xung nhịp được tạo ra cho toàn bộ quá trình mô phỏng, do đó nó được định nghĩa bên trong một câu lệnh Process riêng biệt. Trong khi đó, tín hiệu Reset chỉ được yêu cầu khi bắt đầu các hoạt động, do đó nó không được xác định bên trong câu lệnh Process. Phần còn lại của các quy trình/phương pháp để viết các testbench cho các mạch tuần tự giống như các testbench của các mạch tổ hợp.

> Xem thêm: Testbench #1: Kiểm tra mạch tổ hợp

> Xem thêm: Testbench #2: Kiểm tra mạch tổ hợp

a)    Ví dụ thiết kế: bộ đếm mô-đun M

Bộ đếm mô-đun M đếm các giá trị từ 0 đến (M-1). Bản mô tả VHDL của nó được đưa ra trong Bảng 1‑16.

Giải thích mã VHDL trong Bảng 1‑16:

Trong dòng 9, giá trị đếm tối đa, tức là M được xác định cho bộ đếm. Tiếp đến dòng 10 định nghĩa số lượng các bit N được yêu cầu cho bộ đếm để đếm được tới giá trị tối đa (M-1).

Bộ đếm có 2 cổng đầu ra là “count” và “complete_tick”. Trong đó, count được dùng để xuất giá trị đếm hiện tại của bộ đếm; complete_tick được dùng làm cờ báo hiệu khi count đạt đến giá trị cực đại M-1.

Tín hiệu “count_reg” được định nghĩa ở dòng 22 và được gán cho đầu ra “count” ở dòng 42. Vì “count_reg” được định nghĩa là kiểu unsigned, do đó kiểu của nó phải được chuyển đổi thành “std_logic_vector” ở dòng 42 vì cổng đầu ra “count” thuộc loại “std_logic_vector”.

Trong dòng 37, bộ đếm count_next được đặt thành 0 khi đạt đến giá trị tối đa, ngược lại nó sẽ bằng count_reg tăng lên 1. Sau đó, trong chu kỳ đồng hồ tiếp theo, giá trị tăng lên của “count_reg” (được lưu trữ trong “count_next”) lại được gán cho “count_reg” thông qua dòng 29.

Dòng 40 tạo ra một cờ báo cho mỗi lần hoàn thành một vòng đếm.

Bảng 1‑16 Bộ đếm mô-đun M

-- modMCounter.vhd

library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity modMCounter is
    generic (
            M : integer := 5; -- count from 0 to M-1
            N : integer := 3   -- N bits required to count upto M i.e. 2**N >= M
    );
    
    port(
            clk, reset : in std_logic;
            complete_tick : out std_logic;
            count : out std_logic_vector(N-1 downto 0)
    );
end modMCounter;


architecture arch of modMCounter is
    signal count_reg, count_next : unsigned(N-1 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then 
            count_reg <= (others=>'0');
        elsif   clk'event and clk='1' then
            count_reg <= count_next;
        else  -- note that else block is not required
            count_reg <= count_reg;
        end if;
    end process;
    
    -- set count_next to 0 when maximum count is reached i.e. (M-1)
    -- otherwise increase the count
    count_next <= (others=>'0') when count_reg=(M-1) else (count_reg+1);
    
    -- Generate 'tick' on each maximum count
    complete_tick <= '1' when count_reg = (M-1) else '0';
    
    count <= std_logic_vector(count_reg); -- assign value to output port
end arch;

b)    Mô phỏng với khoảng thời gian vô hạn

Trong phần này, chúng ta tạo một testbench sẽ không tự động dừng, tức là nếu chúng ta nhấn nút “Run all” thì mô phỏng sẽ chạy mãi mãi, do đó chúng ta cần nhấn nút “Run” như trong Hình 10.13.

Giải thích testbench

 Bảng 1‑17 Testbench với khoảng thời gian vô hạn cho bộ đếm mô-đun M.

-- modMCounter_tb.vhd

library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity modMCounter_tb is
end modMCounter_tb;


architecture arch of modMCounter_tb is
    constant M : integer := 10;
    constant N : integer := 4;
    constant T : time := 20 ns; 

    signal clk, reset : std_logic;  -- input
    signal complete_tick : std_logic; -- output
    signal count : std_logic_vector(N-1 downto 0);  -- output
begin

    modMCounter_unit : entity work.modMCounter
        generic map (M => M, N => N)
        port map (clk=>clk, reset=>reset, complete_tick=>complete_tick,
                    count=>count);

    -- continuous clock
    process 
    begin
        clk <= '0';
        wait for T/2;
        clk <= '1';
        wait for T/2;
    end process;


    -- reset = 1 for first clock cycle and then 0
    reset <= '1', '0' after T/2;

end arch;

Bảng 1‑17 là testbench cho bộ đếm mô-đun M. Ở đây tín hiệu “clk” được tạo ra trong khối Process riêng biệt, tức là các dòng 27-33. Bằng cách này, tín hiệu xung nhịp sẽ có sẵn trong suốt quá trình mô phỏng. Hơn nữa, tín hiệu Reset được đặt thành ‘1’ ngay từ đầu và sau đó được đặt thành ‘0’ trong chu kỳ đồng hồ tiếp theo (dòng 37). Nếu có thêm các tín hiệu đầu vào, thì các tín hiệu đó có thể được xác định trong câu lệnh Process riêng biệt, như đã thảo luận trong các testbench của mạch tổ hợp.

Kết quả mô phỏng được thể hiện trong Hình 1‑5, trong đó các giá trị của bộ đếm đi từ 0 đến 9 khi M được đặt bằng 10 (tức là A trong hệ thập lục phân). Hơn nữa, sử dụng nút “run” để chạy mô phỏng các mạch tuần tự (thay vì run-all), như được hiển thị trong hình.

Testbench kiểm tra mạch tuần tự

Hình 1‑5. Kế quả mô phỏng bằng testbench trong Bảng 1‑17.

c)     Mô phỏng trong thời gian hữu hạn và lưu dữ liệu

Để chạy mô phỏng trong khoảng thời gian hữu hạn, chúng ta cần cung cấp số lượng xung nhịp mà chúng ta muốn chạy mô phỏng thông qua hằng số “num_of_clocks”, như được hiển thị trong dòng 23 của Bảng 1‑18. Sau đó, tại dòng 47-52 mã VHDL được thêm vào để đóng tệp sau số lượng xung nhịp mong muốn “num_of_clocks”. Dữ liệu được lưu vào tệp như được thảo luận trong phần (1.2.1.f). Bây giờ, nếu chúng ta nhấn nút run-all, thì trình mô phỏng sẽ dừng sau “num_of_clocks” chu kỳ. Lưu ý rằng, nếu dữ liệu ở định dạng signed hoặc unsigned, thì dữ liệu đó không thể được lưu vào tệp. Chúng ta cần chuyển đổi dữ liệu sang định dạng khác, ví dụ Integer, natural hoặc std_logic_vector, v.v. trước khi lưu vào tệp, như được hiển thị trong Dòng 73. Các dạng sóng mô phỏng và kết quả được lưu lần lượt được hiển thị trong Hình 1‑6 và Hình 1‑7.

 Bảng 1‑18. Testbench với thời gian mô phỏng hữu hạn cho bộ đếm mô-đun M

-- modMCounter_tb2.vhd

library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
use ieee.std_logic_textio.all; -- require for std_logic etc.

entity modMCounter_tb2 is
end modMCounter_tb2;


architecture arch of modMCounter_tb2 is
    constant M : integer := 3;  -- count upto 2 (i.e. 0 to 2)
    constant N : integer := 4;
    constant T : time := 20 ns; 

    signal clk, reset : std_logic;  -- input
    signal complete_tick : std_logic; -- output
    signal count : std_logic_vector(N-1 downto 0);  -- output

    -- total samples to store in file
    constant num_of_clocks : integer := 30; 
    signal i : integer := 0; -- loop variable
    file output_buf : text; -- text is keyword

begin

    modMCounter_unit : entity work.modMCounter
        generic map (M => M, N => N)
        port map (clk=>clk, reset=>reset, complete_tick=>complete_tick,
                    count=>count);


    -- reset = 1 for first clock cycle and then 0
    reset <= '1', '0' after T/2;

    -- continuous clock
    process 
    begin
        clk <= '0';
        wait for T/2;
        clk <= '1';
        wait for T/2;

        -- store 30 samples in file
        if (i = num_of_clocks) then
            file_close(output_buf);
            wait;
        else
            i <= i + 1;
        end if;
    end process;


    -- save data in file : path is relative to Modelsim-project directory
    file_open(output_buf, "input_output_files/counter_data.csv", write_mode);
    process(clk)
        variable write_col_to_output_buf : line; -- line is keyword
    begin
        if(clk'event and clk='1' and reset /= '1') then  -- avoid reset data
            -- comment below 'if statement' to avoid header in saved file
            if (i = 0) then 
              write(write_col_to_output_buf, string'("clock_tick,count"));
              writeline(output_buf, write_col_to_output_buf);
            end if; 

            write(write_col_to_output_buf, complete_tick);
            write(write_col_to_output_buf, string'(","));
            -- Note that unsigned/signed values can not be saved in file, 
            -- therefore change into integer or std_logic_vector etc.
             -- following line saves the count in integer format
            write(write_col_to_output_buf, to_integer(unsigned(count))); 
            writeline(output_buf, write_col_to_output_buf);
        end if;
    end process;
end arch;

Testbench kiểm tra mạch tuần tự

Hình 1‑6. Kết quả mô phỏng bằng testbench trong Bảng 1‑18.

Testbench kiểm tra mạch tuần tự

Hình 1‑7 Kết quả được lưu bằng testbench trong Bảng 1‑18.

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