EDA Playground'da Dene

wait(event.triggered) ve @(event) Karşılaştırması

Gün 4: Süreçler Arası İletişim (IPC) | Olay tetikleme (->), bekleme (@, wait) ve senkronizasyon

Özellikle karmaşık doğrulama ortamlarında (örneğin UVM tabanlı bir VIP geliştirirken veya katı standartlara tabi projelerin testbench'lerinde), simülatörün delta döngüleri (delta cycles) üzerinde tam kontrolümüz yoktur. Farklı bileşenlerin (sequence, monitor, scoreboard vb.) aynı zaman adımında (time step) hangi sırayla çalışacağı belirsizdir (non-deterministic). Bu belirsizlik, @(event) kullanıldığında "race condition" (yarış durumu) yaratır ve simülasyonların nedensiz yere kilitlenmesine (hang) yol açar.

Altın Kural:

  • State / Durum Kontrolü: "Bu olay oldu mu? (Olduysa devam et, geride kalmayayım)" mantığı varsa -> wait(event.triggered)
  • Zamanlama / Gecikme: "Bir sonraki tetiklemeye kadar (örneğin sonraki clock cycle) dur ve kesinlikle bekle" mantığı varsa -> @(event)

UVM mimarisinde daha gelişmiş componentler arası haberleşme için standart SystemVerilog event'leri yerine uvm_event ve uvm_event_pool kullanımı da çok yaygındır. Bu konuya da ilerideki derslerde değineceğiz.

İki Bekleme Yönteminin Farkı

Bir olayı (event) beklemenin iki yolu vardır ve aralarındaki fark race condition'ların kaynağıdır:

  • @(event) (kenar tetiklemeli): Sürecin olayı yakalayabilmesi için tetiklemeden önce beklemeye geçmiş olması gerekir. Aynı zaman adımında (time step) önce tetikleyip sonra @ ile beklerseniz, tetiklemeyi kaçırırsınız ve süreç asılı kalır.
  • wait(event.triggered) (durum/seviye kontrolü): Olayın o zaman adımında tetiklenip tetiklenmediğini sorgular. Tetikleme wait'ten hemen önce, hatta aynı anda gerçekleşse bile bunu algılar ve süreç bloklanmadan devam eder.

Neden Önemli?

Karmaşık testbench'lerde hangi sürecin aynı zaman adımında önce çalışacağı belirsizdir (non-deterministic). Eğer bir bileşen "olay zaten oldu mu?" sorusunu @(event) ile sorarsa, olayı bir delta döngüsü farkıyla kaçırıp simülasyonu kilitleyebilir. Bu yüzden durum kontrolü için wait(event.triggered), belirgin bir sonraki tetiklemeyi beklemek için @(event) tercih edilir.

Kaynak Kod

module wait_triggered_usage;
  event sync_event;

  initial begin
    #10;
    
    // Önce event'i tetikleyelim
    $display("[%0t] Triggering the event.", $time);
    -> sync_event;

    // wait(.triggered), event aynı time step'te tetiklense bile çalışır 
    wait(sync_event.triggered);
    $display("[%0t] wait(sync_event.triggered) caught the event successfully.", $time);

    -> sync_event;
    
    // NOT:Bir de bunu dene, Line 15'i yoruma al:
    /*fork
      begin
        #5;
        $display("[%0t] Parallel thread: Triggering the event again.", $time);
        -> sync_event;
      end
    join_none*/

    $display("[%0t] Now waiting with @(sync_event).", $time);
    @(sync_event);
    
    // Bu satırı göremezsin. Çünkü aynı thread'de aynı time slotta event'i
    // tetikledin ve bekledin. @ operatoru thread'i blokladı.
    // Yukarıdaki fork join_none'ı denediğinde paralel thread'de tetiklemiş oldun
    $display("[%0t] @(sync_event) caught the second trigger successfully!", $time);
  end

endmodule

Kodun Açıklaması

  • Modül tek bir sync_event olayı ve tek bir initial blok içerir; böylece tüm işlemler aynı süreçte ve çoğu aynı zaman adımında gerçekleşir.
  • #10 gecikmesinden sonra -> sync_event; ile olay tetiklenir.
  • Hemen ardından wait(sync_event.triggered); çağrılır. Olay aynı zaman adımında tetiklendiği halde, .triggered özelliği bunu algılar ve süreç takılmadan devam eder; "caught the event successfully" mesajı yazılır. İşte wait(...triggered)'ın gücü budur.
  • Sonra ikinci kez -> sync_event; tetiklenir ve hemen @(sync_event); ile beklemeye geçilir. Burada kritik nokta şudur: tetikleme @'ten önce ve aynı zaman adımında olduğu için, @ operatörü bu tetiklemeyi yakalayamaz. Süreç bu satırda sonsuza kadar bloklanır.
  • Bu yüzden son $display satırı ("@(sync_event) caught the second trigger") asla çalışmaz. Yorum içindeki fork...join_none bloğunu etkinleştirip ikinci -> sync_event;'i yorum satırı yaparsanız, olay paralel bir süreçten ve farklı bir anda tetikleneceği için @ artık yakalayabilir.

Önemli Noktalar

  • Aynı süreçte aynı zaman adımında bir olayı önce tetikleyip sonra @ ile beklemek klasik bir hatadır; @ o tetiklemeyi kaçırır ve süreç asılı kalır.
  • "Bu olay zaten oldu mu?" mantığı için her zaman wait(event.triggered) kullanın; durum kontrolünde delta döngüsü yarışlarına karşı güvenlidir.
  • @(event)'i, olayı başka bir süreçten ve gelecekte tetiklenecek şekilde beklerken kullanın (örneğin saat kenarı, monitör sinyali).
  • Koddaki yorumlu fork...join_none örneği, tetiklemeyi paralel bir sürece taşıyarak @'in nasıl çalışır hale geldiğini göstermek için bilinçli olarak konmuştur; deneyerek farkı kendiniz gözlemleyin.
  • Gerçek doğrulama ortamlarında bu tür kilitlenmeler saatler kaybettirebilir; bekleme yöntemini niyetinize (durum mu, kenar mı?) göre seçmek en iyi pratiktir.