2013-01-11 13 views
6

Mam coś podobnego do następującego kodu pseudo:właściwe wykorzystanie „Wyłącz widelcu” w SystemVerilog

for (lets say 10 iterations) 
begin 
// Do some configuration changes 
    fork 
     begin 
     ///apply input to design 
     end 
     begin 
     while (1) 
     /// at particular point update expected status/values 
     end 
     begin 
     while (1) 
     /// read status and verify with expected values 
     end 
    join_any 
end 

Od Kod: stosowanie wejścia może złamać widelec, ponieważ pozostałe 2 wątków pracują pod czas (1) chcę wyłączyć wszystkie wątki między każdej iteracji dla IE raz jest stosowany strumień wejścia - wyłączenie wszystkich zrodził wątki do momentu uruchomienia kolejnej iteracji (z nową konfiguracją)

więc zmodyfikowane powyżej kodu

.... 
join_any 
disable_fork 
end 

To jednak wydaje się wyłączać dla pętli i/lub czegoś podobnego, czego nie rozumiem, ale efekt jest zawieszony. Czy ktoś może mi wyjaśnić przyczynę i rozwiązanie?

+0

Może to zmienić się w nieprzydatne pytanie: nie zwolniłem semafora po wyłączeniu widelca, który nie spowodowałby kolejnych transakcji. Aktualnie debuguję zaktualizuję później – wisemonkey

+0

Co próbujesz osiągnąć? Czy na pewno potrzebujesz tutaj bloku równoległego? –

+0

Dzięki @ Adam12 tak te bloki muszą być równoległe, tak jak potrzebuję weryfikacji funkcjonalności. Próbuję zweryfikować, czy odczyt podczas aktualizacji do danego rejestru w toku daje prawidłowe dane. I tak, problem polegał na tym, że nie wydawałem semafora. Rozwiązałem go i teraz działa poprawnie – wisemonkey

Odpowiedz

18

"Wyłącz widelec" zabija nie tylko procesy uruchomione przez twój fork ... join_any, ale także wszelkie inne procesy, które są potomkami tego samego procesu, który wykonuje disable-fork. Jeśli uruchomiłeś jakiekolwiek inne procesy (używając, na przykład, fork ... join_none) wcześniej w życiu tego procesu, to te inne procesy również zostaną zabite.

Możesz raczej łatwo się przed tym zabezpieczyć, powodując, że widelec ... join_any i jego późniejszy wyłącznik-fork, uruchamia się w nowym procesie potomnym. Ogranicza to efekt twojego wyłączenia-widelca, dzięki czemu może wpływać tylko na nowo uruchomione procesy, na których Ci zależy, i nie ma żadnych innych niepożądanych efektów.

Czy to załączając cały bałagan w „widelec zacząć ... koniec join” tak:

fork begin // isolate the following code as a single child process 
    fork // launch the processes you wish to manage 
    apply_input(); 
    update_status(); 
    verify_status(); 
    join_any // kill off the *_status threads when apply_input terminates 
    disable fork; 
end join // end of child process isolation 

Jest to dobrze znany problem z widelcem ... join_any i widelcem ... join_none . Zostało to ostatnio omówione na forum Weryfikacja Gildii i jest opisane w rozdziałach # 79 i # 80 książki Sutherland and Mills "Verilog and SystemVerilog Gotchas".

Umieszczanie "początku wideł" i "łączenia końcowego" na pojedynczych liniach jest niezwykłe, ale podoba mi się to jako sposób na to, aby było oczywiste, że synchronicznie synchronizuję dokładnie jeden proces potomny. Normalnie byłoby to bezużyteczne, ale w tej sytuacji jest to niezbędne.

Ten idiom jest tak powszechne, a więc łatwo dostać się źle, że może wolisz ująć ją w parę makr (nie lubię tego, ale ...):

`define BEGIN_FIRST_OF fork begin fork 
`define END_FIRST_OF join_any disable fork; end join 

teraz można napisać ...

`BEGIN_FIRST_OF 
    apply_input(); 
    update_status(); 
    verify_status(); 
`END_FIRST_OF 

gdzie nazwy „... FIRST_OF” mają odzwierciedlać podobieństwo do Specman (e) konstruktem językowym, który robi to samo.

+0

Dzięki, to ma więcej sensu. W moim teście jest tylko jeden rozwidlenie, więc moje podejście działa, ale zmieniam je na proponowane, aby odizolować je od przyszłych zmian. I zgadzam się z tobą, nie będę używał definicji przynajmniej dopóki nie przyzwyczaję się do właściwej metodologii. – wisemonkey

0

Wyłącz widelec powinien działać dobrze. Oto mały przykład.

module top; 

     initial 
     begin 
      $display(" BEFORE fork time = %0t ",$time); 

      for(int i = 0 ; i < 3 ; i++) 
      begin 
      fork 
       automatic int j = i; 
       begin 
        $display(" Action thread started time = %0t j: %0d",$time,j); 
        #(10*j); 
        $display(" Action thread ended time = %0t j: %0d",$time,j); 
       end 

       begin 
       $display(" Entered timeout_15 = %0t j: %0d",$time,j); 
       #15; 
       $display(" Ended timeout_15 = %0t j: %0d",$time,j); 
       end 
      join_any 

      disable fork; // it kills all processes 
      $display("---------------------------------------------------"); 
     end 

     #100; // This is to make sure if there any threads .. 

     $display(" time = %0d Outside the main fork ",$time); 
     $display(" time = %0d After wait fork ",$time); 
     end 
    endmodule 

wyjście ::

BEFORE fork time = 0 
Action thread started time = 0 j: 0 
Entered timeout_15 = 0 j: 0 
Action thread ended time = 0 j: 0 
--------------------------------------------------- 
Action thread started time = 0 j: 1 
Entered timeout_15 = 0 j: 1 
Action thread ended time = 10 j: 1 
--------------------------------------------------- 
Action thread started time = 10 j: 2 
Entered timeout_15 = 10 j: 2 
Ended timeout_15 = 25 j: 2 
--------------------------------------------------- 
time = 125 Outside the main fork 
time = 125 After wait fork 
+0

Dlaczego to nie zabija wszystkich innych wątków? –

0

Należy rozważyć użycie procesy zamiast wyłączyć w SystemVerilog

process process1; 
process process2; 
fork 
    begin 
    process1 = process::self(); 
    # do something in process 1 
    end 
    begin 
    process2 = process::self(); 
    # do something in process 2 
    end 
join_any 
if (process1 != null && process1.status != process::FINISHED) 
    process1.kill(); 
if (process2 != null && process2.status != process::FINISHED) 
    process2.kill(); 

To ma być bezpieczniejsze niż wyłączenie.

Powiązane problemy