UVM: UVM 1.1 vs UVM 1.2 - IKSciting
1936
post-template-default,single,single-post,postid-1936,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

UVM 1.1 vs UVM 1.2

VMM, OVM 등을 기반으로 하여 발전해 온 UVM(Universal Verification Methodology)은 사실상 현존하는 verification methodology를 대통합했다고 말할 수도 있겠다. 현재 1.2 버전까지 release 되어 있으며 최근에는 이를 기반으로 하여 IEEE standard로 채택되기도 하였다. 관련 내용은 IEEE Standard for UVM 1800.2-2017 페이지를 참고하자.

이 글을 작성하는 현시점에 가장 많이 사용되는 UVM의 버전은 1.1 버전, 더 정확히 말하자면 1.1d 버전일 것이다. 실제로 대다수의 vendor에서 제공하는 simulator의 경우에도 UVM 1.1d와 UVM 1.2를 지원하되 UVM 1.1d를 default로 채택하고 있다. 그만큼 1.1d 버전이 완성도 높은 stable 버전인 이유도 있지만 이미 UVM 1.1d에 기반하여 갖춰진 검증 환경을 backward compatibility 이슈 등의 risk를 감수하며 UVM 1.2로 넘어가야 할 큰 이유를 아직 찾지 못했기 때문일 수도 있다.

먼저 UVM 1.1d에서 UVM 1.2로 업데이트 되면서 바뀐 내용에 대해 간략하게 알아보자. 기존 UVM 1.1d의 bug fix, enhancement, cleanup 등을 포함하고 있다. 주의할 점은 일부 변화는 이전 UVM 버전과 backward compatible 하지 않기 때문에, migration 시 그에 맞는 처리를 해주어야 한다는 것이다. UVM 1.2의 source code를 내려받으면 UVM 1.1d에서 UVM 1.2로의 migration을 도와주는 script도 포함되어 있지만 완벽하지는 않아 여전히 manual 수정은 불가피할 듯 하다.

Report Macros

UVM 1.1에서 `uvm_info와 같은 basic messaging macro를 통해 messaging을 관리하였다. UVM 1.2에서는 `uvm_info_begin`uvm_info_end와 같은 message trace macro와 `uvm_message_add_tag와 같은 message element macro를 추가하여 messaging의 flexibility를 향상시킬 수 있도록 하였다.

// basic messaging macros
`uvm_info
`uvm_warning
`uvm_error
`uvm_fatal
`uvm_info_context
`uvm_warning_context
`uvm_error_context
`uvm_fatal_context
// message trace macros
`uvm_info_begin
`uvm_info_end
`uvm_warning_begin
`uvm_warning_end
`uvm_error_begin
`uvm_error_end
`uvm_fatal_begin
`uvm_fatal_end
`uvm_info_context_begin
`uvm_info_context_end
`uvm_warning_context_begin
`uvm_warning_context_end
`uvm_error_context_begin
`uvm_error_context_end
`uvm_fatal_context_begin
`uvm_fatal_context_end
// message element macros
`uvm_message_add_tag
`uvm_message_add_int
`uvm_message_add_string
`uvm_message_add_object

기본적인 사용법과 그에 따른 결과는 다음과 같다.

`uvm_info_begin("MY_ID", "my message", UVM_LOW)
  `uvm_message_add_tag("my_tag", "red")
  `uvm_message_add_int(my_int, UVM_DEC)
  `uvm_message_add_string(my_string)
  `uvm_message_add_object(my_obj)
`uvm_info_end
UVM_INFO testbench.sv(14) @ 0: reporter [MY_ID] my message
 +------------------------------------------------------------------------
 +Name               Type                                  Size  Value    
 +------------------------------------------------------------------------
 +element_container  uvm_report_message_element_container  -     @337     
 +  my_tag           string                                3     red      
 +  my_int           integral                              32    'd1024   
 +  my_string        string                                9     my_string
 +  my_obj           <unknown>                             -     @335     
 +------------------------------------------------------------------------

이와 같이 message 출력 시 추가적인 정보를 전달할 수 있다는 장점이 있지만, 기존 macro만으로도 충분히 message 출력에 어려움이 없으며 backward compatibility 문제를 고려한다면 자주 사용되는 messaging macro는 더욱 UVM 1.1 방식을 고수하는 것을 권장한다.

Automatic Objection Control for a Sequence

UVM 1.2는 sequence 실행 전/후에 자동으로 objection을 raise/drop 할 수 있도록 set_automatic_phase_objection function을 제공한다. 다음 예제를 통해 사용법을 확인할 수 있다. 만약 sequence가 default_sequence 설정 방식에 의해 start 되었다면 starting phase가 설정되지만 start method 방식에 의해 start되었다면 set_automatic_phase_objection function을 사용하기 위해서 set_starting_phase를 이용하여 해당 sequence를 starting phase로 등록해야 함에 유의해야 한다. 관련 내용은 Starting a Sequence 페이지를 참고하자.

function my_sequence::new(string name = "");
  super.new();
  set_automatic_phase_objection(1);
endfunction: new

언뜻 보면 자동으로 objection을 관리해주기 때문에 유용할 것으로 보이지만 set_automatic_phase_objection function을 사용하는 것은 특별히 권장되지 않는다. 기본적으로 objection에 대한 raise/drop은 비교적 많은 연산량을 요구하기 때문에 특별한 이유가 있지 않은 한 Objection Mechanism 페이지에서 소개한 것과 같이 test-level에서 objection을 관리하고, sequence-level에서는 objection control을 하지 않는 것이 좋다. Sequence-level에서 objection을 raise/drop 할 경우 objection이 더 높은 level로 계속해서 propagation되므로 불필요한 작업을 수행하게 된다.

아래는 Command Line Processors for Debugging 페이지에서 소개한 바 있는 +UVM_OBJECTION_TRACE 옵션을 적용하여 위 code에 대한 simulation을 수행한 결과를 나타낸다. Objection에 대한 trace 결과 sequence에서 uvm_test_top까지 objection이 계속해서 propagation 되는 것을 확인할 수 있다.

UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer.uvm_sequence raised 1 objection(s) (automatic phase objection): count=1  total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer added 1 objection(s) to its total (raised from source object , automatic phase objection): count=0  total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.env.agent added 1 objection(s) to its total (raised from source object , automatic phase objection): count=0  total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.env added 1 objection(s) to its total (raised from source object , automatic phase objection): count=0  total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top added 1 objection(s) to its total (raised from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=1
UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_top added 1 objection(s) to its total (raised from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=1
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer.uvm_sequence dropped 1 objection(s) (automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer.uvm_sequence all_dropped 1 objection(s) (automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer subtracted 1 objection(s) from its total (dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent.sequencer subtracted 1 objection(s) from its total (all_dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent subtracted 1 objection(s) from its total (dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env.agent subtracted 1 objection(s) from its total (all_dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env subtracted 1 objection(s) from its total (dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top.env subtracted 1 objection(s) from its total (all_dropped from source object , automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top subtracted 1 objection(s) from its total (dropped from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_test_top subtracted 1 objection(s) from its total (all_dropped from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (dropped from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=0
UVM_INFO @ 25000: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (all_dropped from source object uvm_test_top.env.agent.sequencer.uvm_sequence, automatic phase objection): count=0  total=0
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 25000: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

Setting Default Sequence from Command Line

UVM 1.1에서 uvm_config_db를 이용하여 sequencer의 default sequence를 설정할 수 있었으며, UVM 1.2에서는 command line에서도 default sequence 설정이 가능하도록 다음과 같은 방식을 제공하고 있다.

+uvm_set_default_sequence=<seqr>,<phase>,<type>

Starting a Sequence 페이지에서 설명한 것처럼 애초에 default sequence를 설정하여 sequence를 start 하는 방법은 권장되지 않기 때문에 +uvm_set_default_sequence 방식 역시 사용을 권장하지 않는다. 대신 test에서 start method를 사용하여 sequence를 start 하도록 한다.

Guarded starting_phase

UVM 1.1에서 sequence-level objection control을 할 경우 다음과 같이 수행할 수 있었다.

task my_sequence::pre_start();
  // uvm 1.1d
  starting_phase.raise_objection(this, get_type_name());
endtask: pre_start

task my_sequence::post_start();
  // uvm 1.1d
  starting_phase.drop_objection(this, get_type_name());
endtask: post_start

UVM 1.2에서는 starting_phase가 guard 되어 바로 접근할 수 없으며, get_starting_phase를 이용하여 starting phase를 받아와야 한다. 이는 set_starting_phase를 수행할 때도 동일하다.

task my_sequence::pre_start();
  // uvm 1.2
  uvm_phase starting_phase = get_starting_phase();
  starting_phase.raise_objection(this, get_type_name());
endtask: pre_start

task my_sequence::post_start();
  // uvm 1.2
  uvm_phase starting_phase = get_starting_phase();
  starting_phase.drop_objection(this, get_type_name());
endtask: post_start

Constructor of uvm_object

UVM 1.1에서는 uvm_object에 대한 constructor가 필수 사항이 아니었지만, UVM 1.2에서는 반드시 constructor를 기술하여야 한다. 만약 +UVM_OBJECT_DO_NOT_NEED_CONSTRUCTOR 옵션을 설정하는 경우 constructor를 기술하지 않을 수 있지만 권장하지는 않는다. UVM 버전에 무관하게 constructor를 기술할 것을 권장한다.

class my_object extends uvm_object;

  `uvm_object_utils(my_object)
    
  // constructor required for UVM 1.2
  function new(string name = "my_object");
    super.new(name);
  endfunction: new

endclass: my_object

Checking for Bad Component Names

UVM 1.1에서는 component name에 대한 checking을 하지 않았으나, UVM 1.2에서는 ‘ ‘, ‘…’, ‘a.b.c.d’와 같은 좋지 않은 component name에 대한 checking을 통해 해당 component 접근 시 문제가 발생하지 않도록 방지하고 있다. 많은 경우 component name은 실제 instance name과 동일하게 유지하여 component name으로 인한 문제는 발생하지 않도록 하는 것이 좋다.

다음 예제에서 monitor에 대한 component name의 경우 ‘.’이 포함되어 있기 때문에 simulation 결과 UVM_WARNING이 출력됨을 확인할 수 있다.

function void my_agent::build_phase(uvm_phase phase);
  super.build_phase(phase);
  driver = my_driver::type_id::create("driver", this);
  sequencer = my_sequencer::type_id::create("sequencer", this);
  // violates UVM component name constraints
  monitor = my_monitor::type_id::create("bad.component.name", this); 
endfunction: build_phase
UVM_WARNING /playground_lib/uvm-1.2/src/base/uvm_traversal.svh(267) @ 0: reporter [UVM/COMP/NAME] the name "bad.component.name" of the component "uvm_test_top.env.agent.bad.component.name" violates the uvm component name constraints

No Meta Characters/Regular Expressions for Field Names of uvm_config_db

UVM 1.2에서는 uvm_config_db의 field name에 meta character 또는 regular expression을 사용하지 않도록 guide 함으로써 performance issue 또는 semantic issue가 발생하지 않도록 방지하고 있다. 이를 지키지 않을 경우 UVM_WARNING을 통해 경고한다.

function void my_test::build_phase(uvm_phase phase);
  // ...
    
  uvm_config_int::set(this, "", "/z?mycomplexint/", 4);
  uvm_config_string::set(this, "", "/mycomplexint/", "xx");
  uvm_config_int::set(this, "", "/my*int/", 2);
  uvm_config_int::set(this, "", "/my_complex.*/", 3);
endfunction: build_phase
UVM_WARNING /playground_lib/uvm-1.2/src/base/uvm_resource.svh(1416) @ 0: reporter [UVM/RSRC/NOREGEX] a resource with meta characters in the field name has been created "/z?mycomplexint/"
UVM_WARNING /playground_lib/uvm-1.2/src/base/uvm_resource.svh(1416) @ 0: reporter [UVM/RSRC/NOREGEX] a resource with meta characters in the field name has been created "/mycomplexint/"
UVM_WARNING /playground_lib/uvm-1.2/src/base/uvm_resource.svh(1416) @ 0: reporter [UVM/RSRC/NOREGEX] a resource with meta characters in the field name has been created "/my*int/"
UVM_WARNING /playground_lib/uvm-1.2/src/base/uvm_resource.svh(1416) @ 0: reporter [UVM/RSRC/NOREGEX] a resource with meta characters in the field name has been created "/my_complex.*/"

set_config_*, get_config_* Deprecated

set_config_*get_config_*가 UVM 1.2에서 deprecated 되었다. UVM 1.1을 사용하더라도 set_config_*get_config_*의 사용은 권장하지 않는다. 대신 uvm_config_db를 사용하는 것이 권장된다.

Factory Singleton Removed

UVM 1.1에서 factory에 접근하기 위해서는 factory singleton을 이용하여 바로 접근 가능하였으나, UVM 1.2에서는 uvm_factory::get method를 이용하여 접근하여야 한다.

function void my_test::start_of_simulation_phase(uvm_phase phase);
  // uvm 1.1d
  factory.print();
endfunction: start_of_simulation_phase
function void my_test::start_of_simulation_phase(uvm_phase phase);
  // uvm 1.2
  uvm_factory factory = uvm_factory::get();
  factory.print();
endfunction: start_of_simulation_phase

Undoing Factory Override

UVM 1.2에서는 factory override 후 다시 override 이전으로 되돌릴 수 있도록 지원하고 있다. 즉, factory override 시 동일한 type을 사용할 수 있다는 뜻이다. 하지만 일반적인 경우 이러한 작업은 필요하지 않다.

// perform an override
set_type_override_by_type(old_type::get_type(), new_type::get_type());
// undo the override - warning in UVM 1.1
set_type_override_by_type(old_type::get_type(), old_type::get_type());

Setting Objection Propagation Mode

위에서도 언급한 것처럼 objection은 uvm_test_top까지 계속해서 propagation 해서 올라가게 된다. 불필요하게 low-level에서 objection을 raise/drop 할 경우 performance 이슈가 발생할 수 있다. UVM 1.2에서는 만약 정말 low-level에서 objection control을 할 필요가 있을 때 performance 이슈를 최소화하기 위해 모든 hierarchy level마다 propagation 하지 않고 바로 uvm_test_top으로 propagation 할 수 있도록 설정할 수 있다.

이러한 objection propagation mode 설정은 set_propagate_mode method에 의해 이루어지며 mode argument가 0일 경우 위에서 말한 것처럼 바로 uvm_test_top으로 propagation되고, 1일 경우 기존과 같이 각 hierarchy level마다 propagation 된다.

task my_test::run_phase(uvm_phase phase);
  uvm_objection objection;
  super.run_phase(phase);
  objection = phase.get_objection();
  objection.set_propagate_mode(0); // turn off objection propagation
  phase.raise_objection(this);
  if (!seq.randomize()) begin
    `uvm_fatal(get_name(), "randomization fail");
  end
  seq.start(env.agent.sequencer);
  phase.drop_objection(this);
endtask: run_phase

Core Service Container

UVM 1.2에서 UVM의 factory 등 일부 특정 module을 replace 하기 쉽도록 core service라는 container를 제공하여 modularization 하고 있다. 일반적으로 이러한 core service container가 필요한 경우는 없을 것으로 예상된다.

다음은 UVM factory에 접근하는 두 가지 방식을 나타낸다. 결과는 동일하며, 후자가 core service container를 이용한 접근 방식이다.

uvm_factory factory = uvm_factory::get();
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory = cs.get_factory()

Summary

지금까지 UVM 1.2에서 변경된 주요 내역에 대해 알아보았다. 과연 UVM 1.1d와 UVM 1.2 중 어떤 버전을 사용하는 것이 좋을까?

위에서 살펴본 것과 같이 대부분의 수정사항은 UVM 전문가들이 제시하는 가이드라인에 비추어 볼 때 큰 의미를 갖지 못한다. 그와 동시에 이전 버전의 UVM과 compatible 하지 않기 때문에 manual migration 작업이 필요하다. 이러한 이유를 근거로 하여 개인적으로 현시점에서의 UVM 1.2 migration은 반드시 필요하지는 않다고 생각된다. 현재 한창 진행 중인 프로젝트에 대해서는 특히 risk가 크다.

IEEE standard로 채택된 UVM의 경우 아직 source code가 release 되지 않았으며, 곧 early-alpha 버전이 release 될 예정이라고 알려져 있다. IEEE standard release의 경우 Accellera release보다 훨씬 엄격하기 때문에 UVM WG(working group), Accellera, IEEE 그리고 여러 사용자에 의해 충분히 많은 논의과 검증을 거친 후 확정되므로, 추후 IEEE standard source code가 release 된 이후 어느 정도 시간이 지났을 때 해당 버전 적용을 검토하는 것이 좋지 않을까 생각된다.

References

Tags:
No Comments

Post A Comment