EDA Playground'da Dene

fork...join

Gün 4: Süreçler Arası İletişim (IPC) | Eşzamanlı süreçler: join, join_any, join_none

Bu derste SystemVerilog'da eşzamanlı (paralel) süreçleri başlatmanın temel yapı taşı olan fork...join bloklarını ve üç farklı sonlanma biçimini öğreneceksiniz. Bir testbench'te sürücü (driver), monitör ve sıralayıcı (sequencer) gibi bileşenlerin aynı anda çalışması bu yapı sayesinde mümkün olur.

fork...join Nedir?

fork...join bloğu, içindeki her bir ifadeyi (statement) ayrı bir paralel süreç (process/thread) olarak başlatır. Normal sıralı (sequential) kod yukarıdan aşağıya satır satır işlenirken, fork bloğunun içindeki satırlar aynı simülasyon zamanında eş zamanlı olarak hayata geçer. Bu, donanımın doğası gereği paralel olduğu bir dünyada gerçek davranışı modellemek için kritiktir.

fork ile açılan blok üç farklı anahtar kelimeyle kapatılabilir ve ana sürecin ne zaman devam edeceği bu seçime göre değişir:

  • join → Ana sürecin devam edebilmesi için forkdaki TÜM alt süreçlerin bitmesini bekler. Toplam süre, en uzun süren süreç kadardır.
  • join_any → Alt süreçlerden HERHANGİ BİRİ (yani ilk biten) tamamlandığında ana süreç devam eder. Diğer süreçler arka planda çalışmaya devam eder.
  • join_none → Ana süreç HİÇBİRİNİ BEKLEMEDEN anında devam eder; alt süreçler arka planda yaşamaya başlar.

Neden Önemli ve Nerede Kullanılır?

  • Doğrulama ortamları: UVM ve klasik testbench'lerde generator, driver, monitor gibi bileşenler genellikle fork...join_none ile başlatılıp birbirinden bağımsız çalışır.
  • Timeout / watchdog kalıpları: join_any ile "ya iş biter ya da zaman aşımı olur" mantığı kurulur.
  • Stimulus üretimi: Birden fazla arabirimi aynı anda sürmek için paralel süreçler şarttır.

Ayrıca fork içine begin...end blokları koyarak iç içe (nested) yapılar oluşturabilirsiniz; bu durumda her begin...end bloğu kendi içinde sıralı çalışan tek bir paralel süreç olur.

Kaynak Kod

// =============================================================================
// GUN 4 - Konu 1: fork...join, fork...join_any, fork...join_none
// =============================================================================

module fork_join;
  // Gorev tanimlari
  task automatic work(string name, int delay);
    $display("  [%0t] %s basladi (sure: %0dns)", $time, name, delay);
    #delay;
    $display("  [%0t] %s bitti", $time, name);
  endtask

  initial begin
    $display("=== fork...join Turleri ===\n");

    // --- fork...join: TUM surecler bitene kadar bekle ---
    $display("--- fork...join (hepsi bitsin) ---");
    $display("  [%0t] Baslangic", $time);
    fork
      work("Gorev_A", 30);
      work("Gorev_B", 10);
      work("Gorev_C", 20);
    join
    $display("  [%0t] fork...join tamamlandi (en uzun: 30ns)\n", $time);

    // --- fork...join_any: HERHANGI BIRI bitince devam et ---
    $display("--- fork...join_any (ilk biten) ---");
    $display("  [%0t] Baslangic", $time);
    fork
      work("Gorev_D", 30);
      work("Gorev_E", 10);
      work("Gorev_F", 20);
    join_any
    $display("  [%0t] fork...join_any devam etti (ilk biten: 10ns)\n", $time);
    #20;  // Digerlerinin bitmesini bekle

    // --- fork...join_none: HICBIRINI bekleme ---
    $display("--- fork...join_none (beklemeden devam) ---");
    $display("  [%0t] Baslangic", $time);
    fork
      work("Gorev_G", 30);
      work("Gorev_H", 10);
      work("Gorev_I", 20);
    join_none
    $display("  [%0t] fork...join_none hemen devam etti", $time);
    #0;  // Delta cycle - fork'lar baslasin
    $display("  [%0t] #0 sonrasi (gorevler basladi)", $time);
    #35;  // Tumunun bitmesini bekle

    // --- Çok satırlı fork ---
    $display("\n--- Ic Ice Fork ---");
    $display("  [%0t] Baslangic", $time);
    fork
      begin  // Sirali blok 1
        work("Sirali_1a", 5);
        work("Sirali_1b", 5);  // 1a'dan sonra baslar
      end
      begin  // Sirali blok 2
        work("Sirali_2a", 3);
        work("Sirali_2b", 7);
      end
    join
    $display("  [%0t] Ic ice fork tamamlandi\n", $time);

    $display("=== fork...join Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • work görevi, automatic olarak tanımlanmıştır. Bu çok önemlidir: fork içinde aynı görev birden fazla kez paralel çağrıldığında, automatic her çağrı için ayrı bir yığın (stack) çerçevesi oluşturur ve name ile delay argümanları birbirine karışmaz.
  • fork...join bölümü: Gorev_A (30ns), Gorev_B (10ns) ve Gorev_C (20ns) aynı anda başlar. Ana süreç en uzun süren Gorev_A bitene kadar, yani 30ns boyunca bekler.
  • fork...join_any bölümü: Gorev_D, Gorev_E, Gorev_F paralel başlar. En kısa süren Gorev_E (10ns) bitince ana süreç hemen devam eder. Hemen ardından gelen #20; ifadesi, hâlâ arka planda koşan diğer görevlerin bitmesine zaman tanımak için konmuştur.
  • fork...join_none bölümü: Süreçler başlatılır ama ana süreç beklemeden bir sonraki satıra geçer. Buradaki #0; (delta cycle gecikmesi) ilginçtir: ana süreç bir an için yürütmeyi bırakır ve böylece fork ile başlatılan süreçlerin ilk satırları çalışma fırsatı bulur. Sonraki #35; ise tüm görevlerin tamamlanmasını bekler.
  • İç içe fork: Bloğun içinde iki ayrı begin...end vardır. Her biri kendi içinde sıralı çalışır: örneğin Sirali_1a bittikten sonra Sirali_1b başlar. Ancak iki begin...end bloğu birbirine göre paraleldir. join sayesinde ana süreç her iki bloğun da bitmesini bekler.

Önemli Noktalar

  • fork içinde paralel çağrılan görevler/fonksiyonlar automatic olmalıdır; aksi halde paylaşılan statik değişkenler yüzünden veriler birbirine karışır.
  • join_none kullandıktan hemen sonra başlatılan süreçlerin gerçekten çalışmaya başladığını görmek için #0 veya bir gecikme gerekebilir; aksi halde ana süreç onlara fırsat vermeden ilerleyebilir.
  • join_any sonrası arka planda hâlâ koşan süreçler vardır. Bunları açıkça beklemek (wait fork) veya iptal etmek (disable fork) gerekebilir, yoksa simülasyon beklenmedik biçimde uzayabilir.
  • Bir fork bloğunun içine begin...end koymak, o blok içindeki ifadeleri tek bir paralel sürece dönüştürür; sıralı ve paralel davranışı bu şekilde harmanlayabilirsiniz.
  • Simülasyon zamanı ($time) çıktısını izleyerek hangi sürecin ne zaman başlayıp bittiğini takip etmek, paralel davranışı anlamanın en sağlam yoludur.