• Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

  • Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

  • Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

Hiển thị các bài đăng có nhãn simtool. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn simtool. Hiển thị tất cả bài đăng

Thứ Hai, 29 tháng 4, 2019

[SystemC][High Level Design]Bài 5 - Hướng dẫn cài đặt một số phần mềm và thư viện để compile

Bài viết này trình bày cách cài đặt một số phần mềm như Cygwin, gtkwave, Vivado và thư viện SystemC trên Windows để kiểm tra, compile và mô phỏng SystemC code. Về cơ bản, sau khi đã viết đầy đủ model code như hướng dẫn ở bài 1 bài 2 bài 3 và bài 4. Chúng ta cần một số công cụ sau đây để phục vụ kiểm tra, biên dịch và chạy mô phỏng.

1) Cygwin Terminal
Cygwin là một phần mềm tập hợp các công cụ mã nguồn mở và các công cụ GNU tương tự như trên Linux để chạy trên Windows. Chúng ta cài đặt phần mềm này để sử dụng công cụ gcc/g++ biên dịch code SystemC.
Các bạn tải Cygwin ở đây: https://www.cygwin.com/
Chú ý, Cygwin có hai phiên bản 32 bit và 64 bit (hình minh họa trong bài này ứng với bản cài đặt Cygwin64 nhưng với Cygwin32 là tương tự). Cả hai đều compile được SystemC bằng g++ nhưng các bạn hãy cài bản 32 bit để có thể chạy mô phỏng được. Trên bản Cygwin64, hiện file .exe sau khi biên dịch không thể chạy mô phỏng như mong muốn nhưng nhóm tác giả chưa tìm ra cách khắc phục.
Hình 1: Tải Cygwin Terminal
Sau khi tải về, các bạn tiến hành cài đặt theo các bước sau đây (chú ý, việc cài đặt sẽ tải các gói công cụ từ các site cung cấp mã nguồn nên cần kết nối internet ổn định):
  • Bước 1, thông tin tổng quan về Cygwin và phiên bản cài đặt
Hình 2: Cygwin - bước 1, thông tin chung về Cygwin
  • Bước 2, tùy chọn cài đặt
    • Install from Internet: Cài trực tiếp các gói công cụ cần thiết qua internet -> Chọn option này
    • Dowload Without Installing: Chỉ tải source code chứ không cài đặt'
    • Install from Local Directory: Cài đặt từ source code mà bạn đã có sẵn trên máy tính
Hình 3: Cygwin - bước 2, chọn phương pháp cài đặt

  • Bước 3, chọn thư mục cài đặt tại "Root directory" và quyền sử dụng Cygwin:
    • All Users: Tất cả các user được tạo trên máy tình đều có thể sử dụng -> Chọn option này
    • Just me: Chỉ User mà bạn dùng để cài đặt mới có thể sử dụng Cygwin

Hình 4: Cygwin - bước 3, chọn thư mục cài đặt và quyền sử dụng

  • Bước 4, chọn thư mục lưu các file cần tải trong quá trình cài đặt

Hình 5: Cygwin - bước 4, chọn thư mục lưu các file cần tải trong quá trình cài đặt

  • Bước 5, chọn cách kết nối internet, chọn "Direct Connection" nếu bạn không có thiết lập gì đặc biệt.

Hình 6: Cygwin - bước 5, chọn cách kết nối internet mà bạn muốn thiết lập

  • Bước 6, chọn site để tải các gói cài đặt, bạn chọn site bất kỳ, nếu trong quá trình cài đặt, bạn thấy việc tải quá chậm hoặc không thể kết nối thì hãy thoát và chọn lại site khác.

Hình 7: Cygwin - bước 6, chọn site mà bạn sẽ tải các gói cài đặt Cygwin

  • Bước 7, tải các thông tin cài đặt từ site đã chọn ở bước 6

Hình 8: Cygwin - bước 7, tải các thông tin cần thiết từ site đã chọn

  • Bước 8, lựa chọn các công cụ (tool) bạn muốn cài đặt.
  • View: Chọn nhóm. "Full" sẽ liệt kê tất cả các công cụ được hỗ trợ
  • Search: Liệt kê các công cụ chứa từ khóa muốn tìm
  • Để tùy chọn cài đặt hãy chú ý các cột sau:
    • New: nhấp các ô của vào cột này để chọn tác vụ mà bạn muốn
      • Skip: bỏ qua, không cài đặt
      • Uninstall: gỡ bỏ công cụ đã cài đặt trước đó
      • Install hoặc "dãy số phiên bản": Cài đặt
      • Reinstall: Cài đặt lại công cụ đã có trước đó
    • Package hoặc Description: Tên công cụ
    • Size: dung lượng của gói công cụ
  • Cygwin là một tập hợp các công cụ của Unix có thể chạy trên nền Windows, vơi mục tiêu sử dụng Cygwin để biên dịch và kiểm tra code SystemC thì bạn cần cài đặt:
    • GNU compiler Collection (C, ...): Đây là gcc, một trình biên dịch code C
    • GNU compiler Collection (C++): Đây là g++, một trình biên dịch code C++
    • The GNU version of the "make" utility: Đây là công cụ "make", dùng để cài đặt các thư viện SystemC 
Hình 9: Cygwin - bước 8, lựa chọn các công cụ (tool) bạn muốn cài đặt cho Cygwin
  • Bước 9, cài đặt các công cụ đã chọn

Hình 10: Cygwin - bước 9, cài đặt
Sau khi cài đặt, chương trình có biểu tượng sau đây là Cygwin.
Hình 11: Cygwin logo
Thêm đường dẫn các tool của Cygwin, ví dụ C:\cygwin\bin, đến biến môi trường PATH của Windows để tránh lỗi không tìm thấy file cygwin1.dll.
Hình 12: Thêm đường dẫn thư mục cài đặt Cygwin đến biến PATH
2) Cài đặt thư viện SystemC
Thư viện SystemC được tải tại website:
https://www.accellera.org/downloads/standards/systemc

Hình 13: Tải thư viện SystemC
SystemC có nhiều phiên bản khác nhau, bản mới nhất là 2.3.3. Sau khi giải nén bạn sẽ có thư mục "systemc-2.3.3".
Hình 14: Thư mục dùng để cài đặt SystemC sau giải nén
Bước 1, mở Cygwin, vào thư mục đã giải nén chạy lệnh sau:
./configure --prefix=<đường dẫn thư mục sẽ cài SystemC>
Ví dụ:
./configure --prefix=/cygdrive/d/20.Project/1.ModelBaseDesign/SystemC_lib


Hình 15: Kết quả chạy configure
Bước 2, chạy lệnh cài đặt:
make install
Hình 16: Quá trình cài đặt thư viện SystemC
Nếu trước đó đã cài đặt nhưng bây giờ cần cài lại thì cần chạy lệnh sau trước khi "make install"
make clean
Đối với thư viện SystemC-2.2.0 trở về trước, cần chỉnh sửa file systemc-2.2.0/src/systemc.h trước khi cài đặt. Tìm đến các dòng code sau:
    using std::wctomb;
    using std::mbstowcs;
    using std::wcstombs;
Xóa 2 dòng:
    //using std::wctomb;
    using std::mbstowcs;
    //using std::wcstombs;
Hoặc sửa thành:
#if !defined(__CYGWIN__) && !defined(__CYGWIN32)
    using std::wctomb;
    using std::wcstombs;
#endif
Phiên bản SystemC-2.3.* không cần chỉnh.

3) Tải và cài đặt phần mềm gtkwave để đọc file vcd xem waveform
Khi mô phỏng SystemC code, chúng ta có thể tạo (dump) file waveform với định dạng .vcd. gtkwave là một trong nhưng công cụ miễn phí hỗ trợ việc đọc và hiển thị waveform.
Các bạn có thể tải các phiên bản muốn sử dụng ở đây: http://gtkwave.sourceforge.net/
Hình 17: Tải GTKWave
Có rất nhiều phiên bản nhưng ở đây, nhóm tác giả sử dụng phiên bản 32 bit gtkwave-3.3.100-bin-win32 đã build sẵn, chỉ cần tải về -> giải nén -> sử dụng.
Hình 18: Tải bản GTKWave cho win32
Chú ý, nhóm tác giả có thử tải bản gtkwave-3.3.100-bin-win64 nhưng bản này bị lỗi khi chạy chương trình gtkwave\bin\gtkwave.exe.
4) Cài đặt phần mềm Vivado để tổng hợp SystemC
Đây là một phần mềm dành cho FFGA của Xilinx nhưng sẽ được nhóm tác giả sử dụng để biên dịch từ SystemC code (model code) thành Verilog code để kiểm chứng khả năng tổng hợp của model code.
Việc tải và đặt đã được hướng dẫn chi tiết ở đường dẫn sau:
5) Biên dịch model code với g++ trên Cygwin và xem waveform với gtkwave
Sau khi đã cài Cygwin (bản 32 bit) và công cụ g++ kèm theo. Các bạn hãy thử kiểm tra với model code được mô tả sau đây. Chú ý, model code này lấy từ tài liệu về SystemC của Peeter Ellervee trên website "https://wiki.ttu.ee". Đây là một model có cả phần design, test và tạo file waveform .vcd nên nhóm tác giả dùng để kiểm chứng các tool đã cài đặt, còn việc làm thế nào để viết và hiểu code này, các bạn sẽ được hướng dẫn tiếp trong những bài tiếp theo với ví dụ SCPU của nhóm tác giả.
Model code 1 - header file 
#include "systemc.h"
SC_MODULE (mux) {
  int const static bit_number=8;
  sc_in <sc_bv <bit_number> > a_in;
  sc_in <sc_bv <bit_number> > b_in;
  sc_in <bool> juht;
  sc_out <sc_bv <bit_number> > out_sig;
  SC_CTOR(mux):a_in("a_in"),b_in("b_in"),
    juht("juht"),out_sig("out_sig") {
    cout<<"mux constructor"<<endl;
    //
    SC_METHOD( choose_out );
      sensitive<<juht<<a_in<<b_in;
  }
  //
  void choose_out() {
    if (juht.read())
      out_sig.write(a_in.read());
    else out_sig.write(b_in.read());
  }
};

Model code 2 - body file
#include "systemc.h"
#include "mux.h"
SC_MODULE (t_mux) {
  int const static num_bits=8;
  sc_signal <sc_bv <num_bits> > t_a_in;
  sc_signal <sc_bv <num_bits> > t_b_in;
  sc_signal <bool> t_juht;
  sc_signal <sc_bv <num_bits> > t_out_sig;
  mux* mux_instance;
  void stimulus();
  SC_CTOR(t_mux):t_a_in("t_a_in"),
    t_b_in("t_b_in"),t_juht("t_juht"),
    t_out_sig("t_out_sig") {
    cout<<"t_mux constructor"<<endl;
    mux_instance=new mux("mux");
    mux_instance->a_in(t_a_in);
    mux_instance->b_in(t_b_in);
    mux_instance->juht(t_juht);
    mux_instance->out_sig(t_out_sig);
    //
    SC_THREAD(stimulus);
  }
};
void t_mux::stimulus()
{
  t_a_in.write("11111111");
  t_b_in.write("00001111");
  t_juht.write((bool)0);
  wait(1, SC_PS);
  t_juht.write((bool)1);
  wait(10, SC_NS);
  t_juht.write((bool)0);
  wait(10, SC_NS);
  t_a_in.write("11110000");
  t_b_in.write("10101010");
  t_juht.write((bool)0);
  wait(10, SC_NS);
  t_juht.write((bool)1);
  wait();
}
int sc_main(int args, char* argv[])
{
  t_mux testMux("t_mux");
  sc_trace_file *trcf=
  sc_create_vcd_trace_file("trace-it");
  if(trcf==NULL) cout<<"Sorry, no tracing..."<<endl;
  sc_trace(trcf, testMux.t_a_in, "t_a_in");
  sc_trace(trcf, testMux.t_b_in, "t_b_in");
  sc_trace(trcf, testMux.t_juht, "t_juht");
  sc_trace(trcf, testMux.t_out_sig, "t_out_sig");
  sc_start(40, SC_NS);
  sc_close_vcd_trace_file(trcf);
  return 0;
}
Bước 1, biên dịch model code.
Mở Cygwin và chuyển đến thư mục chứa model code, ví dụ ở đây là 8560w:/cygdrive/d/mux
Hình 19: Thưc mục chứa model code
Chạy lệnh biên dịch:
g++ -I. -I/cygdrive/d/20.Project/1.ModelBaseDesign/SystemC_lib32_233/include -L. -L/cygdrive/d/20.Project/1.ModelBaseDesign/SystemC_lib32_233/lib-cygwin -o t_mux t_mux.cpp -lsystemc -lm
Trong đó:
  • -I là đường dẫn thư mục cai đặt chứa các file header (.h)
  • -L là đường dẫn thư mục cài đặt thư viện SystemC, ứng với từng nền tảng cụ thể tên thư mục này sẽ khác nhau, ví dụ đối với cygwin32 là "lib-cygwin, đối với cygwin64 là "lib-cygwin64, trên hệ điều hành linux 64 bit là "lib-linux64, ...
  • -o t_mux là tạo file ngõ ra của quá trình biên dịch. Đây là một file thực thi (.exe) tên t_mux
  • t_mux.cpp là file TOP
Hình 20: Biên dịch model code
Sau khi biên dịch thành công, file t_mux.exe được tạo ra. Đây là một file thực thi có đuôi .exe. Chú ý, trên hệ điều hành linux, file được tạo ra sẽ là "t_mux" chứ không phải "t_mux.exe"
Hình 21: Kết quả biên dịch
Bước 2, chạy mô phỏng bằng cách thực thi chương trình C (.exe)
./t_mux.exe
Hình 22: Thực thi chương trình t_mux.exe
Sau khi thực thi chương trình t_mux.exe, file waveform trace-it.vcd được tạo ra. Tên file thể hiện trong model code, các bạn có thể đọc lại để biết.
Hình 23: Kết quả chạy mô phỏng và tạo waveform
Bước 3, xem waveform để debug (nếu cần)
Mở phần mềm gtkwave (gtkwave\bin\gtkwave.exe). Trên menu, chọn File->Open New Tab, mở file .vcd
Hình 24: Mở file .vcd
Thêm các tín hiệu được trace đến cửa sổ wave, các tín hiệu này phải được khai báo trong model code bằng sc_trace.
Hình 25: Add các tín hiệu được trace đến cửa sổ wave
Chọn YES ở cửa sổ sau:
Hình 26: Xác nhận các tín hiệu được add đến wave
Cuối cùng, chúng ta có thể quan sát waveform của tất cả các tín hiệu cần debug.
Hình 27: Quan sát waveform bằng gtkwave
Lịch sử cập nhật:
1. 2019.Apr.29 - Tạo lần đầu

Danh sách tác giả:
1. Lê Hoàng Vân
2. Trương Công Hoàng Việt
3. Nguyễn Hùng Quân

Thứ Bảy, 9 tháng 3, 2019

[Questa SIM][UVM] Hướng dẫn cơ bản để sử dụng thư viện UVM trên QuestaSim

Bài viết này là một hướng dẫn tải, cài đặt và chạy mô phỏng sử dụng thư viện UVM (Universal Verification Methodology). Bài viết dành cho các bạn bắt đầu tìm hiểu về UVM nhưng chưa biết bắt đầu từ đâu, làm thế nào để chạy thử một ví dụ cơ bản. Bài viết này không hướng dẫn chi tiết về UVM và chỉ hướng dẫn một ví dụ để các bạn có công cụ để chạy thử nghiệm và nghiên cứu tiếp.

1) Một vài lưu ý trước khi bắt đầu
Các thông số về phần mềm và phần cứng mà tác giả đang sử dụng:
  1. Phần mềm mô phỏng: QuestaSim-64 10.2c. Cách cài đặt và sử dụng các bạn hãy đọc bài viết hướng dẫn.
  2. Hệ điều hành Window 7 Professional 64-bit
  3. CPU: Intel core i7-2760QM 2.4GHz
  4. RAM: 8 GB
Nếu bạn là người hoàn toàn mới tiếp xúc và bắt đầu nghiên cứu về mô phỏng cho các thiết kế vi mạch hoặc FPGA. Bạn hãy tải và chạy thử một vài ví dụ trong bài viết hướng dẫn về QuestaSim ở trên trước khi đọc bài viết này.
Tại sao dùng QuestaSim? Tác giả không dùng các phần mềm mô phỏng khác như VCS (Synopsys) hoặc Incisive, Xcelium (Cadence) mà dùng QuestaSim (hoặc ModelSim) vì nó dễ tìm source và cài đặt.
2) Tải thư viện UVM ở đâu?
Sau khi cài QuestaSim, mặc định phần mềm có thể hỗ trợ thư viện UVM hoặc không. Tuy nhiên, thư viện UVM được hỗ trợ sẵn có thể không phải là phiên bản mới nhất. Để tải và sử dụng phiên bản mới nhất bạn cần vào trang web của Accellera và truy cập link sau:


Trong link trên bạn cần chú ý các mục sau:
  • Current release: Phiên bản UVM mới nhất được công bố hiện tại.
  • Previous releases: Các phiên bản UVM đã công bố trước đó.
Ở đây, tác giả không dùng phiên bản UVM mới nhất là UVM 2017 v1.0 mà sử dụng phiên bản UVM1.2 (2014-06). Các bạn hãy tải các phần sau:
  • Code của thư viện ở link "Class Library Code", file: uvm-1.2.tar.gz
  • Hướng dẫn của thư viện ở link "UVM User Guide", file: uvm_users_guide_1.2.pdf
  • Mô tả chi tiết các class của thư viện ở link "Standard Universal Verification Methodology Class Reference", file: UVM_Class_Reference_Manual_1.2.pdf
Hình 1: Tải thư viện UVM và các hướng dẫn sử dụng
Sau khi giải nén thư viện UVM bạn sẽ có được thư mục uvm-1.2 như sau:
  • bin: Chứa một số script perl
  • docs: Hướng dẫn về các class của UVM, nội dung như file UVM_Class_Reference_Manual_1.2.pdf
  • examples: Ví dụ chạy trên phần mềm VCS
  • src: Toàn bộ source code của thư viện UVM
  • release-notes: Liệt kê tất cả các điểm mới cập nhật của phiên bản này so với phiên bản trước đó
  • UVM_reference.html: Hướng dẫn về các class của UVM như trong thư mục docs.
Hình 2: Thư mục thư viện UVM1.2
3) Làm thế nào sử dụng thư viện UVM trên QuestaSim?
Tạo một file ví dụ có nội dung như sau:
Ví dụ 1: Ví dụ cơ bản sử dụng thư viện UVM
//Include UVM library
`include "uvm.sv"
import uvm_pkg::*;
//Create the example class
class hello_world extends uvm_test;
  //Register hello_world to Factory
  `uvm_component_utils (hello_world)
  //constructor
  function new (string name = "hello_world", uvm_component parent);
    super.new(name, parent);
  endfunction  //
  virtual task run_phase (uvm_phase phase);
    `uvm_info("WARNING", "HELLO WORLD", UVM_LOW);
  endtask: run_phase

endclass: hello_world
//
//Call and run hello_world
//
module tb;
  //
  initial begin  
    run_test ("hello_world");
  end  // 
endmodule: tb
Nội dung của ví dụ trên được lưu trong một file tên hello_world.sv gồm các thành phần sau:
  1. Gọi và sử dụng các thành phần thư viện UVM ở khai báo include và import
  2. Tạo một class mở rộng từ uvm_test để in ra dòng "HELLO WORLD". Class này giống như một test.
  3. Tạo module để chạy test hello_world.
Mở phần mềm QuestaSim, tạo một project mới và thêm file trên vào project. Nếu bạn compile theo lệnh mặc định của GUI, ví dụ bấm chuột phải vào file hello_world.sv và chọn Compile/Compile Selected thì QuestaSim sẽ báo lỗi.
Hình 3: Lỗi khi compile bằng GUI
Nhấp chuột vào dòng ảnh báo lỗi bạn có thể thấy các thông điệp lỗi như sau:

** Error: D:/4.Blog/uvm/hello_world.sv(2): Cannot open `include file "C:/questasim64_10.2c/ovm-2.1.2/../verilog_src/ovm-2.1.2/src/uvm.sv".
** Error: D:/4.Blog/uvm/hello_world.sv(7): (vlog-2163) Macro `uvm_component_utils is undefined.
** Error: D:/4.Blog/uvm/hello_world.sv(7): near "(": syntax error, unexpected '(', expecting function or task

Lý do là vì QuestaSim sẽ không hiểu các thành phần của UVM. Trường hợp dùng GUI, hoạt động Compile/Compile Selected đã chọn tương đương với việc thực thi một lệnh trong cửa sổ Transcript của QuestaSim như sau:

vlog -work work -vopt -sv D:/4.Blog/uvm/hello_world.sv

Trong đó:

  • vlog là lệnh compile
  • -work work là option tạo ra thư viện làm việc tên work sau khi compile. Thư viện này nằm bên của sổ Library.
  • -sv D:/4.Blog/uvm/hello_world.sv là option chỉ ra cho QuestaSim biết vị trí file code System Verilog
Để compile và chạy file hello_world.sv với thư viện UVM1.2 đã chuẩn bị phía trên. Các bạn thực hiện theo thứ tự các lệnh sau trên của sổ Transcript của QuestaSim:

#Compile
vlog -work work -sv hello_world.sv +incdir+C:/questasim64_10.2c/uvm-1.2/src +define+UVM_CMDLINE_NO_DPI +define+UVM_REGEX_NO_DPI +define+UVM_NO_DPI

#Simulate
vsim -novopt work.tb

#Running
run 1ns

Hoặc bạn lưu tất cả lệnh trên vào 1 file text ,ví dụ như run.do, sau đó chạy lệnh sau trên cửa sổ Transcript của QuestaSim:

do run.do

Lưu ý, các bạn hãy thay đoạn lệnh sau bằng đường dẫn thư mục UVM1.2 trên máy tính của bạn.

+incdir+C:/questasim64_10.2c/uvm-1.2/src

Sau khi chạy các lệnh trên, QuestaSim sẽ hiện lên 1 của sổ báo hoàn thành quá trình mô phỏng, hãy chọn "No". Nếu bạn chọn "Yes", trình mô phỏng QuestaSim sẽ đóng.
Hình 4: Cửa số báo quá trình mô phỏng đã hoàn tất
Quan sát kết quả trên Transcript của QuestaSim, bạn sẽ thấy có đoạn sau:

 UVM_INFO @ 0: reporter [RNTST] Running test hello_world...
# UVM_INFO C:/questasim64_10.2c/uvm-1.2/src/base/uvm_traversal.svh(279) @ 0: reporter [UVM/COMP/NAMECHECK] This implementation of the component name checks requires DPI to be enabled
# UVM_INFO hello_world.sv(14) @ 0: uvm_test_top [WARNING] HELLO WORLD
# UVM_INFO C:/questasim64_10.2c/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
# --- UVM Report Summary ---
#
# ** Report counts by severity
# UVM_INFO :    4
# UVM_WARNING :    0
# UVM_ERROR :    0
# UVM_FATAL :    0
# ** Report counts by id
# [RNTST]     1
# [UVM/COMP/NAMECHECK]     1
# [UVM/RELNOTES]     1
# [WARNING]     1
#

Hãy chú ý đến các vị trí tô đỏ, các vị trí này thể hiện kết quả từ ví dụ hello_world.sv.

Chú ý, nếu bạn gặp vấn đề về việc thiếu C/C++ compiler hãy đọc bài viết hướng dẫn cài đặt QuestaSim.

Lịch sử cập nhật:
1) 2019.July.31 - Thêm chú ý về việc cài đặt C/C++compiler

Chủ Nhật, 6 tháng 8, 2017

[Verification] Hướng dẫn tạo testbench tự kiểm tra thiết kế bằng Verilog, System Verilog

Bài viết này hướng dẫn các bạn viết một testbench đơn giản tự động kiểm tra một thiết kế. Đồng thời qua đó các bạn có thể hiểu cơ bản về việc mô phỏng và kiểm tra thiết kế. Nội dung của bài viết này dành cho các bạn mới bắt đầu tìm hiểu cách viết một testbench Verilog.

1. Môi trường mô phỏng là gì?
Môi trường mô phỏng (simulation environment) hay môi trường kiểm tra (verification environment) của thiết kế là một tổ hợp các thành phần (bao gồm cả thiết kế) cho phép công cụ mô phỏng có thể sử dụng để thực thi tính toán các dữ liệu đầu ra của thiết kế dựa trên các dữ liệu đầu vào được môi trường cung cấp. Dữ liệu đầu ra có thể hiện dưới nhiều dạng khác nhau như một file dữ liệu, hoặc hiển thị trên ngõ ra chuẩn (màn hình).
Với cùng một thiết kế, môi trường mô phỏng được xây dựng bởi các kỹ sư khác nhau có thể khác nhau nhưng đều thực thi hai nhiệm vụ cơ bản:
  1. Cấp giá trị ngõ vào cho thiết kế hoạt động
  2. Giám sát giá trị ngõ ra của thiết kế tương ứng với những giá trị ngõ vào đã cấp để đảm bảo thiết kế hoạt động chính xác
Testbench là một thành phần của môi trường mô phỏng thực thi trực tiếp hai nhiệm vụ trên hoặc chỉ cấp giá trị ngõ vào cho thiết kế hoạt động. Thuật ngữ testbench cũng có thể dùng để hàm ý nói về một môi trường mô phỏng hoàn chỉnh.

Cấu trúc môi trường mô phỏng cơ bản gồm:
  1. DUT (Design Under Test) còn được gọi là UUT (Unit Under Test) chính là thiết kế cần mô phỏng và kiểm tra. DUT sẽ được gọi và kết nối với các thành phần kiểm tra ở bên trong môi trường mô phỏng hoặc trực tiếp trong testbench.
  2. Testbench
    1. Stimulus hoặc Driver hoặc Test Vector Generator là thành phần tạo và cung cấp dữ liệu đầu vào của DUT, lái các tín hiệu đầu vào của DUT
    2. Monitor hoặc Observer là thành phần giám sát và kiểm tra các giá trị ngõ ra của DUT
Hình 1. Cấu trúc cơ bản của môi trường mô phỏng
Xin lưu ý với các bạn, đây chỉ là cấu trúc cơ bản. Hiện nay, với sự phức tạp ngày càng tăng của các thiết kế môi trường mô phỏng cũng được xây dựng dựa trên các giao thức và phương pháp được chuẩn hóa ví dụ như OVM, VMM, UVM,... Các giao thức này phân chia môi trường thành nhiều thành phần với tên gọi khác nhau nhưng vẫn không nằm ngoài 2 mục đích là tạo dữ liệu đầu vào và giám sát dữ liệu đầu ra của thiết kế.
2. Mô tả DUT 

Phần này mô tả DUT sẽ sử dụng trong bài viết này. DUT là một bộ đếm Johnson được mô tả chi tiết ở link sau:

RTL code của bộ đếm Johnson N bit:
module johnson_counter (clk, rst_n, js_count);
//
parameter N  = 4;
//
//Interface
//
input clk;
input rst_n;
output reg [N-1:0] js_count;
wire js_msb_inv;
assign js_msb_inv = ~js_count[N-1];
always @ (posedge clk) begin
 if (~rst_n)
   js_count[N-1:0] <= 0;
 else
   js_count[N-1:0] <= {js_count[N-2:0], js_msb_inv};
end
endmodule
Bộ đếm này đã được mô phỏng trong bài viết sau:


sử dụng testbench có nội dung như sau:
module tb_jc;
parameter N = 4;
//
//Interface
//
reg clk;
reg rst_n;
wire [N-1:0] js_count;
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
initial begin
  clk = 0;
forever #10 clk = !clk;
end
initial begin
  rst_n = 0;
#20
rst_n = 1;
end
endmodule 
Testbench trên đơn giản chỉ làm nhiệm vụ:
  1. Gọi thiết kế
  2. Tạo giá trị ngõ vào cho xung clock clk và tín hiệu reset rst_n (Stimulus).
Monitor khi chạy mô phỏng testbench trên là cửa sổ waveform của trình mô phỏng. Qua cửa sổ này, chúng ta quan sát bằng mắt để kiểm tra giá trị của tín hiệu ngx ra js_count theo hoạt động của xung clock và reset.
Việc kiểm tra bằng mắt như trên sẽ gây mất thời gian, phụ thuộc vào "tình trạng" người quan sát nên dễ nhầm lẫn. Nhất là với các thiết kế phức tạp thì việc làm như vậy là không đảm bảo tính đúng đắn của thiết kế. Chính vì vậy, việc xây dựng các môi trường và testbench tự kiểm tra là cần thiết.
3. Mô tả cấu trúc testbench
Một file testbench có thể gồm các thành phần cơ bản như sau:
  1. Định nghĩa chỉ dẫn mô phỏng là các khai báo về đơn vị thời gian timescale, define, include các file liên quan
  2. Tạo khối testbench bằng cặp từ khóa module/endmodule
  3. Khai báo các tham số, hằng số như các parameter
  4. Khai báo giao tiếp với DUT: các tín hiệu sẽ kết nối với DUT
  5. Gọi DUT
  6. Khai báo các tín hiệu, biến sử dụng nội bộ trong testbench
  7. Tạo Stimulus
  8. Tạo Monitor
  9. Giới hạn thời gian chạy mô phỏng của testbench
Trong đó:
  • Các thành phần bắt buộc phải có đối với testbench tối thiểu là 2, 4, 5, 7. Testbench loại này như đã nói ở mục 2 phía trên.
  • Các thành phần bắt buộc phải có đối với testbench tự động kiểm tra là 2, 4, 5, 7 và 8.
Sau đây, tôi xin mô tả chi tiết từng phần của một testbench dùng để tự động kiểm tra DUT bộ đếm Johnson theo những thành phần đã trình bày ở trên.

3.1 Định nghĩa chỉ dẫn mô phỏng

Khai báo đơn vị thời gian mô phỏng
`timescale 1ns/1ns
3.2 Tạo khối testbench
module tb_auto;
   //testbench body
endmodule
3.3 Khai báo các tham số, hằng số
parameter N = 4;
parameter END_TIME = 100;
parameter VALUE_NUM = 2*N;
Trong đó:
  • N là số bit của DUT bộ đếm Johnson
  • VALUE_NUM là số giá trị cần phải kiểm tra của DUT
  • END_TIME là thời gian giới hạn của testbench khi chạy mô phỏng tính theo số chu kỳ xung clock clk. Ở đây, sau 100 xung clock clk thì mô phỏng sẽ kết thúc.
3.4 Khai báo giao tiếp với DUT
reg clk;
reg rst_n;
wire [N-1:0] js_count;
Đây chính là các ngõ vào và ngõ ra của DUT, Trong đó:
  • Ngõ vào được khai báo kiểu biến (variable) để gán giá trị lái DUT
  • Ngõ ra khai báo kiểu net chỉ để giám sát
3.5 Gọi DUT
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
Dòng code trên gọi module johnson_counter và thiết lập số bit cho bộ đếm là N = 4 bit.

3.6 Khai báo các tín hiệu, biến sử dụng nội bộ

reg [N-1:0] test_value[2*N-1:0];
integer i,j;

Trong đó:
  • test_value là một mảng dùng để lưu các giá trị cần kiểm tra, giá trị này sẽ được sử dụng để so sánh với ngõ ra DUT để biết DUT có hoạt động đúng hay không.
  • i,j là hai biến lặp dùng để xây dựng Stimulus và Monitor
3.7  Tạo Stimulus

Tạo clock với chu kỳ 20 đơn vị thời gian. Đơn vị thời gian ở đây là 1ns, được quy định bởi 3.1. Nếu không có định nghĩa trước về đơn vị thời gian thì testbench sẽ chạy theo đơn vị thời gian mặc định của trình mô phỏng:

initial begin
       clk = 0;
forever #10 clk = !clk;
end

Tạo reset tích cực trong 20ns ở hai điểm là thời điểm bắt đầu mô phỏng 0ns và sau khi chạy 200ns. Điểm thứ 2 là để kiểm tra DUT có hoạt động chính xác khi bị reset trong lúc hoạt động hay không.

initial begin
        rst_n = 0;
#20
rst_n = 1;
#200
rst_n = 0;
#20
rst_n = 1;
end

3.8 Tạo Monitor

Tạo giá trị kiểm tra ngõ ra DUT. Phương pháp tạo ở đây là sử dụng một mảng lưu lại tất cả các giá trị mong muốn được kiểm tra theo thứ tự đúng như cách mà DUT hoạt động. Lưu ý, kỹ sư mô phỏng sẽ xây dựng dựa trên mô tả kỹ thuật (specification) của DUT chứ không phải theo thiết kế DUT của người thiết kế nên cách thực hiện có thể giống và khác với với code của người thiết kế.

initial begin
  j = VALUE_NUM;
  test_value[0] = 0;
  for (i = 1; i < VALUE_NUM; i=i+1) begin
 test_value[i] = {test_value[i-1][N-2:0], ~test_value[i-1][N-1]};
  end
end

Chú ý, số lượng giá trị cần kiểm tra là VALUE_NUM sẽ được lưu theo thứ tự từ ô thứ 0 đến ô thứ VALUE_NUM-1. Tuy nhiên, biến j được gán giá trị ban đầu ngoài tầm này, j = VALUE_NUM là để sử dụng xác định thời điểm testbench bắt đầu kiểm tra sau này. Cụ thể, testbench chỉ kiểm tra bắt đầu tính từ khi DUT được reset. Thời điểm bắt đầu mô phỏng cho đến trước khi reset, giá trị DUT là không xác định nên không kiểm tra.

Tạo địa chỉ để lấy giá trị test từ mảng test_value:

always @ (posedge clk) begin
  if (~rst_n) j <= 0;
else if (j == VALUE_NUM-1) j <= 0;
else j <= j + 1;
end

So sánh và kiểm tra giá trị reset của DUT:

always @ (*) begin
  if (!rst_n && (j != VALUE_NUM) && (js_count[N-1:0] != test_value[j])) begin
   $display ("--------------- SIMULATION FAIL ---------------");
  $display ("[%t]The reset value of DUT: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
   $stop;
   end
end

Ở đây, giá trị reset được test từ khi:
  1. Tín hiệu reset tích cực mức thấp, tương ứng rst_n=0
  2. Sau khi DUT đã reset, tương ứng, j phải khác giá trị khởi tạo ban đầu
Nếu giá trị ngõ ra DUT js_count khác giá trị kiểm tra test_value, testbench sẽ hiển thị báo sai; hiện vị trí thời gian sai; hiện giá trị DUT và giá trị kiểm tra tại vị trí phát hiện sai. Cuối cùng, dừng mô phỏng bởi $stop.

So sánh và kiểm tra ngõ ra DUT trong lúc hoạt động và không có reset:

always @ (posedge clk) begin
  if (rst_n && (js_count[N-1:0] != test_value[j])) begin
    $display ("--------------- SIMULATION FAIL ---------------");
   $display ("[%t]The DUT value: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
    $stop;
end
end

Khối kiểm tra trên chỉ lấy mẫu kiểm tra tại các vị trí cạnh lên xung clock và báo lỗi bất cứ khi nào giá trị ngõ ra DUT khác với giá trị kiểm tra. Mô phỏng cũng dừng ngay khi phát hiện lỗi.

3.9 Giới hạn thời gian chạy mô phỏng của testbench

initial begin
  repeat (END_TIME) begin
@ (posedge clk);
  end
$display ("[%t]--------------- SIMULATION PASS ---------------", $time);
$stop;
end

Kết thúc thời gian chạy mô phỏng, sau số cạnh lên xung clock clk được quy định bởi END_TIME, testbench sẽ báo mô phỏng thành công với thông điệp "SIMULATION PASS". Điều kiện PASS ở đây không cần vì chỉ cần xuất hiện 1 lỗi thì testbench đã dừng và báo FAIL ngay lập tức.

3.10 Nội dung file testbench hoàn chỉnh
`timescale 1ns/1ns
module tb_auto;
//parameter
parameter N = 4;
parameter END_TIME = 100;
parameter VALUE_NUM = 2*N;
//
//Interface of DUT
//
reg clk;
reg rst_n;
wire [N-1:0] js_count;
//DUT instance
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
//internal variables
reg [N-1:0] test_value[2*N-1:0];
integer i,j;
//Generate clock
initial begin
  clk = 0;
forever #10 clk = !clk;
end
//Generate reset
initial begin
  rst_n = 0;
#20
rst_n = 1;
#200
rst_n = 0;
#20
rst_n = 1;
end
//END SIMULATION
//After END_TIME cycles, if any error occurs, this testcase is ended and pased
initial begin
  repeat (END_TIME) begin
 @ (posedge clk);
end
$display ("[%t]--------------- SIMULATION PASS ---------------", $time);
$stop;
end
//
//Automatic TEST
//
//Generate the test value table
initial begin
  j = VALUE_NUM;
  test_value[0] = 0;
  for (i = 1; i < VALUE_NUM; i=i+1) begin
 test_value[i] = {test_value[i-1][N-2:0], ~test_value[i-1][N-1]};
  end
end
//Create the address to search the test value from the above table
always @ (posedge clk) begin
  if (~rst_n) j <= 0;
else if (j == VALUE_NUM-1) j <= 0;
else j <= j + 1;
end
//
//Compare and check
//
//Check the reset value
always @ (*) begin
  if (!rst_n && (j != VALUE_NUM) && (js_count[N-1:0] != test_value[j])) begin
 $display ("--------------- SIMULATION FAIL ---------------");
$display ("[%t]The reset value of DUT: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
$stop;
end
end
//Check the output of the Johnson counter
always @ (posedge clk) begin
  if (rst_n && (js_count[N-1:0] != test_value[j])) begin
 $display ("--------------- SIMULATION FAIL ---------------");
$display ("[%t]The DUT value: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
$stop;
end
end
endmodule 

4. Kết quả chạy trên Questa SIM

Việc biên dịch và chạy mô phỏng với Questa SIM các bạn hãy tham khảo hướng dẫn ở link:


Ở đây, tôi chỉ phân tích kết quả mô phỏng. Nếu mô phỏng PASS, các bạn sẽ quan sát trên cửa sổ Transcript hiện thông báo "SIMULATION PASS" và thời điểm kết thúc mô phỏng ngay trước thông điệp. Đồng thời, một dòng báo vị trị code trong testbench làm ngắt mô phỏng, chính là dòng $stop trong mục 3.9.
Hình 2. Thông điệp báo PASS trên cửa sổ transcript
Nếu giá trị reset bị sai, tác giả  đã sửa giá trị reset trong RTL code của DUT thành 1 (lưu ý phải biên dịch lại DUT), kết quả mô phỏng như sau:
Hình 3: Sai giá trị reset
Sau khi reset, nếu giá trị DUT sai trong khi hoạt động thì kết quả sẽ như sau. Lưu ý, tôi sửa RTL của DUT thành bộ đếm binary tuần tự như sau:

always @ (posedge clk) begin
 if (~rst_n)
   js_count[N-1:0] <= 0;
 else
   js_count[N-1:0] <= js_count + 1;
end 

Chuỗi giá trị sẽ là:
  1. Bộ đếm Binary 4 bit:    0000 -> 0001 -> 0010
  2. Bộ đếm Johnson 4 bit:  0000 -> 0001 -> 0011
Vì vậy, testbench sẽ báo lỗi tại vị trí ngõ ra DUT bằng 0010 và giá trị cần test là 0011.
Hình 4. Thông điệp báo lỗi bộ đếm
Việc tạo một testbench kiểm tra cho phép kiểm tra nhiều trường hợp trong thời gian mô phỏng dài mà không cần tốn công dò dạng sóng. Việc xem waveform chỉ thực hiện khi bạn mới xây dựng testbench để kiểm tra tính đúng đắn của testbench và khi phát hiện lỗi để gỡ lỗi (debug).
Trên đây chỉ là một hướng dẫn cơ bản giúp bạn nhanh chóng viết một testbench. Để một DUT được kiểm tra toàn diện và đảm bảo tính đúng đắn cao thì testbench và môi trường mô phỏng có thể cần thêm nhiều thành phần khác.

Lịch sử cập nhật:
1) 2019.11.10 - Cập nhật hình ảnh

[Questa SIM] Hướng dẫn cài đặt và chạy mô phỏng cơ bản với Questa SIM

1. Giới thiệu về Questa SIM

Questa SIM là một trình mô phỏng của hãng Mentor Graphics hỗ trợ đa nền tảng như hệ điều hành UNIX, Linux và Windows. Nếu bạn nào đã từng sử dụng ModelSim, một trình mổ phỏng khác của Mentor Graphics, thì việc làm quen với Questa SIM rất nhanh chóng và dễ dàng.

Questa SIM hỗ trợ biên dịch các thiết kế viết bằng Verilog HDL, VHDL và SystemC và hỗ trợ mô phỏng các đối tượng viết bằng Verilog HDL, VHDL, SystemC, System Verilog, PSL, UPF (Unified Power Format).

Việc chạy mô phỏng với Questa SIM có thể thực hiện bằng giao diện phần mềm (GUI - Graphical User Interface), bằng chế độ lệnh (Command-line) hoặc bằng chế độ batch.
  • GUI: Chế độ sử dụng giao diện phần mềm để thực thi mô phỏng bằng các nút, menu tùy chọn hoặc bằng cách lệnh trong cửa sổ transcript mà giao diện hỗ trợ
  • Command-line: Dùng lệnh để chạy mô phỏng bằng cửa sổ lệnh (command prompt) của hệ điều hành. Questa SIM sẽ bắt đầu thực thi với các biến được đặc tả trong file modelsim.ini, một file được trình mô phỏng tự động tạo ra, hoặc một file DO tùy chọn do người dùng tạo ra.
  • Batch: Dùng lệnh để chạy mô phỏng bằng cửa sổ lệnh của hệ điều hành với các biến được nhập trực tiếp từ ngõ vào chuẩn (bàn phím) hoặc từ file; và xuất ra ngõ ra chuẩn (màn hình) hoặc file.
Hình 1. Trình tự thực thi mô phỏng trong Questa SIM
Trình tự mô phỏng gồm 5 bước cơ bản:
  1. Thu thập các file đầu vào (Verilog, VHDL, SystemC) và ánh xạ các thư viện. Bước này tự động tạo ra file modelsim.ini như đã nói ở trên.
  2. Phân tích kiểm tra code và biên dịch
  3. Tối ưu và tạo cơ sở dữ liệu đã biên dịch
  4. Mô phỏng
  5. Gỡ lỗi (Debug)
2. Download gói cài đặt


3. Cài đặt
3.1/ Giải nén thư mục cài đặt
Sau khi giải nén, thư mục QuestaSim10.2c\Mentor Graphics Questa sim SE (64bit) 10.2c chứa các file sau:
  1. questasim-gcc-4.5.0-mingw64.zip
  2. questasim-win64-10.2c.exe
  3. regassistuvm_4.3_win.exe
  4. Thuốc cho tool
3.2/ Cài đặt Questa SIM
Chạy file questasim-win64-10.2c.exe để cài đặt Questa SIM.
Chọn Next → Agree → Next → Yes  → Yes
Đến cửa sổ sau đây thì chọn NO
Hình 2. Cài đặt Questa SIM
3.3/ Cài đặt thưc viện UVM

Đây là thư viện sử dụng cho phương pháp mô phỏng mới nhất hiện nay là UVM (Universal Verification Methodology).

Chạy file regassistuvm_4.3_win.exe để cài đặt.
Chọn Next -> chọn thư mục cài đặt và Next
Hình 3. Cài đặt thư viện UVM
3.4/ C/C++ compiler cho môi trường UVM
Giải nén file questasim-gcc-4.5.0-mingw64.zip và copy thư mục "gcc-4.5.0-mingw64" vào thư mục cài đặt QuestaSim. Đây là gcc compiler dùng để tổng hợp DPI (Direct Programming Interface) dùng cho môi trường mô phỏng UVM.
Lỗi thường gặp khi thiếu C/C++ compiler:
** Fatal: (vsim-7019) Can't locate a C/C++ compiler for 'DPI Export Compilation'.
3.5/ Tạo thuốc cho Questa SIM

Copy file MentorKG.exe và patch_dll.bat trong thư mục 
QuestaSim10.2c\Mentor Graphics Questa sim SE (64bit) 10.2c\c-r-a-c-k vào thư mục cài đặt c:\questasim64_10.2c\win64\

Chạy file patch_dll.bat sẽ tự động tạo ra file mgls.dll.

Chạy file MentorKG.exe, cửa sổ hiện ra như sau:
Hình 4. Chạy file MentorKG.exe
Chờ cho đến khi file ".txt" sau được tạo ra và hiện lên như sau:
Hình 5. File *.txt
Lưu lại file trên dưới tên với đuôi mở rộng .dat trong thư mục cài đặt c:\questasim64_10.2c\win64\ 

Bấm chuột phải lên Computer -> Properties -> Advanced system
  • Chọn Environment Variables tab -> Chọn new ở khung User variables for
  • Thêm:
    • Variable name: LM_LICENSE_FILE
    • Variable value: c:\questasim64_10.2c\win64\<tên file>.dat đã tạo ở trên
Hình 6. Thiết lập biến môi trường cho Questa SIM
Khởi động thử Questa SIM, hiện giao diện bình thường là OK.
Hình 7. Giao diện Questa SIM-64 10.2c
4. Chạy mô phỏng với giao diện (GUI) của Questa SIM

4.1 Đầu vào mô phỏng
Đầu vào mô phỏng gồm một file thiết kế (đây là bộ đếm Johnson tham khảo ở lilnk sau http://nguyenquanicd.blogspot.com/2017/08/verilog-rtl-code-mo-ta-cac-loai-bo-em.html) và một file testbench tạo giá trị ngõ vào cho việc mô phỏng thiết kế:
  1. File thiết kế: johnson_counter.v
  2. File testbench: tb_jc.v
Ở đây file testbench chỉ cần tạo xung clock clk và reset ban đầu rst_n.

File RTL code của thiết kế:
module johnson_counter (clk, rst_n, js_count);
//
parameter N  = 4;
//
//Interface
//
input clk;
input rst_n;
output reg [N-1:0] js_count;
wire js_msb_inv;
assign js_msb_inv = ~js_count[N-1];
always @ (posedge clk) begin
 if (~rst_n)
   js_count[N-1:0] <= 0;
 else
   js_count[N-1:0] <= {js_count[N-2:0], js_msb_inv};
end
endmodule

File testbench:
module tb_jc;
parameter N = 4;
//
//Interface
//
reg clk;
reg rst_n;
wire [N-1:0] js_count;
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
initial begin
  clk = 0;
forever #10 clk = !clk;
end
initial begin
  rst_n = 0;
#20
rst_n = 1;
end
endmodule 
4.2 Tạo project
Chọn File->New->Project...

Đặt tên project (Project name) và chọn đường dẫn lưu project (Project location) trong cửa sổ sau:
Hình 8. Tạo project
Tạo file thiết kế (Verilog, VHDL, SystemC) hoặc file mô phỏng mới (Create New File) hoặc thêm các file đã tạo sẵn vào project (Add Existing File) trong cửa sổ sau:
Hình 9. Tạo mã nguồn mô phỏng
Ở đây, tôi chọn Add Existing File và dẫn đến 2 file johnson_counter.v và tb_jc.v đã được viết trước. Open->OK->Close
Hình 10. Chọn file
4.3 Biên dịch
Bấm chuột phải vào một trong các file và chọn Compile->compile All
Hình 11. Biên dịch các file
Chú ý, ký hiệu "?" ở cột Status biểu thị file chưa được biên dịch. Nếu biên dịch lỗi thì Status là dấu chéo màu đỏ, biên dịch tốt thì Status là dấu chỉ thị màu xanh.  Xem thông tin ở cửa sổ Transcript để thấy các thông tin cảnh báo. Ví dụ ở đây, file johnson_counter.v biên dịch thành công, file tb_jc.v biên dịch lỗi.
Hình 12. Cảnh báo trên cửa sổ Transcript
Nếu lỗi, hãy nhấp chột vào dòng cảnh báo trên cửa sổ Transcript để biết lý do và vị trí lỗi. Như ví dụ này, file tb_jc.v bị lỗi ở dòng 10 gần ký hiệu "#" và đây là lỗi cú pháp. Dòng 10 như sau:

johnson_counter dut #(.N(N)) (.clk(clk), .rst_n(rst_n), .js_count(js_count));
Tôi khai báo tên gọi module là dut đặt đằng trước việc gán giá trị tham số trong khi nó phải đặt sau như dòng code sau đây:
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
Sau khi sửa, bạn hãy biên dịch lại là thành công như sau:
Hình 13. Biên dịch lại các file

4.4 Chạy mô phỏng
Sau khi biên dịch thành công, đối tượng được biện dịch sẽ xuất hiện trong thư mục work của thẻ library như sau:
Hình 14. Thẻ library
Bấm chuột phải vào đối tượng testbench tb_jc để chọn chế độ chạy mô phỏng. Ở đây, tôi chọn simulate without optimization để tạo ra các tín hiệu có thể quan sát dạng sóng.
Hình 15. Chạy mô phỏng
Sau khi thực thi, một vài cửa số khác hiện lên gồm:
  • Cửa sổ sim thể hiện cấu trúc thứ bậc của các đối tượng đang được mô phỏng. Ví dụ, tb_jc gọi thiết kế có tên dut. Ngang cấp với dut là hai khối initial. Bên trong dut có 1 khối always và một phát biểu assign.
  • Cửa số Objects thể hiện các tín hiệu, biến tương ứng có trong đối tượng bên cửa sổ sim.
  • Cửa sổ Wave sẽ là nơi để quan sát dạng sóng các tín hiệu
Lựa chọn các tín hiệu cần quan sát, bấm chuột phải và Add wave
Hình 16. Lấy tín hiệu để xem dạng sóng
Chú ý bên trên thanh công cụ có một nhóm các button để điều khiển việc chạy mô phỏng. Ở đây tôi chọn chạy mô phỏng với thời gian 500 ns. Bấm run cạnh ô 500 ns.
Hình 17. Chạy mô phỏng và xem waveform

Lịch sử cập nhật:
1) 2019.July.13 - Thêm mục 3.4 C/C++ compiler cho môi trường UVM
2) 2020.Jan.04 - Sửa link hình ảnh
3) 2020.Apr.25 - Sửa link tải QuestaSim