사진, 코드 출처: chipverify
Sequence
와 Driver
간의 데이터 트랜잭션 전달을 제어하는 중요한 컴포넌트입니다. uvm_sequencer
라는 UVM 라이브러리 클래스를 상속받아 사용되는 것이 권장되며, Sequencer
에는 2가지 타입이 존재합니다.
// class uvm_sequencer #(type REQ = uvm_sequence_item, RSP = REQ) extends uvm_sequencer_param_base #(REQ, RSP);
uvm_sequencer #(my_data, data_rsp) seqr0; // with RSP
uvm_sequencer #(my_data) seqr0; // without RSP
my_data
: Sequence가 생성하는 request transaction 타입(item).
data_rsp
: Driver가 반환하는 response transaction 타입.
위에는 response가 있는 type, 아래는 response가 없는 type인데, 보통 아래 type의 sequencer을 주로 사용합니다. Driver의 반환값보다는 그냥 monitor
를 통해 검증을 하기 때문인 것으로 추정됩니다.
사용법으로는 UVM library에 작성된 sequencer를 상속받아 custom sequencer을 만들어서 사용하거나, 그냥 따로 sequencer를 만들 필요 없이 uvm_sequencer 그 자체를 활용하는 방법이 있습니다.
//sequencer 생성
class my_sequencer extends uvm_sequencer #(my_data);
`uvm_component_utils (my_sequencer)
function new (string name, uvm_component parent);
super.new (name, parent);
endfunction
endclass
//agent에서 활용
class agent extends uvm_agent;
`uvm_component_utils(agent)
function new(string name="agent", uvm_component parent=null);
super.new(name, parent);
endfunction
driver d0; // Driver handle
monitor m0; // Monitor handle
my_sequencer s0; // Sequencer Handle
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
s0 = my_sequencer::type_id::create("s0", this);
d0 = driver::type_id::create("d0", this);
m0 = monitor::type_id::create("m0", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
d0.seq_item_port.connect(s0.seq_item_export);
endfunction
endclass
단순히 uvm_sequencer를 상속받고 특별한 기능을 추가적으로 수행하지는 않습니다. 추가적인 기능을 추가해도 되지만 그렇게 사용하는 경우는 잘 보지 못했습니다.
중요한 부분은 #(my_data)
입니다. Sequencer
의 type을 item(여기선 my_data)
으로 해줘야합니다. 기본적인 uvm_sequencer는 type이 정해져있지 않기 때문이죠.
딱히 추가적인 기능을 추가하고 싶지 않다면 그냥 uvm_sequencer를 사용해도 됩니다. 단 인스턴스할 때, 위에처럼 type을 item으로 해주세요.
class agent extends uvm_agent;
`uvm_component_utils(agent)
function new(string name="agent", uvm_component parent=null);
super.new(name, parent);
endfunction
driver d0; // Driver handle
monitor m0; // Monitor handle
uvm_sequencer #(my_data) s0; // Sequencer Handle
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
s0 = uvm_sequencer#(my_data)::type_id::create("s0", this);
d0 = driver::type_id::create("d0", this);
m0 = monitor::type_id::create("m0", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
d0.seq_item_port.connect(s0.seq_item_export);
endfunction
endclass
그냥 라이브러리 내장 uvm_sequencer
를 사용했습니다. 인스턴스할 때, my_data
타입으로 선언해주었습니다.
어떤 기업에서 사용하는 AMBA BUS Verification IP를 봤는데, 사용법 1을 많이 사용하더라구요. 추가적인 기능구현은 없었는데도 불구하고 말이죠. 어떤 방식을 쓰던 자유인 것 같습니다.
😍읽어주셔서 감사합니다~
댓글, 지적, 하트 환영합니다.🌈