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_phase sırası, my_agent / my_env / base_test zinciri, sequence'ler.
  • SystemVerilog Gün 2: kalıtım (extends), virtual metotlar 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_gen ile 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, "istenirse new("drv", parent) ile bir my_driver yaratabilirim" 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 *_utils yazılmış sınıflar kayıtlıdır.
  • type_id::create("drv", this) (makro tarafından değil, uvm_component_registry tarafı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ıyla my_error_driver::get_type() yazarsınız; elinizde bir nesne varken gerçek tipini obj.get_object_type() ile öğrenirsiniz (bir my_driver handle'ı aslında my_error_driver tutuyor olabilir).
  • get_type_name() her sınıfta ayrıca tanımlandığı için print_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_item kancası: run_phase'deki get_next_item / item_done dö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_item olarak ayırmak, türetilmiş sınıfların yalnızca bu metodu ezmesini sağlar. virtual olmazsa override işe yaramaz: factory my_error_driver yaratsa bile run_phase içindeki drive_item(req) çağrısı taban sınıfın metoduna gider (SystemVerilog Ders 14).

  • my_error_driver veriyi bozar, sonra super.drive_item(tr) ile normal sürüşü yapar. Türetilmiş sınıfın kendi `uvm_component_utils makrosu olmak zorundadır; aksi hâlde my_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_driver istendiğinde my_error_driver yarat." 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. Driver my_agent::build_phase'de yaratıldığı ve bu da super.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ı. Ama my_env'in kendisini override etmek isteseydiniz (base_test::build_phase env'i super içinde yaratır) satır mutlaka super'den önce olmalıydı. Alışkanlık olarak override satırlarını türetilmiş testin build_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_test env'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ıp this ile 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, create anındaki yol ile yapılır: Factory, agent_inst2 kendi build_phase'inde my_driver::type_id::create("drv", this) dediğinde yolu uvm_test_top.env_inst.agent_inst2.drv olarak hesaplar ve tablodaki desenle uvm_is_match (glob) ile karşılaştırır. Joker kullanabilirsiniz: "env_inst.*" env altındaki tüm my_driver isteklerini, "*.agent_inst2.*" ise adı agent_inst2 olan 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_driver değil my_slow_driver oldu.
  • 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) ve set_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):

  1. 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.
  2. Tip override tablosu: İstenen tip için bir tip override varsa uygulanır.
  3. Zincir: Bulunan override tipi için 1–2 adımları yeniden çalıştırılır. A→B ve B→C kuralları varsa A isteği C döndürür.
  4. 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.

01 — FACTORY NEDİR?

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.

ELABORATION

1 · Kayıt

`uvm_component_utils her sınıf için bir vekil (proxy) üretir ve factory'nin kayıt defterine yazar.

BUILD_PHASE

2 · Üretim

type_id::create("drv", this) factory'ye gider; factory vekil üzerinden new("drv", parent) çağırıp nesneyi döndürür.

TEST SEÇER

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.

02 — OVERRIDE LABORATUVARI

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ı.

SENARYO
ORTAM

KAYNAK KODtest · env · agent · sequence

DURUMcanlı

print_topology()Name · Type

FACTORY GÜNLÜĞÜistek → karar

03 — MEYDAN OKUMA

Factory hangi tipi yaratır?


    
SERİ 0 · EN İYİ 0
04 — ÖZET

Aklında kalsın.

Kayıt + tablo`uvm_*_utils kaydı, set_*_override kuralı yazar; type_id::create ikisine de bakar.
Instance > tipYolu eşleşen instance kuralı tip kuralını ezer. Aynı tip için ikinci tip override öncekini değiştirir.
Zincir çözülürA→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.
Override türetilmiş tip olmalıAksi hâlde nesne yaratılır ama $cast başarısız olur: UVM_FATAL [FCTTYP].
Önce kural, sonra createOverride, etkileyeceği 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::body hâlâ req = my_transaction::type_id::create("req") yazıyor; req handle'ının tipi my_transaction, ama işaret ettiği nesne corner_transaction. req.randomize() with { write_en == 1'b1; } çağrısı nesnenin gerçek tipindeki tüm kısıtları (c_corner dahil) çö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, etki run_phase'de: Transaction'lar simülasyon ortasında yaratılır; ama kural çok önceden tabloda olduğu için her create doğru tipi döndürür. Override tablosu zamanla ilgilenmez, yalnızca "kural create'ten önce var mıydı?" sorusuyla ilgilenir.
  • Türetilmiş alana erişim: corner_transaction'a yeni bir alan ekleseydiniz (bit is_corner), driver'daki req ü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, $cast zincirlerinden 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: *_utils makroları kaydı, set_*_override çağrıları kuralları yazar; type_id::create ikisine de bakar.
  • Override create'ten önce: Kural, etkilemesi gereken create çağrısından önce tabloda olmalıdır. Türetilmiş testin build_phase'inin en başı (gerekirse super.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 $cast başarısız olur; UVM_FATAL [FCTTYP] ile karşılaşırsınız. Kalıtım ağacını bozmayın.
  • virtual olmayan metot override edilemez: Factory doğru nesneyi yaratsa bile çağrı taban sınıfa gider. Kanca metotlarını (drive_item, do_compare, body...) daima virtual tanımlayın.
  • new() factory'yi atlar: Gün 1'den beri "her zaman create" dememizin gerçek sebebi bu derstir. Bir bileşeni new ile 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, araya env.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 *_utils yazmamak: my_error_driver::get_type() derlenmez ("type_id not found"). Her override tipi kendi makrosuyla kayıtlı olmalıdır.
  • Uyumsuz tipe override: my_driver yerine my_monitor → UVM_FATAL [FCTTYP] Factory did not return a component of type 'my_driver'.... Override tipi mutlaka extends zinciriyle orijinale bağlı olmalıdır.
  • set_inst_override(..., tam_yol, this): this göreceli yol ister; tam yol verilirse uvm_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.
  • virtual eksik kanca: Factory doğru nesneyi yaratır, log'da my_error_driver görünür ama davranış değişmez. Metot imzasının başındaki virtual'ı 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ızca get_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

  1. error_test'teki override satırını super.build_phase(phase)'den sonraya taşıyın. Driver yine override edildi mi? Neden? Ardından my_env'den türeyen bir my_big_env yazıp onu da override etmeye çalışın; satırın yeri bu kez fark ediyor mu?
  2. my_slow_driver'ı my_driver yerine doğrudan uvm_driver #(my_transaction)'dan türetin ve mixed_test'i çalıştırın. Aldığınız UVM_FATAL mesajının ID'si ne? Neden factory nesneyi yaratmasına rağmen simülasyon duruyor?
  3. mixed_test'teki instance override yolunu "env_inst.*" yapın. Hangi driver'lar my_slow_driver oldu? Tip override'ın hiç uygulanmadığı bir bileşen kaldı mı?
  4. my_agent::build_phase'de drv = my_driver::type_id::create("drv", this); satırını drv = new("drv", this); yapın ve error_test'i koşun. factory.print() kuralı gösteriyor mu? print_topology() ne diyor? Satırı geri alın.
  5. Kodda hiç override olmadan, yalnızca +uvm_set_type_override=my_driver,my_error_driver argümanıyla base_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.
  6. Zincir: mixed_test'e my_error_driver::type_id::set_type_override(my_slow_driver::get_type()); satırını ekleyin. agent_inst.drv artık hangi tipte? Çözümleme kurallarındaki 3. adımla açıklayın.
  7. drive_item metodunun başındaki virtual'ı silin (hem taban hem türetilmiş sınıfta). Topoloji my_error_driver diyor; peki ERR_DRV mesajları 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.