UVM'in Temelleri ve Testbench Yapısı
UVM Gün 1: Temeller ve Testbench Yapısı | Bileşen hiyerarşisi, factory, fazlar (phasing), raporlama ve objection mekanizması
Amaç
Bu dersin amacı, okuyanların SystemVerilog kullanarak temel bir UVM sınıf hiyerarşisi oluşturmasını, UVM raporlama mekanizmasını kullanmasını ve UVM fazlarının (phasing) nasıl çalıştığını uygulamalı olarak görmesini sağlamaktır.
Ön Koşullar
- Temel SystemVerilog Nesne Yönelimli Programlama (OOP) bilgisi; özellikle kalıtım,
virtualmetodlar vesuperçağrıları (SystemVerilog Gün 2). - SystemVerilog bitirme projesindeki (Gün 6–7) generator → driver → monitor → scoreboard → environment → test zincirine aşinalık. UVM bu zincirin standartlaştırılmış hâlidir.
- UVM kütüphanesinin derlenebildiği bir simülatör (Örn: Questa, Xcelium, VCS). EDA Playground'da "UVM 1.2" seçeneği yeterlidir.
UVM Serisinin Yol Haritası
Seri, SystemVerilog bitirme projesinin bıraktığı yerden başlar ve aynı ALU'yu UVM ile yeniden doğrulayarak biter. Her gün bir öncekinin üzerine kurulur:
| Gün | Konu | Bu günün sonunda elinizde ne var? |
|---|---|---|
| 1 (bu ders) | Bileşen hiyerarşisi, fazlar, raporlama, objection | Çalışan ama boş bir test → env → agent iskeleti |
| 2 | uvm_sequence_item, sequencer–driver el sıkışması, pasif monitor, ACTIVE/PASSIVE agent |
Agent'ın iç mimarisi |
| 3 | TLM: analysis port/imp, scoreboard, coverage, report_phase |
Gözlem ve denetim altyapısı |
| 4 | uvm_config_db, sequence'ler, start_item/finish_item, hiyerarşik sequence'ler |
Stimulus üreten, konfigüre edilebilir ortam |
| 5 | Factory: kayıt, type_id::create, tip/instance override |
Ortama dokunmadan test varyantları |
| 6 | Virtual interface, gerçek driver/monitor, tb_top + DUT |
ALU'ya bağlı, pin süren ortam |
| 7 | Predictor + scoreboard, coverage, test hiyerarşisi, regresyon | Kendi kendini denetleyen tam testbench |
| 8 | Virtual sequence/sequencer, çok agent, arbitrasyon, lock/grab |
Sistem seviyesi senaryo koordinasyonu |
UVM Neden Var? SystemVerilog Projesinden UVM'e
SystemVerilog bitirme projesinde her şeyi elle kurdunuz: mailbox'ları siz yarattınız, bileşenleri build() içinde siz bağladınız, bitişi wait(...) zincirleriyle siz yönettiniz. Bu yaklaşım çalışır; ama her proje kendi "environment", "test", "report" kalıbını yeniden icat eder ve iki ekibin testbench'leri birbiriyle konuşamaz. UVM (Universal Verification Methodology), bu kalıpları Accellera/IEEE 1800.2 standardı altında toplayan bir sınıf kütüphanesi + kurallar bütünüdür:
| SystemVerilog projesinde elle yaptığınız | UVM'de hazır gelen |
|---|---|
class ALU_Environment + build() |
uvm_env + build_phase() |
fork gen.run(); drv.run(); ... join_any |
Tüm bileşenlerin run_phase()'i otomatik paralel |
wait(gen.done.triggered); wait(scb.total_count == ...) |
raise_objection / drop_objection |
new(aif, aif) ile vif enjeksiyonu |
uvm_config_db |
+TEST= + case ile test seçimi |
run_test() + +UVM_TESTNAME= + factory |
$display("[Driver] ...") |
`uvm_info("DRIVER", ..., UVM_LOW) (verbosity, ID filtreleme, dosya/satır) |
mailbox #(ALU_Transaction) |
uvm_sequencer ↔ uvm_driver el sıkışması, uvm_analysis_port |
env.gen = corner_gen ile tür değiştirme |
Factory override: set_type_override_by_type(...) (Gün 5) |
Bu derste yalnızca ilk sütundaki iskeleti UVM diliyle yeniden kuracaksınız: bileşen hiyerarşisi, fazlar ve raporlama. DUT, interface ve veri akışı Gün 6–7'nin konusudur.
Benzetme: SystemVerilog projesi, bir evi kendi çizdiğiniz planla sıfırdan inşa etmekti. UVM, aynı evi prefabrik modüllerle kurmaktır: odaların (bileşenlerin) nasıl birleşeceği, elektriğin (fazların) hangi sırayla verileceği, kapı standartları (TLM portları) baştan bellidir. Daha az özgürlük, çok daha fazla hız ve başkasının evini anında anlama yeteneği.
Lab 1.1: UVM Raporlama ve Fazların Gözlemlenmesi
Bu adımda basit bir UVM bileşeni (component) oluşturup, temel fazları (build_phase, connect_phase, run_phase) ezeceğiz (override) ve her bir fazın içine UVM raporlama makroları ekleyeceğiz.
Görev: my_agent adında uvm_agent sınıfından türetilen bir bileşen oluşturun.
package my_uvm_pkg;
// UVM paketini import et ve makrolari dahil et
// (`uvm_info, `uvm_component_utils gibi tum ters tirnakli yapilar bu dosyadan gelir)
import uvm_pkg::*;
`include "uvm_macros.svh"
// Agent sinifi: uvm_agent'tan turetilir
class my_agent extends uvm_agent;
// Register component to UVM factory
`uvm_component_utils(my_agent)
// Constructor
function new(string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
// Build phase: Executed top-down
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("AGENT_BUILD", "Executing build_phase for my_agent...", UVM_LOW)
endfunction
// Connect phase: Executed bottom-up
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("AGENT_CONN", "Executing connect_phase for my_agent...", UVM_LOW)
endfunction
// Run phase: Time-consuming phase, executed as a parallel task
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
`uvm_info("AGENT_RUN", "Starting run_phase for my_agent...", UVM_LOW)
// Simulate some delay
#10;
`uvm_info("AGENT_RUN", "Finished run_phase delay for my_agent.", UVM_HIGH)
endtask
endclass
Kodun Açıklaması
Bu blok, ileride hiyerarşinin en alt yapı taşı olacak my_agent bileşenini tanımlar:
package my_uvm_pkg;ile tüm sınıflarımızı tek bir isim alanı (namespace) içine topluyoruz.import uvm_pkg::*;satırı UVM kütüphanesindeki sınıfları görünür kılar;`include "uvm_macros.svh"ise`uvm_infogibi makroları tanımlar. İkisi birbirinin yerine geçmez: makrolar önişlemci düzeyinde çalıştığı içinimportile gelmez, mutlakaincludeedilmelidir (SystemVerilog "Makro Kullanımı" dersine bakın). Bu satırı unutmak, UVM'e başlayanların aldığı ilk derleme hatasıdır.class my_agent extends uvm_agent;ifadesi, kendi sınıfımızı UVM'in hazıruvm_agenttemel sınıfından türetir; böylece fazlama (phasing) ve raporlama altyapısını hazır olarak devralırız.`uvm_component_utils(my_agent)makrosu sınıfı **UVM Factory**'ye kaydeder. Bu kayıt, ileridetype_id::createile nesne üretebilmenin ön koşuludur.function new(string name, uvm_component parent)yapıcı metodu (constructor) bileşeni adı ve hiyerarşideki ebeveyni (parent) ile oluşturur;super.new(name, parent)çağrısı bu bilgiyi üst sınıfa iletir.build_phasebirfunction'dır ve yukarıdan aşağıya (top-down) çalışır; içindekisuper.build_phase(phase)çağrısı temel sınıfın kurulum işlemlerinin önce çalışmasını garanti eder.`uvm_info("AGENT_BUILD", ..., UVM_LOW)makrosu ise bir kimlik (ID), bir mesaj veUVM_LOWdetay seviyesiyle log basar.connect_phaseda birfunction'dır ancak aşağıdan yukarıya (bottom-up) çalışır; burada bileşenler arası bağlantılar yapılır.run_phasebirtaskolarak tanımlanmıştır çünkü içinde#10;gibi simülasyon zamanı harcayan ifadeler bulunur. Son`uvm_infomesajıUVM_HIGHseviyesinde basıldığı için varsayılan ayarlarda ekranda görünmez.
UVM Fazları: Hangi Sırayla, Hangi Yönde?
uvm_component'ten türeyen her sınıf aynı faz dizisini izler. Bu dersin örneğinde kullanılanlar kalın yazılmıştır:
| Faz | Tür | Yön | Ne yapılır? |
|---|---|---|---|
build_phase |
function | Yukarıdan aşağıya (top-down) | Alt bileşenleri create et, config_db oku |
connect_phase |
function | Aşağıdan yukarıya (bottom-up) | TLM port/export bağla, vif ata |
end_of_elaboration_phase |
function | Bottom-up | Topolojiyi yazdır, son kontroller |
start_of_simulation_phase |
function | Bottom-up | Simülasyon öncesi mesajlar |
run_phase |
task | Tüm bileşenlerde paralel | Zaman tüketen asıl iş; objection ile süre kontrolü |
extract_phase |
function | Bottom-up | Son verileri topla (kapsam, sayaçlar) |
check_phase |
function | Bottom-up | Tutarlılık kontrolleri (örn. boş kalmayan scoreboard kuyruğu) |
report_phase |
function | Bottom-up | Nihai raporlar |
final_phase |
function | Top-down | Temizlik |
run_phase ayrıca 12 alt faza bölünebilir (reset_phase, configure_phase, main_phase, shutdown_phase...), ancak çoğu proje yalnızca run_phase kullanır. Faz sırası, SystemVerilog projesinde elle yazdığınız build() → reset() → run() → report() zincirinin standartlaştırılmış hâlidir.
run_test("base_test")
│
├─ build_phase base_test ─▶ my_env ─▶ my_agent (top-down)
├─ connect_phase my_agent ─▶ my_env ─▶ base_test (bottom-up)
├─ end_of_elaboration print_topology()
├─ run_phase hepsi ayni anda; objection dusunce biter (zaman akar)
├─ extract / check / report_phase (bottom-up)
└─ final_phase → $finish
Lab 1.2: UVM Hiyerarşisinin (Testbench Ağacı) Kurulması
Agent bileşenimizi oluşturduk. Şimdi bu Agent'ı bir Environment (uvm_env) içine, Environment'ı da bir Test (uvm_test) içine yerleştireceğiz. UVM'de alt bileşenler her zaman bir üst bileşenin build_phase metodu içerisinde yaratılır.
Görev: my_env ve base_test sınıflarını oluşturun ve hiyerarşiyi bağlayın.
// Define the environment class
class my_env extends uvm_env;
`uvm_component_utils(my_env)
// Handle for our agent
my_agent agent_inst;
function new(string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction
// Instantiate the agent in the build_phase using UVM factory
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("ENV_BUILD", "Building my_env...", UVM_LOW)
// Create the agent using the factory create method
agent_inst = my_agent::type_id::create("agent_inst", this);
endfunction
endclass
// Define the base test class
class base_test extends uvm_test;
`uvm_component_utils(base_test)
// Handle for our environment
my_env env_inst;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
// Instantiate the environment
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TEST_BUILD", "Building base_test...", UVM_LOW)
// Create the environment
env_inst = my_env::type_id::create("env_inst", this);
endfunction
// Print the UVM topology in the end_of_elaboration phase
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
// Raise and drop objections in the run_phase to control simulation time
virtual task run_phase(uvm_phase phase);
// Raise objection to prevent simulation from ending immediately
phase.raise_objection(this);
`uvm_info("TEST_RUN", "Test is running...", UVM_LOW)
#50; // Wait for some simulation time
`uvm_info("TEST_RUN", "Test finished.", UVM_LOW)
// Drop objection to allow simulation to end
phase.drop_objection(this);
endtask
endclass
endpackage : my_uvm_pkg // Lab 1.1'de acilan paket burada kapanir
Kodun Açıklaması
Bu blok, testbench ağacının orta ve üst katmanlarını kurar (ve Lab 1.1'de açılan my_uvm_pkg paketini endpackage ile kapatır; tüm sınıflar aynı paketin içindedir):
class my_env extends uvm_env;ileuvm_envtemel sınıfından bir ortam (environment) türetilir ve`uvm_component_utils(my_env)ile Factory'ye kaydedilir.my_agent agent_inst;satırı, ortamın içinde yaşayacak agent için bir tutamaç (handle) tanımlar; bu yalnızca bir göstergedir, nesne henüz yaratılmamıştır.- Nesne,
my_env'inbuild_phasemetodu içindeagent_inst = my_agent::type_id::create("agent_inst", this);ile yaratılır.type_id::createdoğrudannewyerine kullanılır çünkü Factory üzerinden esnek (override edilebilir) nesne üretimi sağlar;thisifadesi yeni bileşenin ebeveynini belirtir. class base_test extends uvm_test;ile en üstteki test sınıfıuvm_test'ten türetilir vemy_env env_inst;tutamacı tanımlanır. Test'inbuild_phasemetodu, aynı şekildeenv_inst = my_env::type_id::create("env_inst", this);ile ortamı yaratır. Bu zincir sayesinde Test → Env → Agent hiyerarşisi yukarıdan aşağıya kurulur.end_of_elaboration_phasemetodunun içindekiuvm_top.print_topology();çağrısı, tüm bileşenler kurulduktan sonra testbench ağacını ekrana çizer. Bu faz, build ve connect tamamlandıktan sonra çalıştığı için tüm hiyerarşi hazırdır.uvm_top, hiyerarşinin kökü olan tekil (singleton)uvm_rootnesnesine işaret eder; aynı nesneyeuvm_root::get()ile de erişilir. Komut satırından+UVM_VERBOSITY=UVM_HIGHverildiğinde topoloji kodsuz da yazdırılabilir, ama build sırasında eksik bir bileşeni görmek için bu çağrıyı testte tutmak iyi bir alışkanlıktır.- Test'in
run_phasegörevindephase.raise_objection(this);ile simülasyonun erkenden bitmesi engellenir;#50;ile bir süre beklenir vephase.drop_objection(this);ile itiraz geri çekilerek simülasyonun sonlanmasına izin verilir.raise/dropçiftinin dengeli olması kritiktir.
Lab 1.3: Top Modül ve run_test() Çağrısı
UVM sınıfları dinamiktir, donanım (DUT) ve arayüzler (Interfaces) ise statiktir. Simülasyonu başlatmak için standart bir SystemVerilog modülüne ve UVM'in beyni olan run_test() fonksiyonunu çağırmaya ihtiyacımız var.
Görev: tb_top modülünü yazın ve simülasyonu tetikleyin.
// Top level static module
module tb_top;
// Import UVM package
import uvm_pkg::*;
import my_uvm_pkg::*;
// Initial block to start the UVM phasing
initial begin
// Start the test named "base_test"
run_test("base_test");
end
endmodule
Kodun Açıklaması
Bu blok, dinamik UVM dünyası ile statik donanım dünyasını birleştiren köprüdür:
module tb_top;standart bir SystemVerilog modülüdür. UVM sınıfları çalışma zamanında (runtime) yaratılan dinamik nesnelerken, modüller simülasyonun başında var olan statik yapılardır; bu yüzden simülasyonu başlatacak giriş noktası bir modül olmak zorundadır.import uvm_pkg::*;UVM çekirdeğini,import my_uvm_pkg::*;ise kendi yazdığımızmy_agent,my_envvebase_testsınıflarını modülün içine görünür kılar.initial begin ... endbloğu simülasyonun en başında bir kez çalışır.run_test("base_test");çağrısı UVM'in beynidir: argüman olarak verilen sınıf adını Factory'de arar, o sınıftan bir nesne yaratır ve tüm fazlama (build, connect, run vb.) mekanizmasını otomatik olarak başlatır. Test adını burada string olarak vermek, simülatöre+UVM_TESTNAME=...argümanıyla farklı testleri kod değiştirmeden çalıştırma esnekliği de tanır.
Önemli Noktalar
- Factory kaydı şarttır: Her bileşeni
`uvm_component_utilsile Factory'ye kaydedin. Bu olmadantype_id::createçalışmaz ve override mekanizmasından faydalanamazsınız. - İsimler hiyerarşik yolu oluşturur:
create("agent_inst", this)çağrısındaki string, bileşeninget_full_name()sonucundauvm_test_top.env_inst.agent_instolarak görünür. Bu yol, log mesajlarında,uvm_config_dbadreslemesinde ve topolojide kullanılır; değişken adıyla (agent_inst) aynı tutmak hata ayıklamayı kolaylaştırır. uvm_test_topsabit bir isimdir:run_test("base_test")ile yaratılan test nesnesinin hiyerarşik adı her zamanuvm_test_top'tur; test sınıfının adı değil. Config_db yollarını yazarken bunu hatırlayın.- Her zaman
create, aslanewdeğil: Bileşen nesnelerinitype_id::createile yaratın.newile sabitlenen nesneler test esnekliğini ve yeniden kullanılabilirliği yok eder. superçağrılarını silmeyin:build_phase,connect_phasegibi metotların başındasuper.<faz>(phase)çağrısını koruyun; aksi halde temel sınıfın arka plandaki kurulum ve konfigürasyon işlemleri atlanır.- Faz tipine dikkat: Zaman harcamayan kurulum işleri
functionfazlarında (build/connect), zaman akışı gerektiren senaryolar isetaskfazlarında (run) yapılmalıdır. - Objection dengesi:
run_phaseiçinde herraise_objectionçağrısının karşılığında birdrop_objectionolduğundan emin olun; aksi takdirde simülasyon ya hiç başlamaz ya da hiç bitmez. - Verbosity'yi bilinçli kullanın: Kritik mesajları
UVM_LOW, detaylı bilgileriUVM_HIGH/UVM_DEBUGile basın; böylece log kalabalığını+UVM_VERBOSITYargümanıyla istediğiniz anda yönetebilirsiniz.
Çalıştırma ve Beklenen Çıktılar
Bu kodları tek bir testbench.sv dosyası içinde toplayıp simülatörünüzde çalıştırdığınızda, aşağıdaki gibi bir çıktı görmelisiniz:
- Önce
base_test, ardındanmy_envve en sonmy_agentiçinbuild_phaselogları (Yukarıdan Aşağıya - Top-Down). uvm_top.print_topology()sayesinde ekrana çizilmiş testbench ağacı.connect_phaselogları (Aşağıdan Yukarıya - Bottom-Up): öncemy_agent, sonramy_env.- Zamanın akmaya başladığı
run_phaselogları. UVM_HIGHverbosity seviyesi ile yazılan logun (Finished run_phase delay...) varsayılan ayarlarda ekranda görünmediğini fark edeceksiniz. Bunu görmek için simülatöre+UVM_VERBOSITY=UVM_HIGHargümanı vermeniz gerekir.- Simülasyonun sonunda UVM otomatik bir rapor özeti basar:
UVM_INFO,UVM_WARNING,UVM_ERROR,UVM_FATALsayıları. Sıfır hata ile biten bir koşudaUVM_ERROR : 0satırını görmelisiniz; regresyon betikleri genellikle bu satırı okur.
Bir log satırını okuma rehberi:
UVM_INFO testbench.sv(39) @ 0: uvm_test_top.env_inst.agent_inst [AGENT_BUILD] Executing build_phase for my_agent...
│ │ │ │ │ │ │
│ dosya satır zaman hiyerarşik yol ID mesaj
└── önem seviyesi (INFO/WARNING/ERROR/FATAL)
Dosya ve satır bilgisi `uvm_info makrosunun `__FILE__/`__LINE__ kullanmasından gelir; $display ile bu bilgiyi elle yazmanız gerekirdi.
Komut Satırı Argümanları: Kodu Değiştirmeden Kontrol
UVM, derlenmiş bir testbench'i yeniden derlemeden yönlendirmek için bir dizi + argümanı tanır. Regresyon betikleri tamamen bunlara dayanır; en sık kullanılanlar:
| Argüman | Ne yapar? | Örnek |
|---|---|---|
+UVM_TESTNAME=<ad> |
run_test() hangi test sınıfını yaratacak (factory ile, Gün 5) |
+UVM_TESTNAME=base_test |
+UVM_VERBOSITY=<seviye> |
Tüm bileşenler için eşik: UVM_NONE, UVM_LOW, UVM_MEDIUM (varsayılan), UVM_HIGH, UVM_FULL, UVM_DEBUG |
+UVM_VERBOSITY=UVM_HIGH |
+uvm_set_verbosity=<yol>,<ID>,<seviye>,<faz> |
Tek bir bileşen (ve altı) için, belirli bir ID'de, belirli bir fazda eşik | +uvm_set_verbosity=<yol>,_ALL_,UVM_DEBUG,run |
+UVM_TIMEOUT=<süre>,<YES|NO> |
Simülasyon zaman aşımı; NO ise koddaki set_timeout bunu ezemez |
+UVM_TIMEOUT=500000,NO |
+UVM_MAX_QUIT_COUNT=<n>,<YES|NO> |
n adet UVM_ERROR sonrası simülasyonu durdur |
+UVM_MAX_QUIT_COUNT=5,NO |
+UVM_OBJECTION_TRACE |
Her raise/drop'u sayaçla birlikte bas |
"Simülasyon neden bitmiyor?" sorusunun ilk aracı |
+UVM_PHASE_TRACE |
Faz geçişlerini bas | Hangi faz ne zaman başladı/bitti |
+UVM_CONFIG_DB_TRACE |
Her set/get ve eşleşmesini bas (Gün 4) |
"get neden 0 döndü?" |
+uvm_set_type_override=..., +uvm_set_inst_override=... |
Factory override (Gün 5) | +uvm_set_type_override=my_driver,my_error_driver |
+UVM_DUMP_CMDLINE_ARGS |
UVM'in gördüğü tüm argümanları bas | Betik hata ayıklama |
Örneğin yalnızca driver'ı konuşturmak için: +uvm_set_verbosity=uvm_test_top.env_inst.agent_inst.drv,_ALL_,UVM_DEBUG,run; _ALL_ tüm ID'ler, son alan ise fazın adıdır (run, build …).
Kendi argümanlarınızı da okuyabilirsiniz; SystemVerilog'un $value$plusargs'ı çalışır, ama UVM'in uvm_cmdline_processor'ı argümanları tek yerden ve hata kontrolüyle verir:
// base_test::build_phase icinde: +N_ITEMS=50 argumanini oku
uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();
string s;
if (clp.get_arg_value("+N_ITEMS=", s))
n_items = s.atoi();
Kural: bir koşudan diğerine değişen her şey (test adı, verbosity, seed, paket sayısı) komut satırından gelmeli; kodda sabitlenmemelidir. Aynı derlemeyle yüzlerce koşu, regresyonun ekonomisidir.
Objection Derinlemesine: Boşaltma Süresi ve Zaman Aşımı
Son paketin kaderi: drain time
run_phase, tüm objection'lar düştüğü anda biter. Testin drop_objection'ı genellikle son sequence'in finish_item'ı döndüğünde gelir; ama o anda DUT son paketi henüz işlemektedir, monitor sonucu henüz görmemiştir. Çözüm, UVM'e "sayaç sıfırlandıktan sonra şu kadar daha bekle" demektir:
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this, "ana senaryo");
phase.phase_done.set_drain_time(this, 100); // sifira dustukten sonra 100 birim daha
// ... sequence'ler ...
phase.drop_objection(this, "ana senaryo");
endtask
raise/drop'un ikinci argümanı bir açıklamadır; +UVM_OBJECTION_TRACE çıktısında görünür ve "kim, neden kaldırdı?" sorusunu cevaplar. (IEEE 1800.2'de phase_done yerine phase.get_objection().set_drain_time(...) yazılır; UVM 1.2 ikisini de kabul eder.)
Birden fazla objection
Sayaç toplamdır: test 1 kaldırdı, bir bileşen 1 kaldırdı → faz, ikisi de düşene kadar sürer. Bu, bir sequence'in objection kaldırıp düşürmeyi unutmasının simülasyonu neden sonsuza kadar askıda bıraktığını açıklar. Gün 4'teki kural buradan gelir: objection'ı test yönetir; bileşenler yalnızca istisnai durumlarda (örneğin bir monitor'ün "paket yarım kaldı, bitmesini bekle" demesi) kaldırır ve mutlaka aynı yerde düşürür.
UVM_INFO ... [OBJTN_TRC] Object uvm_test_top raised 1 objection(s) (ana senaryo): count=1 total=1
UVM_INFO ... [OBJTN_TRC] Object uvm_test_top dropped 1 objection(s) (ana senaryo): count=0 total=0
UVM_INFO ... [OBJTN_TRC] Object uvm_test_top all_dropped ... drain time 100 starting
Zaman aşımı: sonsuz bekleyişe karşı sigorta
Objection hiç düşmezse UVM varsayılan olarak 9200 saniye (UVM_DEFAULT_TIMEOUT) sonra UVM_FATAL [PH_TIMEOUT] ile durur; çoğu simülasyon için bu "sonsuz" demektir. Gerçekçi bir sınır koyun:
// base_test::build_phase (veya end_of_elaboration_phase) icinde
uvm_top.set_timeout(1ms, 0); // 0: komut satiri +UVM_TIMEOUT ile ezilemez
Zaman aşımı bir hata ayıklama aracıdır, bitiş mekanizması değil: testiniz zaman aşımıyla bitiyorsa ya bir objection düşmemiştir ya da bir get_next_item/get sonsuza kadar beklemektedir (Gün 2'deki "item_done unutuldu" durumu). +UVM_OBJECTION_TRACE ile kimin sayaçta kaldığını bulun.
Sık Yapılan Hatalar
- **
`include "uvm_macros.svh"'i unutmak:**`uvm_infoiçin "undefined macro" hatası. Paketin başındaimportile birlikte yazın. new()imzasını değiştirmek: Factory, bileşenlerinew(string name, uvm_component parent)imzasıyla yaratır. Fazladan argüman eklerseniztype_id::createderlenmez. Ek parametreleruvm_config_dbile taşınır (Gün 4).super.build_phase(phase)'i silmek:uvm_component'inbuild_phase'iuvm_config_dbileapply_config_settingsgibi işler yapar; silinirse field makrolarıyla otomatik konfigürasyon çalışmaz.- Objection'ı
build_phase'de kaldırmaya çalışmak: Objection yalnızcataskfazlarında (run_phaseve alt fazları) anlamlıdır. - Bir bileşeni
run_phaseiçinde yaratmak:build_phasebittikten sonracreateçağrısı UVM hatası üretir; hiyerarşi elaboration sonunda donar. run_test()çağrısına yanlış string vermek: Factory'de kayıtlı olmayan bir ad verilirseUVM_FATALile "Requested test not found" alınır;`uvm_component_utilskaydını ve yazımı kontrol edin.
Kendinizi Deneyin
my_agent'ınbuild_phase'ine#10;ekleyin. Derleyici ne diyor? Nedenbuild_phasezaman tüketemez?base_test'tekiphase.drop_objection(this);satırını silip çalıştırın. Simülasyon ne zaman bitti? (İpucu: UVM'in varsayılan timeout'u 9200 s'dir; o zamana kadar bekleyebilir ya da aracın kendi zaman aşımı devreye girer.) Sonraraise_objection'ı silip tekrar deneyin.+UVM_VERBOSITY=UVM_HIGHile çalıştırın ve gizli mesajın göründüğünü doğrulayın. Sonra+UVM_VERBOSITY=UVM_NONEile hangi mesajların kaldığını gözlemleyin.my_enviçine ikinci bir agent (agent_inst2) ekleyin veprint_topology()çıktısında göründüğünü doğrulayın.build_phaseloglarının sırası nasıl değişti?run_test("base_test")yerinerun_test()(argümansız) yazın ve testi+UVM_TESTNAME=base_testkomut satırı argümanıyla seçin. Bu yaklaşımın SystemVerilog projesindeki+TEST=+caseçözümünden farkı ne?
Hızlı Kontrol: build_phase neden top-down, connect_phase neden bottom-up çalışır?
Bir bileşenin alt bileşenlerini yaratabilmesi için önce kendisinin var olması gerekir; bu yüzden build yukarıdan aşağıya iner (test → env → agent). Bağlantı ise alt bileşenlerin portları hazır olduktan sonra anlamlıdır; bu yüzden connect aşağıdan yukarıya çıkar (agent kendi driver↔sequencer bağlantısını yapar, sonra env agent'ın portlarını scoreboard'a bağlar).
UVM Gün 1: Öğrenci Soru ve Cevap Rehberi
Soru 1: Sınıfların başında kullandığımız `uvm_component_utils() makrosu tam olarak ne işe yarıyor? Yazmazsak ne olur?
Cevap: Bu makro, oluşturduğumuz sınıfı UVM Factory adını verdiğimiz özel bir kayıt defterine kaydeder. Eğer bu makroyu yazmayı unutursanız, UVM Factory bu sınıfın varlığından haberdar olmaz. Bu durumda ileride nesneyi type_id::create() ile yaratmaya çalıştığınızda hata alırsınız. Ayrıca bu makro sayesinde ileride kodun hiçbir yerini değiştirmeden, bu sınıf yerine başka bir sınıfı "override" (ezme/yerine koyma) işlemi ile kullanabiliriz. UVM'in yeniden kullanılabilirlik (reusability) gücünün kalbi bu makrodur.
Soru 2: Nesneleri yaratırken SystemVerilog'un kendi yöntemi olan new() metodunu kullanmak varken, neden type_id::create() gibi uzun bir yazım kullanıyoruz?
Cevap: Eğer my_agent = new() yazarsanız, o değişkene kesinlikle ve sadece my_agent sınıfını atamış olursunuz. Yani donanıma sert bir şekilde lehimlemişsiniz gibi düşünün.
Ancak my_agent::type_id::create() yazdığınızda, arka planda UVM Factory'ye şu soruyu sorarsınız: "Bana bir my_agent lazım, ama acaba testin başka bir yerinde bu sınıf yerine başka bir özel sınıf (örneğin my_error_agent) kullanmam istendi mi?" Factory kontrolü yapar ve size doğru olan nesneyi verir. create() kullanmak kodunuzu esnek, dinamik ve gelecekteki değişikliklere açık hale getirir.
Soru 3: Neden build_phase bir function (fonksiyon) olarak tanımlanmışken, run_phase bir task (görev) olarak tanımlanmış?
Cevap: SystemVerilog'da function'lar simülasyon zamanı (time) harcayamazlar; anında (0 delta time) çalışıp bitmek zorundadırlar. task'ler ise içinde #10, @(posedge clk), wait() gibi zaman geçiren ifadeler barındırabilir.
Test ortamı kurulurken (nesnelerin yaratılması, bağlanması vs.) zaman akmamalıdır, her şey simülasyonun 0. anından önce hazır olmalıdır. Bu yüzden build_phase ve connect_phase birer function'dır.
Ancak run_phase simülasyonun asıl aktığı, verilerin gönderildiği ve saat vuruşlarının beklendiği yerdir. Bu yüzden zaman harcayabilen bir task olmak zorundadır.
Soru 4: build_phase yukarıdan aşağıya (Top-Down) çalışırken, neden connect_phase aşağıdan yukarıya (Bottom-Up) çalışıyor?
Cevap: Bu tamamen mantıksal bir zorunluluktur. Bir şeyin alt bileşenlerini yaratmak için önce o şeyi yaratmanız gerekir. Yani önce env yaratılmalı ki, onun içindeki agent yaratılabilsin. Bu yüzden build_phase yukarıdan aşağıya inmelidir (Test -> Env -> Agent).
Bağlantı (Connect) aşamasında ise, en alt seviyedeki arayüzlerin (interface) veya portların bağlandığından emin olup, sorunsuz olduklarını gördükten sonra üst hiyerarşideki bağlantıları yapmak daha güvenlidir. Bu nedenle connect_phase içeriden dışarıya (aşağıdan yukarıya) doğru ilerler.
Soru 5: Kodun içinde phase.raise_objection() ve drop_objection() gördük. Bunları yazmazsak simülasyon çöker mi?
Cevap: Çökmez, ancak simülasyon 0 anında biter ve hiçbir test senaryosu çalışmaz.
UVM'de simülasyonun ne zaman biteceğine karar veren mekanizma "Objection" (İtiraz) mekanizmasıdır. UVM tüm bileşenlere şunu sorar: "Simülasyonu bitiriyorum, itirazı olan var mı?". Eğer bir test veya bileşen raise_objection demezse, UVM simülasyonu anında sonlandırır. drop_objection ise "Benim işim bitti, itirazımı geri çekiyorum, istersen simülasyonu kapatabilirsin" demektir.
Soru 6: Raporlama kısmında UVM_LOW, UVM_MEDIUM, UVM_HIGH gibi seviyeler var. Neden sadece "yazdır" deyip geçmiyoruz?
Cevap: Büyük bir projede milyonlarca log mesajı olabilir. Eğer her mesaj ekrana yazdırılırsa simülasyon inanılmaz derecede yavaşlar ve asıl hatayı bulmanız imkansız hale gelir.
Bu "verbosity" (detay) seviyeleri mesajları filtrelememizi sağlar.
- UVM_LOW: Çok önemli mesajlar. Her zaman yazdırılır.
- UVM_MEDIUM: Varsayılan seviye. Normal test akışı.
- UVM_HIGH: Daha detaylı bilgi, veri paketlerinin içerikleri gibi. Sadece istenirse yazdırılır.
- UVM_DEBUG: Sadece hata ararken (debugging) görmek isteyeceğiniz en ince detaylar.
Simülatöre verilen bir parametre ile hangi seviyenin altının ekrana basılacağına dinamik olarak karar verebiliriz.
Soru 7: Metotların içinde hep super.build_phase(phase) yazıyoruz. Kendi kodumuzu zaten yazıyoruz, bunu silsek olmaz mı?
Cevap: Bunu silmek tehlikelidir ve tavsiye edilmez. Sınıfımız (örneğin base_test) uvm_test isimli UVM ana sınıfından miras alır. UVM'in kendi kütüphanesindeki o üst sınıfta, arka planda çalışan önemli konfigürasyon ayarları veya kayıt işlemleri olabilir. super.build_phase(phase) diyerek "Önce benim miras aldığım UVM babasının build işlemleri çalışsın, sonra benim kendi yazdığım kodlar çalışsın" garantisini veriyoruz. Silerseniz arka plandaki konfigürasyon mekanizmalarını bozabilirsiniz.
Soru 8: Test ağacını (Test, Env, Agent) kurduk ama ortada test edilecek bir donanım (DUT) modülü yok. Bu kod şu an neyi test ediyor?
Cevap: Şu an hiçbir şeyi test etmiyor! 1. günün amacı donanımı değil, doğrulama altyapısının iskeletini ayağa kaldırmaktır. Önce evimizin temelini ve odalarını (Env, Agent, Test) inşa ediyoruz. İlerleyen günlerde, donanımı (DUT) temsil eden modülü getireceğiz, sinyalleri (Interface) tanımlayacağız ve Agent'ımızın içindeki bileşenlerle bu donanımı sürmeye (Driver) ve izlemeye (Monitor) başlayacağız.