Mailbox
Gün 4: Süreçler Arası İletişim (IPC) | Bileşenler arası veri aktarımı: put, get, peek, try_get
Bu derste SystemVerilog'un yerleşik mailbox sınıfını öğreneceksiniz. Olaylar (events) sadece "bir şey oldu" sinyali taşırken, mailbox gerçek veriyi bir süreçten diğerine güvenli biçimde aktaran bir FIFO kuyruğudur ve doğrulama ortamlarının bel kemiğidir.
Mailbox Nedir?
mailbox, süreçler arasında veri taşımak için kullanılan, FIFO (ilk giren ilk çıkar) mantığıyla çalışan dahili bir sınıftır. İçine nesne veya basit tip konulabilir; özellikle mailbox #(tip) parametreli (type-parameterized) biçimi, yalnızca belirtilen tipte veri kabul ederek tip güvenliği sağlar.
Temel metotları şunlardır:
put(veri)→ Mailbox'a veri koyar. Mailbox sınırlıysa (bounded) ve doluysa, yer açılana kadar bloklar (bekler).get(degisken)→ FIFO sırasıyla bir eleman alır ve mailbox'tan çıkarır. Boşsa veri gelene kadar bloklar.peek(degisken)→ Elemanı çıkarmadan kopyasını okur; mailbox'ın içeriği değişmez.try_get(degisken)→ Bloklamadan denemeli alış yapar; başarılıysa1, mailbox boşsa0döner.num()→ Mailbox'taki eleman sayısını döndürür.
Bounded vs Unbounded ve Kullanım Yerleri
- Sınırsız (unbounded):
new()ile oluşturulur; kapasitesi sınırsızdır,putasla bloklamaz. - Sınırlı (bounded):
new(N)ile oluşturulur; en fazlaNeleman alır. Dolduğundaputbekler. Bu, gerçek donanımdaki sınırlı tampon (buffer) davranışını modellemek ve akış kontrolü (flow control) sağlamak için idealdir.
Mailbox, klasik üretici-tüketici (producer-consumer) kalıbının doğal aracıdır: bir generator transaction üretip mailbox'a koyar, bir driver bunları alıp işler. İki taraf birbirinin hızını bilmek zorunda kalmadan, bloklama davranışı sayesinde otomatik olarak senkronize olur.
Kaynak Kod
// =============================================================================
// GUN 4 - Konu 4: Mailbox - Bilesenler Arasi Veri Aktarimi
// =============================================================================
class Transaction;
int id;
int data;
string source;
function new(int id, int data, string source = "unknown");
this.id = id;
this.data = data;
this.source = source;
endfunction
function void display();
$display(" TXN #%0d: data=%0d, source=%s", id, data, source);
endfunction
endclass
module mailbox_ornek;
mailbox #(Transaction) mbx_bounded; // Sinirli mailbox
mailbox #(Transaction) mbx_unbounded; // Sinirsiz mailbox
mailbox #(int) mbx_simple; // Basit tip mailbox
// --- Basit Mailbox: put ve get ---
initial begin
$display("=== Mailbox Kullanimi ===\n");
$display("--- Basit Mailbox (int) ---");
mbx_simple = new(); // Sinirsiz
// put: Veri koy
mbx_simple.put(10);
mbx_simple.put(20);
mbx_simple.put(30);
$display(" 3 eleman eklendi, boyut = %0d", mbx_simple.num());
// get: Veri al (FIFO)
begin
int val;
mbx_simple.get(val);
$display(" get() = %0d, boyut = %0d", val, mbx_simple.num());
mbx_simple.get(val);
$display(" get() = %0d, boyut = %0d", val, mbx_simple.num());
end
// peek: Almadan bak
begin
int val;
mbx_simple.peek(val);
$display(" peek() = %0d, boyut = %0d (degismedi)", val, mbx_simple.num());
end
// try_get: Bloklama yapmadan al
begin
int val;
if (mbx_simple.try_get(val))
$display(" try_get() = %0d", val);
if (!mbx_simple.try_get(val))
$display(" try_get() basarisiz (bos mailbox)");
end
end
// --- Sinirli Mailbox ile Producer-Consumer ---
initial begin
#50;
$display("\n--- Sinirli Mailbox (bounded=3) ---");
mbx_bounded = new(3); // Maks 3 eleman
fork
// Producer
begin : producer
for (int i = 0; i < 6; i++) begin
Transaction t;
t = new(i, $urandom_range(100,999), "Producer");
$display(" [%0t] Producer: #%0d gonderiyor... (kuyruk=%0d)",
$time, i, mbx_bounded.num());
mbx_bounded.put(t); // Dolu ise bekler (blocking)
$display(" [%0t] Producer: #%0d gonderildi", $time, i);
#5;
end
end
// Consumer (yavas)
begin : consumer
Transaction t;
repeat (6) begin
#15; // Yavas tuketim
mbx_bounded.get(t);
$display(" [%0t] Consumer: alindi ->", $time);
t.display();
end
end
join
$display(" [%0t] Producer-Consumer tamamlandi", $time);
end
// --- Sinirsiz Mailbox ile coklu tuketici ---
initial begin
#250;
$display("\n--- Coklu Tuketici ---");
mbx_unbounded = new();
fork
// Uretici
begin
for (int i = 0; i < 10; i++) begin
Transaction t;
t = new(i, i*100, "Gen");
mbx_unbounded.put(t);
#3;
end
end
// Tuketici 1
begin
Transaction t;
repeat (5) begin
mbx_unbounded.get(t);
$display(" [%0t] Tuketici-1: TXN#%0d (data=%0d)", $time, t.id, t.data);
#7;
end
end
// Tuketici 2
begin
Transaction t;
repeat (5) begin
mbx_unbounded.get(t);
$display(" [%0t] Tuketici-2: TXN#%0d (data=%0d)", $time, t.id, t.data);
#10;
end
end
join
$display(" [%0t] Coklu tuketici tamamlandi", $time);
#10;
$display("\n=== Mailbox Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
Transactionsınıfı: İçindeid,datavesourcealanları bulunur;display()metodu bunları okunaklı biçimde yazdırır. Mailbox'ların nesne taşıma yeteneğini göstermek için kullanılır.- Üç mailbox bildirimi:
mbx_simplebasitinttipi taşır,mbx_boundedsınırlı (new(3)) vembx_unboundedsınırsızTransactionmailbox'larıdır. - Basit mailbox bölümü:
mbx_simplesırasıylaput(10),put(20),put(30)ile doldurulur venum()ile boyut 3 olarak gösterilir. Ardındanget(val)ile FIFO sırayla10ve20alınır.peek(val)bir sonraki elemanı (30) çıkarmadan okur; bu yüzden boyut değişmez. Son olaraktry_get, önce başarılı olur (30'u alır), ikinci çağrıda mailbox boş olduğu için bloklamadan başarısız döner. - Sınırlı mailbox (producer-consumer):
#50zamanında başlar.mbx_bounded = new(3)ile kapasite 3'tür.producer6 transaction üretipputeder, ama mailbox dolduğunda bekler;consumerise#15aralıklarla yavaşçagetyapar. Bu, sınırlı tamponun nasıl akış kontrolü sağladığını gösterir: hızlı üretici, yavaş tüketici yer açana kadar bloklanır.joinile her ikisinin bitmesi beklenir. - Çoklu tüketici bölümü:
#250zamanında çalışır. Sınırsızmbx_unbounded'a tek bir üretici 10 transaction koyarken, iki tüketici (Tuketici-1veTuketici-2) aynı mailbox'tan paralelgetyapar. Aynı kaynaktan birden fazla tüketicinin nasıl iş paylaştığını (load balancing) örnekler.
Önemli Noktalar
putvegetbloklayıcı (blocking) metotlardır: dolu bir bounded mailbox'aput, boş bir mailbox'tangetbekler. Bu otomatik bekleme, üretici-tüketici senkronizasyonunu kendiliğinden sağlar.- Bloklamadan çalışmak istediğinizde
try_get(ve benzeritry_put/try_peek) kullanın; dönüş değerini mutlaka kontrol edin. peek, elemanı kuyrukta bırakır; aynı veriye birden fazla kez bakmak veya almadan incelemek için kullanışlıdır.- Sınırlı mailbox (
new(N)) gerçek donanım tamponlarını modellemek ve test sırasında geri-basınç (backpressure) davranışını doğrulamak için tercih edilir. mailbox #(Transaction)gibi parametreli kullanım tip güvenliği sağlar; yanlış tipte veri konmasını derleme zamanında engeller. Parametresizmailboxise tip denetimsizdir ve önerilmez.- Çoklu tüketici senaryosunda her transaction yalnızca tek bir tüketici tarafından
getedilir; aynı veriyi birden çok tüketiciye dağıtmak için ayrı mailbox'lar veya yayın (broadcast) mekanizması gerekir.