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.