Kuyruklar (Queues)
Gün 1: SystemVerilog'a Giriş ve Veri Tipleri | Kuyruk yapısı ve metodları: push, pop, find, sort
Bu derste SystemVerilog kuyruklarını (queue) öğreniyoruz: iki ucundan da büyüyüp küçülebilen, FIFO/LIFO davranışını destekleyen ve doğrulamada sıkça kullanılan esnek bir veri yapısı.
Kuyruk: iki ucundan da açık bir tüp.
Bir uçtan eklersin, diğer uçtan çıkarırsın — ya da tam tersi. Hangi uçları seçtiğin davranışı belirler.
push_back → pop_front
İlk giren ilk çıkar. Scoreboard ve FIFO modellerinin temel deseni.
push_back → pop_back
Son giren ilk çıkar: yığın (stack) gibi davranır.
[$:2]
int q [$:2]; en fazla 3 eleman. Dolu kuyruğa ekleme reddedilir.
Her metodu kendi elinle çalıştır.
Değeri ve indeksi ayarla, düğmelere bas. Elemanların nasıl kaydığını, dönüştüğünü ve çıktığını izle.
KAYNAK KODq_int
DURUMcanlı
SİMÜLATÖR ÇIKTISI$display
Bu komutlardan sonra kuyruk ne olur?
Aklında kalsın.
q[0] ilk, q[$] son$ işareti "son indeks" demektir; dilimleme için q[a:b].find with filtreleritem her elemanı temsil eder; find değerleri, find_index indeksleri döndürür.size() > 0 kontrol et.[$:N]En fazla N+1 eleman; doluyken ekleme reddedilir — gerçek FIFO derinliğini belgelemek için kullan.Kuyruk Nedir?
Kuyruk (queue), $ sembolüyle tanımlanan, çalışma zamanında boyutu değişebilen sıralı bir koleksiyondur. Dinamik diziye benzer ama her iki uçtan da hızlı ekleme/çıkarma yapabilmesiyle ayrılır. Bu yüzden testbench'lerde scoreboard, FIFO modeli ve geçici veri tamponları için idealdir.
int q_int [$];→ sınırsız (unbounded) kuyrukint bounded_q [$:9];→ en fazla 10 elemanlı sınırlı (bounded) kuyruk- Kuyruk elemanları
structgibi karmaşık tipler de olabilir (packet_t q_pkt [$];)
Temel Metodlar
- Ekleme:
push_back()(sona),push_front()(başa),insert(idx, val)(belirli indekse) - Çıkarma:
pop_front()(baştan),pop_back()(sondan),delete(idx)(indeksten sil) - Erişim:
q[0]ilk,q[$]son eleman,q[a:b]dilimleme (slicing) - Sorgu/düzen:
size(),sort(),rsort(),reverse(),shuffle()
Arama: find ve with
Kuyruklar (ve diziler) güçlü bir with yan tümcesiyle filtreleme sunar. find with (item == 30) koşulu sağlayan elemanları, find_index with (...) ise bu elemanların indekslerini döndürür. Burada item, her elemanı temsil eden örtük (implicit) değişkendir. FIFO davranışı genellikle push_back + pop_front ikilisiyle modellenir.
Kaynak Kod
// =============================================================================
// GUN 1 - Konu 6: Kuyruklar (Queues) ve Metodlari
// =============================================================================
module kuyruklar;
// Kuyruk tanimlama: $ sembolu ile
int q_int [$]; // Bos kuyruk
string q_str [$]; // String kuyruk
int bounded_q [$:9]; // Maksimum 10 elemanli sinirli kuyruk
typedef struct {
int id;
string name;
int data;
} packet_t;
typedef struct {
int id;
int data;
int cksum;
} packet_w_cksum;
packet_t q_pkt [$]; // Struct kuyruk
packet_w_cksum q_pkt_cksum [$];
initial begin
$display("=== SystemVerilog Kuyruklar (Queues) ===\n");
// --- Temel Islemler ---
$display("--- Temel Kuyruk Islemleri ---");
// push_back: Sona ekle
q_int.push_back(10);
q_int.push_back(20);
q_int.push_back(30);
q_int.push_back(40);
q_int.push_back(50);
$display(" push_back sonrasi: %p", q_int);
$display(" Boyut = %0d", q_int.size());
// push_front: Basa ekle
q_int.push_front(5);
$display(" push_front(5) sonrasi: %p", q_int);
// pop_front: Bastan cikar
begin
int val;
val = q_int.pop_front();
$display(" pop_front() = %0d, Kuyruk: %p", val, q_int);
end
// pop_back: Sondan cikar
begin
int val;
val = q_int.pop_back();
$display(" pop_back() = %0d, Kuyruk: %p", val, q_int);
end
// --- Ekleme ve Silme ---
$display("\n--- insert ve delete ---");
q_int.insert(2, 25); // Indeks 2'ye 25 ekle
$display(" insert(2, 25): %p", q_int);
q_int.delete(0); // Indeks 0'i sil
$display(" delete(0): %p", q_int);
// --- Erisim ---
$display("\n--- Eleman Erisimi ---");
$display(" Ilk eleman q[0] = %0d", q_int[0]);
$display(" Son eleman q[$] = %0d", q_int[$]);
$display(" Dilimleme q[1:2] = %p", q_int[1:2]);
// --- Arama Metodlari ---
$display("\n--- Arama Metodlari ---");
q_int = '{15, 30, 45, 30, 60, 15};
$display(" Kuyruk = %p", q_int);
begin
int found [$];
found = q_int.find with (item == 30);
$display(" find(==30) = %p", found);
found = q_int.find_index with (item == 30);
$display(" find_index(==30) = %p", found);
found = q_int.find with (item > 40);
$display(" find(>40) = %p", found);
found = q_int.min();
$display(" min = %p", found);
found = q_int.max();
$display(" max = %p", found);
found = q_int.unique();
$display(" unique = %p", found);
end
// --- Siralama ---
$display("\n--- Siralama ---");
q_int.sort();
$display(" sort() = %p", q_int);
q_int.rsort();
$display(" rsort() = %p", q_int);
q_int.reverse();
$display(" reverse() = %p", q_int);
q_int.shuffle();
$display(" shuffle() = %p", q_int);
// --- Struct Kuyruk ---
$display("\n--- Struct Kuyruk ---");
q_pkt.push_back('{id: 1, name: "READ", data: 'hAA});
q_pkt.push_back('{id: 2, name: "WRITE", data: 'hBB});
q_pkt.push_back('{id: 3, name: "READ", data: 'hCC});
foreach (q_pkt[i])
$display(" Paket[%0d]: id=%0d, name=%s, data=0x%02h",
i, q_pkt[i].id, q_pkt[i].name, q_pkt[i].data);
// Struct'ta default kullanımı:
$display("\n--- Default ile atama ---");
q_pkt_cksum.push_back('{id:1, default: 0});
q_pkt_cksum.push_back('{id:2, data: 123, default: 0});
foreach (q_pkt_cksum[i])
$display(" Paket[%0d]: id=%0d, data=%0d, cksum=%0d",
i, q_pkt_cksum[i].id, q_pkt_cksum[i].data, q_pkt_cksum[i].cksum);
// FIFO benzeri kullanim
$display("\n FIFO Kullanimi:");
begin
packet_t p;
while (q_pkt.size() > 0) begin
p = q_pkt.pop_front();
$display(" Isleniyor: id=%0d, %s (0x%02h)", p.id, p.name, p.data);
end
end
$display("\n=== Kuyruklar Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
int q_int [$];sınırsız bir kuyruktur. Ardışıkq_int.push_back(...)çağrılarıyla sona elemanlar eklenir;q_int.push_front(5);ise başa ekler.q_int.size()anlık eleman sayısını verir.q_int.pop_front()baştan,q_int.pop_back()sondan eleman çıkarıp döndürür. Çıkarılan değer örnekte yerelvaldeğişkenine alınır.q_int.insert(2, 25);indeks 2'ye25ekler;q_int.delete(0);indeks 0'daki elemanı siler.- Erişimde
q_int[0]ilk elemanı,q_int[$]son elemanı,q_int[1:2]ise bir dilimi (alt kuyruk) verir. - Arama bölümünde kuyruk
'{15, 30, 45, 30, 60, 15}ile sıfırlanır.q_int.find with (item == 30)değeri 30 olan elemanları,find_index with (item == 30)bunların indekslerini döndürür.find with (item > 40)koşullu filtrelemeyi gösterir.min(),max(),unique()ise tek elemanlı/çok elemanlı sonuç kuyrukları döndürür; bu yüzden hepsifoundadlı kuyruğa atanır. - Sıralama bölümünde
sort()artan,rsort()azalan sırada sıralar;reverse()mevcut sırayı ters çevirir;shuffle()rastgele karıştırır. q_pktbir struct kuyruğudur.q_pkt.push_back('{id: 1, name: "READ", data: 'hAA});gibi struct literal'lar eklenir veforeachile alanları (id,name,data) yazdırılır.q_pkt_cksum.push_back('{id:1, default: 0});satırı struct literal'dadefaultanahtarını gösterir: açıkça verilmeyen tüm alanlar (data,cksum)0ile doldurulur. İkinci örnekte yalnızcadata: 123verilir, kalanlar yinedefault: 0olur.- Sonda FIFO kullanımı:
while (q_pkt.size() > 0)döngüsündeq_pkt.pop_front()ile paketler ekleniş sırasıyla işlenir.
Önemli Noktalar
- Kuyruklar her iki uçtan da verimli ekleme/çıkarma sağlar; FIFO için
push_back+pop_front, LIFO (yığın) içinpush_back+pop_backkullanın. find,find_index,min,max,uniquegibi metodlar yeni bir kuyruk döndürür; bu yüzden sonucu daima bir kuyruğa atayın (örnektekifoundgibi).withyan tümcesindekiitemörtük değişkendir; karmaşık elemanlardaitem.alan_adi(örneğinitem.pkt_type) şeklinde alanlara erişebilirsiniz.- Boş bir kuyrukta
pop_front/pop_backçağırmak, standarda göre bir uyarı üretir ve eleman tipinin varsayılan değerini (intiçin0) döndürür; bu "sahte sıfır" scoreboard'da gerçek veri gibi görünüp hatayı gizleyebilir. Çıkarmadan öncesize() > 0kontrolü yapın. - Sınırlı kuyruğa (
[$:N]) kapasitesinin üzerindepush_backyapmak da hata vermez: fazla eleman uyarıyla düşürülür. Dolu olduğunda bloklayan bir yapı istiyorsanız kuyruk değil, Gün 4'te göreceğiniz sınırlımailboxgereklidir. structliteral'larındadefaultanahtarı, belirtilmeyen alanları tek seferde başlatmanın temiz bir yoludur ve unutulan alan hatalarını azaltır.- Sınırlı kuyruk (
[$:N]) kapasiteyi belgelemek ve sınırlamak için kullanışlıdır; tasarımdaki gerçek FIFO derinliğini modellemek için idealdir. $sembolü iki farklı yerde iki farklı anlam taşır: bildirimde (int q [$]) "sınırsız kuyruk", indekste (q[$]) ise "son eleman" demektir.q[1:$]dilimi "1. elemandan sona kadar" anlamına gelir.
Kuyruk Metodları Özet Tablosu
| Metod | Ne yapar? | Kuyruğu değiştirir mi? | Dönüş |
|---|---|---|---|
push_back(v) / push_front(v) |
Sona / başa ekler | Evet | — |
pop_back() / pop_front() |
Sondan / baştan çıkarır | Evet | Eleman |
insert(i, v) |
i indeksine ekler |
Evet | — |
delete(i) / delete() |
i'yi siler / tümünü boşaltır |
Evet | — |
size() |
Eleman sayısı | Hayır | int |
sort() / rsort() / reverse() / shuffle() |
Yerinde sıralar / ters çevirir / karıştırır | Evet | — |
find with / find_index with |
Koşula uyan eleman / indeksleri | Hayır | Kuyruk |
find_first with / find_last with |
İlk / son uyan eleman | Hayır | Kuyruk (0 veya 1 elemanlı) |
min() / max() / unique() |
Uç değerler / tekrarsızlar | Hayır | Kuyruk |
sum() / product() / and() / or() / xor() |
İndirgeme | Hayır | Eleman tipi |
Benzetme: Kuyruk, bir bankadaki sıra gibidir:
push_backsıraya giren müşteri,pop_frontişlemi biten müşteridir.push_frontise "öncelikli müşteri"nin en öne geçmesidir;q[$]sıranın en sonundaki kişiye bakmaktır. Scoreboard'da beklenen değerler bu sırada bekler; DUT'tan çıktı geldiğinde sıradaki ilk beklenenle karşılaştırılır.
Doğrulamada Tipik Kuyruk Kalıpları
// 1) Scoreboard: beklenen degeri sakla, cikti gelince karsilastir
expected_q.push_back(exp_value); // Driver tarafinda
...
if (expected_q.size() == 0)
$error("Beklenmeyen cikti!");
else if (expected_q.pop_front() !== observed)
$error("Uyusmazlik!");
// 2) Belirli bir alana gore arama (struct kuyrugunda)
match_q = pkt_q.find_first with (item.id == target_id);
if (match_q.size()) $display("Bulundu: %p", match_q[0]);
// 3) Kosula uyan elemani sil (indeksini bul, sonra delete)
idx_q = pkt_q.find_first_index with (item.id == target_id);
if (idx_q.size()) pkt_q.delete(idx_q[0]);
Sık Yapılan Hatalar
- Boş kuyruktan
popetmek: Sahte varsayılan değer döner, hata gizlenir. Her zamansize()kontrol edin. findsonucunu tek değişkene atamak:int x = q.find with (item == 30);derleme hatasıdır; sonuç bir kuyruktur.find_firstbile 0 veya 1 elemanlı kuyruk döndürür.foreachiçinde kuyruğu değiştirmek: Döngü sırasındadelete/pushyapmak indeksleri kaydırır ve eleman atlanmasına yol açar. Önce silinecek indeksleri toplayın, sonra tersten silin ya dafindile yeni kuyruk üretin.q = {}ileq.delete()'i karıştırmak: İkisi de kuyruğu boşaltır; ancakq = {}ifadesi yeni boş bir kuyruk atar. Sonuç aynı olsa da niyeti netleştirmek içindelete()tercih edin.
Kendinizi Deneyin
q_intkuyruğundafind_first with (item > 40)vefind_last with (item > 40)metodlarını deneyin;findile farkını yazın.q_pktkuyruğundanname == "READ"olan paketleri silen bir döngü yazın (ipucu:find_indexile indeksleri bulun, büyükten küçüğe sırayladelete).bounded_q'ya 12 elemanpush_backedin vesize()yazdırın. Fazla elemanlara ne oldu?q_int.sum()ileq_int.sum() with (item * 2)sonuçlarını karşılaştırın.
Hızlı Kontrol: FIFO ve LIFO davranışı için hangi metod çiftleri kullanılır?
FIFO (ilk giren ilk çıkar): push_back + pop_front. LIFO / yığın (son giren ilk çıkar): push_back + pop_back. Doğrulamada scoreboard'lar neredeyse her zaman FIFO davranışını kullanır çünkü DUT çıktıları girdi sırasıyla gelir.