← Back to list

Learning UVM with me Day 2

Day 2 學習目標

CHI-AN TSAI · 2026-08-09 07:30 · 0 claps · 8.1 min read
#system-verilog #vmu #uvm-training
Open on Medium ↗
Wiki topics: EDU · Education & Learning

Learning UVM with me Day 2

Day 2 學習目標

  1. 寫出 uvm_sequence_item
  2. 寫出 uvm_sequence
  3. 理解 sequencer 到底在做什麼
  4. 寫出基本 driver
  5. 寫出基本 monitor
  6. 理解 get_next_item() / item_done()
  7. 理解 start_item() / finish_item()
  8. 看懂完整的 transaction flow
  9. 能自己畫出 UVM data flow

UVM架構示意圖

UVM架構示意圖

假設有個簡單的Adder

module adder (
    input  logic       clk,
    input  logic [7:0] a,
    input  logic [7:0] b,
    output logic [8:0] sum
);

    always_ff @(posedge clk) begin
        sum <= a + b;
    end

endmodule

首先我們要先建立一個Transaction

class my_adder_transaction extends uvm_sequence_item;

  rand bit [7:0] a;
  rand bit [7:0] b;
  bit [8:0] sum;

  constraint adder_c{
    a<255;
    b<255;
}

  `uvm_object_utils(my_adder_transaction) //註冊到 UVM Factory
  `uvm_field_int(a,UVM_DEFAULT)
  `uvm_field_int(b,UVM_DEFAULT)
  `uvm_field_int(sum,UVM_DEFAULT)
  `uvm_object_utils_end

  function new(string name = "adder_transaction");
    super.new(name);
  endfunction

endclass

1. Sequence

接著建立Sequence

class my_adder_sequence extends uvm_sequence #(my_adder_transaction); //這個 Sequence 產生的是 my_adder_transaction
  `uvm_object_utils(my_adder_sequence)

   my_adder_transaction trans;

  function new(string name = "my_adder_sequence");
    super.new(name);
  endfunction : new

  task body(); //Sequence 真正產生 stimulus 的地方
    trans = new(); //建立一個transaction
    start_item(trans); //把這個 transaction 交給 Sequencer
    assert(req.randmize());
    finish_item(trans); //transaction 準備好了,可以交給 Sequencer / Driver 了
  endtask : body

endclass : my_adder_sequence

body()是Sequence 真正產生 stimulus 的地方,Sequence 要做什麼事情,都寫在 body 裡

body()
 ↓
產生 transaction
 ↓
randomize
 ↓
送出去

整個sequence在做的事情就是

建立 transaction
       ↓
randomize
       ↓
完成 transaction
       ↓
交給 Sequencer

如果要產生多筆transaction呢? 要在body()中加入repeat()

task body(); //Sequence 真正產生 stimulus 的地方
  repeat(200) begin
    trans = new(); //建立一個transaction
    start_item(trans); //把這個 transaction 交給 Sequencer
    assert(trans.randmize());
    finish_item(trans); //transaction 準備好了,可以交給 Sequencer / Driver 了
  end
 endtask : body

2. Sequencer

接著是sequencer,前面有提到Sequencer 負責管理 Sequence 產生的 transaction,並提供給 Driver

class my_adder_sequencer extends uvm_sequencer #(my_adder_transaction);
  `uvm_component_utils(my_adder_sequencer)

  function new(string name = "my_adder_sequencer", uvm_component.parent = null);
  super.new(name, parent);
  endfunction

endclass

3. Driver

從 Sequencer 拿 transaction,然後轉成pin-level輸入到 DUT

class my_adder_driver extends uvm_driver #(my_adder_transaction);
  virtual my_adder_if vif;
  uvm_analysis_port#(my_adder_transaction) drv_port
  `uvm_component_utils(my_adder_driver)


  function new(string name = "my_adder_driver", uvm_component parent);
    super.new(name, parent);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual my_adder_if)::get(this, "","my_adder_if", vif)) begin
      `uvm_fatal("TB", $sformatf("cannot get adder interface from %m"))
    end
    drv_port = new("drv_port", this);
  end function  

  extern task driver_start(my_adder_transaction trans)

  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(trans); //Driver 向 Sequencer 要一個 transaction
      repeat(1)@(posedge vif.clk);
      vif.a <= req.a;
      vif.b <= req.b;
      drv_port.write(my_adder_transaction)
      seq_item_port.item.done(); //Driver 告訴 Sequencer:這筆 transaction 我處理完了
    end
  endtsak
endclass

4. monitor

Monitor 做的事情跟 Driver 相反,driver送transaction進DUT,而monitor從DUT接收transaction

class my_adder_monitor extends uvm_monitor#(my_adder_transaction);
  virtual my_adder_if vif;
  uvm_analysis_port#(my_adder_transaction) ap

  `uvm_component_utils(my_adder_monitor)


  function new(string name = "my_adder_monitor", uvm_component parent);
     super.new(name, parent);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual my_adder_if)::get(this, "","my_adder_if", vif)) begin
      `uvm_fatal("TB", $sformatf("cannot get adder interface from %m"))
    end
    ap = new("ap", this);
  end function

  extern task main_phase(uvm_phase phase);
endclass

 task my_adder_monitor::main_phase(uvm_phase phase);
    my_adder_transaction tr;
    forever begin
    @(posedge vif.clk);
    tr = new("tr");

    tr.a = vif.a;
    tr.b = vif.b;
    tr.sum = vif.sum;
     `uvm_info("MONITOR","Monitor received transaction", UVM_LOW)

    ap.write(tr);
    end
  endtask

總結:

                     Sequence
                        │
                        │ create
                        ↓
                Sequence Item
                        │
                        │
                        ↓
                   Sequencer
                        │
                        │ get_next_item()
                        ↓
                     Driver
                        │
                        │ drive
                        ↓
                       DUT
                        │
                        │ observe
                        ↓
                     Monitor
                        │
                        │ write()
                        ↓
                   Transaction

메타데이터
post_id
7fd79fb665b0
slug
learning-uvm-with-me-day-2-7fd79fb665b0
url
https://medium.com/@chiantsai1997/learning-uvm-with-me-day-2-7fd79fb665b0
canonical_url
https://medium.com/@chiantsai1997/learning-uvm-with-me-day-2-7fd79fb665b0
author_url
https://medium.com/@chiantsai1997
status
ok
fetched_at
2026-08-22 01:25:53