Semaphore
Gün 4: Süreçler Arası İletişim (IPC) | Kaynak yönetimi ve senkronizasyon: mutex, çok portlu erişim
Bu derste SystemVerilog'un yerleşik semaphore sınıfını öğreneceksiniz. Semaphore, sınırlı sayıda paylaşılan kaynağa (örneğin bir bus ya da bellek portları) aynı anda kaç sürecin erişebileceğini kontrol etmenin standart yoludur.
Semaphore Nedir?
Bir semaphore, içinde belirli sayıda anahtar (key) tutan bir sayaçtır. Bir kaynağa erişmek isteyen süreç anahtar ister; anahtar varsa alır ve devam eder, yoksa anahtar serbest kalana kadar bekler. İş bitince anahtarı geri verir. Temel metotları:
new(N)→Nanahtarla bir semaphore oluşturur. Başlangıçtaki anahtar sayısı eş zamanlı erişim sınırını belirler.get(n)→nanahtar almaya çalışır; yeterli anahtar yoksa bloklar (bekler).put(n)→nanahtarı havuza geri koyar ve bekleyen süreçleri uyandırabilir.try_get(n)→ Bloklamadan dener; anahtar varsa alıp1, yoksa0döner.
Mutex ve Çok Portlu Erişim
- Mutex (karşılıklı dışlama):
new(1)ile oluşturulan tek anahtarlı semaphore bir mutex'tir. Aynı anda yalnızca tek bir süreç kaynağa erişebilir; bu, paylaşılan bir bus'ı korumanın klasik yoludur. - Çoklu anahtar:
new(2)gibi birden çok anahtar, aynı anda birden fazla sürecin erişmesine izin verir (örneğin 2 portlu bir bellek). Üçüncü süreç, biri portu serbest bırakana kadar beklemek zorunda kalır.
Neden Önemli ve Nerede Kullanılır?
Birden fazla süreç aynı paylaşılan kaynağa kontrolsüzce eriştiğinde yarış durumları (race conditions) ve veri bozulması oluşur. Semaphore, bus arbitrasyonu, sınırlı portlu belleğe erişim, lisans/kaynak havuzu yönetimi gibi senaryolarda erişimi düzenli ve güvenli kılar.
Kaynak Kod
// =============================================================================
// GUN 4 - Konu 5: Semaphore - Senkronizasyon ve Kaynak Yonetimi
// =============================================================================
module semaphore_ornek;
semaphore bus_lock; // Paylasilan bus erisimi
semaphore mem_lock; // Bellek erisimi (2 port)
int shared_bus_data;
// Bus'a erisen ajan
task automatic bus_agent(string name, int num_txn, int delay);
for (int i = 0; i < num_txn; i++) begin
$display(" [%0t] %s: Bus erisimi istiyor...", $time, name);
bus_lock.get(1); // 1 anahtar al (yoksa bekle)
$display(" [%0t] %s: Bus KILITLENDI, islem #%0d basliyor", $time, name, i);
shared_bus_data = $urandom;
#delay; // Islem suresi
$display(" [%0t] %s: Islem #%0d tamamlandi (data=0x%08h)",
$time, name, i, shared_bus_data);
bus_lock.put(1); // 1 anahtar geri ver
$display(" [%0t] %s: Bus SERBEST", $time, name);
#2; // Islemler arasi bekleme
end
endtask
// Bellege erisen ajan
task automatic mem_agent(string name, int delay);
for (int i = 0; i < 3; i++) begin
if (mem_lock.try_get(1)) begin // Bloklamadan dene
$display(" [%0t] %s: Bellek erisimi SAGLANDI", $time, name);
#delay;
mem_lock.put(1);
$display(" [%0t] %s: Bellek serbest", $time, name);
end else begin
$display(" [%0t] %s: Bellek MESGUL, atliyor", $time, name);
end
#5;
end
endtask
initial begin
$display("=== Semaphore Kullanimi ===\n");
// --- Tek anahtarli semaphore (mutex) ---
$display("--- Bus Mutex (1 anahtar) ---");
bus_lock = new(1); // 1 anahtar = mutex
fork
bus_agent("Master_A", 3, 10);
bus_agent("Master_B", 3, 8);
bus_agent("Master_C", 2, 12);
join
// --- Cok anahtarli semaphore ---
$display("\n--- Bellek (2 port = 2 anahtar) ---");
mem_lock = new(2); // 2 es zamanli erisim
fork
mem_agent("Port_1", 8);
mem_agent("Port_2", 6);
mem_agent("Port_3", 10);
join
$display("\n=== Semaphore Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
- Modülde iki semaphore tanımlanır: paylaşılan bus için
bus_lockve bellek içinmem_lock.shared_bus_dataise bus üzerinden taşınan ortak veridir. bus_agentgörevi: Her işlem öncesibus_lock.get(1)ile bir anahtar almaya çalışır; anahtar yoksa bekler. Anahtarı alınca "Bus KILITLENDI" yazar,#delaykadar işlem yapar vebus_lock.put(1)ile anahtarı geri verir. Böylece aynı anda yalnızca bir ajan bus'ı kullanabilir.- Bus mutex bölümü:
bus_lock = new(1)ile tek anahtar, yani bir mutex oluşturulur.forkiçindeMaster_A,Master_BveMaster_Cparalel başlatılır. Üçü de bus'a erişmek ister ama tek anahtar olduğu için sırayla erişirler; çıktıdaki "Bus erisimi istiyor" / "KILITLENDI" / "SERBEST" sırası bu karşılıklı dışlamayı gösterir.joinile hepsinin bitmesi beklenir. mem_agentgörevi:mem_lock.try_get(1)ile bloklamadan dener. Anahtar varsa belleğe erişir ve#delaysonrasımem_lock.put(1)ile bırakır; anahtar yoksa beklemek yerine "Bellek MESGUL, atliyor" deyip geçer. Bu,getiletry_getarasındaki farkın güzel bir örneğidir.- Çoklu port bölümü:
mem_lock = new(2)ile iki anahtar verilir; yani 2 ajan aynı anda belleğe erişebilir.Port_1,Port_2,Port_3paralel çalışır ve üçüncüsü çoğunlukla anahtar bulamayıp atlar.
Önemli Noktalar
new(1)ile oluşturulan semaphore bir mutex gibi davranır ve paylaşılan tek bir kaynağı korumanın en doğru yoludur.- Aldığınız anahtarı mutlaka geri verin (
put); aksi halde anahtar havuzu tükenir ve diğer süreçler sonsuza kadar bloklanır (deadlock). getbloklar,try_getbloklamaz: işlemin beklemesini istiyorsanızget, "kaynak meşgulse atla" davranışı istiyorsanıztry_getkullanın.- Anahtar sayısı (
new(N)) doğrudan eş zamanlı erişim sınırını belirler; donanımdaki port/kaynak sayısına göre seçilmelidir. get(n)/put(n)ile birden fazla anahtar alıp verebilirsiniz; ancak aldığınız ve verdiğiniz anahtar sayılarının dengeli olmasına dikkat edin.- Semaphore yalnızca erişimi düzenler, veri taşımaz; aktarılacak veri için (burada
shared_bus_datagibi) ayrı bir paylaşılan değişken kullanılır. put(n)ile başlangıçta verilenden fazla anahtar koymak mümkündür (new(1)sonra iki kezput(1)→ 2 anahtar). Semaphore bunu denetlemez; "aldığın kadar ver" disiplini tamamen size aittir. Bu, mutex'in kazara iki sürece açılmasının klasik nedenidir.get(2)isteyen bir süreç, havuzda 1 anahtar varken bekler; bu sıradaget(1)isteyen başka bir süreç o tek anahtarı alabilir. Yani semaphore FIFO adaleti garanti etmez; büyük istekler sürekli küçük isteklerin gerisinde kalabilir (starvation).
IPC Üçlüsünü Ayırt Etmek
| Soru | Araç | Örnek cümle |
|---|---|---|
| "Bir şey oldu mu?" | event |
"Reset bitti." |
| "Şu veriyi al." | mailbox |
"İşte sürülecek transaction." |
| "Sıra bende mi?" | semaphore |
"Bus'ı kullanma izni." |
Üçünü birleştiren gerçekçi bir sahne: driver, generator'dan mailbox ile transaction alır, bus'ı sürmek için semaphore anahtarı ister, işi bitince monitor'e event ile "paket gitti" der.
Benzetme: Semaphore, bir kütüphanede ödünç verilen okuma odası anahtarlarıdır. 2 anahtar varsa aynı anda 2 kişi çalışabilir; üçüncü kişi ya kapıda bekler (
get) ya da "doluysa kafeye giderim" der (try_get). Anahtarı iade etmeyen biri (putunutuldu) odayı sonsuza kadar kilitler.
Doğrulamada Nerede Kullanılır?
- Çok master'lı bus modeli: Birden fazla driver aynı interface'i sürüyorsa, bir transaction'ın ortasında başka driver'ın araya girmesini
new(1)ile engellersiniz. - Sınırlı kaynak taklidi: DUT'un 4 kanalı varsa
new(4)ile aynı anda en fazla 4 işlem yürütür, beşincisini bekletirsiniz. - UVM'de karşılığı: Sequencer'ın
lock()/grab()mekanizması, bir sequence'in sequencer'ı tek başına kullanmasını sağlar; mantık tamamen semaphore mantığıdır. UVM'de ayrıcauvm_pool/semaphorekombinasyonları paylaşılan kaynak yönetiminde kullanılır.
Sık Yapılan Hatalar
putunutmak: Özelliklereturnile erken çıkan ya dadisableile öldürülen süreçlerde anahtar geri verilmez → deadlock. Kritik bölgeyi kısa tutun ve her çıkış yolundaputolduğundan emin olun.- Fazla
put: Mutex iki kez açılır, iki süreç aynı anda kritik bölgeye girer; veri bozulur ama hata mesajı alınmaz. try_getsonucunu kontrol etmemek:void'(mem_lock.try_get(1));yazıp devam eden süreç, anahtar alamadığı hâlde korumalı bölgeye girer.- Semaphore'u
new()etmeden kullanmak:nullhandle üzerindengetçağrısı çalışma zamanı hatasıdır;new(N)çağrısını bileşen kurulumunda yapın. - Semaphore ile veri taşımaya çalışmak: Anahtar sayısı bir sayaçtır, mesaj değildir. Veri için mailbox kullanın.
Kendinizi Deneyin
bus_lock = new(1)yerinenew(2)yapın. Çıktıda iki master'ın aynı anda "KILITLENDI" yazdığı anları bulun;shared_bus_data'nın hangi master'a ait olduğu belirsizleşti mi?bus_agentiçindekibus_lock.put(1);satırını silin. Simülasyon nerede takıldı? Hangi master'lar hiç bus alamadı?mem_agent'tatry_getyerinegetkullanın. "MESGUL, atliyor" mesajları kayboldu mu? Zamanlama nasıl değişti?Master_Aiçinbus_lock.get(1)yerinebus_lock.get(2)yazın (mutex'te 2 anahtar istemek). Ne oldu? Bu davranış starvation kavramını nasıl gösteriyor?
Hızlı Kontrol: semaphore s = new(1); ile oluşturulan yapıya ne ad verilir ve neden?
Mutex (karşılıklı dışlama kilidi). Tek anahtar olduğu için aynı anda yalnızca bir süreç get(1) ile kritik bölgeye girebilir; diğerleri anahtar put(1) ile iade edilene kadar bekler.