Sınıflar ve Nesneler
Gün 2: Nesne Yönelimli Programlama (OOP) | OOP temelleri: sınıf tanımlama, nesne oluşturma, handle kavramı
Bu derste SystemVerilog'da nesne yönelimli programlamanın temel taşı olan sınıfları (class) ve nesneleri (object) öğreneceğiz. Bir sınıfın nasıl tanımlandığını, nesnelerin new() ile nasıl oluşturulduğunu ve handle (referans) kavramının değişken kopyalamasından neden farklı olduğunu inceleyeceğiz.
Sınıf ve Nesne Nedir?
Sınıf (class), birbirine ait verileri (özellikleri) ve bu veriler üzerinde çalışan metodları tek bir şablon altında toplayan bir yapıdır. Sınıf yalnızca bir tasarımdır; bellekte yer kaplamaz. Nesne (object) ise bu şablondan new() ile üretilen, bellekte gerçekten var olan somut bir örnektir.
Donanım doğrulamada sınıflar; transaction, paket, sürücü (driver), monitör gibi test bileşenlerini modellemek için kullanılır. Sınıflar dinamik olduğundan yalnızca simülasyon ortamında (testbench) yaşarlar, sentezlenmezler.
- Özellikler (properties): Sınıfın verilerini tutan değişkenlerdir (
int id,bit [7:0] datagibi). - Metodlar (methods): Sınıfın davranışını tanımlayan
functionvetask'lardır. - Constructor (
new): Nesne oluşturulurken çalışan, özellikleri başlatan özel metoddur.
Handle (Referans) Kavramı
SystemVerilog'da bir sınıf değişkeni, nesnenin kendisini değil, nesneye işaret eden bir handle (referans/işaretçi) tutar. Bu, C/C++'daki pointer mantığına benzer ve OOP'yi anlamanın en kritik noktasıdır:
- Bir sınıf değişkeni tanımlandığında ama
new()çağrılmadığında, değerinullolur (henüz bir nesneyi işaret etmez). - İki handle aynı nesneye işaret edebilir. Bir handle'ı diğerine atamak nesneyi kopyalamaz, sadece aynı nesneye ikinci bir referans oluşturur.
- Bu nedenle bir handle üzerinden yapılan değişiklik, aynı nesneyi işaret eden tüm handle'lardan görülür.
Kaynak Kod
// =============================================================================
// GUN 2 - Konu 1: OOP Temelleri - Siniflar (Classes) ve Nesneler (Objects)
// =============================================================================
class Packet;
// Ozellikler (Properties)
int id;
bit [7:0] data;
string name;
// Constructor (Kurucu Metod)
function new(int id = 0, bit [7:0] data = 0, string name = "default");
this.id = id;
this.data = data;
this.name = name;
endfunction
// Metod: Paketi yazdir
function void display();
$display(" Packet: id=%0d, data=0x%02h, name=%s", id, data, name);
endfunction
endclass
module siniflar_ve_nesneler;
initial begin
Packet pkt1, pkt2, pkt3;
$display("=== Siniflar ve Nesneler ===\n");
// --- Nesne Olusturma (Instantiation) ---
$display("--- Nesne Olusturma ---");
pkt1 = new(); // Varsayilan degerlerle
pkt2 = new(1, 8'hAB, "veri_paketi"); // Parametreli
pkt3 = new(2, 8'hFF, "kontrol"); // Parametreli
pkt1.display();
pkt2.display();
pkt3.display();
// --- Nesne Ozelliklerine Erisim ---
$display("\n--- Ozellik Erisimi ---");
pkt1.id = 99;
pkt1.data = 8'hDE;
pkt1.name = "guncellenmis";
$display(" Guncellenen pkt1:");
pkt1.display();
// --- Handle (Referans) Davranisi ---
$display("\n--- Handle (Referans) ---");
begin
Packet pkt_ref;
pkt_ref = pkt2; // Kopyalama degil, ayni nesneye referans
pkt_ref.data = 8'h00;
$display(" pkt_ref.data = 0x%02h (degistirildi)", pkt_ref.data);
$display(" pkt2.data = 0x%02h (ayni nesne!)", pkt2.data);
end
// --- null Kontrolu ---
$display("\n--- null Kontrolu ---");
begin
Packet pkt_null; // Baslatilmamis -> null
if (pkt_null == null)
$display(" pkt_null henuz olusturulmadi (null)");
pkt_null = new(10, 8'hBB, "yeni");
if (pkt_null != null)
$display(" pkt_null artik mevcut:");
pkt_null.display();
end
// --- Nesne Dizisi ---
$display("\n--- Nesne Dizisi ---");
begin
Packet pkt_arr [5];
foreach (pkt_arr[i]) begin
pkt_arr[i] = new(i, i * 16, $sformatf("pkt_%0d", i));
end
foreach (pkt_arr[i])
pkt_arr[i].display();
end
$display("\n=== Siniflar ve Nesneler Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
Packetsınıfı:id,datavenameözelliklerinden oluşur. Bu üç değişken herPacketnesnesinin kendine ait verisidir.new()constructor'ı: Varsayılan değerli argümanlar (id = 0,data = 0,name = "default") alır.this.id = idifadesi, nesneninidözelliğine parametre olarak geleniddeğerini atar;thisile özellik ve parametre ayrıştırılır.display()metodu: Nesnenin güncel değerlerini ekrana yazdırır.voiddöndürdüğü için bir değer üretmez.- Nesne oluşturma:
pkt1 = new()varsayılan değerlerle,pkt2 = new(1, 8'hAB, "veri_paketi")ise parametreli olarak nesne yaratır. Hernew()çağrısı bellekte ayrı bir nesne üretir. - Özellik erişimi:
pkt1.id = 99gibi nokta operatörü (.) ile nesnenin özelliklerine doğrudan erişilip değer atanabilir. - Handle davranışı:
pkt_ref = pkt2satırıpkt2'nin işaret ettiği nesneyi kopyalamaz;pkt_refilepkt2aynı nesneyi gösterir. Bu yüzdenpkt_ref.data = 8'h00yapıldığındapkt2.datada0x00olur. nullkontrolü:pkt_nulltanımlanır amanew()çağrılmadığı içinnull'dır.if (pkt_null == null)ile bu durum kontrol edilir; ardındannew(10, 8'hBB, "yeni")ile gerçek bir nesneye bağlanır.- Nesne dizisi:
Packet pkt_arr [5]beş adet handle tutar.foreachdöngüsünde her eleman için ayrı ayrınew()çağrılır; aksi halde dizinin tüm elemanlarınullkalırdı.
Önemli Noktalar
- Bir sınıf değişkeni tanımlamak nesne oluşturmaz. Nesneyi bellekte var etmek için mutlaka
new()çağrılmalıdır; aksi halde handlenullkalır. nullbir handle üzerinden özelliğe veya metoda erişmek (örneğinpkt_null.display()nesne yokken) çalışma zamanı hatasına yol açar. Erişimden önce gerekirse== nullkontrolü yapın.- Handle ataması (
pkt_ref = pkt2) kopya değildir; gerçek bir kopya gerekiyorsa özel bircopy()metodu yazılmalıdır. - Nesne dizilerinde dizinin kendisini bildirmek elemanları oluşturmaz; her elemana ayrı
new()gerekir. thisanahtar kelimesi, parametre adı ile özellik adı çakıştığında özelliği işaret etmek için kullanılır ve kodu okunaklı kılar.- SystemVerilog'da çöp toplama (garbage collection) otomatiktir: bir nesneye işaret eden son handle da
nullolduğunda ya da kapsam dışına çıktığında nesne simülatör tarafından serbest bırakılır. C++'taki gibideleteyoktur; bellek sızıntısı yerine "yanlışlıkla hâlâ bir handle tutuyor olmak" asıl risktir. - Sınıflar
moduleiçinde, birpackageiçinde ya da derleme biriminde (compilation unit) tanımlanabilir. Gerçek projelerde sınıflar neredeyse her zaman bir package içinde toplanır; böylece birden fazla modül/dosya aynı sınıfıimportile paylaşır.
Struct ile Class Arasındaki Fark
Gün 1'de struct ile ilişkili verileri gruplamıştık. Peki neden class'a ihtiyaç var?
struct |
class |
|
|---|---|---|
| Bellek | Değişken tanımlandığı anda ayrılır | Yalnızca new() çağrıldığında ayrılır |
Atama (b = a) |
Değer kopyalanır (bağımsız iki kopya) | Handle kopyalanır (aynı nesne) |
| Metodlar | Yok | function / task içerebilir |
| Kalıtım, polimorfizm | Yok | extends, virtual ile var |
| Rastgeleleştirme | Yalnızca std::randomize ile |
rand alanlar + randomize() yerleşik |
| Sentezlenebilirlik | packed ise evet |
Hayır (yalnızca testbench) |
| Tipik rol | Pin seviyesi veri, bus paketi | Transaction, driver, monitor, scoreboard |
Benzetme: Sınıf bir ev planı, nesne o plandan inşa edilmiş gerçek bir ev, handle ise evin adresidir. Planı çizmek (sınıfı tanımlamak) arsa kaplamaz;
new()evi inşa eder. İki kişiye aynı adresi vermek (pkt_ref = pkt2) iki ev yapmaz, iki kişi aynı eve girer. Adresi olmayan bir kâğıt (nullhandle) ile kapıyı çalmaya çalışırsanız (pkt_null.display()) simülasyon çöker.
Sık Yapılan Hatalar
new()unutmak:Packet p; p.id = 5;yazmak,nullhandle üzerinden erişim nedeniyle çalışma zamanı hatası verir. Sınıf değişkeni tanımlamak nesne yaratmaz.Döngüde tek nesne yaratıp kuyruğa koymak: Aşağıdaki kod aynı nesneyi 5 kez kuyruğa ekler; son değişiklik hepsini etkiler.
Packet p = new(); repeat (5) begin p.id++; q.push_back(p); // HATA: ayni handle 5 kez eklendi endDoğrusu her turda
p = new();çağırmaktır.Handle karşılaştırması ile içerik karşılaştırmasını karıştırmak:
pkt1 == pkt2iki handle'ın aynı nesneyi gösterip göstermediğini sorar, alanların eşitliğini değil. İçerik karşılaştırması için bircompare()metodu yazmalısınız (sonraki ders).Dizi tanımlayıp elemanları
newetmemek:Packet arr[5];beşnullhandle içerir; her elemana ayrınew()gerekir.
Kendinizi Deneyin
pkt_ref = pkt2;satırından sonrapkt_ref = new(7, 8'h77, "yeni");ekleyin.pkt2.datadeğişti mi? Neden?Packetsınıfınafunction bit is_same_object(Packet other); return (this == other); endfunctionmetodu ekleyin;pkt1,pkt2vepkt_refile test edin.pkt_arrdizisininew()çağırmadandisplay()etmeyi deneyin ve simülatörün verdiği hata mesajını not alın. Bu mesajı tanımak gerçek projelerde çok zaman kazandırır.pkt1 = null;yazıp ardındanif (pkt1 == null)kontrolü yapın. Nesneye başka handle kalmadıysa ne oldu?
Hızlı Kontrol: Packet a = new(); Packet b = a; b.id = 9; sonrası a.id nedir?
9. Çünkü b = a nesneyi kopyalamaz, yalnızca handle'ı kopyalar; a ve b aynı nesneyi gösterir. Bağımsız kopya için b = a.copy() gibi açık bir kopyalama metodu gerekir.