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

VHDL Testbench #2: 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.

Bài viết này nằm trong loạt bài viết về chủ đề “test bench cho 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 viết testbench mô phỏng khác nhau.

Phần 1 xem ở đây.

e)    Testbench đọc dữ liệu từ tệp bên ngoài

Trong loại testbench này dữ liệu dùng để kiểm tra mạch sẽ được đọc từ một tệp dữ liệu tách biệt có tên “read_file_ex.txt. Dữ liệu được lưu trong tệp có định dạng như chỉ ra Hình 1‑6.

day hoc stem vhdl testbench

Hình 1‑6. Dữ liệu trong tệp “read_file_ex.txt”.

Giải thích testbench trong Bảng 1‑13:

  • Để đọc tệp dữ liệu bên ngoài, đầu tiên chúng ta cần xác định một bộ đệm kiểu văn bản “text”, bộ đệm này có thể lưu trữ các giá trị của tệp trong đó, như được chỉ ra trong Dòng 17. Tệp được mở ở chế độ đọc và các giá trị của nó được lưu trữ trong bộ đệm này tại Dòng 32.
  • Tiếp theo, chúng ta cần xác định biến để đọc giá trị từ bộ đệm. Vì có 4 loại giá trị (tức là a, b, c và dấu cách) trong tệp “read_file_ex.txt”, do đó chúng ta cần xác định 4 biến để lưu trữ chúng, như được thể hiện trong các Dòng 24-26. Vì, biến c có 2 bit, do đó Dòng 25 khai báo một biến kiểu vectơ 2 bit. Hơn nữa, một biến kiểu ký tự “character” được xác định tại Dòng 26 để lưu các khoảng trắng.
  • Sau đó, các giá trị được đọc và lưu trữ trong các biến tại các Dòng 36-42. Các giá trị này được gán cho các tín hiệu thích hợp tại các Dòng 45-47.
  • Cuối cùng, tệp được đóng tại Dòng 52.

Kết quả mô phỏng bằng testbench này được hiển thị trong Hình 1‑7.

Bảng 1‑13. Testbench đọc dữ liệu từ tệp bên ngoài.

-- read_file_ex.vhd


library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_textio.all; -- require for writing/reading std_logic etc.

entity read_file_ex is
end read_file_ex;

architecture tb of read_file_ex is
    signal a, b : std_logic;
    signal c : std_logic_vector(1 downto 0);

    -- buffer for storing the text from input read-file
    file input_buf : text;  -- text is keyword

begin     

tb1 : process
    variable read_col_from_input_buf : line; -- read lines one by one from input_buf

    variable val_col1, val_col2 : std_logic; -- to save col1 and col2 values of 1 bit
    variable val_col3 : std_logic_vector(1 downto 0); -- to save col3 value of 2 bit
    variable val_SPACE : character;  -- for spaces between data in file

    begin
     
        -- if modelsim-project is created, then provide the relative path of 
        -- input-file (i.e. read_file_ex.txt) with respect to main project folder
        file_open(input_buf, "VHDLCodes/input_output_files/read_file_ex.txt",  read_mode); 
       -- else provide the complete path for the input file as show below 
       -- file_open(input_buf,"E:/VHDLCodes/input_output_files/read_file_ex.txt", read_mode); 

        while not endfile(input_buf) loop
          readline(input_buf, read_col_from_input_buf);
          read(read_col_from_input_buf, val_col1);
          read(read_col_from_input_buf, val_SPACE);   -- read in the space character
          read(read_col_from_input_buf, val_col2);
          read(read_col_from_input_buf, val_SPACE);   -- read in the space character
          read(read_col_from_input_buf, val_col3);

          -- Pass the read values to signals
          a <= val_col1;
          b <= val_col2;
          c <= val_col3;

          wait for 20 ns;  --  to display results for 20 ns
        end loop;

        file_close(input_buf);             
        wait;
    end process;
end tb ; -- tb

day hoc stem vhdl testbench

Hình 1‑7. Kết quả mô phòng bằng testbench trong Bảng 1‑13.

f)    Testbench viết dữ liệu tới tệp bên ngoài

Trong testbench được chỉ ra trongBảng 1‑14, các loại giá trị khác nhau được định nghĩa và sau đó được lưu trữ vào tệp bên ngoài. Ở đây, tệp được mở ở chế độ ‘write_mode’ (không phải ‘append_mode’) để ghi dữ liệu vào tệp.

Giải thích testbench trong Bảng 1‑14:

  • Để ghi dữ liệu vào tệp, trước tiên chúng ta cần xác định một bộ đệm (Dòng 15) để nạp nội dung của tệp (Dòng 27) cần ghi dữ liệu trong quá trình mô phỏng.
  • Tiếp theo, chúng ta cần định nghĩa một biến kiểu “line” sẽ được dùng để lưu trữ các giá trị cần ghi vào bộ đệm, như trong Dòng 19. Biến này lưu trữ ba loại giá trị gồm chuỗi (Dòng 31 và 34, v.v.), tín hiệu “a” (Dòng 35) và biến “b” (Dòng 37).

Lưu ý rằng, hai từ khóa được sử dụng để ghi dữ liệu vào tệp là “write” và “writeline”. Từ khóa “write” lưu trữ các giá trị vào trong “write_col_to_output_buf” và “writeline” ghi các giá trị từ “write_col_to_output_buf” vào trong tệp theo từng dòng. Hãy nhớ rằng, tất cả các câu lệnh ‘write’ trước ‘writeline’ sẽ được viết trên cùng một dòng, ví dụ: Các dòng 34-37 sẽ được viết trên cùng một dòng như trong Hình 1‑8. Kết quả mô phỏng cho testbench được hiển thị trong Hình 1‑9.

Bảng 1‑14. Testbench viết dữ liệu tới tệp bên ngoài.

-- write_file_ex.vhd


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

entity write_file_ex is
end write_file_ex;

architecture tb of write_file_ex is
    signal a : std_logic;

    file output_buf : text;  -- text is keyword
begin     

    tb1 : process
        variable write_col_to_output_buf : line; -- line is keyword
        variable b : integer := 40;
        begin
            a <= '1';  -- assign value to a
            wait for 20 ns; 

            -- if modelsim-project is created, then provide the relative path of 
            -- input-file (i.e. read_file_ex.txt) with respect to main project folder
          file_open(output_buf, "VHDLCodes/input_output_files/write_file_ex.txt",  write_mode); 
     -- else provide the complete path for the input file as show below 
     --file_open(output_buf, "E:/VHDLCodes/input_output_files/write_file_ex.txt",  write_mode); 

            write(write_col_to_output_buf, string'("Printing values"));
            writeline(output_buf, write_col_to_output_buf);  -- write in line 1

            write(write_col_to_output_buf, string'("a = "));
            write(write_col_to_output_buf, a);
            write(write_col_to_output_buf, string'(", b = "));
            write(write_col_to_output_buf, b);
            writeline(output_buf, write_col_to_output_buf);    -- write in new line 2

            write(write_col_to_output_buf, string'("Thank you"));
            writeline(output_buf, write_col_to_output_buf);   -- write in new line 3

            file_close(output_buf);
            wait; -- indefinitely suspend process
        end process;
end tb ; -- tb

day hoc stem vhdl testbench

Hình 1‑8. Dữ liệu được viết tới tệp “write_file_ex.txt”.

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

g)     Testbench sử dụng tệp csv để nhập và xuất dữ liệu

Trong phần này, cả thao tác đọc và ghi đều được thực hiện trong testbench trong Bảng 1‑15. Hơn nữa, tệp csv được sử dụng cho các hoạt động đọc và ghi. Nội dung của các tệp đầu vào và đầu ra lần lượt được chỉ ra trong Hình 1‑11 và Hình 1‑12.

Testbench này là sự kết hợp của các testbench trong Bảng 1‑13 và Bảng 1‑14 nên để hiểu được testbench này vui lòng đọc lại phần giải thích của các testbench trên.

Các tính năng bổ sung được thêm vào testbecnh này được hiển thị bên dưới:

  • Các dòng 63-64 được thêm vào để bỏ qua hàng tiêu đề, tức là bất kỳ hàng nào không bắt đầu bằng boolean-number (xem dòng 42).
  • Ngoài ra, lỗi sẽ được thông báo cho giá trị “b” nếu nó không phải là boolean. Tương tự, chức năng này cũng có thể được thêm cho các giá trị khác.
  • Cuối cùng, lỗi được thông báo trong tệp CSV tại các Dòng 96-109. Điều này có thể được thấy trong Hình 1‑12. Việc tìm vị trí lỗi bằng tệp csv luôn dễ dàng hơn khi so sánh với việc kiểm tra dạng sóng mô phỏng (hãy thử tìm lỗi bằng cách sử dụng Hình 1‑10 và so sánh với Hình Hình 1‑12).

Bảng 1‑15. Testbench sử dung tệp csv để nhập và xuất dữ liệu.

-- read_write_file_ex.vhd

-- testbench for half adder, 

-- Features included in this code are
    -- inputs are read from csv file, which stores the desired outputs as well
    -- outputs are written to csv file
    -- actual output and calculated outputs are compared
    -- Error message is displayed in the file
    -- header line is skipped while reading the csv file


library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_textio.all; -- require for writing/reading std_logic etc.

entity read_write_file_ex is
end read_write_file_ex;

architecture tb of read_write_file_ex is
    signal a, b : std_logic;
    signal sum_actual, carry_actual : std_logic;
    signal sum, carry : std_logic;  -- calculated sum and carry by half_adder

    -- buffer for storing the text from input and for output files
    file input_buf : text;  -- text is keyword
    file output_buf : text;  -- text is keyword

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

  tb1 : process
  variable read_col_from_input_buf : line; -- read lines one by one from input_buf
  variable write_col_to_output_buf : line; -- write lines one by one to output_buf

  variable buf_data_from_file : line;  -- buffer for storind the data from input read-file
  variable val_a, val_b : std_logic; 
  variable val_sum, val_carry: std_logic;
  variable val_comma : character;  -- for commas between data in file

  variable good_num : boolean;
  begin
   
  -- ####################################################################
   -- Reading data

      -- if modelsim-project is created, then provide the relative path of 
      -- input-file (i.e. read_file_ex.txt) with respect to main project folder
      file_open(input_buf, "VHDLCodes/input_output_files/half_adder_input.csv",  read_mode); 
      -- else provide the complete path for the input file as show below 
      -- file_open(input_buf, "E:/VHDLCodes/input_output_files/read_file_ex.txt",  read_mode); 

      -- writing data
      file_open(output_buf, "VHDLCodes/input_output_files/half_adder_output.csv",  write_mode); 

      write(write_col_to_output_buf, 
        string'("#a,b,sum_actual,sum,carry_actual,carry,sum_test_results,carry_test_results"));
      writeline(output_buf, write_col_to_output_buf);

      while not endfile(input_buf) loop
        readline(input_buf, read_col_from_input_buf);
        read(read_col_from_input_buf, val_a, good_num);
        next when not good_num;  -- i.e. skip the header lines

        read(read_col_from_input_buf, val_comma);           -- read in the space character
        read(read_col_from_input_buf, val_b, good_num);  
        assert good_num report "bad value assigned to val_b";

        read(read_col_from_input_buf, val_comma);           -- read in the space character
        read(read_col_from_input_buf, val_sum);
        read(read_col_from_input_buf, val_comma);           -- read in the space character
        read(read_col_from_input_buf, val_carry);

        -- Pass the variable to a signal to allow the ripple-carry to use it
        a <= val_a;
        b <= val_b;
        sum_actual <= val_sum;
        carry_actual <= val_carry;

        wait for 20 ns;  --  to display results for 20 ns

        write(write_col_to_output_buf, a);
        write(write_col_to_output_buf, string'(","));
        write(write_col_to_output_buf, b);
        write(write_col_to_output_buf, string'(","));
        write(write_col_to_output_buf, sum_actual);
        write(write_col_to_output_buf, string'(","));
        write(write_col_to_output_buf, sum);
        write(write_col_to_output_buf, string'(","));
        write(write_col_to_output_buf, carry_actual);
        write(write_col_to_output_buf, string'(","));
        write(write_col_to_output_buf, carry); 
        write(write_col_to_output_buf, string'(","));
        
        -- display Error or OK if results are wrong
        if (sum_actual /= sum) then
          write(write_col_to_output_buf, string'("Error,"));
        else
          write(write_col_to_output_buf, string'(","));  -- write nothing
          
        end if;

        -- display Error or OK based on comparison
        if (carry_actual /= carry) then
          write(write_col_to_output_buf, string'("Error,"));
        else
          write(write_col_to_output_buf, string'("OK,"));
        end if;


        --write(write_col_to_output_buf, a, b, sum_actual, sum, carry_actual, carry);
        writeline(output_buf, write_col_to_output_buf);
      end loop;

      file_close(input_buf);             
      file_close(output_buf);             
      wait;
  end process;
end tb ; -- tb

day hoc stem vhdl testbench

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

day hoc stem vhdl testbench

Hình 1‑11.   Nội dung của tệp “half_adder_input.cvs”.

day hoc stem vhdl testbench

Hình 1‑12. Nội dung của tệp “half_adder_output.csv”.

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