UVM Eğitimi 4. Gün Laboratuvar Kılavuzu: Sequence'lar, Stimulus Yönetimi ve Konfigürasyon
Amaç
Bu laboratuvarın amacı, UVM testbench'ine dinamik senaryolar eklemek için Sequence sınıfları (uvm_sequence) oluşturmak, hiyerarşik (iç içe) senaryolar yazmak ve UVM Configuration Database (uvm_config_db) kullanarak testten en alt seviyedeki bileşenlere (örneğin Driver'a) parametre/konfigürasyon aktarımı yapmaktır.
Ön Koşullar
- 1., 2. ve 3. gün konuları (Agent hiyerarşisi, Sequence Item, TLM).
- SystemVerilog rand değişkenler ve with kısıtları (constraints) hakkında bilgi.
Lab 4.1: UVM Configuration DB Kullanımı
uvm_config_db, testbench'in herhangi bir yerinden bir kutuya bilgi koyup (set), başka bir yerinden bu kutudaki bilgiyi güvenle almamızı (get) sağlayan sihirli bir veri tabanıdır. Genellikle sanal arayüzleri (virtual interface) veya konfigürasyon nesnelerini taşımak için kullanılır.
uvm_config_db Fonksiyonları ve Parametreleri
Veri tabanına bir şey yazmak için set(), okumak için ise get() metodu kullanılır. Her iki metot da temel olarak 4 adet parametre alır:
Sözdizimi (Syntax):
uvm_config_db#(TYPE)::set(context, inst_name, field_name, value);
uvm_config_db#(TYPE)::get(context, inst_name, field_name, value);
- #(TYPE) : Taşınacak verinin tipidir. int, string, virtual interface veya konfigürasyon sınıfı (uvm_object) olabilir.
- context (Bağlam): İşlemin yapıldığı referans noktasıdır. Genellikle veriyi gönderen/alan sınıfın kendisi olan this anahtar kelimesi kullanılır. Global bir gönderim için uvm_root::get() veya null kullanılabilir.
- inst_name (Örnek Adı): Hedefin, context'e göre hiyerarşik yoludur.
- Gönderirken (Set): "env.agent.driver" gibi hedefin tam veya göreceli adresi yazılır.
- Alırken (Get): Sınıf zaten hedefin kendisiyse, "benim içimde ara" manasına gelen boş string "" kullanılır.
- field_name (Alan Adı): Bu bilgiye verdiğimiz özel etikettir (Örn: "timeout_degeri", "vif"). Okuyan taraf, tam olarak bu etiketi arayarak veriyi bulur.
- value (Değer): set işleminde kutuya konulan asıl veri, get işleminde ise kutudan çıkan verinin kaydedileceği değişkendir.
Basit Kullanım Örnekleri:
// 1. Passing a simple integer
uvm_config_db#(int)::set(this, "*", "max_loops", 100);
// 2. Passing a string globally
uvm_config_db#(string)::set(null, "*", "greeting", "Hello UVM!");
// 3. Receiving a string in any component
string my_str;
if (uvm_config_db#(string)::get(this, "", "greeting", my_str))
`uvm_info("CFG", $sformatf("Got string: %s", my_str), UVM_LOW)
Kodun Açıklaması
Bu blok, uvm_config_db'nin en temel set ve get kullanımlarını üç küçük örnekle gösterir:
- 1. Örnek (
uvm_config_db#(int)::set): Tip parametresi olarakintverilir ve veri tabanına"max_loops"etiketiyle100değeri yazılır.contextolarakthis,inst_nameolarak ise"*"(joker karakter) kullanılmıştır."*"ifadesi "bu bağlamın altındaki tüm bileşenler bu değere erişebilsin" anlamına gelir; yani değerin görünürlüğünü olabildiğince geniş tutar. - 2. Örnek (
uvm_config_db#(string)::set): Bu kez tipstring'tir vecontextolaraknullverilmiştir.nullbağlam, atamayı global (en tepeden) yapar; böylece"greeting"etiketli"Hello UVM!"değeri testbench'in herhangi bir noktasından okunabilir. - 3. Örnek (
uvm_config_db#(string)::get): Önce değeri tutacak birmy_strdeğişkeni tanımlanır.getmetodu okuma başarılı olursa1, başarısız olursa0döndürdüğü için çağrı birifkoşulu içine alınmıştır.inst_nameolarak boş string""kullanılması "tam olarak bu bileşene gönderilen değeri ara" demektir. Değer bulunursauvm_infomakrosu ile$sformatfkullanılarak okunan metin ekrana basılır.
Bu adımda, Test sınıfından Driver sınıfına bir "gecikme süresi" (delay_time) parametresi aktaracağız.
Görev 1: base_test sınıfının build_phase metodunda bir uvm_config_db ataması (set) yapın.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env_inst;
// Constructor
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_inst = my_env::type_id::create("env_inst", this);
// Set a configuration integer for the driver's delay
// Context: this (base_test)
// Target Path: "env_inst.agent_inst.drv"
// Key/Field: "driver_delay"
// Value: 35
uvm_config_db#(int)::set(this, "env_inst.agent_inst.drv", "driver_delay", 35);
`uvm_info("TEST_CFG", "Set driver_delay to 35 via config_db.", UVM_LOW)
endfunction
// ... (Other phases will be added later)
endclass
Kodun Açıklaması
Bu blok, konfigürasyon değerini gönderen (set eden) tarafı tanımlar:
uvm_component_utils(base_test): Sınıfı UVM fabrikasına (factory) kaydeder; böylecebase_testismiyle dinamik olarak yaratılabilir vetype_id::createile örneklenebilir.function new(...): Bileşenin kurucusudur (constructor).super.new(name, parent)çağrısıyla üst sınıfın kurucusunu çalıştırarak bileşeni UVM hiyerarşisineparentaltına bağlar.build_phase: Bileşenlerin yukarıdan aşağıya (top-down) yaratıldığı fazdır. İlk olaraksuper.build_phase(phase)çağrılır.my_env::type_id::create("env_inst", this):envnesnesini fabrika üzerinden yaratır. İkinci argümanthisile yaratılanenv_inst'inbase_test'in çocuğu olduğu belirtilir.uvm_config_db#(int)::set(this, "env_inst.agent_inst.drv", "driver_delay", 35): Asıl konfigürasyon atamasıdır.contextolarakthis(yanibase_test),inst_nameolarak hedefe giden hiyerarşik yol"env_inst.agent_inst.drv",field_nameolarak"driver_delay"etiketi vevalueolarak35verilir. Bu satır, "ileride bu yolda yaratılacak olan driver,driver_delayetiketiyle 35 değerini bulsun" anlamına gelir.uvm_info("TEST_CFG", ...): Atamanın yapıldığınıUVM_LOWayrıntı seviyesinde loglar.
Görev 2: 2. Gün yazdığımız my_driver sınıfının build_phase metodunu güncelleyerek bu değeri okuyun (get).
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
// Variable to hold the delay time
int delay_time = 10; // Default value if not set by config_db
// Constructor
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);
// Get the configuration integer
// Context: this (my_driver)
// Target Path: "" (Look exactly at this component)
// Key/Field: "driver_delay"
// Variable to store: delay_time
if(uvm_config_db#(int)::get(this, "", "driver_delay", delay_time)) begin
`uvm_info("DRV_CFG", $sformatf("Successfully got driver_delay: %0d", delay_time), UVM_LOW)
end else begin
`uvm_warning("DRV_CFG", "Could not get driver_delay! Using default.")
end
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_info("DRIVER", $sformatf("Driving Transaction: Addr=0x%0h, WE=%0b", req.addr, req.write_en), UVM_LOW)
// Use the dynamically configured delay time
#(delay_time);
seq_item_port.item_done();
end
endtask
endclass
Kodun Açıklaması
Bu blok, konfigürasyon değerini alan (get eden) ve onu kullanan tarafı tanımlar:
int delay_time = 10;: Okunacak değeri tutacak değişkendir.10varsayılan değeri,config_db'den okuma başarısız olursa devreye girer; böylece simülasyon yine de güvenli bir değerle devam eder.build_phaseiçindekiuvm_config_db#(int)::get(this, "", "driver_delay", delay_time):setile gönderilen değeri okur.contextolarakthis(driver'ın kendisi),inst_nameolarak boş string""("tam olarak bana gönderileni ara") vefield_nameolarak"driver_delay"kullanılır. Okunan değer doğrudandelay_timedeğişkenine yazılır.if (...) ... else ...:getbaşarılı olursa1döndürür veuvm_infoile okunan değer loglanır; başarısız olursauvm_warningile uyarı basılır ve varsayılan değerle devam edilir. Bu savunmacı yaklaşım, eksik konfigürasyonun sessizce gözden kaçmasını engeller.run_phaseiçindekiseq_item_port.get_next_item(req): Sequencer'dan sıradaki transaction'ı (req) bloklayarak çeker; transaction gelene kadar bekler.#(delay_time): Sürme işlemi sırasındaconfig_db'den dinamik olarak okunan gecikmeyi uygular. Sabit bir değer yerine konfigüre edilebilirdelay_timekullanılması, aynı driver'ın farklı testlerde farklı zamanlamalarla çalışmasını sağlar.seq_item_port.item_done(): Driver'ın işlemi tamamladığını sequencer'a (ve dolayısıylafinish_item'da bekleyen sequence'a) bildirir; handshake'i kapatır.
Lab 4.2: Temel Sequence'ların Oluşturulması
Sequencer ve Driver artık komut bekliyor. Modern UVM'de senaryoları (sequence) yazarken doğrudan start_item() ve finish_item() API'leri kullanılır.
Önemli Kavram: start_item ve finish_item Nasıl Çalışır? (Handshake Mekanizması)
Bu iki metot, Sequence (Garson), Sequencer (Sipariş Sırası) ve Driver (Aşçı) arasındaki senkronizasyonu sağlar:
- start_item(req) (İzin İsteme): Sequence, "Elimde yeni bir işlem var, Driver müsait mi?" diye sorar. Driver get_next_item() komutunu çağırana kadar bu satır kodun akışını bloklar (bekletir).
- Late Randomization (Geç Rastgeleleştirme): Dikkat ederseniz koddaki req.randomize() işlemi start_item'dan sonra yapılmıştır. Bunun sebebi veriyi tam Driver'ın müsait olduğu (iznin çıktığı) milisaniyede üretmektir. Böylece veri, simülasyonun o anki en güncel durumuna göre şekillenir.
- finish_item(req) (Teslimat ve Onay Bekleme): İçi doldurulmuş paket Driver'a yollanır. Ancak Sequence işine devam etmez; Driver fiziksel sürme işlemini bitirip item_done() çağrısını yapana kadar bu satırda bekler.
Görev: Bu mekanizmayı kullanarak; biri sadece YAZMA işlemi üreten (write_sequence), diğeri sadece OKUMA işlemi üreten (read_sequence) iki temel senaryo yazın.
// ----------------------------------------------------------------------------
// SEQUENCE 1: Write Sequence
// ----------------------------------------------------------------------------
class write_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(write_sequence)
function new(string name = "write_sequence");
super.new(name);
endfunction
// The 'body' task contains the behavior of the sequence
virtual task body();
// Create the transaction object
req = my_transaction::type_id::create("req");
// Step 1: Wait for driver to be ready (Handshake begins)
start_item(req);
// Step 2: Late Randomization with inline constraints
if (!req.randomize() with { write_en == 1'b1; }) begin
`uvm_error("SEQ", "Randomization failed!")
end
// Step 3: Send to driver and wait for item_done (Handshake completes)
finish_item(req);
endtask
endclass
// ----------------------------------------------------------------------------
// SEQUENCE 2: Read Sequence
// ----------------------------------------------------------------------------
class read_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(read_sequence)
function new(string name = "read_sequence");
super.new(name);
endfunction
virtual task body();
req = my_transaction::type_id::create("req");
start_item(req);
if (!req.randomize() with { write_en == 1'b0; }) begin
`uvm_error("SEQ", "Randomization failed!")
end
finish_item(req);
endtask
endclass
Kodun Açıklaması
Bu blok, iki temel senaryoyu (write_sequence ve read_sequence) tanımlar. İkisi de uvm_sequence #(my_transaction) sınıfından türer:
uvm_object_utils(...): Sequence'lar bileşen değil nesne (object) olduğu içinuvm_component_utilsyerineuvm_object_utilsile fabrikaya kaydedilir.function new(string name = ...): Sequence kurucusu yalnızca birnameargümanı alır; çünkü nesnelerin hiyerarşik birparent'ı yoktur (bileşenlerin aksine).virtual task body();: Sequence'ın asıl davranışını içeren görevdir. Sequence başlatıldığında otomatik olarakbodyçalışır.req = my_transaction::type_id::create("req"): Gönderilecek transaction nesnesi fabrika üzerinden yaratılır.start_item(req): Handshake'i başlatır; driverget_next_itemçağırıp müsait olana kadar bu satır akışı bloklar.req.randomize() with { write_en == 1'b1; }: Geç rastgeleleştirme (late randomization) adımıdır vestart_item'dan sonra yapılır.withbloğundaki satır içi kısıt (inline constraint),write_sequence'tewrite_en'i1(yazma),read_sequence'te ise0(okuma) olmaya zorlar.randomize()başarısız olursa0döndürür; bu durumdauvm_errorile hata basılır.finish_item(req): Doldurulmuş transaction'ı driver'a teslim eder ve driveritem_doneçağırana kadar bekleyerek handshake'i tamamlar.
İki sequence arasındaki tek anlamlı fark, randomize() with içindeki kısıttır (write_en == 1'b1 yazma için, write_en == 1'b0 okuma için).
Lab 4.3: Hiyerarşik (Master) Sequence Oluşturma
Birden fazla alt senaryoyu tek bir çatı altında toplayarak karmaşık senaryolar yaratabiliriz. Buna "Hierarchical Sequences" denir.
Görev: 2 kez yazma, ardından 2 kez okuma yapan bir ana sequence (main_sequence) oluşturun.
class main_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(main_sequence)
// Handles for sub-sequences
write_sequence wr_seq;
read_sequence rd_seq;
function new(string name = "main_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_info("MAIN_SEQ", "Starting main hierarchical sequence...", UVM_LOW)
// Execute 2 Write sequences
repeat(2) begin
wr_seq = write_sequence::type_id::create("wr_seq");
// To start a sub-sequence, pass the current sequencer (m_sequencer)
wr_seq.start(m_sequencer, this);
end
// Execute 2 Read sequences
repeat(2) begin
rd_seq = read_sequence::type_id::create("rd_seq");
rd_seq.start(m_sequencer, this);
end
`uvm_info("MAIN_SEQ", "Main sequence finished.", UVM_LOW)
endtask
endclass
Kodun Açıklaması
Bu blok, alt sequence'ları çağıran hiyerarşik (ana) sequence'ı tanımlar:
write_sequence wr_seq;veread_sequence rd_seq;: Alt senaryolara ait tutamaçlar (handle) sınıf içinde tanımlanır.body()içindekirepeat(2) begin ... end: Aynı işlemin iki kez tekrarlanmasını sağlar; önce iki yazma, ardından iki okuma üretilir.wr_seq = write_sequence::type_id::create("wr_seq"): Her tekrarda alt sequence fabrika üzerinden yeniden yaratılır. Döngü içinde yeniden yaratmak, her transaction'ın taze bir nesne olmasını sağlar.wr_seq.start(m_sequencer, this): Alt sequence'ı çalıştıran kritik satırdır.startmetodu alt sequence'ınbody'sini tetikler. Birinci argümanm_sequencer, ana sequence'ın hâlihazırda üzerinde koştuğu sequencer'dır; bu sayede alt sequence'lar ayrıca bir sequencer'a atanmadan aynı sequencer üzerinde çalışır. İkinci argümanthis, alt sequence'ın "parent sequence"ını belirtir ve hiyerarşik ilişkiyi (parent/child) kurarak ilerleme/öncelik yönetimini düzgün tutar.
m_sequencer, sequence başlatıldığında UVM tarafından otomatik olarak ayarlanan, sequence'ın bağlı olduğu sequencer'a işaret eden yerleşik bir değişkendir; bu yüzden alt sequence'ları aynı sequencer'a yönlendirmek için onu doğrudan kullanabiliriz.
Lab 4.4: Sequence'ın Test İçinden Tetiklenmesi
Artık hiyerarşik senaryomuzu test sınıfının (base_test) run_phase'i içerisinde tetikleyebiliriz. Simülasyon süresini kontrol etmek için objection (raise_objection / drop_objection) mekanizmasını kullanmayı unutmayacağız.
Görev: base_test sınıfını güncelleyerek main_sequence'i Agent'ın içindeki Sequencer üzerinde başlatın.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env_inst;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_inst = my_env::type_id::create("env_inst", this);
uvm_config_db#(int)::set(this, "env_inst.agent_inst.drv", "driver_delay", 35);
endfunction
virtual task run_phase(uvm_phase phase);
// Declare the sequence
main_sequence m_seq;
// 1. Raise objection to start simulation time
phase.raise_objection(this);
`uvm_info("TEST", "Starting main sequence...", UVM_LOW)
// 2. Create the sequence
m_seq = main_sequence::type_id::create("m_seq");
// 3. Start the sequence on the designated sequencer
m_seq.start(env_inst.agent_inst.sqr);
`uvm_info("TEST", "Main sequence completed.", UVM_LOW)
// Add a little extra time before shutting down
#50;
// 4. Drop objection to end simulation
phase.drop_objection(this);
endtask
endclass
Kodun Açıklaması
Bu blok, hem konfigürasyon atamasını (build_phase) hem de senaryonun tetiklenmesini (run_phase) bir arada gösterir:
build_phase: Önceki gibienv_inst'itype_id::createile yaratır veuvm_config_db#(int)::set(...)ile driver'adriver_delay = 35değerini gönderir.run_phaseiçindekimain_sequence m_seq;: Çalıştırılacak ana sequence için bir tutamaç tanımlanır.phase.raise_objection(this): Simülasyon zamanının erken bitmesini engeller. Objection kaldırıldığı sürece UVM, bu fazın hâlâ işi olduğunu bilir ve simülasyonu sonlandırmaz.m_seq = main_sequence::type_id::create("m_seq"): Ana sequence nesnesi fabrika üzerinden yaratılır.m_seq.start(env_inst.agent_inst.sqr): Sequence'ı, agent'ın içindeki sequencer (sqr) üzerinde başlatır. Buradastart'a sequencer'ın tam hiyerarşik referansı verilir; bu, sequence'ı doğru sequencer'a bağlar vebody'sinin çalışmasını başlatır. Sequence tamamen bitene kadar bu satır bekler.#50;: Son transaction sürüldükten sonra sinyallerin yerleşmesi için simülasyona biraz ek süre tanır.phase.drop_objection(this): Objection'ı düşürür. Bekleyen başka objection kalmadığında UVMrun_phase'i sonlandırır ve simülasyon temiz bir şekilde biter.
Önemli Noktalar
Bu dersin tamamından çıkarılması gereken en kritik püf noktaları ve en iyi pratikler şunlardır:
- String tabanlı adresleme:
uvm_config_dbilesetyaparken hedefi ("env_inst.agent_inst.drv"gibi) string olarak yazın; çünkübuild_phasetop-down çalışır ve değer gönderilirken hedef bileşen henüz yaratılmamış olabilir. Geniş erişim için"*"jokerini kullanmaktan çekinmeyin. getdönüş değerini her zaman kontrol edin:getçağrısını birifiçine alıp başarısız durumu (uvm_warning+ varsayılan değer) ele alın. Bu, eksik veya hatalı yazılmış birfield_name'in sessizce gözden kaçmasını engeller.- Geç rastgeleleştirme (late randomization):
randomize()'ı her zamanstart_item'dan sonra,finish_item'dan önce yapın. Böylece veri, driver'ın gerçekten hazır olduğu en güncel simülasyon durumuna göre üretilir. - Doğru fabrika makrosunu seçin: Bileşenler (
uvm_component) içinuvm_component_utils, sequence'lar ve transaction'lar (uvm_object) içinuvm_object_utilskullanın. Tüm nesnelerinewyerinetype_id::createile yaratarak fabrika override'larından faydalanın. m_sequencerile yeniden kullanım: Hiyerarşik sequence'larda alt sequence'larıstart(m_sequencer, this)ile başlatın; bu, alt sequence'ları ana sequence ile aynı sequencer'a bağlar ve parent/child ilişkisini kurar.- Objection'ı yalnızca testte yönetin: Simülasyonun başlangıç/bitiş kontrolünü (
raise_objection/drop_objection) her zaman testin en üstrun_phase'inde yapın; performans nedeniyle bunu tek tek sequence'lar içinde kullanmaktan kaçının.
Özet ve Beklenen Sonuç
Simülasyon çalıştırıldığında testbench aşağıdaki sırayla çalışacaktır:
- build_phase sırasında test, config_db'ye 35 değerini yazacak.
- Driver, config_db'den 35 değerini başarıyla okuduğunu loglayacak (DRV_CFG).
- run_phase'de test main_sequence'i başlatacak.
- main_sequence sırasıyla iki adet YAZMA (write_en=1), ardından iki adet OKUMA (write_en=0) işlemi üretecek.
- Driver bu işlemleri alıp ekrana yazdıracak (Driving Transaction...) ve her işlemde 35 zaman birimi bekleyecek.
- İşlemler bittikten 50 zaman birimi sonra objection düşülecek ve simülasyon başarıyla tamamlanacaktır.
4. Gün Sıkça Sorulan Sorular (FAQ)
Bu bölüm eğitim sırasında 4. Gün konuları ile ilgili olarak öğrencilerden gelebilecek soruları ve yanıtlarını içerir.
Soru 1: uvm_config_db kullanırken neden "set" işleminde hedef yolunu "env_inst.agent_inst.drv" şeklinde string (metin) olarak yazıyoruz? Sınıf referanslarını doğrudan kullansak olmaz mı?
Cevap: build_phase yukarıdan aşağıya (top-down) çalışır. Biz base_test'in build_phase'i içerisindeyken, agent_inst ve drv henüz yaratılmamış olabilir (bellekte yer kaplamazlar). Olmayan bir objenin referansını veremezsiniz. Bu yüzden UVM, "İleride bu yolda (path) yaratılacak olan bileşen bu değeri alsın" mantığıyla string tabanlı hiyerarşik bir adresleme sistemi kullanır.
Soru 2: Neden uvm_do gibi daha kısa makroları kullanmak yerine transaction yaratma, kısıtlama (randomize) ve gönderme işlemlerini uzun uzun elimizle yazıyoruz?
Cevap: Eski UVM kodlarında uvm_do makroları çok popülerdi. Ancak bu makrolar arka planda tam olarak ne olduğunu (ve hata çıktığında hangi satırda patladığını) gizlerler. Modern UVM standartlarında ve endüstrideki büyük projelerde, debug edilebilirliğinin (hata ayıklama) yüksek olması nedeniyle API tabanlı start_item / finish_item yapısı zorunlu tutulmaktadır.
Soru 3: Sequence'lar (uvm_sequence) neden uvm_component değil de uvm_object soyundan geliyor?
Cevap: uvm_component sınıfları (Env, Agent, Driver) donanım iskeleti gibidir; simülasyonun 0. anında yaratılırlar ve simülasyon bitene kadar asla yok edilmezler. Sequence'lar ise dinamiktir. Simülasyonun ortasında yaratılır, içindeki görevleri yapar ve işleri bitince bellekten silinirler. Bu kısa ömürlü ve "veri tabanlı" yapıları nedeniyle uvm_object (daha spesifik olarak uvm_sequence_item) soyundan gelirler. Component fazlarına (build_phase, connect_phase vb.) sahip değildirler.
Soru 4: Objection (raise_objection) kaldırma/indirme işlemini neden Sequence'ın kendi içinde (body taskı içinde) yapmadık da base_test içinde yaptık?
Cevap: Sequence içinde objection kullanmak mümkündür ancak kesinlikle tavsiye edilmez. Büyük bir projede binlerce transaction ve yüzlerce alt-sequence çalışabilir. Her biri için objection raise/drop yapmak performansı inanılmaz derecede düşürür. Endüstri standardı olarak simülasyonun ne zaman başlayıp ne zaman biteceğine her zaman testin (örneğin base_test) en üst run_phase'i karar vermelidir. Test objection'ı kaldırır, ana sequence'ı başlatır, sequence bitince objection'ı düşürür.
Soru 5: Transaction'ı neden start_item çağrısından ÖNCE değil de SONRA randomize ediyoruz? Önce randomize edip sonra göndersek ne kaybederiz?
Cevap: Teknik olarak start_item'dan önce randomize etmek de derlenir ve çalışır, ancak "geç rastgeleleştirme" (late randomization) endüstride bilinçli olarak tercih edilen bir tekniktir. start_item, driver get_next_item çağırana kadar akışı bloklar; yani driver'ın gerçekten o transaction'ı isteyeceği ana kadar bekler. Veriyi tam o anda üretirsek, simülasyonun en güncel durumundan (örneğin scoreboard'dan gelen geri besleme, register değerleri veya bir önceki işlemin sonucu) faydalanabiliriz. Eğer veriyi çok erken üretirsek, transaction sıraya girene kadar geçen sürede bu bilgiler bayatlayabilir ve kısıtlarımız (randomize() with) eski duruma göre çözülmüş olur. Kısacası geç randomize, en akıllı ve en güncel stimulus'u üretmenin yoludur.
Soru 6: Hiyerarşik sequence'te alt sequence'ı wr_seq.start(m_sequencer, this) ile başlatırken kullandığımız m_sequencer tam olarak nedir ve nereden gelir?
Cevap: m_sequencer, her sequence'ın içinde otomatik olarak bulunan, o sequence'ın hangi sequencer üzerinde koştuğunu gösteren yerleşik (built-in) bir tutamaçtır. Biz ana sequence'ı testte m_seq.start(env_inst.agent_inst.sqr) ile başlattığımızda, UVM m_seq'in m_sequencer alanını otomatik olarak o sequencer'a (sqr) ayarlar. Dolayısıyla ana sequence'ın body'si içindeyken m_sequencer zaten doğru sequencer'a işaret eder. Alt sequence'ları başlatırken bu değeri tekrar kullanmak, onları el yordamıyla bir sequencer referansı bulmaya zorlamadan aynı sequencer'a bağlamamızı sağlar. İkinci argüman olarak verdiğimiz this ise alt sequence'ın "parent"ını ana sequence yapar; bu parent/child ilişkisi öncelik (priority) ve ilerleme yönetiminin sağlıklı çalışması için önemlidir.