오늘은 UVM 환경에서 stimulus generator인 Sequence에 대해 알아보겠습니다.😁
(항상 감사합니다. 사진, 코드 출처: chipverify, https://wikidocs.net/170344)
data item
으로 구성되며, 이러한 item들을 다양한 순서, 방식으로 결합하여 원하는 검증 시나리오를 만들어둔 stimulus generator입니다.
Syntax
virtual class uvm_sequence #( type REQ = uvm_sequence_item,
type RSP = REQ ) extends uvm_sequence_base
uvm_sequence
는 uvm_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를 만드는 방법을 소개하겠습니다.
uvm_sequence
를 상속받아 작성하며, 생성되는 transaction(item)의 type를 parameter로 주고 생성하기class base_sequence extends uvm_sequence #(packet);
uvm_sequence
를 상속(extend)받습니다.// 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가 반드시 적어야할 생성자 함수 정의하는 부분입니다.
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
매크로를 사용하지 않고 하나하나 풀어서 써주었습니다. start
와 finish
메소드를 통해 driver와 소통하므로 두 메소드 사이에 원하는 작업을 작성해줍니다. 위에선 randomize하고 UVM 객체의 상태와 내부 정보를 출력하는 print 메소드를 작성했습니다.pre_body
와 post_body
task는 optional하며 특정 문구를 출력하는 uvm_info
매크로를 작성했습니다.제가 UVM 검증에서 가장 중요하게 생각하는 component 중 하나인 Sequence에 대해 간단하게 알아보았습니다. 깊게 들어가면 더 많은 내용이 있는데 그 부분들은 간단하게 다 훑고나서 작성해보도록 하곘습니다. 감사합니다!😍