UVM Factory ve Override Mekanizması
UVM Gün 5: Factory, Override ve Test Varyantları | Kayıt (registration),
type_id::create, tip ve instance override, override zinciri, nesne override'ı, komut satırından override,factory.print()
Amaç
Bu dersin amacı, Gün 1'den beri her sınıfın başına yazdığınız `uvm_component_utils makrosunun ve type_id::create çağrısının arkasındaki mekanizmayı, yani UVM Factory'yi anlamak; ardından bu mekanizmayı kullanarak ortamın tek satırına dokunmadan driver'ı, transaction'ı veya herhangi bir bileşeni test seviyesinden değiştirmeyi (override) öğrenmektir. Dersin sonunda "hata enjekte eden driver", "yavaş driver" ve "köşe değer üreten transaction" gibi test varyantlarını yalnızca birer override satırıyla kurabileceksiniz.
Ön Koşullar
- Gün 1–4 konuları: bileşen hiyerarşisi,
build_phasesırası,my_agent/my_env/base_testzinciri, sequence'ler. - SystemVerilog Gün 2: kalıtım (
extends),virtualmetotlar ve$cast(SystemVerilog dersleri 13–14). Factory override, çok biçimliliğin (polymorphism) UVM'deki kurumsallaşmış hâlidir. - SystemVerilog bitirme projesindeki
ALU_Corner_Test(Ders 44): generator'ıenv.gen = corner_genile değiştirmiştiniz. Bu derste aynı işi UVM'in standart yoluyla yapacaksınız.
Factory Nedir? Neden new() Değil?
Gün 1'deki Soru-Cevap bölümünde şu cümle geçmişti: "my_agent::type_id::create() yazdığınızda arka planda UVM Factory'ye 'bana bir my_agent lazım, ama başka bir sınıf kullanmam istendi mi?' diye sorarsınız." Bu derste o sorunun tam olarak nasıl sorulduğunu ve nasıl cevaplandığını göreceksiniz.
Factory, simülasyon boyunca yaşayan tek (singleton) bir nesnedir ve iki defter tutar:
| Defter | İçinde ne var? | Kim yazar? |
|---|---|---|
| Kayıt defteri (registry) | Factory'nin tanıdığı her tip için bir vekil (proxy) nesne: "bu tipten nesne nasıl yaratılır?" bilgisini taşır | `uvm_component_utils / `uvm_object_utils makroları, derleme/elaboration sırasında otomatik |
| Override tablosu | "A istenirse B ver" kuralları; hiyerarşinin tamamı (tip override) veya belirli bir yol (instance override) için |
Test (nadiren env), set_type_override / set_inst_override çağrılarıyla; ya da komut satırı |
type_id::create("drv", this) çağrısı factory'ye gider; factory önce override tablosuna bakar, uygun bir kural varsa istenen tip yerine override tipini yaratır ve döndürür. new() ise doğrudan yapıcıyı çağırır: factory'ye hiç uğramaz, dolayısıyla hiçbir override onu etkileyemez.
Benzetme: Factory bir eczanedir. Reçetede "my_driver" yazar (istenen tip). Eczacı önce ilaç yerine geçerlilik listesine (override tablosu) bakar: "bu ilaç yerine muadili (my_error_driver) verilecek" kuralı varsa muadili verir.
new()ise ilacı doğrudan depodan kendiniz almaktır; eczacı araya girmez, muadil kuralı işlemez.
SystemVerilog Projesinden Buraya
| SystemVerilog projesi (Ders 44) | UVM factory |
|---|---|
ALU_Corner_Test::run() içinde env.build() sonrası env.gen = corner_gen; |
Test'in build_phase'inde tek satır: ALU_Generator::type_id::set_type_override(...) |
Test, env'in iç yapısını (gen2drv_mbx) bilmek zorunda |
Test env'in içini bilmez; yalnızca "tip A yerine B" der |
Değişiklik yalnızca build()'den sonra yapılabilir |
Override, create çağrısından önce kaydedilir; nesne doğru tiple doğar |
Her yeni varyant için run() kopyalanır (kod kokusu) |
Varyant = yeni test sınıfı + override satırı |
+TEST= + case ile test seçimi |
+UVM_TESTNAME= → factory testi adıyla yaratır |
Son satır önemlidir: run_test("base_test") çağrısı da factory'yi kullanır (create_component_by_name). Test sınıflarını `uvm_component_utils ile kaydetmenizin sebebi tam olarak budur.
Lab 5.1: Kayıt ve Üretim — Makronun Arkasında Ne Var?
`uvm_component_utils(my_driver) yazdığınızda derleyici sınıfınızın içine kabaca şunları ekler:
// `uvm_component_utils(my_driver) makrosunun (sadelestirilmis) acilimi:
class my_driver extends uvm_driver #(my_transaction);
// 1) Vekil (proxy) tipi: factory bu tip uzerinden my_driver yaratmayi bilir
typedef uvm_component_registry #(my_driver, "my_driver") type_id;
// 2) Vekili dondurur: override cagrilarinda "orijinal/override tip" olarak kullanilir
static function type_id get_type();
return type_id::get(); // ilk cagrida vekil factory'ye KAYDEDILIR
endfunction
// 3) Nesne uzerinden gercek tipi sorgulamak icin (virtual!)
virtual function uvm_object_wrapper get_object_type();
return type_id::get();
endfunction
// 4) print_topology ve loglarda gorunen isim
virtual function string get_type_name();
return "my_driver";
endfunction
endclass
Kodun Açıklaması
type_id,uvm_component_registry #(my_driver, "my_driver")tipinin takma adıdır. Bu sınıf bir vekildir:my_driver'ın kendisi değil, "istenirsenew("drv", parent)ile birmy_driveryaratabilirim" diyen küçük bir nesne. Factory, sınıfların kendisini değil bu vekilleri tanır.type_id::get(), vekilin tekil örneğini döndürür ve ilk çağrıda onu factory'ye kaydeder (factory.register(me)). UVM bu çağrıyı statik bir başlatıcıyla tetiklediği için siz hiçbir şey yapmadan, simülasyon başlamadan önce tüm*_utilsyazılmış sınıflar kayıtlıdır.type_id::create("drv", this)(makro tarafından değil,uvm_component_registrytarafından sağlanır) şunu yapar:factory.create_component_by_type(type_id::get(), parent.get_full_name(), "drv", parent). Yani factory'ye üç bilgi gider: istenen tipin vekili, hiyerarşik yol (uvm_test_top.env_inst.agent_inst+.drv) ve ebeveyn. Yol bilgisi instance override'ların eşleşmesi için gereklidir.get_type()statik,get_object_type()sanaldır. Override kaydederken sınıf adıylamy_error_driver::get_type()yazarsınız; elinizde bir nesne varken gerçek tipiniobj.get_object_type()ile öğrenirsiniz (birmy_driverhandle'ı aslındamy_error_drivertutuyor olabilir).get_type_name()her sınıfta ayrıca tanımlandığı içinprint_topology()çıktısındaki "Type" sütunu, handle'ın tipini değil gerçekten yaratılan nesnenin tipini gösterir. Override'ın işe yarayıp yaramadığını görmenin en hızlı yolu budur.
`uvm_object_utils aynı şeyi uvm_object_registry ile yapar; tek fark create(name)'in parent almamasıdır. Parametreli sınıflar için `uvm_component_param_utils(T#(P)) / `uvm_object_param_utils kullanılır; bu makrolar tip **adı** kaydetmez (her parametre seti ayrı tiptir), dolayısıyla böyle sınıflar yalnızca tip tabanlı (get_type()) override edilebilir, string/komut satırı tabanlı değil.
Lab 5.2: Tip Override — Hata Enjekte Eden Driver
Önce Gün 4'teki my_driver'a küçük bir düzenleme yapalım: paketin sürüldüğü kısmı virtual bir kanca (hook) metoduna taşıyalım. Böylece türetilmiş driver'lar yalnızca bu metodu ezer, run_phase döngüsünü kopyalamak zorunda kalmaz. (SystemVerilog Ders 44'te ALU_Corner_Test::run() için önerdiğimiz configure_env() kancasının aynısı.)
Görev 1: my_driver'ı kanca metotlu hâle getirin, ardından iki türetilmiş driver yazın.
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
int delay_time = 10; // Gun 4: config_db'den okunur
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(int)::get(this, "", "driver_delay", delay_time))
`uvm_warning("DRV_CFG", "driver_delay bulunamadi, varsayilan kullaniliyor")
endfunction
// Dongu sabittir; degisen kisim drive_item'dir
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
seq_item_port.get_next_item(req);
drive_item(req); // <-- kanca: turetilmis siniflar burayi ezer
seq_item_port.item_done();
end
endtask
// Kanca: tek bir paketi pinlere "surer"
virtual task drive_item(my_transaction tr);
`uvm_info("DRIVER", $sformatf("Driving: addr=0x%0h data=0x%0h we=%0b",
tr.addr, tr.data, tr.write_en), UVM_LOW)
#(delay_time);
endtask
endclass
// ---------------------------------------------------------------------------
// VARYANT 1: Hata enjekte eden driver (her ucuncu adreste veriyi bozar)
// ---------------------------------------------------------------------------
class my_error_driver extends my_driver;
`uvm_component_utils(my_error_driver) // override tipi de KAYITLI olmali
function new(string name = "my_error_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual task drive_item(my_transaction tr);
if (tr.addr % 3 == 0) begin
tr.data = ~tr.data;
`uvm_info("ERR_DRV", $sformatf("Hata enjekte edildi: addr=0x%0h data=0x%0h",
tr.addr, tr.data), UVM_LOW)
end
super.drive_item(tr); // normal surus devam eder
endtask
endclass
// ---------------------------------------------------------------------------
// VARYANT 2: Yavas driver (her paket 4 kat uzun surer)
// ---------------------------------------------------------------------------
class my_slow_driver extends my_driver;
`uvm_component_utils(my_slow_driver)
function new(string name = "my_slow_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual task drive_item(my_transaction tr);
`uvm_info("SLOW_DRV", "Yavas surus: 4x gecikme", UVM_LOW)
#(3 * delay_time);
super.drive_item(tr);
endtask
endclass
Görev 2: base_test'e dokunmadan, driver'ı değiştiren yeni bir test yazın.
class error_test extends base_test;
`uvm_component_utils(error_test)
function new(string name = "error_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
// Override, driver YARATILMADAN once kaydedilmeli.
// Driver, agent'in build_phase'inde yaratilir; o da super.build_phase'in
// yarattigi env'in altinda calisir. Bu yuzden override satiri super'den ONCE gelir.
my_driver::type_id::set_type_override(my_error_driver::get_type());
super.build_phase(phase); // base_test: env_inst = my_env::type_id::create(...)
endfunction
endclass
Çalıştırma: +UVM_TESTNAME=error_test. base_test'teki print_topology() çıktısında artık şu satırı görmelisiniz:
Name Type Size Value
-----------------------------------------------------------------
uvm_test_top error_test - @335
env_inst my_env - @348
agent_inst my_agent - @357
drv my_error_driver - @387 <-- istenen my_driver idi
mon my_monitor - @367
sqr my_sequencer - @376
Kodun Açıklaması
drive_itemkancası:run_phase'dekiget_next_item/item_donedöngüsü protokolden bağımsızdır ve her driver'da aynıdır; değişen yalnızca "paketi nasıl sürerim" kısmıdır. Bu kısmıvirtual task drive_itemolarak ayırmak, türetilmiş sınıfların yalnızca bu metodu ezmesini sağlar.virtualolmazsa override işe yaramaz: factorymy_error_driveryaratsa bilerun_phaseiçindekidrive_item(req)çağrısı taban sınıfın metoduna gider (SystemVerilog Ders 14).my_error_driververiyi bozar, sonrasuper.drive_item(tr)ile normal sürüşü yapar. Türetilmiş sınıfın kendi`uvm_component_utilsmakrosu olmak zorundadır; aksi hâldemy_error_driver::get_type()diye bir şey olmaz ve override kaydedilemez.my_driver::type_id::set_type_override(my_error_driver::get_type())cümlesi factory'nin override tablosuna şu satırı ekler: "hiyerarşinin neresinde olursa olsun,my_driveristendiğindemy_error_driveryarat." Aynı işi yapan eşdeğer yazımlar:// uvm_component metodu, tip tabanli: set_type_override_by_type(my_driver::get_type(), my_error_driver::get_type()); // uvm_component metodu, string tabanli (yalnizca parametresiz, adi kayitli siniflar): set_type_override("my_driver", "my_error_driver"); // dogrudan factory nesnesi uzerinden: uvm_factory factory = uvm_factory::get(); factory.set_type_override_by_type(my_driver::get_type(), my_error_driver::get_type());Sıralama: Override tablosu, factory'ye create anında sorulur. Kural, ilgili
createçağrısından önce tabloda olmalıdır. Drivermy_agent::build_phase'de yaratıldığı ve bu dasuper.build_phase(phase)'in başlattığı zincirin sonrasında çalıştığı için, bu örnekte override satırınısuper'den sonra yazsanız da çalışırdı. Amamy_env'in kendisini override etmek isteseydiniz (base_test::build_phaseenv'isuperiçinde yaratır) satır mutlakasuper'den önce olmalıydı. Alışkanlık olarak override satırlarını türetilmiş testinbuild_phase'inin en başına koyun; Gün 1'deki "super'i silmeyin" kuralı geçerliliğini korur, yalnızca override satırları onun önüne geçer.error_testenv'in içini bilmez:my_env,my_agent, sequence'ler değişmedi. Aynı ortam, aynı sequence'ler, farklı driver. Factory'nin asıl değeri budur: ortam yeniden kullanılabilir kalır, varyantlar test katmanında yaşar.
Lab 5.3: Instance Override — Yalnızca Belirli Bir Yol
Tip override "her yerde" geçerlidir. Bazen yalnızca tek bir örneği değiştirmek istersiniz: iki agent'lı bir ortamda yalnızca ikinci agent'ın driver'ı yavaş olsun gibi. Bunun için instance override kullanılır ve hedef, hiyerarşik yol ile belirtilir.
Görev: my_env'e ikinci bir agent ekleyin (Gün 1, Kendinizi Deneyin #4) ve yalnızca onun driver'ını değiştiren bir test yazın.
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent_inst;
my_agent agent_inst2; // ayni sinif, ikinci ornek
// ... scoreboard / coverage (Gun 3) degismedi
function new(string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent_inst = my_agent::type_id::create("agent_inst", this);
agent_inst2 = my_agent::type_id::create("agent_inst2", this);
endfunction
endclass
class mixed_test extends base_test;
`uvm_component_utils(mixed_test)
function new(string name = "mixed_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
// 1) Her yerde: my_driver -> my_error_driver
my_driver::type_id::set_type_override(my_error_driver::get_type());
// 2) Yalnizca agent_inst2 icinde: my_driver -> my_slow_driver
// Yol, 'this' (uvm_test_top) bagina GORECELIDIR:
// tam yol = uvm_test_top.env_inst.agent_inst2.drv
my_driver::type_id::set_inst_override(my_slow_driver::get_type(),
"env_inst.agent_inst2.drv", this);
super.build_phase(phase);
endfunction
endclass
Topoloji çıktısı:
uvm_test_top mixed_test
env_inst my_env
agent_inst my_agent
drv my_error_driver <-- tip override
mon my_monitor
sqr my_sequencer
agent_inst2 my_agent
drv my_slow_driver <-- instance override tip override'i EZDI
mon my_monitor
sqr my_sequencer
Kodun Açıklaması
set_inst_override(override_tipi, yol, this): Üçüncü argüman verildiğinde yol, o bileşenin tam adına eklenir:"uvm_test_top" + "." + "env_inst.agent_inst2.drv". Üçüncü argümanı vermezseniz yolu tam yazmanız gerekir ("uvm_test_top.env_inst.agent_inst2.drv"). İki yazımı karıştırıpthisile birlikte tam yol vermek,uvm_test_top.uvm_test_top....gibi hiçbir şeyle eşleşmeyen bir desen üretir ve override sessizce etkisiz kalır.- Eşleşme,
createanındaki yol ile yapılır: Factory,agent_inst2kendibuild_phase'indemy_driver::type_id::create("drv", this)dediğinde yoluuvm_test_top.env_inst.agent_inst2.drvolarak hesaplar ve tablodaki desenleuvm_is_match(glob) ile karşılaştırır. Joker kullanabilirsiniz:"env_inst.*"env altındaki tümmy_driveristeklerini,"*.agent_inst2.*"ise adıagent_inst2olan her agent'ın altındakileri yakalar. - Öncelik: Hem tip hem instance override eşleşiyorsa instance kazanır. Bu yüzden
agent_inst2.drv,my_error_driverdeğilmy_slow_driveroldu. - Eşdeğer yazımlar:
set_inst_override_by_type("env_inst.agent_inst2.drv", my_driver::get_type(), my_slow_driver::get_type())(uvm_component metodu, yol göreceli) veset_inst_override("env_inst.agent_inst2.drv", "my_driver", "my_slow_driver")(string tabanlı).
Override Çözümleme Kuralları
Factory bir create isteğini şu sırayla çözer (uvm_factory::find_override_by_type):
- Instance override tablosu: İstenen tip için kayıtlı instance override'lar kayıt sırasıyla taranır; yolu eşleşen ilk kural uygulanır.
- Tip override tablosu: İstenen tip için bir tip override varsa uygulanır.
- Zincir: Bulunan override tipi için 1–2 adımları yeniden çalıştırılır.
A→BveB→Ckuralları varsaAisteğiCdöndürür. - Hiçbir kural yoksa istenen tipin kendisi yaratılır.
| Durum | Sonuç |
|---|---|
Aynı tip için iki kez set_type_override |
Varsayılan replace=1: son çağrı öncekini değiştirir |
Aynı tip, aynı yol için iki set_inst_override |
İlk kayıt kazanır (instance kuralları kayıt sırasıyla taranır) |
| Hem instance hem tip override eşleşiyor | Instance override |
| Override tipi orijinalden türemiyor | Nesne yaratılır ama $cast başarısız olur → UVM_FATAL [FCTTYP] |
new() ile yaratılan nesne |
Factory'ye sorulmaz; hiçbir override işlemez |
Türetilmiş tip için override (my_error_driver → X), ama kod my_driver::type_id::create diyor |
my_driver isteği my_error_driver'a yönlenmediyse ikinci kural hiç tetiklenmez; kurallar istenen tipe göre aranır, kalıtım ağacına göre değil |
Aşağıdaki laboratuvarda bu kuralları canlı izleyebilirsiniz: senaryoyu seçin, build_phase'i çalıştırın ve her create çağrısının factory'ye gidip tablolara bakmasını, sonra hiyerarşide hangi tipin doğduğunu görün.
Reçete yazarsın, eczacı muadili verir.
type_id::create bir reçetedir: "bana bir my_driver lazım". Factory önce override tablosuna bakar; bir muadil kuralı varsa onu yaratır. new() ise eczacıya hiç uğramaz.
1 · Kayıt
`uvm_component_utils her sınıf için bir vekil (proxy) üretir ve factory'nin kayıt defterine yazar.
2 · Üretim
type_id::create("drv", this) factory'ye gider; factory vekil üzerinden new("drv", parent) çağırıp nesneyi döndürür.
3 · Override
Testteki tek satır, "my_driver istenince my_error_driver ver" kuralını yazar. Env'in hiçbir satırı değişmez.
build_phase'i çalıştır, factory'nin kararını izle.
Bir senaryo seç ve çalıştır. Soldaki hiyerarşi kurulurken her create çağrısı sağdaki factory'ye gider; tablolar taranır, karar verilir ve bileşen gerçek tipiyle doğar. Pembe = override edildi, sarı = new() ile factory atlandı.
KAYNAK KODtest · env · agent · sequence
DURUMcanlı
print_topology()Name · Type
FACTORY GÜNLÜĞÜistek → karar
Factory hangi tipi yaratır?
Aklında kalsın.
`uvm_*_utils kaydı, set_*_override kuralı yazar; type_id::create ikisine de bakar.A→B ve B→C varsa A isteği C döndürür; factory kuralı yeniden uygular.new() factory'yi atlarDoğrudan yapıcı çağrısı hiçbir override görmez — ve hata da vermez.$cast başarısız olur: UVM_FATAL [FCTTYP].create'ten önce kaydedilmeli; testin build_phase'inin başı doğru yerdir.Lab 5.4: Nesne Override — Transaction'ı Değiştirmek
Override yalnızca bileşenler için değildir; uvm_object türevleri (transaction, sequence, konfigürasyon nesneleri) de factory'den yaratıldığı sürece override edilebilir. En yaygın kullanım, sequence'e dokunmadan üretilen paketlerin kısıtlarını daraltmaktır.
Görev: Yalnızca köşe adresler üreten bir transaction yazın ve write_sequence / read_sequence'i değiştirmeden onu devreye alın.
class corner_transaction extends my_transaction;
`uvm_object_utils(corner_transaction)
function new(string name = "corner_transaction");
super.new(name);
endfunction
// Ek kisit: adres yalnizca kose degerlerden secilsin.
// Taban siniftaki kisitlar ve sequence'teki 'with { write_en == ... }' aynen gecerli.
constraint c_corner { addr inside {8'h00, 8'h01, 8'hFE, 8'hFF}; }
endclass
class corner_test extends base_test;
`uvm_component_utils(corner_test)
function new(string name = "corner_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
// Sequence'ler run_phase'de 'my_transaction::type_id::create("req")' der;
// factory onlara artik corner_transaction verecek.
my_transaction::type_id::set_type_override(corner_transaction::get_type());
super.build_phase(phase);
endfunction
endclass
Driver logları (+UVM_TESTNAME=corner_test):
UVM_INFO ... [DRIVER] Driving: addr=0xff data=0x3a91c2d0 we=1
UVM_INFO ... [DRIVER] Driving: addr=0x1 data=0x7b0e55f4 we=1
UVM_INFO ... [DRIVER] Driving: addr=0xfe data=0x9c2d1a08 we=0
UVM_INFO ... [DRIVER] Driving: addr=0x0 data=0x11e4f3b7 we=0
Kodun Açıklaması
- Sequence kodu değişmedi:
write_sequence::bodyhâlâreq = my_transaction::type_id::create("req")yazıyor;reqhandle'ının tipimy_transaction, ama işaret ettiği nesnecorner_transaction.req.randomize() with { write_en == 1'b1; }çağrısı nesnenin gerçek tipindeki tüm kısıtları (c_cornerdahil) çözer; çünkürandomize()sanal davranır. Bu, SystemVerilog Ders 14'teki "handle tipi ≠ nesne tipi" ilkesinin doğrudan uygulamasıdır. - Override
build_phase'de, etkirun_phase'de: Transaction'lar simülasyon ortasında yaratılır; ama kural çok önceden tabloda olduğu için hercreatedoğru tipi döndürür. Override tablosu zamanla ilgilenmez, yalnızca "kuralcreate'ten önce var mıydı?" sorusuyla ilgilenir. - Türetilmiş alana erişim:
corner_transaction'a yeni bir alan ekleseydiniz (bit is_corner), driver'dakireqüzerinden doğrudan erişemezdiniz;corner_transaction ct; if ($cast(ct, req)) ...gerekir. Alanları taban sınıfta tanımlayıp davranışı türetilmişte değiştirmek,$castzincirlerinden kurtarır. - Driver'da
item_done()sonrasıreq'i saklamayın: Override edilmiş transaction'lar da aynı ömür kurallarına tabidir (Gün 4, Sık Yapılan Hatalar).
Lab 5.5: Komut Satırından Override ve factory.print()
Kod yazmadan, yalnızca simülatör argümanıyla da override tanımlayabilirsiniz. Regresyonda "aynı testi hata enjekteli driver ile bir kez daha koş" demenin en ucuz yolu budur:
# Tip override: istenen_tip,override_tip[,replace]
vsim ... +UVM_TESTNAME=base_test +uvm_set_type_override=my_driver,my_error_driver
# Instance override: istenen_tip,override_tip,TAM_yol
vsim ... +UVM_TESTNAME=base_test \
+uvm_set_inst_override=my_driver,my_slow_driver,uvm_test_top.env_inst.agent_inst2.drv
Bu argümanlar string tabanlı olduğu için yalnızca adı kayıtlı (parametresiz, *_utils ile kaydedilmiş) tipler için çalışır ve run_test() başlamadan, yani hiçbir bileşen yaratılmadan önce uygulanır. Yazım hatası yaparsanız UVM, override tipinin kayıtlı olmadığını belirten bir UVM_ERROR [TYPNTF] basar ve override'ı uygulamaz; hata sayacı arttığı için koşu kırmızıya döner ama mesajı okumazsanız sebebini driver'da ararsınız. Bu yüzden önemli bir regresyonda override'ın gerçekten uygulandığını topolojiden de doğrulayın.
Tabloyu görmek için factory.print() kullanın; en uygun yer end_of_elaboration_phase'dir (tüm override'lar kaydedilmiş, hiyerarşi kurulmuştur):
// base_test icinde
virtual function void end_of_elaboration_phase(uvm_phase phase);
uvm_factory factory = uvm_factory::get();
super.end_of_elaboration_phase(phase);
factory.print(0); // 0: yalnizca override'lar, 1: + kayitli tipler
uvm_top.print_topology(); // gercekte ne yaratildi?
endfunction
mixed_test için çıktı:
#### Factory Configuration (*)
Instance Overrides:
Requested Type Override Path Override Type
-------------- -------------------------------------- --------------
my_driver uvm_test_top.env_inst.agent_inst2.drv my_slow_driver
Type Overrides:
Requested Type Override Type
-------------- ---------------
my_driver my_error_driver
factory.print() kuralları, print_topology() sonucu gösterir. Bir override "çalışmıyor" dediğinizde önce kuralın tabloda olup olmadığına, sonra yolun ve tipin gerçekten eşleşip eşleşmediğine bakın; iki çıktının birlikte okunması hemen her factory sorununu çözer.
Önemli Noktalar
- Factory = kayıt defteri + override tablosu:
*_utilsmakroları kaydı,set_*_overrideçağrıları kuralları yazar;type_id::createikisine de bakar. - Override
create'ten önce: Kural, etkilemesi gerekencreateçağrısından önce tabloda olmalıdır. Türetilmiş testinbuild_phase'inin en başı (gerekirsesuper.build_phase'den önce) doğru yerdir. - Instance > tip; son tip override kazanır; zincir çözülür: Çözümleme kuralları tablosunu ezberlemenize gerek yok, ama
print_topology()ile sonucu her zaman doğrulayın. - Override tipi orijinalden türemeli: Factory nesneyi yaratır ama
$castbaşarısız olur;UVM_FATAL [FCTTYP]ile karşılaşırsınız. Kalıtım ağacını bozmayın. virtualolmayan metot override edilemez: Factory doğru nesneyi yaratsa bile çağrı taban sınıfa gider. Kanca metotlarını (drive_item,do_compare,body...) daimavirtualtanımlayın.new()factory'yi atlar: Gün 1'den beri "her zamancreate" dememizin gerçek sebebi bu derstir. Bir bileşeninewile yarattığınız anda onun için yazılacak hiçbir override çalışmaz, hata da almazsınız.- Override test katmanında yaşar: Env ve agent'lar genel kalır; her varyant yeni bir test sınıfı + birkaç override satırıdır. SystemVerilog projesindeki "
run()'ı kopyala, arayaenv.gen = ...sok" kalıbı burada biter. - Nesneler de override edilir: Transaction ve sequence'ler factory'den yaratıldığı sürece (
type_id::create) test seviyesinden değiştirilebilir; kısıt daraltma için en temiz yol budur.
Factory, Config DB ve Polymorphism: Hangisi Ne Zaman?
| İhtiyaç | Araç | Örnek |
|---|---|---|
| Bir bileşenin davranışını değiştirmek (farklı sınıf) | Factory override | Hata enjekteli driver, yavaş driver |
| Bir bileşene değer/handle vermek (aynı sınıf) | uvm_config_db (Gün 4) |
driver_delay = 35, vif, is_active |
| Üretilen veriyi şekillendirmek | Nesne override veya sequence seçimi | corner_transaction, write_sequence yerine burst_sequence |
| Handle üzerinden doğru metodun çağrılması | virtual metot (SystemVerilog Gün 2) |
drive_item, body |
Üçü birlikte çalışır: config_db neyi, factory kimi, virtual ise nasılı belirler. Bir driver'ın gecikmesini değiştirmek için override yazmak (yeni sınıf) aşırıdır; config_db yeter. Driver'ın protokol davranışını değiştirmek için config_db'ye mode bayrakları yığmak ise kodu if ormanına çevirir; override yazın.
Sık Yapılan Hatalar
- Override'ı
create'ten sonra kaydetmek: Kural tabloya girer ama nesne çoktan yaratılmıştır;factory.print()kuralı gösterir,print_topology()eski tipi. En sık görülen factory hatasıdır. - Türetilmiş sınıfa
*_utilsyazmamak:my_error_driver::get_type()derlenmez ("type_id not found"). Her override tipi kendi makrosuyla kayıtlı olmalıdır. - Uyumsuz tipe override:
my_driveryerinemy_monitor→UVM_FATAL [FCTTYP] Factory did not return a component of type 'my_driver'.... Override tipi mutlakaextendszinciriyle orijinale bağlı olmalıdır. set_inst_override(..., tam_yol, this):thisgöreceli yol ister; tam yol verilirseuvm_test_top.uvm_test_top.env_inst...deseni oluşur, hiçbir şeyle eşleşmez, override sessizce atlanır.factory.print()çıktısındaki yolu kontrol edin.virtualeksik kanca: Factory doğru nesneyi yaratır, log'damy_error_drivergörünür ama davranış değişmez. Metot imzasının başındakivirtual'ı kontrol edin.- Komut satırında yazım hatası:
+uvm_set_type_override=my_driver,my_errr_driver→UVM_ERROR [TYPNTF] ... is not registered with the factory, override uygulanmaz. Aynı hata kod içindeki string tabanlıset_type_override("my_driver", "my_errr_driver")için de geçerlidir; tip tabanlı (get_type()) yazım bu hatayı derleme zamanında yakalar. - Parametreli sınıfı string ile override etmek:
set_type_override("my_fifo#(8)", ...)eşleşmez; parametreli sınıflarda yalnızcaget_type()tabanlı override kullanın. - Aynı override'ı hem kodda hem komut satırında vermek: Zararsızdır (tip override'da son kazanır, instance'ta ilk) ama hangi kuralın uygulandığı belirsizleşir; bir kaynak seçin.
Kendinizi Deneyin
error_test'teki override satırınısuper.build_phase(phase)'den sonraya taşıyın. Driver yine override edildi mi? Neden? Ardındanmy_env'den türeyen birmy_big_envyazıp onu da override etmeye çalışın; satırın yeri bu kez fark ediyor mu?my_slow_driver'ımy_driveryerine doğrudanuvm_driver #(my_transaction)'dan türetin vemixed_test'i çalıştırın. AldığınızUVM_FATALmesajının ID'si ne? Neden factory nesneyi yaratmasına rağmen simülasyon duruyor?mixed_test'teki instance override yolunu"env_inst.*"yapın. Hangi driver'larmy_slow_driveroldu? Tip override'ın hiç uygulanmadığı bir bileşen kaldı mı?my_agent::build_phase'dedrv = my_driver::type_id::create("drv", this);satırınıdrv = new("drv", this);yapın veerror_test'i koşun.factory.print()kuralı gösteriyor mu?print_topology()ne diyor? Satırı geri alın.- Kodda hiç override olmadan, yalnızca
+uvm_set_type_override=my_driver,my_error_driverargümanıylabase_test'i koşun; sonra argümandaki tip adında bilinçli bir yazım hatası yapın ve UVM'in ne bastığını not alın. - Zincir:
mixed_test'emy_error_driver::type_id::set_type_override(my_slow_driver::get_type());satırını ekleyin.agent_inst.drvartık hangi tipte? Çözümleme kurallarındaki 3. adımla açıklayın. drive_itemmetodunun başındakivirtual'ı silin (hem taban hem türetilmiş sınıfta). Topolojimy_error_driverdiyor; pekiERR_DRVmesajları basılıyor mu? Bu deney factory ile polymorphism'in neden ayrı şeyler olduğunu gösterir.
Hızlı Kontrol: Aynı anda hem my_driver → my_error_driver tip override'ı hem de agent_inst2.drv için my_driver → my_slow_driver instance override'ı varsa, agent_inst.drv ve agent_inst2.drv hangi tiplerde yaratılır?
agent_inst.drv için yalnızca tip override eşleşir → my_error_driver. agent_inst2.drv için hem instance hem tip override eşleşir; instance override önceliklidir → my_slow_driver. Factory daha sonra my_slow_driver için de kural arar (zincir); yoksa bu tipi yaratır.
UVM Gün 5: Sıkça Sorulan Sorular (FAQ)
Soru 1: Override'ı neden test'te yapıyoruz? Env'in build_phase'ine yazsak olmaz mı?
Cevap: Teknik olarak çalışır; ama env'in görevi ortamı kurmak, testin görevi senaryoyu seçmektir. Override bir senaryo kararıdır ("bu koşuda hata enjekteli driver kullan"). Env'e yazdığınız anda o env'i kullanan her test o override'ı alır ve ortam yeniden kullanılabilir olmaktan çıkar. Kural: env ve agent'lar override yazmaz, yalnızca create ile override'a açık olur.
Soru 2: get_type() ile get_object_type() arasındaki fark nedir?
Cevap: get_type() statiktir ve sınıf adıyla çağrılır (my_error_driver::get_type()): "bu sınıfın vekili". get_object_type() sanaldır ve bir nesne üzerinden çağrılır (drv.get_object_type()): "bu nesnenin gerçek tipinin vekili". Override sonrası my_driver drv handle'ı bir my_error_driver tutuyorsa drv.get_object_type() my_error_driver'ın vekilini, my_driver::get_type() ise my_driver'ınkini döndürür. drv.get_type_name() de aynı şekilde "my_error_driver" der.
Soru 3: Override edilen sınıfın yapıcı (new) imzası aynı olmak zorunda mı?
Cevap: Evet. Factory, vekil üzerinden new(name, parent) (nesnelerde new(name)) çağırır; türetilmiş sınıf farklı bir imza sunarsa vekil sınıfı derlenmez. Ek parametre gerekiyorsa uvm_config_db ile taşınır (Gün 4). Bu kural Gün 1'deki "new() imzasını değiştirmeyin" uyarısının nedenidir.
Soru 4: Factory override ile polymorphism aynı şey mi?
Cevap: Birbirini tamamlar ama aynı değildir. Polymorphism (SystemVerilog Ders 14), bir taban handle üzerinden çağrılan virtual metodun nesnenin gerçek tipine göre seçilmesidir. Factory override ise o handle'a hangi tipte nesnenin konulacağına create anında karar verir. Factory doğru nesneyi yaratır, polymorphism doğru metodu çağırır. Biri olmadan diğeri işe yaramaz: virtual olmayan bir kanca override edilmiş nesnede bile taban davranışı gösterir (Kendinizi Deneyin #7).
Soru 5: Instance override yolunda uvm_test_top'ı yazmalı mıyım?
Cevap: API'ye bağlı. type_id::set_inst_override(tip, yol, this) ve set_inst_override_by_type(yol, orijinal, override) (uvm_component metodu) göreceli yol ister; uvm_test_top önüne otomatik eklenir. factory.set_inst_override_by_type(orijinal, override, yol) ve +uvm_set_inst_override=... ise tam yol ister. Şüphede kaldığınızda factory.print() çıktısındaki "Override Path" sütunu hangi desenin kaydedildiğini gösterir.
Soru 6: uvm_config_db ile is_active değiştirmek de bir tür override değil mi?
Cevap: Hayır; config_db değer taşır, factory tip seçer. is_active = UVM_PASSIVE demek agent'ın sınıfını değiştirmez, yalnızca build_phase'deki bir if'i etkiler. Override ise drv handle'ına bambaşka bir sınıfın nesnesini koyar. İkisini karıştırmanın pratik sonucu şudur: bir davranış farkını if (mode == ...) ile mi, yoksa türetilmiş sınıfla mı ifade edeceğinize karar verirken "bu fark bir değer mi, bir tip mi?" diye sorun.