[UVM] Sequence에 대하여(개념, 작성법)

장원·2024년 12월 30일
1

UVM

목록 보기
5/6
post-thumbnail

오늘은 UVM 환경에서 stimulus generator인 Sequence에 대해 알아보겠습니다.😁

Sequence란 무엇인가요?


(항상 감사합니다. 사진, 코드 출처: chipverify, https://wikidocs.net/170344)

  • UVM Sequence는 여러 data item으로 구성되며, 이러한 item들을 다양한 순서, 방식으로 결합하여 원하는 검증 시나리오를 만들어둔 stimulus generator입니다.
  • 간단하게 말하자면, 검증하고 싶은 item들을 원하는 작업하고 원하는 순서대로 모아둔 것입니다.
  • Sequence는 할당된 sequencer에 의해 실행되며, Sequencer는 데이터 항목들을 driver로 전달합니다.
  • 따라서 시퀀스는 검증 계획의 stimulus을 구성합니다.
  • 아래의 하이라키를 따릅니다.


Syntax

 virtual class uvm_sequence #( type REQ = uvm_sequence_item,
                               type RSP = REQ ) extends uvm_sequence_base

uvm_sequenceuvm_sequence_base를 상속받고, 파라미터를 2개 받지만 sequence에서 RSP와 REQ는 같습니다. REQ type은 driver로 request 하는 transaction 이며, RSP type은 driver로 부터 response 받는 transaction 입니다. 두 파라미터 argument가 같으니 아래처럼 하나만 적어줍니다.

class base_sequence extends uvm_sequence #(packet);

Sequence 작성법

Sequence를 만드는 방법을 소개하겠습니다.

1. uvm_sequence를 상속받아 작성하며, 생성되는 transaction(item)의 type를 parameter로 주고 생성하기

class base_sequence extends uvm_sequence #(packet);
  • UVM 내장 라이브러리인 uvm_sequence를 상속(extend)받습니다.
  • 선언해둔 Item, 즉 전달할 data packet을 파라미터로 설정하여 packet 타입의 transaction 객체가 됩니다.

2. `uvm_object_utils 매크로로 factory에 등록하고 new함수 정의하기

// base_sequence is user-given name for this class that has been derived from "uvm_sequence"
class base_sequence extends uvm_sequence;

	// [Recommended] Makes this sequence reusable. Note that we are calling
	// `uvm_object_utils instead of `uvm_component_utils because sequence is a uvm_transaction object
	`uvm_object_utils (base_sequence)

	// This is standard code for all components
    function new (string name = "my_sequence");
    	super.new (name);
    endfunction
endclass
  • uvm_object_utils 부분은 base_sequence라는 class를 factory에 등록하는 매크로입니다. 등록만 했으니 나중에 이 base_sequence를 사용할 땐 이 객체를 사용하는 component에서 create해줘야 사용할 수 있습니다.

    factory란?
    Factory는 객체를 생성하고 관리하는 데 사용되는 UVM의 핵심 메커니즘입니다. Factory는 검증 환경에서 객체를 동적으로 생성하고 override할 수 있는 유연성을 제공합니다.
    uvm_component_utils 또는 uvm_object_utils 매크로를 사용하여 factory에 등록하며, type_id::create객체를 생성합니다.

  • function new부분은 모든 component가 반드시 적어야할 생성자 함수 정의하는 부분입니다.


3. ⭐body 작성하기(실제로 sequencer를 통해 driver에 값을 보내는 부분)

  • Sequence내에서 실행되는 시나리오 code는 body()라는 이름을 가진 task 내에 작성합니다.
  • uvm_do 계열의 매크로나 start_item() 과 같은 메소드를 사용합니다.
  • body()앞 뒤로 해주고 싶은 작업이 았다면 pre_body() task와 post_body() task를 사용하기도 합니다.

Example

class item extends uvm_sequence;
  `uvm_object_utils(item)
  function new(string name="item");
    super.new(name);
  endfunction

  virtual task body();
	repeat(10)begin
		`uvm_do(item);
		//`uvm_do_with, `uvm_do_on 등 여러가지 매크로가 존재
		//optinal: get_response(rsp);
	end
  endtask
endclass

item에 있는 변수를 randomize하고 driver로 보내는 과정을 10번 반복하는 시나리오를 body에 적어주었습니다. 여기서는 uvm_do 매크로를 사용했습니다.

uvm_do 계열 매크로란?
Item(=transaction)을 생성하고, randomize 하여 sequecer를 통해 driver로 전달하는 매크로 입니다.
uvm_do_with: randomize 하는 과정에서 constraint를 적용합니다.
uvm_do_on: 사용자 정의 sequencer를 사용할 때 사용합니다. uvm_do는 uvm 내장 sequencer를 사용할 때 사용합니다.
uvm_do_on_with도 있습니다.

uvm_do 매크로와 내부 메소드는 아래처럼 정의되어 있습니다.

`define uvm_do(req)
	`uvm_create(req);	//item생성
    start_item(req);
    req.randomize();	//변수를 randomize하여 검증하기 위해 item값들을 randomize합니다.
    finish_item(req);
    
task start_item(req);
	sqr.wait_for_grant();	//sqr은 sequencer입니다.
    sqr.begin_tr(req);
endtask

task finish_item(req);
	sqr.send_request(req);
    sqr.wait_for_item_done();
    sqr.end_tr(req);
endtask

uvm_do macro가 item을 sequencer를 통해 driver로 전달해준다고 했습니다. 어떤 원리로 전달하는 것일까요? Item은 sequencer와 driver사이의 handshaking 방식을 통해 전달됩니다.

Handshaking 과정을 나타낸 그림입니다. 그림 기준으로 위에서부터 차근차근 실행됩니다.
create_item: Item을 create합니다.
wait_for_grant: 시퀀서로 부터 item(transaction) 전달에 대한 permission을 기다립니다. 시퀀서는 해당 permission을 driver의 get_next_item를 통해 받죠.
get_next_item: Driver가 시퀀서로 permission을 줍니다. wait_for_grant와 handshake하는 것이죠. 이제 전달이 시작되며 Time stamp도 찍힙니다.
randomize: Item을 randomize합니다.
send_request: Driver가 가져갈 item을 보내줍니다.
driver logic: 메소드는 아니고 driver가 DUT에 item을 보내는 실행단계를 뜻합니다.
item_done: Driver가 Item을 DUT로 send하는 작업이 다 끝났음을 알립니다.
wait_for_item_done: Driver가 item_done을 호출할 때까지 차단됩니다(기다립니다). item_done과 handshake를 하는 것이죠.


body task에 uvm_do() 를 사용할 수도 있지만, uvm_do 내에서 수행하는 각 단계를 user가 커스터마이징하여 작성해도 됩니다.

Example

   virtual task pre_body ();
      `uvm_info ("BASE_SEQ", $sformatf ("Optional code can be placed here in pre_body()"), UVM_MEDIUM)
   endtask

   virtual task body ();
     `uvm_info ("BASE_SEQ", $sformatf ("Starting body of %s", this.get_name()), UVM_MEDIUM)
      data_obj = my_data::type_id::create ("data_obj");   	//item 생성
      
      repeat (n) begin									//n번 반복
         start_item (data_obj);							//start 메소드 사용
         data_obj.randomize ();							//randomize
         data_obj.print();								//print 메소드 사용, object class의 라이브러리 함수
         finish_item (data_obj);						//finish
      end
      `uvm_info (get_type_name (), $sformatf ("Sequence %s is over", this.get_name()), UVM_MEDIUM)
   endtask

   virtual task post_body ();
      `uvm_info ("BASE_SEQ", $sformatf ("Optional code can be placed here in post_body()"), UVM_MEDIUM)
   endtask
  • uvm_do매크로를 사용하지 않고 하나하나 풀어서 써주었습니다. startfinish 메소드를 통해 driver와 소통하므로 두 메소드 사이에 원하는 작업을 작성해줍니다. 위에선 randomize하고 UVM 객체의 상태와 내부 정보를 출력하는 print 메소드를 작성했습니다.
  • pre_bodypost_body task는 optional하며 특정 문구를 출력하는 uvm_info 매크로를 작성했습니다.
  • 자유도가 높고 디버그하기 유용하기 때문에 이렇게 풀어서 사용하는 것을 추천합니다.
  • 이렇게 작성한 sequence는 test component에서 create하여 사용하게 됩니다.

제가 UVM 검증에서 가장 중요하게 생각하는 component 중 하나인 Sequence에 대해 간단하게 알아보았습니다. 깊게 들어가면 더 많은 내용이 있는데 그 부분들은 간단하게 다 훑고나서 작성해보도록 하곘습니다. 감사합니다!😍

profile
반도체, HW ,SW 탐구생활

0개의 댓글