UVM: Driver Sequencer Handshake Mechanism - IKSciting
1895
post-template-default,single,single-post,postid-1895,single-format-standard,bridge-core-2.8.7,qodef-qi--no-touch,qi-addons-for-elementor-1.7.1,qode-page-transition-enabled,ajax_fade,page_not_loaded,,qode-title-hidden,qode_grid_1300,footer_responsive_adv,qode-content-sidebar-responsive,qode-theme-ver-27.1,qode-theme-bridge,qode_header_in_grid,wpb-js-composer js-comp-ver-6.6.0,vc_responsive,elementor-default,elementor-kit-838

Driver Sequencer Handshake Mechanism

일반적인 UVM agent는 하나의 driver, 하나의 sequencer 그리고 하나의 monitor를 가진다. 물론 active agent가 아닌 passive agent의 경우 driver, sequencer 없이 monitor로만 구성되기도 한다. Active agent 내에서 test sequence가 DUT(Design-under-Test)에 signal-level로 전달되는 과정과 driver와 sequencer가 communication 하는 handshake mechanism에 대하여 설명한다.

UVM driver sequencer handshake mechanism

위 그림에서 각 component의 역할을 간단히 기술하면 다음과 같다.

  • Sequence – 검증하고자 하는 scenario/plan/intent를 transaction-level로 기술하는 역할
  • Sequencer – sequence를 driver로 전달하고, driver로부터 response를 받는 역할
  • Driver – transaction을 signal-level로 변환하여 virtual interface를 통해 DUT로 전달하는 역할

Sequence의 가장 핵심이 되는 body 부분의 code부터 하나씩 확인해보자. Sequence에서 transaction, 즉, sequence item을 driver로 보내기 위해 start_item이라는 method를 사용한다. 필요에 따라 randomization을 거친 후, 최종적으로 finish_item을 통해 driver와의 communication을 끝낸다. 이러한 단계는 `uvm_do 또는 `uvm_create`uvm_send와 같은 매크로를 사용하여 단순화 가능하지만, 이러한 방식을 권장하지는 않는다. 자세한 내용은 Sending a Transaction 페이지를 참고하자.

class my_sequence extends uvm_sequence#(my_transaction);

  // ...

  virtual task body();
    req = my_transaction::type_id::create("req");
    start_item(req);
    if (!req.randomize()) begin
      `uvm_fatal(get_type_name(), "randomization fail");
    end
    finish_item(req);
  endtask: body
endclass: my_sequence

보통의 경우 sequencer는 매우 간단한 형태를 갖는다. Sequencer에서 특별한 작업을 해야 하는 경우가 아니라면 아래와 같이 한 줄로 표현 가능하다.

typedef uvm_sequencer#(my_transaction) my_sequencer;

Driver는 sequencer가 보내주는 transaction이 있는지 기다리기 위한 get_next_item과 signal-level의 DUT driving 그리고 DUT driving이 끝났음을 알리는 item_done을 계속해서 반복한다.

class my_driver extends uvm_driver#(my_transaction);

  // ...

  virtual task run_phase(uvm_phase phase);
    // ...
    
    forever begin
      seq_item_port.get_next_item(req);
      drive();
      seq_item_port.item_done();
    end
  endtask: run_phase

  virtual task drive();
    @(negedge vif.clk) vif.in <= req.data;
  endtask: drive

endclass: my_driver

또한 Starting a Sequence 페이지에 설명된 것과 같이 sequencer에 원하는 sequence를 start 시키고, 실제로 sequencer와 driver를 연결하는 작업까지 완료되어야 한다. Driver와 sequencer의 연결은 미리 정의된 seq_item_port와 seq_item_export를 이용하면 된다.

class my_test extends uvm_test;

  // ...

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);
    seq.start(env.agent.sequencer);
    phase.drop_objection(this);
  endtask: run_phase

endclass: my_test
class my_agent extends uvm_agent;

  // ...

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    driver.seq_item_port.connect(sequencer.seq_item_export);
  endfunction: connect_phase

endclass: my_agent

다음은 실제로 sequence에서 하나의 transaction을 실행할 때 sequence와 driver에서 code가 실행되는 순서를 출력한 것이다.

UVM_INFO my_sequence.sv(30) @ 0:     uvm_test_top.env.agent.sequencer@@seq [my_sequence] start_item
UVM_INFO my_driver.sv(43)   @ 20000: uvm_test_top.env.agent.driver         [my_driver  ] get_next_item
UVM_INFO my_sequence.sv(33) @ 20000: uvm_test_top.env.agent.sequencer@@seq [my_sequence] randomize
UVM_INFO my_sequence.sv(36) @ 20000: uvm_test_top.env.agent.sequencer@@seq [my_sequence] finish_item
UVM_INFO my_driver.sv(46)   @ 20000: uvm_test_top.env.agent.driver         [my_driver  ] drive
UVM_INFO my_driver.sv(49)   @ 25000: uvm_test_top.env.agent.driver         [my_driver  ] item_done

Driver-sequencer communication에서 문제가 있는 경우 simulation hang으로 이어지는 경우도 있는데, 위와 같이 단순한 경우는 이해하기 어렵지 않지만, 여러 개의 sequence에서 여러 개의 transaction이 동시에 수행되는 경우, 동작에 대한 정확한 이해 및 관련 동작의 debugging이 다소 어려울 수도 있다. 위에서 살펴보았던 그림을 다시 한 번 살펴보자.

UVM driver sequencer handshake mechanism

start_itemget_next_itemfinish_item의 경우 점선으로 표시된 박스는 해당 구문에서 출발한 화살표가 자신에게 돌아오기 전까지 blocking 되는 구문임을 표시하기 위하여 크게 표시하였으므로 이에 유의하자. Handshake 동작을 제대로 이해하기 위해서는 blocking 개념에 대해서도 이해하고 있어야 하므로 위 그림의 start_item을 기준으로 blocking 개념에 대해 간단히 설명한다. Sequence에는 start_itemrandomize, finish_item이 차례대로 기술되어 있지만, start_item이 시작된 후 바로 randomize가 수행되는 것이 아니라, driver에서 get_next_item이 수행된 이후에서야 비로소 randomize가 수행된다. 이를 blocking 된다고 표현하며 get_next_itemitem_done에도 동일하게 적용된다.

  • start_item @ sequence – blocking
  • get_next_item @ driver – blocking
  • randomize @ sequence
  • finish_item @ sequence – blocking
  • drive DUT @ driver
  • item_done @ driver

마지막으로 driver에서 사용하는 try_next_item에 대해서 간단히 설명한다. 종종 get_next_item을 대신하여 사용되는데, get_next_item과 달리 non-blocking 구문이라는 차이가 있다. 따라서 다음과 같이 try_next_item의 결과가 null인지 아닌지 check 할 필요가 있다. try_next_item의 결과가 null이 아닐 경우에만 DUT를 drive 하고, null일 경우 idle clock을 추가하는 등의 처리를 해줌으로써 simulation hang에 대비해야 한다. 일반적으로 get_next_item을 더 많이 사용하지만, reusability 측면에서 try_next_item의 사용을 권장하기도 한다.

class my_driver extends uvm_driver#(my_transaction);

  // ...

  virtual task run_phase(uvm_phase phase);
    // ...
    
    forever begin
      seq_item_port.try_next_item(req);
      if (req == null) begin
        // insert idle clock
        idle();
      end else begin
        // drive DUV
        drive();
      end
      seq_item_port.item_done();
    end
  endtask: run_phase

  virtual task idle();
    @(negedge vif.clk) vif.in <= 0;
  endtask: drive

  virtual task drive();
    @(negedge vif.clk) vif.in <= req.data;
  endtask: drive

endclass: my_driver

사실 UVM에서의 driver와 sequencer 간의 communication은 위 설명보다 더 복잡하기 때문에 이해를 쉽게 하기 위해 개념 위주로만 간략히 설명하였음을 밝힌다.

References

4 Comments
  • 초보개발자
    Posted at 14:17h, 16 September Reply

    잘 정리된 포스트 정말 감사합니다. 잘 보고있습니다.

    • IKS
      Posted at 14:17h, 16 September Reply

      감사합니다.

  • 초보개발자2
    Posted at 10:46h, 17 July Reply

    여러 개의 sequence에서 여러 개의 transaction이 동시에 수행되는 경우, 라고 말씀하신 부분이 궁금해서요.
    예를 들어 sequence 가 A,B,C,가 있고, start_item() 으로 sequence 를 실행시키면 A,B,C 순서로 시킬 수 밖에 없지 않나요?
    드라이버가 각각 3개씩 있는 것이 아니라면, 하나의 Driver 입장에서는 A B C 순으로 들어올 것 같은데요.
    어떻게 여러 개의 Transaction이 동시에 수행이 될 수 있는지 궁금합니다.

    • IKS
      Posted at 19:01h, 18 August Reply

      UVM sequence arbitration에 대해서 한 번 알아보시면 될 것 같아요.
      여러 개의 sequence가 하나의 driver를 access 할 수 있고, 그 순서는 설정된 arbitration mode에 따라 다르게 동작합니다.
      FIFO, weighted, random, strict, user 등 다양한 arbitration mode가 존재하구요.
      아래 링크 한 번 참고해 보세요.
      https://learnuvmverification.com/index.php/2016/07/19/uvm-sequence-arbitration/

Post A Comment