EDA Playground'da Dene

wait fork ve disable fork

Gün 4: Süreçler Arası İletişim (IPC) | Süreç bekleme, iptal etme ve timeout kalıbı

Bu derste paralel süreçleri yönetmenin iki temel aracını öğreneceksiniz: wait fork ile arka planda koşan tüm alt süreçlerin bitmesini beklemek ve disable fork ile bunları zorla sonlandırmak. Bu ikisini birleştirerek doğrulamada çok sık kullanılan timeout (zaman aşımı) kalıbını da kuracaksınız.

wait fork ve disable fork

Önceki derste join_none ile başlattığımız süreçlerin arka planda çalışmaya devam ettiğini görmüştük. Peki bu süreçleri sonradan nasıl kontrol ederiz? İşte bu noktada iki anahtar yapı devreye girer:

  • wait fork → Çağrıldığı süreç, o ana kadar başlatılmış tüm alt süreçler (forklanmış process'ler) bitene kadar bekler. join_none ile başlatılıp daha sonra "hepsi tamamlandı mı?" kontrolü yapmak istediğinizde idealdir.
  • disable fork → Çağrıldığı sürecin başlattığı tüm aktif alt süreçleri anında iptal eder (öldürür). join_any sonrası "biri bitti, gerisini umursamıyorum" senaryolarında kullanılır.

Timeout (Zaman Aşımı) Kalıbı

Doğrulamada en kritik kalıplardan biri budur: Bir işin ya başarıyla tamamlanmasını ya da belirli bir süre içinde bitmemesi durumunda hata vermesini istersiniz. Bunun için:

  1. fork içine iki dal koyarsınız: biri asıl işi yapan görev, diğeri sadece bir gecikme (#timeout) bekleyen bir bekçi (watchdog).
  2. join_any ile hangisi önce biterse ana süreç devam eder.
  3. disable ile geriye kalan (yavaş olan ya da bekçi olan) dal iptal edilir.

Bu kalıp, simülasyonların sonsuza kadar takılıp kalmasını (hang) engeller ve hatalı tasarımları erken yakalar. Adlandırılmış bloklar (fork : timeout_block) sayesinde disable timeout_block ile yalnızca o bloğu hedefli biçimde sonlandırabilirsiniz.

Kaynak Kod

// =============================================================================
// GUN 4 - Konu 2: wait fork ve disable fork
// =============================================================================

module wait_disable_fork;

  task automatic slow_task(string name, int delay);
    $display("  [%0t] %s basladi", $time, name);
    #delay;
    $display("  [%0t] %s bitti", $time, name);
  endtask

  // --- wait fork: Tum cocuk sureclerin bitmesini bekle ---
  initial begin
    $display("=== wait fork ve disable fork ===\n");

    $display("--- wait fork ---");
    $display("  [%0t] Ana surec basladi", $time);
    
    fork
      slow_task("Task_A", 20);
      slow_task("Task_B", 40);
    join_none
    
    $display("  [%0t] join_none sonrasi, diger is yapiliyor...", $time);
    #10;
    $display("  [%0t] Ek is bitti, simdi fork'lari bekliyoruz", $time);
    
    wait fork;  // Task_A ve Task_B bitene kadar bekle
    $display("  [%0t] wait fork: Tum gorevler tamamlandi!\n", $time);
  end

  // --- disable fork: Tum cocuk surecleri sonlandir ---
  initial begin
    #100;
    $display("--- disable fork ---");
    $display("  [%0t] Baslangic", $time);

    fork
      begin
        slow_task("Uzun_Task", 100);  // Cok uzun
      end
      begin
        slow_task("Kisa_Task", 10);
      end
    join_any

    $display("  [%0t] join_any: Biri bitti, digerini iptal ediyoruz", $time);
    disable fork;  // Kalan surecleri sonlandir
    $display("  [%0t] disable fork: Tum bekleyen gorevler iptal edildi\n", $time);
  end

  // --- Zaman asimi (Timeout) kalibi ---
  initial begin
    #300;
    $display("--- Timeout Kalibi ---");
    $display("  [%0t] Baslangic", $time);

    fork : timeout_block
      begin
        slow_task("Hedef_Task", 50);
        $display("  [%0t] Hedef gorev basarili!", $time);
      end
      begin
        #30;  // 30ns timeout
        $display("  [%0t] ZAMAN ASIMI! 30ns icinde bitmedi", $time);
      end
    join_any
    disable timeout_block;
    $display("  [%0t] Timeout blogu tamamlandi\n", $time);

    #10;
    $display("=== wait/disable fork Sonu ===");
    $finish;
  end

endmodule

Kodun Açıklaması

  • Birinci initial bloğu (wait fork): Task_A (20ns) ve Task_B (40ns) join_none ile başlatılır, böylece ana süreç beklemeden devam eder. Ana süreç önce #10 kadar başka bir iş yapar, ardından wait fork; ile her iki görevin de bitmesini bekler. En uzun görev Task_B 40ns sürdüğü için bekleme bu noktada tamamlanır.
  • İkinci initial bloğu (disable fork): Bu blok #100 zamanında başlar. Uzun_Task (100ns) ve Kisa_Task (10ns) paralel başlatılır. join_any sayesinde ilk biten Kisa_Task ana süreci serbest bırakır. Hemen ardından çağrılan disable fork;, hâlâ koşmakta olan Uzun_Task'i zorla sonlandırır; bu yüzden onun "bitti" mesajını hiç görmeyiz.
  • Üçüncü initial bloğu (Timeout kalıbı): #300 zamanında çalışır ve adlandırılmış bir blok olan timeout_block kullanır. İçinde Hedef_Task (50ns) ile 30ns'lik bir bekçi dalı yarışır. Bekçi 30ns < 50ns olduğu için önce biter; join_any ana süreci serbest bırakır ve "ZAMAN AŞIMI!" mesajı yazılır. Ardından disable timeout_block; ile hâlâ koşan Hedef_Task iptal edilir.

Önemli Noktalar

  • wait fork, çağrıldığı süreç tarafından başlatılmış alt süreçleri bekler; başka bloklardaki süreçleri kapsamaz. Bu kapsam farkına dikkat edin.
  • disable fork etki alanı geniştir ve mevcut süreçteki tüm alt forkları öldürür. Sadece belirli bir bloğu sonlandırmak için adlandırılmış blok (fork : isim) tanımlayıp disable isim; kullanın.
  • Timeout kalıbında join_any sonrası disable çağrısını unutmayın; aksi halde yavaş görev arka planda çalışmaya devam edebilir.
  • disable ile sonlandırılan bir görev, gecikmesinin (#delay) ortasında "öldürülür"; bu yüzden bitiş mesajları çalışmaz. Kaynak temizliği gerektiren durumlarda bu davranışı göz önünde bulundurun.
  • Bu üç blok farklı # gecikmeleriyle (10/100/300) başladığı için birbirine karışmadan, çıktıda sırayla görüntülenir; bağımsız senaryoları aynı modülde göstermenin temiz bir yoludur.