[System Verilog] Overview – 3 process, communication

System Verilog Process

System Verilog에서 process는 독립적으로 실행되는 code입니다. Thread라고도 하는 process는 Verilog에서 운용되던 begin~end 문 안에서 아래 세 가지 방법이 추가되어 진행될 수 있습니다.

  • fork – join
  • fork – join_any
  • fork – join_none
글 설명 이미지, fork 종류
fork 종류

fork – join

fork – join 문에서는 모든 process가 동시에 시작되고, 모든 process가 종료될 때까지 기다렸다가 이어서 join 뒤의 process를 진행합니다. 예시로 이해해 볼까요?

module top;
  
  initial begin
    #1 $display("[%0t ns] Start fork", $time);
    
    //Process Start
    fork
      //Tread 1
      #5 $display("[%0t ns] Thread1", $time);
      
      //Thread 2
      begin
        #2 $display("[%0t ns] Thread2_step1", $time);
        #4 $display("[%0t ns] Thread2_step2", $time);
      end
      
      //Thread 3
      #10 $display("[%0t ns] Thread3", $time);
    join
    
    //Process end
    $display("[%0t ns] After fork", $time);
  end
  
endmodule

The simulation results are as follows.

Simulation result
Simulation result

fork-join 문은 중첩이 가능합니다.

module tb;
  
  initial begin
    $display("[%0t] Main Thread: fork start", $time);
    
    fork
      
      fork
        print(20,"Thread1_step1");
        print(30,"Thread1_step2");
      join
      
      print(10,"Thread2");
      
    join
    
    $display("[%0t] Main Thread: fork finished", $time);
    
  end
  
  task automatic print (int _time, string t_name);
    #(_time) $display("[%0t] %s", $time, t_name);
  endtask
endmodule

The simulation results are as follows.

Simulation result
Simulation result

fork – join_any

fork – join_any 문도 모든 process가 동시에 시작되고, 가장 먼저 완료된 process 이후에 다음 process를 진행합니다. 예시를 확인해 봅시다.

module tb;
  
  initial begin
    $display("[%0t] Main Thread: fork start", $time);
    
    fork
      print(20, "Thread1_0");
      print(30, "Thread1_1");
      print(10, "Thread2");
    join_any
    
    $display("[%0t] Main Thread: fork finished", $time);
    
  end
  
  task automatic print (int _time, string t_name);
    #(_time) $display("[%0t] %s", $time, t_name);
  endtask
  
endmodule

The simulation results are as follows.

Simulation result
Simulation result

아래 예시 코드도 동일한 결과를 보입니다.

module tb;
  
  initial begin
    $display("[%0t] Main Thread: fork start", $time);
    
    fork
      fork
        print(20, "Thread1_0");
        print(30, "Thread1_1");
      join_any
      print(10, "Thread2");
    join_any
    
    $display("[%0t] Main Thread: fork finished", $time);
    
  end
  
  task automatic print (int _time, string t_name);
    #(_time) $display("[%0t] %s", $time, t_name);
  endtask
  
endmodule

fork – join_none

fork – join_none 문은 process가 동시에 시작되지만, fork – join이나 fork – join_any와 달리 어떤 process도 완료되기를 기다리지 않습니다. 예시로 이해해 봅시다.

module tb;
  
  initial begin
    $display("[%0t] Main Thread: fork start", $time);
    
    fork
      print(20, "Thread1_0");
      print(30, "Thread1_1");
      print(10, "Thread2");
    join_none
    
    $display("[%0t] Main Thread: fork finished", $time);
    
  end
  
  task automatic print (int _time, string t_name);
    #(_time) $display("[%0t] %s", $time, t_name);
  endtask
  
endmodule

The simulation results are as follows.

Simulation 결과
Simulation 결과

fork – join_none 뒤에 오는 display 문이 process를 기다리지 않고 실행된 것을 확인할 수 있습니다.

Disable fork

Disable fork 문을 사용하면 진행 중인 process를 종료할 수 있습니다. 우선 disable을 사용하지 않은 예시를 살펴볼까요?

module tb;
  
  initial begin
    fork
      
      //Thread1
      #40 $display("[%0t ns] show #40", $time);
      
      //Thread2
      begin
        #20 $display("[%0t ns] show #20", $time);
        #50 $display("[%0t ns] show #50", $time);
      end
      
      //Thread3
      #60 $display("[%0t ns] TIMEOUT", $time);
    join_any
    
    //fork done
    $display("[%0t ns] fork - join_any done", $time);
  end
  
endmodule

가장 먼저 끝나는 process인 Thread1이 마친 뒤 join_any 뒤에 오는 display가 실행됩니다.

Simulation result
Simulation result

그러면 disable fork를 사용한 예시를 살펴보겠습니다.

module tb;
  
  initial begin
    fork
      
      //Thread1
      #40 $display("[%0t ns] show #40", $time);
      
      //Thread2
      begin
        #20 $display("[%0t ns] show #20", $time);
        #50 $display("[%0t ns] show #50", $time);
      end
      
      //Thread3
      #60 $display("[%0t ns] TIMEOUT", $time);
    join_any
    
    //fork done
    $display("[%0t ns] fork - join_any done, disable fork", $time);
    
    disable fork;
  end
  
endmodule

Disable fork로 인해 표시된 process는 실행되지 않고 fork 문이 종료됩니다.

Simulation result
Simulation result

Wait fork

Wait fork는 fork – join 문에서 모든 process가 끝날 때까지 기다립니다. 예시를 확인해 볼까요?

module tb;
  
  initial begin
    fork
      
      //Thread1
      #40 $display("[%0t ns] show #40, Thread1", $time);
      
      //Thread2
      begin
        #20 $display("[%0t ns] show #20, Thread2", $time);
        #50 $display("[%0t ns] show #50, Thread2", $time);
      end
      
      //Thread3
      #60 $display("[%0t ns] TIMEOUT, Thread3", $time);
    join_any
    
    //join_any done
    $display("[%0t ns] fork - join_any done, wait fork", $time);
    
    wait fork;
    
    //fork done
    $display("[%0t ns] fork - join done", $time);
  end
  
endmodule

fork 문 안에 있는 process 중 가장 먼저 끝나는 Thread1 뒤에 join_any done이 display 됩니다. 그리고 wait fork로 인해 모든 process가 종료된 후 fork done이 display 됩니다.

Simulation result
Simulation result

그렇다면 wait fork는 모든 process를 다 끝날 때까지 기다릴까요?? 아래 예시를 보면 이해할 수 있습니다.

module tb;
  
  initial begin
    //First fork
    fork
      
      //Thread1
      #40 $display("[%0t ns] show #40, Thread1", $time);
      
      //Thread2
      begin
        #20 $display("[%0t ns] show #20, Thread2", $time);
        #50 $display("[%0t ns] show #50, Thread2", $time);
      end
      
      //Thread3
      #60 $display("[%0t ns] TIMEOUT, Thread3", $time);
    join_any
    
    //join_any done
    $display("[%0t ns] fork - join_any done", $time);
    
    //Second fork
    fork
      #10 $display("[%0t ns] wait for 10ns", $time);
      #20 $display("[%0t ns] wait for 20ns", $time);
    join_any
    
    wait fork;
    
    //fork done
    $display("[%0t ns] fork - join done", $time);
  end
  
endmodule

원래 코드에서 Second fork를 추가하였습니다. 그래도 First fork가 더 늦게 끝나는데요, wait fork로 인해 First fork의 모든 process를 기다립니다.

Simulation result
Simulation result

이렇게 System Verilog에서 사용하는 process인 fork 문의 종류와 예시를 살펴봤습니다.

System Verilog Communication

검증을 진행할 때 testbench에 있는 component끼리 data를 주고받거나 output value를 check 해야 할 경우가 있습니다. System Verilog에서는 여러 mechanism을 통해 Thread나 component 간의 control flow를 제어할 수 있습니다.

  • Events: Testbench내에 각각의 event handle을 이용하여 Thread 간 동기화
  • Semaphores: Semaphore를 이용하여 Thread 간 필요한 공유 자원을 교대로 접근할 수 있게 함
  • Mailbox: Thread와 components 사이에 mailbox를 이용해 데이터의 교환을 할 수 있게 함

Semaphore

Semaphore는 System Verilog에 내장된 class로, shared resources에 access를 제어하는 데 사용됩니다. Semaphore bucket에 key의 개수가 정해져 있어서 key를 가지고 있는 process만 resource에 접근할 수 있고, key가 없으면 다른 process가 key를 반환할 때까지 기다립니다.

System Verilog에서 지원하는 semaphore method는 다음과 같습니다.

NameDescription
function new();초기에 Semaphore bucket에 할당한 키의 개수를 지정함.
function void put();반환될 semaphore의 키 개수를 지정함.
task get();Semaphore에서 얻을 키 개수를 지정함.
Semaphore key를 받을 수 있는 상황일 시, 계속 대기.
function int try_get();Semaphore에서 필요한 키를 얻기 위해 키 개수를 지정함. 키가 없어도 대기하지 않음
Semaphore method

예시를 확인해 볼까요?

module tb;

  semaphore key;

  initial begin
    key = new(1);
    fork
      personA();
      personB();
      #25 personA();
    join_none
  end

  task getRoom (bit [1:0] id);
    $display("[%0t] Trying to get a room for id [%0d] ...", $time, id);
    key.get(1);
    $display("[%0t] Room key retrieved id [%0d] ...", $time, id);
  endtask

  task putRoom (bit [1:0] id);
    $display("[%0t] Leaving room id [%0d] ...", $time, id);
    key.put(1);
    $display("[%0t] Room key put back id [%0d] ...", $time, id);
  endtask

  task personA ();
    getRoom(1);
    #20 putRoom(1);
  endtask

  task personB ();
    #5  getRoom(2);
    #10 putRoom(2);
  endtask

endmodule

The simulation results are as follows.

Simulation result
Simulation result

References: chip verify

Similar Posts