Chủ Nhật, 11 tháng 8, 2019

[UVM] Bài 5 - System Verilog code của môi trường UVM cho UART-APB (bản draff)

Hiện nhóm tác giả đã xây dựng xong phiên bản thô (draff) của môi trường UVM cho UART-APB. Đến thời điểm bài viết này được xuất bản, môi trường vẫn chưa được kiểm tra và debug hoàn chỉnh. Bài viết này sẽ mô tả về cấu trúc các file, cách tạo testcase và chạy mô phỏng trên môi trường hiện tại để các bạn đọc quan tâm có thể tìm hiểu và nghiên cứu từ bây giờ.
Trước khi đọc bài này, các bạn nên đọc kỹ lại các bài viết trước đó như bài 1, bài 2, bài 3, bài 4các bài trình bày về class của System Verilog. Đặc biệt, nếu bạn là người mới bắt đầu, hãy tập trung đọc hiểu và thực hành các ví dụ cơ bản của các bài trước để hiểu trước khi đi vào môi trường UVM này.

1) Cấu trúc môi trường UVM
Môi trường UVM cho UART-APB đã được mô tả trong lần phần tích đầu tiên ở bài 2. Trong quá trình xây dựng môi trường, một số thành phần khác cần được thêm vào để giải quyết một số vấn đề khi xây dựng môi trường thực tế.
Cấu trúc môi trường UVM hiện tại sẽ được mô tả trong phần này. Các bạn có thể đọc và so sánh với bài 2 để tìm điểm giống và khác nhau giữa cấu trúc mô tả trong bài 2 và cấu trúc trong bài này.
Chú ý, các phần cơ bản hầu như không đổi, chỉ một vài thành phần mới được thêm vào giúp việc xây dựng testcase dễ dàng hơn.
Phương pháp để bạn đọc dễ đọc và tìm hiểu source code của môi trường này:
  1. Phân tích sơ đồ cấu trúc môi trường.
    • Tên trong sơ đồ là tên thực tế xuất hiện trong môi trường.
    • Tên ngoài dấu ngoặc đơn là tên instance hoặc tên đối tượng (object)
    • Tên trong dấu ngoặc đơn là tên module hoặc tên class
  2. Tìm file code tương ứng với thành phần đã mô tả trong sơ đồ
  3. Kiểm chứng lại mối liên hệ giữa các thành phần trong source code và sơ đồ cấu trúc môi trường
Hình 1: Cấu trúc môi trường UVM hiện tại
Cấu trúc source code:
  • checker
    • apb_protocol_checker.sv - Phần lõi thực thi chức năng kiểm tra giao thức APB
    • apb_protocol_checker_top.sv - Phần kết nối APB checker với các instance của UART trong dut_top
    • uart_protocol_checker.sv - Phần lõi thực thi chức năng kiểm tra giao thức UART
    • uart_protocol_checker_top.sv - Phần kết nối UART checker với các instance của UART trong dut_top
  • dut
    • uart_apb_if.v - module chứa các thanh ghi cấu hình và trạng thái, kết nối với bus APB
    • uart_receiver.v - module truyền dữ liệu nối tiếp theo chuẩn UART
    • uart_transmitter.v - module nhận dữ liệu nối tiếp theo chuẩn UART
    • uart_top.v - TOP level của lõi IP UART-APB, kết nối các module chức năng
    • dut_top.v - TOP level của DUT kết nối 2 instance UART.
  • pat
    • trialPat/cVSequence.sv - Testcase (test pattern, testbench) của người kiểm tra. Mỗi thư mục là 1 testcase và đều chứa một file testcase suy nhất tên cVSequence.sv
    • ...
  • sim
    • uart_define.h - Chứa các define của UART
    • testTop.sv - Kết nối các thành phần môi trường mức TOP, gọi và chạy testcase
    • run_sim.do - Script chạy mô phỏng trên QuestaSim GUI, được dùng để chạy testcase khi cần debug
    • add_wave.do - Dùng cho script run_sim.do
    • run_qsim.pl - Script Perl chạy mô phỏng trên Cygwin terminal ở chế  độ batch mode
  • uvm_comp
    • cApbMasterAgent.sv - Kết nối Sequencer, Driver và Monitor
    • cApbMasterDriver.sv - Driver lái APB interface
    • cApbMasterMonitor.sv - Monitor giám sát APB interface và Interrupt interface
    • cApbMasterSequencer.sv - Lấy APB transaction từ Sequence chuyển đến Driver
    • cApbTransaction.sv - APB transaction, là dữ liệu và thông tin điều khiển APB interface sẽ gửi cho Driver
    • cCommonSequence.sv - Chứa các class chung được sử dụng để tạo ra các transaction.
    • cEnv.sv - Môi trường liên kết các Agent, Scoreboard và cVSequencer
    • cScoreboard.sv - Scoreboard dùng để kiểm tra dữ liệu trên 2 instance UART, dữ liệu kiểm tra lấy từ Monitor
    • cTest.sv - Thành phần cố định thực thi testcase, testcase viết trong cVSequence.sv sẽ được gọi và thực thi trong class này. Class này sẽ được gọi và thực thi ở method run_test() trong testTop.sv
    • cVSequence.sv - là một bản copy của testcase được thực thi lấy từ thư mục pat/<thư mục testcase>/cVSequence.sv
    • cVSequencer.sv - là thành phần trung gia chuyển các transaction đến các Agent
    • ifDut.sv - là các định nghĩa interface của DUT
    • uMacro.svh - là các macro được sử dụng trong testcase (pat/<thư mục testcase>/cVSequence.sv)
2) Làm thế nào để tạo một testcase?
2.1) Bước 1: Tạo class sequence chung để điều khiển các transaction
File: cCommonSequence.sv

Mục đích: Tạo class cho phép sinh ra các transaction như mong muốn của người test

Giải thích: Theo cấu trúc transaction, các biến pwrite, paddr, pwdata, pstrb, apbSeqEnapbConEn là các biến random. Giá trị của các biến này sẽ được tạo ngẫu nhiên khi method randomize() được thực thi. Để cấu hình các thanh ghi UART-APB đến giá trị mong muốn, chúng ta cần có các class ràng buộc lại giá trị của các biến trên.

Ví dụ, xét class cApbMasterWriteSeq, đây là class tạo ra transaction ghi trên APB. Để thực hiện điều này, biến pwrite của transaction luôn gán là 1 và transaction luôn được enable (apbSeqEn=1). Các giá trị như địa chỉ, dữ liệu ghi và pstrb có thể được gán thông qua biến addr, data và be.

Ví dụ 1: class cApbMasterWriteSeq
class cApbMasterWriteSeq extends uvm_sequence#(cApbTransaction);
     `uvm_object_utils(cApbMasterWriteSeq)
     `uvm_declare_p_sequencer(cApbMasterSequencer)

   cApbTransaction coApbTransaction;

   rand logic conEn;
     rand logic [31:0] addr;
     rand logic [31:0] data;
     rand logic [ 3:0] be;

     function new (string name = "cApbMasterWriteSeq");
         super.new(name);
     coApbTransaction = cApbTransaction::type_id::create("coApbTransaction");
     endfunction

     virtual task body();
         start_item(coApbTransaction);
     //coApbTransaction.randomize();
         assert(coApbTransaction.randomize() with {
       coApbTransaction.apbSeqEn  == 1;
       coApbTransaction.apbConEn  == conEn;
             coApbTransaction.paddr  == addr;
             coApbTransaction.pwdata == data;
             coApbTransaction.pstrb  == be;
             coApbTransaction.pwrite == 1;
         });
         finish_item(coApbTransaction);
     endtask
 endclass

2.2) Bước 2: Tạo Macro để sử dụng các đối tượng class sequence đã tạo ở bước 1
File: uMacro.svh

Mục đích: Sử dụng các đối tượng (object) tạo từ class sequence dễ dàng hơn.

Giải thích: đối tượng của class đã tạo ở bước 1 sẽ được gọi và khởi tạo trong testcase (pat/<thư mục testcase>/cVSequence.sv). Người test sẽ thao tác trên đối tượng này để gửi các transaction đến Driver và lái DUT. Dòng code để thực hiện điều này có thể dài nên nó sẽ được thay bằng một macro giúp rút gọn code của testbench.

Ví dụ, xét macro ApbWriteTX, macro này giúp tạo một transaction ghi gửi đến APB interface của uart_0. Người test có thể điền địa chỉ ghi và dữ liệu ghi mong muốn.

Ví dụ 2: ApbWriteTX(address,value)
`define ApbWriteTX(address,value) \
 `uvm_do_on_with(WriteSeq, p_sequencer.coApbMasterAgentTx.coApbMasterSequencer, { \
                WriteSeq.conEn      == 1'b0; \
                WriteSeq.addr[31:0] == address; \
                WriteSeq.data[31:0] == value; \
                WriteSeq.be[3:0]    == 4'b1111; \
                })

Trong đoạn code ví dụ trên, WriteSeq là đối tượng tạo từ class cApbMasterWriteSeq. Đối tượng này sẽ được khởi tạo trong file testcase (testbench).

2.3) Bước 3: Tạo testcase sử dụng các macro đã tạo ở bước 2
File: pat/<tên thư mục testcase>/cVSequence.sv

Mục đích: Ghi/đọc các thanh ghi của UART để điều khiển DUT hoạt động theo mục đích test.

Giải thích: Lõi IP UART-APB là một APB slave. Để kiểm tra các hoạt động của lõi IP, người test cần cấu hình lõi IP đến chế độ hoạt động mong muốn và giám sát các giá trị cấu hình hoặc bit trạng thái của lõi IP. Điều này thực hiện bằng cách ghi/đọc cá thanh ghi lõi IP thông qua APB interface sử dụng các macro đã tạo ở bước 2.

Ví dụ, để enable uart_0, người test cần ghi "1" vào bit 0 của thanh ghi SE, mô tả ở bài 1. Sau khi ghi, bit này được đọc lại để kiểm tra xem đã được cấu hình đúng hay chưa.

Ví dụ 3: Cấu hình bit enable và kiểm tra lại giá trị cấu hình
`ApbWriteTX(32'h00000004,32'h00000001)
`ApbReadTX(32'h00000004,32'h00000001,32'h00000001)

Chú ý:
  1. testcase của bạn nằm trong pat/<tên thư mục testcase>/cVSequence.sv, bạn có thể viết code tùy ý để tạo trường hợp test mong muốn mà không cần phải làm bước 1 và 2. Việc thực hiện bước 1 và 2 chỉ là 1 trong nhiều cách hỗ trợ tạo testcase.
  2. Trong môi trường này, mỗi testcase là một file cVSequence.sv được đặt trong một thư mục với tên riêng gợi nhớ chức năng test của testcase
  3. Thư mục các testcase để trong UvmEnvUartApb/pat/
Hình 2: Ba bước để tạo một testcase trong môi trường UVM
3) Làm thế nào để chạy một testcase?
Trong môi trường hiện tại, run_test() trong sim/testTop.sv sẽ thực thi class cTest để bắt đầu quá trình mô phỏng. cTest là class sẽ khởi tạo các thành phần UVM, đối tượng coEnv, và chạy testcase của bạn ở run_phase() thông qua đối tượng coVSequence.
Làm sao run_test() biết class mà nó cần thực thi là cTest chứ không phải class nào khác? cTest được truyền đến run_test() thông qua biến UVM_TESTNAME, khai báo ở lệnh chạy mô phỏng trong file run_sim.do hoặc run_qsim.pl.
Hình 3: cTest được khai báo ở lệnh chạy mô phỏng trong script và truyền đến run_test() trong testTop
Để thực thi mô phỏng với 1 testcase, các bạn thực hiện như sau:
cd /UvmEnvUartApb/sim/
Hiện tại môi trường hỗ trợ 2 script:
  1. Chạy mô phỏng trên QuestaSim GUI
    1. Mở QuestaSim GUI (chạy phần mềm Questasim)
    2. Tạo một project mới trong thư mục sim với tên testTop
    3. link hoặc copy đường dẫn file testcase của bạn, ví dụ như ../pat/trialPat/cVSequence.sv đến thư mục /uvm_comp/
    4. Chạy mô phỏng với lệnh do run_sim.do trên cửa sổ transcript của QuestaSim
    5. Chọn "No" khi cửa số báo finish xuất hiện
    6. Xem kết quả trên cửa sổ Transcript hoặc waveform
  2. Chạy mô phỏng ở chế độ batch mode trên Cygwin (có cài Perl)
    1. Sửa biến $SIM_TOOL trong file ./run_qsim.pl để chỉ đến thư mục cài đặt QuestaSim của bạn, ví dụ như C:/questasim64_10.2c/win64. Chú ý, đây là đường dẫn đến đúng thư mục chứa các tool của QuestaSim chứ không phải thư mục cài đặt ngoài cùng.
    2. Chạy mô phỏng với lệnh sau:
./run_qsim.pl <đường dẫn thư mục testcase>
Ví dụ như:
./run_qsim.pl ../pat/trialPat/cVSequence.sv
Script này sẽ copy testcase của bạn đến thư mục /uvm_comp để tổng hợp và chạy mô phỏng.

Nhóm tác giả lưu ý lại một lần nữa, môi trường hiện tại là bản draff, một số thành phần chưa hoàn chỉnh và chưa được debug đầy đủ. Tuy nhiên, việc chạy các testcase để cấu hình và sử dụng UART đã có thể thực hiện được. Vì vậy, các bạn có thể tải về để tìm hiểu.

Dữ liệu có thể dowload:

Lịch sử cập nhật:
1/ 2019.08.11 - Tạo lần đầu
2/ 2019.08.18 - Update hình 1, sửa minh họa kết nối trên scoreboard từ hình <> thành hình tròn

Danh sách tác giả:
1. Phạm Thanh Trâm
2. Đoàn Đức Hoàng (email: hoangbk154@gmail.com)
3. Trương Công Hoàng Việt
4. Nguyễn Sinh Tơn
5. Nguyễn Hùng Quân

4 bình luận:

  1. Trong hình 1 kết nối từ Monitor.analysis_port tới Scoreboard.analysis_imp thì chỗ Scoreboard phải là hình tròn chứ nhỉ?
    Phiên bản sau nếu được các bạn nên dùng Register model thay vì dùng macro cho thao tác đọc/ghi.

    Thân,
    ~D

    Trả lờiXóa
    Trả lời
    1. Cảm ơn bạn đóng góp ý kiến.

      Trong hình 1 kết nối từ Monitor.analysis_port tới Scoreboard.analysis_imp thì chỗ Scoreboard phải là hình tròn chứ nhỉ?
      -> Chính xác, bên mình đã cập nhật lại hình

      Phiên bản sau nếu được các bạn nên dùng Register model thay vì dùng macro cho thao tác đọc/ghi.
      -> Bên mình hiện có bạn đang tiếp tục nghiên cứu vấn đề này, nhóm sẽ còn tiếp tục cập nhật nhưng trong một phiên bản khác khi môi trường hiện tại hoàn thiện với đầy đủ các testcase mong muốn.

      Cũng mong các bạn đọc có kinh nghiệm như bạn tiếp tục quan tâm và đóng góp ý kiến cho nhóm tác giả.

      Xóa
  2. Hình 1 : Cấu Trúc môi trường UVM. Một số chữ nó bị mờ quá đọc không được.

    Trả lờiXóa