SystemVerilog: fork-join_none within a Loop (Multithreading) - IKSciting
2063
post-template-default,single,single-post,postid-2063,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

fork-join_none within a Loop (Multithreading)

이번 포스트에서는 forforeach와 같은 loop 안에서 forkjoin_none block을 사용하는 방법에 대해 소개하고자 한다. 물론 forkjoinforkjoin_any block도 loop 안에서 사용할 수 있지만 가장 흔하게 사용되는 것이 바로 forkjoin_none block을 이용한 multithreading이기 때문이다.

먼저 아래의 예제를 확인해보자. 두 개의 thread를 동시에 실행하기 위하여 forkjoin_none block을 사용하고 있으며, index 값으로 0과 1을 각각 전달하고 있다. 아마 이 부분은 이해하는 데 어려움이 없을 것으로 생각된다.

module top;
  
  initial begin
    fork
      my_thread(0);
      my_thread(1);
    join_none
  end
  
  task my_thread(int idx);
    $display("thread #%1d", idx);
    // do something here...
  endtask: my_thread
  
endmodule: top

하지만 동시에 수행해야 하는 thread의 개수가 매우 많을 때 위와 같은 방식으로 코드를 작성한다면 불필요하게 많은 line을 소모할 수 있다. 뿐만 아니라 thread의 개수가 dynamic하게 변경되는 상황에서는 위 방식을 사용할 수 없다. 이러한 점을 감안하여 코드를 아래와 같이 변경해보았다.

module top;
  
  int N;
  
  initial begin
    N = 3; // can be changed dynamically
    
    for (int i = 0; i < N; i++) begin
      fork
        my_thread(i);
      join_none
    end
  end
  
  task my_thread(int idx);
    $display("thread #%1d", idx);
    // do something here...
  endtask: my_thread
  
endmodule: top

forkjoin_none block과 for loop를 이용하여 위에서 언급한 두 가지 문제를 모두 해결한 것처럼 보인다. 하지만 위 코드를 직접 실행해보면 그렇지 않다는 것을 알 수 있다. (순서는 simulator에 따라 다를 수 있지만) thread #0, thread #1, thread #2가 출력되어야 하나, thread #3만 세 번 출력되는 문제가 발생한다.

사실 이 문제의 원인은 fork-join_none Gotcha 페이지에서 이미 다룬 바 있다. fork에 의해 생성된 child process는 매 iteration마다 바로 실행되는 것이 아니라 parent process가 blocking statement를 만났거나 terminate 되었을 때 실행된다. Child process가 실제로 실행되는 시점에는 for loop가 이미 종료되었으므로 i가 3의 값을 가지고, 그에 따라 thread #3만 세 번 출력되게 된다.

thread #3
thread #3
thread #3

그렇다면 어떻게 해야 원하는 결과를 얻을 수 있을까? 이에 대한 답은 SystemVerilog LRM에서 찾을 수 있다. automatic 변수를 이용한 방법이 제시되어 있는데, automatic 변수는 기본적으로 호출될 때마다 새로운 영역에 생성되어 initialize 되는 특성을 가지고 있다. 따라서 아래 코드에서는 매 iteration마다 0, 1, 2의 값을 가지는 local copy를 보관하게 되고, 이후 child process가 실행되는 시점에 각 local copy를 이용하게 되므로 의도하는 바와 같이 thread #0, thread #1, thread #2를 출력할 수 있는 것이다.

module top;
  
  int N;
  
  initial begin
    N = 3; // can be changed dynamically
    
    for (int i = 0; i < N; i++) begin
      fork
        automatic int j = i; // local copy, j, for each value of i
        my_thread(j);
      join_none
    end
  end
  
  task my_thread(int idx);
    $display("thread #%1d", idx);
    // do something here...
  endtask: my_thread
  
endmodule: top

References

4 Comments
  • Marvin Petzold
    Posted at 08:52h, 19 March Reply

    I’d like to thank you for the efforts you’ve put in writing this website.
    I am hoping to see the same high-grade content by you in the future
    as well. In truth, your creative writing abilities has inspired me to get my very own site now 😉

  • abubakary depillo
    Posted at 14:58h, 19 March Reply

    abubakary depillo

  • anubis sawley
    Posted at 14:28h, 22 March Reply

    anubis sawley

  • Miley Clarke
    Posted at 10:50h, 06 April Reply

    Miley Clarke

Post A Comment