SystemVerilog: local:: Qualifier in Constraint Block - IKSciting
2284
post-template-default,single,single-post,postid-2284,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

local:: Qualifier in Constraint Block

아래와 같이 random 변수 n 값만큼 반복하여 transaction을 보내는 sequence가 있다고 가정해보자. n은 0보다 큰 값을 가질 수 있으며, 특별히 override 하지 않는 한 1 이상 10 이하의 적당한 값을 갖도록 soft constraint를 지정하고 있다.

class my_sequence extends uvm_sequence#(my_transaction);

  `uvm_object_utils(my_sequence)
  
  rand int n;
  
  constraint c_n {
    n > 0;
    soft n inside { [1:10] };
  }

  function new(string name = "my_sequence");
    super.new();
  endfunction: new
  
  task body(); 
    req = my_transaction::type_id::create("req");

    $display("repeating sequence %0d times", n);

    repeat (n) begin
      start_item(req);
      void'(req.randomize());
      finish_item(req);
    end
  endtask: body
  
endclass: my_sequence

위 sequence를 사용하여 test를 구성하는 경우 다음과 같이 test의 run_phase에서 sequence를 randomize 후 start 할 수 있을 것이다. 결과적으로 이 sequence는 soft constraint에 의해 1개 이상 10개 이하의 transaction을 보내게 된다.

task my_test::run_phase(uvm_phase phase);
  super.run_phase(phase);
  
  phase.raise_objection(this);
  
  void'(seq.randomize());
  seq.start(env.agent.sequencer);

  phase.drop_objection(this);
endtask: run_phase

만약 test-level에서 transaction 개수를 특정한 값으로 또는 soft constraint를 벗어나는 어떠한 값으로 조정하고 싶다면 어떻게 해야 할까? 몇 개의 transaction을 보낼 것인지는 다른 조건에 의해 정의될 수도 있고, plus argument를 통해 설정될 수도 있고, uvm_config_db를 통해 전달 받을 수도 있을 것이다. 가령 100개의 transaction을 보내야 하고, 100이라는 값을 test 내 n이라는 변수에 담고 있다고 생각해보자. 그리고 with constraint block을 통해 반복 횟수를 지정하려고 보니, 공교롭게도 sequence의 변수와 test의 변수의 이름이 모두 n으로 동일하다.

task my_test::run_phase(uvm_phase phase);
  // ...

  n = 100;
  
  void'(seq.randomize() with {
    n == n;
  });
  
  seq.start(env.agent.sequencer);

  // ...
endtask: run_phase

일단 위와 같이 code를 작성하고 simulation 후 log를 보면 아래와 같이 출력된다. 의도했던 100회 반복이 되지 않은 것을 보면 의도한 대로 constraint가 제대로 적용되지 않았음을 알 수 있다.

repeating sequence 1 times

Sequence의 nseq.n이라고 명시해 줌으로써 두 n을 다르게 표현하면 원하는 결과를 얻을 수 있을까?

task my_test::run_phase(uvm_phase phase);
  // ...

  n = 100;
  
  void'(seq.randomize() with {
    seq.n == n;
  });
  
  seq.start(env.agent.sequencer);

  // ...
endtask: run_phase

안타깝게도 여전히 결과는 동일하다.

repeating sequence 1 times

해결 방법을 찾기 전에 with constraint block이 동작하는 방식에 대해 이해할 필요가 있다. with constraint block은 해당 block 내에 기술된 qualified 되지 않은 변수를 찾을 때, 가장 먼저 randomize 대상이 되는 object class 내부에서 해당 변수가 있는지 찾아본다. 만약 찾지 못하였다면, randomize method가 call 되는 scope, 즉, local scope이라고 불리는 곳에서 해당 변수가 있는지 찾게 된다. 아래 code의 경우를 예로 들면, sequence-level에서 먼저 찾고, 없으면 test-level에서 찾는다고 이해할 수 있다. Sequence와 test에 모두 동일한 이름의 변수가 있다면 무조건 sequence에 존재하는 변수를 사용하게 되는 것이다.

task my_test::run_phase(uvm_phase phase);
  void'(seq.randomize() with {
    // first, search in the object class that is being randomized (i.e, in the sequence)
    // if not found, search in the scope where randomize method is being called - the "local" scope (i.e., in the test)
  });
endtask: run_phase

그러면 원하는 결과를 얻기 위해서는 어떻게 해야 할까? 지금 원하는 것은 sequence의 n이 test의 n과 같은 값을 가지도록 constraint를 주려는 것이다. with constraint block에서 sequence의 n을 기술할 때는 단순히 n이라고 기술하기만 하면 된다. 반면, test의 n, 즉, local scope의 n을 인식하게 하기 위해서는 SystemVerilog의 local:: qualifier라는 것이 필요하다. 사용 방법은 아래 code에서 확인할 수 있다.

task my_test::run_phase(uvm_phase phase);
  // ...

  n = 100;
  
  void'(seq.randomize() with {
    n == local::n;
  });
  
  seq.start(env.agent.sequencer);

  // ...
endtask: run_phase

이제 simulation log를 보면 의도한 바와 같이 100개의 transaction을 보내는 것을 확인할 수 있다.

repeating sequence 100 times

Summary

with constraint block에서 변수가 resolve 되는 방식과 local:: qualifier를 사용하여 local scope의 변수를 명시하는 방법에 대하여 알아보았다. 실제 검증하면서 겪는 상황과 시행착오에 적용이 쉽도록 UVM code를 기반으로 설명하였지만, 사실 개념 자체는 단순하다. 경우에 따라서는 애초에 위 예제와 같이 서로 다른 class에서 동일한 변수 이름을 사용하지 않는 것이 좋을 수도 있지만, 또 어떤 경우에는 여러 이유로 동일한 변수 이름을 사용할 수 밖에 없는 상황도 존재할 수 있으며, 이 경우에는 local:: qualifier를 활용할 수 있다.

References

No Comments

Post A Comment