Tuesday, October 1, 2019

[UVM] Hướng dẫn cách build một môi trường UVM đơn giản nhất cho việc random test

Tác giả: TrongTran
Ngày: 01/10/2019

Khi nhắc đến việc build một môi trường UVM hầu hết trong chúng ta đều suy nghĩ rằng nó phải thật sự phức tạp và dĩ nhiên thực tế đúng là như vậy (+_+). Mục đích của việc sử dụng UVM là để veify cho những design phức tạp có nhiều case mà trong đó người verify không thể cover toàn bộ các case bằng các phương pháp thông thường được. Mình lấy ví dụ nếu một design đơn giản thì chúng ta hoàn toàn có thể viết testbench đơn giản cover hết tất cả các case và verify bằng phần mềm VCS.

Khi muốn verify cho một design phức tạp (Nhiều input, output) ta cần random test nghĩa là các tín hiệu input được tạo ngẫu nhiên kèm theo các constraint tương ứng với design.
Chẳng hạn ta cần verify cho 1 bộ cộng 4 bit trong đó hai giá trị đầu vào trong khoảng từ [0:8].
Vì vậy khi tạo giá trị input để dưa vào design ta cần đặt constraint là cho input >= 0  và input <= 8 .

Mình đã giới thiệu các thành phần đầy đủ của một môi trường UVM trong các bài trước đây các bạn có thể tham khảo tại trang (Môi trường UVM cho bộ cộng 4 bit):
http://trongtranrvc.blogspot.com/2019/05/design-thiet-ke-bo-cong-4-bit-va-moi.html

Hôm nay mình sẽ hướng dẫn các bạn xây dựng 1 môi trường UVM đơn giản nhất cho một bộ cộng 4 bit. Hay nói đúng hơn là chỉ dùng các thành phần mà thiếu nó môi trường UVM không thể hoạt động được.
Đó là:


  • Khối interface
  • Khối module TOP
  • Khối UVM_env
  • DUT


Trong đó:

DUT ở đây là design cần verify. DUT nghĩa là design under test.
Khối interface có chức năng khai báo các giao tiếp giữa DUT và khối UVM_env.

Trước tiên mình có một design đơn giản (Bộ cộng 8 bit) như bên dưới:

module ADD_8BIT (clk, rst_n, a0, b0, result0);
  input            clk;
  input rst_n;
  input [7:0]      a0;
  input [7:0]      b0;
  output [8:0] result0;
  reg [8:0] result0;

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        result0 <= 9’b0_0000_0000;
      else
        result0 <= a0 + b0;
    end

endmodule: ADD_8BIT

Như vậy ở đây ta sẽ liệt kệ tất cả các input/output của DUT để xây dựng khối interface nhé.

Input bao gồm:


  • clk
  • rst_n
  • a0
  • c0


Output bao gồm 

  • result0


Để thành lập khối inteface ở đây ta có 2 cách:

Cách 1: Bạn define theo kiểu “logic” cho tất cả các port mà không cần định nghĩa input/output cho interface:

interface add_if;
  logic clk;
  logic rst_n;
  logic [7:0] a0;
  logic [7:0] b0;
  logic [8:0] result0;
endinterface: add_if

Với kiểu khai báo này DUT sẽ được connect với interface trong top module. Điều này khá là rườm rà. 
Các bạn thao khảo cách connect trong bài sau:

http://trongtranrvc.blogspot.com/2019/05/design-thiet-ke-bo-cong-4-bit-va-moi.html
Cách 2: Chúng ta khai báo input và output cho interface và tiến hành connect interface với các port của DUT ngay trong file interface bằng dòng lệnh bind.

interface add_if(
  input bit clk,
  input bit rst_n,
  input [7:0] a,
  input [7:0] b,
  input [8:0] result
);

  clocking cb @(posedge clk or negedge rst_n);
    output    a;
    output    b;
    input     result;
  endclocking // cb

endinterface: add_if

Cách khai báo khối clocking khá là hay. Nó sẽ thuận tiện khi bạn có từ 2 DUT cần connect trở lên. Mỗi bộ clocking sẽ tương ứng với một bộ connect giữa interface và DUT.

Với cách khai báo này chúng ta dùng câu lệnh bind để connect DUT và interface như sau:


bind ADD_8BIT add_if add_if0(
  .clk(clk),
.rst_n(rst_n),
  .a(a0),
  .b(b0),
  .result(result0)
);

Bây giờ ta bắt đầu coding cho top module :

//-----------
// module top
//-----------
module top;

  bit clk;
  bit rst_n;
  env environment;
  ADD_8BIT dut(.clk (clk), .rst_n(rst_n));

  initial begin
    environment = new("env");
    // Put the interface into the resource database.
    uvm_resource_db#(virtual add_if)::set("env",
      "add_if", dut.add_if0);
    clk = 0;
    rst_n = 1;
    run_test();
  end
  
  initial begin
    forever begin
      #(50) clk = ~clk;
    end
  end
  
  initial begin
    // Dump waves
    $dumpvars(0, top);
  end
  
endmodule

Viết cho khối UVM_env như sau:

import uvm_pkg::*;
`include "uvm_macros.svh"

//----------------
// environment env
//----------------
class env extends uvm_env;

  virtual add_sub_if m_if;

  function new(string name, uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void connect_phase(uvm_phase phase);
    `uvm_info("LABEL", "Started connect phase.", UVM_HIGH);
    // Get the interface from the resource database.
    assert(uvm_resource_db#(virtual add_sub_if)::read_by_name(
      get_full_name(), "add_sub_if", m_if));
    `uvm_info("LABEL", "Finished connect phase.", UVM_HIGH);
  endfunction: connect_phase

  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    `uvm_info("LABEL", "Started run phase.", UVM_HIGH);
    Begin
repeat (20) begig
      void'(std::randomize(a));
      void'(std::randomize(b));
      @(m_if.cb);
      m_if.cb.a <= a;
      m_if.cb.b <= b;
      m_if.cb.doAdd <= 1'b1;
      repeat(2) @(m_if.cb);
      `uvm_info("RESULT", $sformatf("%0d + %0d = %0d",
        a, b, m_if.cb.result), UVM_LOW);
    end
end
    `uvm_info("LABEL", "Finished run phase.", UVM_HIGH);
    phase.drop_objection(this);
  endtask: run_phase
  
endclass

Như vậy ta đã có 1 môi trường hoàn chỉnh.
Trong trường hợp trên mình random tín hiệu input ngõ vào bằng hai dòng:

      void'(std::randomize(a));
      void'(std::randomize(b));


Cùng chạy và kiểm tra waveform nào:


Log file:

[2019-10-01 08:25:18 EDT] vlib work && vlog '-timescale' '1ns/1ns' '-sv2k9' +incdir+$UVM_HOME/src -l uvm_1_2 -err VCP2947 W9 -err VCP2974 W9 -err VCP3003 W9 -err VCP5417 W9 -err VCP6120 W9 -err VCP7862 W9 -err VCP9201 W9 -err VCP2129 W9 design.sv testbench.sv && vsim -c -do "vsim +UVM_VERBOSITY=UVM_HIGH +access+r; run -all; exit"
VSIMSA: Configuration file changed: `/home/runner/library.cfg'
ALIB: Library `work' attached.
work = /home/runner/work/work.lib
MESSAGE "Pass 1. Scanning modules hierarchy."
MESSAGE_SP VCP2124 "Package uvm_pkg found in library uvm_1_2."
MESSAGE_SP VCP2124 "Package std found in library uvm_1_2."
WARNING VCP2127 "Unable to map port type: unknown for port: uvm_dpi_regcomp in library module: uvm_pkg. Connection rules will not be checked for such port." 938 4
MESSAGE "Pass 2. Processing instantiations."
WARNING VCP2597 "Some unconnected ports remain at instance: dut. Module ADD_SUB has unconnected port(s) : a0, b0, doAdd0, result0." "testbench.sv" 87 1
MESSAGE "Pass 3. Processing behavioral statements."
WARNING VCP5228 "Input port a<wire> is used as lvalue." "design.sv" 33 16
WARNING VCP5228 "Input port b<wire> is used as lvalue." "design.sv" 34 16
WARNING VCP5228 "Input port doAdd<wire> is used as lvalue." "design.sv" 35 20
MESSAGE "Running Optimizer."
MESSAGE "ELB/DAG code generating."
MESSAGE "Unit top modules: top."
MESSAGE "$root top modules: top."
SUCCESS "Compile success 0 Errors 5 Warnings Analysis time: 6[s]."
ALOG: Warning: The source is compiled without the -dbg switch. Line breakpoints and assertion debug will not be available.
done
# Aldec, Inc. Riviera-PRO version 2014.06.88.5387 built for Linux64 on June 25, 2014.
# HDL, SystemC, and Assertions simulator, debugger, and design environment.
# (c) 1999-2014 Aldec, Inc. All rights reserved.
vsim +UVM_VERBOSITY=UVM_HIGH +access+r;
# ELBREAD: Elaboration process.
# ELBREAD: Warning: Package 'uvm_pkg' does not have a `timescale directive, but previous modules do.
# ELBREAD: Warning: Package 'std' does not have a `timescale directive, but previous modules do.
# ELBREAD: Elaboration time 0.6 [s].
# KERNEL: Main thread initiated.
# KERNEL: Kernel process initialization phase.
# KERNEL: Time resolution set to 1ns.
# ELAB2: Elaboration final pass...
# KERNEL: PLI/VHPI kernel's engine initialization done.
# PLI: Loading library '/usr/share/riviera-pro-2014.06-x86_64/bin/libsystf.so'
# KERNEL: Info: Loading library: /usr/share/riviera-pro-2014.06-x86_64/bin/uvm_1_2_dpi
# ELAB2: Create instances ...
# ELAB2: Create instances complete.
# SLP: Started
# SLP: Elaboration phase ...
# SLP: Elaboration phase ... done : 0.1 [s]
# SLP: Generation phase ...
# SLP: Generation phase ... done : 0.1 [s]
# SLP: Finished : 0.1 [s]
# SLP: 0 primitives and 6 (85.71%) other processes in SLP
# SLP: 5 (0.01%) signals in SLP and 18 (0.04%) interface signals
# ELAB2: Elaboration final pass complete - time: 2.8 [s].
# KERNEL: SLP loading done - time: 0.0 [s].
# KERNEL: Warning: You are using the Riviera-PRO EDU Edition. The performance of simulation is reduced.
# KERNEL: Warning: Contact Aldec for available upgrade options - sales@aldec.com.
# KERNEL: SLP simulation initialization done - time: 0.0 [s].
# KERNEL: Kernel process initialization done.
# Allocation: Simulator allocated 76112 kB (elbread=38271 elab2=32450 kernel=5390 sdf=0)
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES]
# KERNEL: ----------------------------------------------------------------
# KERNEL: UVM-1.2
# KERNEL: (C) 2007-2014 Mentor Graphics Corporation
# KERNEL: (C) 2007-2014 Cadence Design Systems, Inc.
# KERNEL: (C) 2006-2014 Synopsys, Inc.
# KERNEL: (C) 2011-2013 Cypress Semiconductor Corp.
# KERNEL: (C) 2013-2014 NVIDIA Corporation
# KERNEL: ----------------------------------------------------------------
# KERNEL:
# KERNEL: *********** IMPORTANT RELEASE NOTES ************
# KERNEL:
# KERNEL: You are using a version of the UVM library that has been compiled
# KERNEL: with `UVM_NO_DEPRECATED undefined.
# KERNEL: See http://www.eda.org/svdb/view.php?id=3313 for more details.
# KERNEL:
# KERNEL: You are using a version of the UVM library that has been compiled
# KERNEL: with `UVM_OBJECT_DO_NOT_NEED_CONSTRUCTOR undefined.
# KERNEL: See http://www.eda.org/svdb/view.php?id=3770 for more details.
# KERNEL:
# KERNEL: (Specify +UVM_NO_RELNOTES to turn off this notice)
# KERNEL:
# KERNEL: ASDB file was created in location /home/runner/dataset.asdb
run -all;
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test ...
# KERNEL: UVM_INFO /home/runner/testbench.sv(17) @ 0: env [LABEL] Started connect phase.
# KERNEL: UVM_INFO /home/runner/testbench.sv(21) @ 0: env [LABEL] Finished connect phase.
# KERNEL: UVM_INFO /home/runner/testbench.sv(49) @ 0: env [LABEL] Started run phase.
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 250: env [RESULT] 66276666 + -1326508646 = 212
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 550: env [RESULT] -248143436 + -1497448405 = 223
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 850: env [RESULT] 1504213226 + 301701818 = 420
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 1150: env [RESULT] 645552047 + 2058643198 = 429
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 1450: env [RESULT] -1610288379 + 840040105 = 174
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 1750: env [RESULT] 1495009730 + -1075557204 = 366
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 2050: env [RESULT] -645848645 + 1738358224 = 395
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 2350: env [RESULT] -960761302 + 418346283 = 85
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 2650: env [RESULT] 962707743 + -1021380542 = 97
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 2950: env [RESULT] 1589473345 + -119742515 = 270
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 3250: env [RESULT] -1588071125 + 1882633593 = 164
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 3550: env [RESULT] 455185444 + -1422306274 = 66
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 3850: env [RESULT] 1417803783 + 674049860 = 75
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 4150: env [RESULT] -1675804522 + 1806084079 = 389
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 4450: env [RESULT] -1429084022 + -873146368 = 138
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 4750: env [RESULT] 47515427 + 2026951346 = 213
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 5050: env [RESULT] -63668992 + 217059333 = 5
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 5350: env [RESULT] -1061674360 + -1487904248 = 144
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 5650: env [RESULT] 1881775662 + 1273020003 = 145
# KERNEL: UVM_INFO /home/runner/testbench.sv(71) @ 5950: env [RESULT] -1532611759 + 23536536 = 233
# KERNEL: UVM_INFO /home/runner/testbench.sv(74) @ 5950: env [LABEL] Finished run phase.
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 5950: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_report_server.svh(862) @ 5950: reporter [UVM/REPORT/SERVER]
# KERNEL: --- UVM Report Summary ---
# KERNEL:
# KERNEL: ** Report counts by severity
# KERNEL: UVM_INFO : 27
# KERNEL: UVM_WARNING : 0
# KERNEL: UVM_ERROR : 0
# KERNEL: UVM_FATAL : 0
# KERNEL: ** Report counts by id
# KERNEL: [LABEL] 4
# KERNEL: [RESULT] 20
# KERNEL: [RNTST] 1
# KERNEL: [TEST_DONE] 1
# KERNEL: [UVM/RELNOTES] 1
# KERNEL:
# RUNTIME: Info: RUNTIME_0068 uvm_root.svh (521): $finish called.
# KERNEL: Time: 5950 ns, Iteration: 57, Instance: /top, Process: @INITIAL#89_0@.
# KERNEL: stopped at time: 5950 ns
# VSIM: Simulation has finished. There are no more test vectors to simulate.
exit
# VSIM: Simulation has finished.


No comments:

Post a Comment

Cách tính BW và latency trong 1 hệ thống SoC sử dụng chuẩn giao tiếp AXI protocol

Tác giả:  TrongTran Ngày:  31/12/2019 Nếu bạn nào đang làm về verification cho system performance (ST) thì bài này sẽ bổ ích cho bạn. Ngày ...