UVM Eğitimi 3. Gün Laboratuvar Kılavuzu: TLM Haberleşmesi, Scoreboard ve Çevre (Environment) Kurulumu
Amaç
Bu laboratuvarın amacı, UVM bileşenleri arasında veri aktarımı sağlayan TLM (Transaction Level Modeling) portlarını kullanmak, verileri doğrulamak için bir Scoreboard tasarlamak ve fonksiyonel kapsama (coverage) verilerini toplamak için iki farklı yaklaşımı (Geleneksel TLM Tabanlı ve Modern Handle Tabanlı) karşılaştırmalı olarak uygulamaktır. Ayrıca simülasyon sonunda kapsama raporlarının nasıl alınacağını göreceğiz.
Ön Koşullar
- 1 ve 2. gün konuları (Agent hiyerarşisi, Sequence Item oluşturma).
- Nesne yönelimli programlamada (OOP) referanslar (handle) ve write gibi fonksiyonların işleyişi.
Lab 3.1: Monitor'e Analysis Port ve Coverage Handle Eklenmesi
Monitor, arayüzden okuduğu veriyi diğer bileşenlere iletmekle görevlidir. Bu veriyi standart TLM yapısıyla (Scoreboard ve TLM-Coverage için) "yayınlamak" (broadcast) için bir uvm_analysis_port kullanacağız. Ayrıca modern yaklaşımı göstermek için Handle-Based Coverage Collector sınıfımızın referansını da Monitor içine tanımlayacağız.
Görev: 2. Gün yazdığımız my_monitor sınıfını aşağıdaki gibi güncelleyin.
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
// 1. Declare the analysis port (Used for Scoreboard and TLM Coverage)
uvm_analysis_port #(my_transaction) ap;
// 2. Declare a handle for direct coverage sampling (Modern approach)
my_handle_coverage cov_handle;
// Constructor
function new(string name = "my_monitor", uvm_component parent = null);
super.new(name, parent);
endfunction
// Build phase: Instantiate the analysis port
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap", this);
endfunction
// Run phase: Monitor signals and broadcast/sample the transaction
virtual task run_phase(uvm_phase phase);
my_transaction tr;
super.run_phase(phase);
forever begin
#20; // Simulated sampling delay
// Create a dummy transaction to represent sampled data
tr = my_transaction::type_id::create("tr");
tr.addr = 8'hAA;
tr.data = 32'h12345678;
tr.write_en = 1'b1;
`uvm_info("MONITOR", "Sampled data from virtual interface...", UVM_LOW)
// 1. Broadcast via TLM (Will be received by Scoreboard and TLM Coverage)
ap.write(tr);
// 2. Send to modern coverage collector via direct function call
if (cov_handle != null) begin
cov_handle.sample_cov(tr.write_en);
end
end
endtask
endclass
Kodun Açıklaması
Bu blokta my_monitor sınıfı iki farklı haberleşme yöntemini aynı anda barındıracak şekilde tasarlanmıştır:
- Bildirimler:
uvm_analysis_port #(my_transaction) apifadesi,my_transactiontipinde verileri "yayınlamak" (broadcast) için kullanılan TLM analiz portunu tanımlar. Yanında tanımlananmy_handle_coverage cov_handleise modern (handle tabanlı) yöntem için bir referanstır; henüz bir nesneye işaret etmez, yalnızca bir el (handle) tutar. build_phase: Buradaap = new("ap", this)ile analiz portu örneklendirilir. UVM'de portlar da bileşen hiyerarşisine bağlı olduğu içinthisparametresi ile sahibine (Monitor'e) bağlanır.run_phase:foreverdöngüsü içinde#20gecikmeyle örnekleme simüle edilir. Her turdamy_transaction::type_id::create("tr")ile factory üzerinden bir transaction üretilir veaddr,data,write_enalanları doldurulur.- İki kanaldan dağıtım:
ap.write(tr)çağrısı, porta bağlı TÜM dinleyicilere (Scoreboard ve TLM tabanlı Coverage) aynı transaction'ı iletir; bu broadcast davranışıdır. Hemen ardındanif (cov_handle != null)kontrolü yapılarak, eğer handle bir nesneye bağlanmışsacov_handle.sample_cov(tr.write_en)ile doğrudan fonksiyon çağrısı yapılır.nullkontrolü, handle bağlanmadan çalıştırılırsa simülasyonun çökmesini engeller.
Lab 3.2: UVM Scoreboard Tasarımı
Scoreboard, Monitor'den gelen işlemleri dinler (Analysis Implementation aracılığıyla) ve doğruluğunu kontrol eder.
Görev: my_scoreboard adında bir sınıf oluşturun. Bir uvm_analysis_imp portu ekleyin ve mecburi olan write() fonksiyonunu tanımlayın.
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
// Declare the analysis implementation port
uvm_analysis_imp #(my_transaction, my_scoreboard) ap_imp;
// Constructor
function new(string name = "my_scoreboard", uvm_component parent = null);
super.new(name, parent);
endfunction
// Build phase
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap_imp = new("ap_imp", this);
endfunction
// The mandatory write() method triggered by ap.write()
virtual function void write(my_transaction tr);
if(tr.addr == 8'hAA && tr.data == 32'h12345678) begin
`uvm_info("SCOREBOARD", "Data matches expected values! [PASS]", UVM_LOW)
end else begin
`uvm_error("SCOREBOARD", "Data mismatch detected! [FAIL]")
end
endfunction
endclass
Kodun Açıklaması
my_scoreboard sınıfı, Monitor'den gelen veriyi alıp doğrulayan pasif bir bileşendir:
uvm_analysis_imp #(my_transaction, my_scoreboard) ap_imp: Bu satır bir analysis "implementation" portu tanımlar. İki tip parametresine dikkat edin: birincisi taşınacak veri tipi (my_transaction), ikincisi isewrite()metodunu barındıran sınıfın kendisidir (my_scoreboard). Bu ikinci parametre, gelen veride hangi sınıfınwrite()metodunun çağrılacağını belirtir.build_phase:ap_imp = new("ap_imp", this)ile implementation portu örneklendirilir.- Zorunlu
write()metodu:uvm_analysis_impkullanan her sınıf,virtual function void write(my_transaction tr)metodunu mutlaka tanımlamak zorundadır. Monitor tarafındaap.write(tr)çağrıldığında, TLM mekanizması otomatik olarak buradakiwrite()fonksiyonunu tetikler. İçeridetr.addrvetr.databeklenen değerlerle karşılaştırılır; eşleşirseuvm_infoile[PASS], eşleşmezseuvm_errorile[FAIL]mesajı basılır.
Lab 3.3: Kapsama (Coverage) Toplayıcıları ve Raporlama
Bu bölümde iki farklı Coverage Collector oluşturacağız ve her ikisine de simülasyon biterken nihai sonuçları ekrana yazdırması için report_phase metodunu ekleyeceğiz.
Yöntem 1: Geleneksel TLM Tabanlı (uvm_subscriber)
uvm_subscriber tek bir porta sahip olduğu için büyük projelerde yetersiz kalabilir.
// APPROACH 1: TLM-based using uvm_subscriber
class my_tlm_coverage extends uvm_subscriber #(my_transaction);
`uvm_component_utils(my_tlm_coverage)
my_transaction cov_tr;
covergroup cg;
option.per_instance = 1;
cp_write_en: coverpoint cov_tr.write_en {
bins read_op = {0};
bins write_op = {1};
}
endgroup
function new(string name = "my_tlm_coverage", uvm_component parent = null);
super.new(name, parent);
cg = new();
endfunction
// Mandatory write method for TLM
virtual function void write(my_transaction t);
cov_tr = t;
cg.sample();
`uvm_info("TLM_COV", $sformatf("Sampled via TLM. write_en=%0b", cov_tr.write_en), UVM_LOW)
endfunction
// Report phase to print the final coverage at the end of the simulation
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("TLM_COV_REPORT", $sformatf("Final Coverage (TLM-Based): %0.2f%%", cg.get_inst_coverage()), UVM_NONE)
endfunction
endclass
Kodun Açıklaması
my_tlm_coverage sınıfı, kapsama verisini TLM portu üzerinden toplayan geleneksel yöntemi gösterir:
extends uvm_subscriber #(my_transaction):uvm_subscriber, içinde hazır biranalysis_exportbarındıran özel bir bileşendir. Bu sayede ayrıca bir port tanımlamaya gerek kalmaz; ancak yalnızca TEK bir giriş portu olduğu için büyük projelerde sınırlayıcıdır.covergroup cgvecoverpoint:cp_write_enadlı coverpoint,cov_tr.write_ensinyalini izler. İçindekibins read_op = {0}vebins write_op = {1}tanımları, sinyalin 0 ve 1 değerlerini ayrı kovalarda (bin) sayar.option.per_instance = 1ise her örnek (instance) için ayrı kapsama hesaplanmasını sağlar.new()constructor:cg = new()ile covergroup örneklendirilir. Covergroup'lar otomatik oluşturulmaz, açıkçanewile başlatılmalıdır.- Zorunlu
write()metodu:uvm_subscriber'dan miras alındığı içinwrite()metodu mutlaka tanımlanır. Gelenttransaction'ıcov_tr'ye atanır vecg.sample()ile o anki değer örneklenir. report_phase: Simülasyon sonundacg.get_inst_coverage()ile o örneğe ait yüzdesel kapsama değeri alınır veUVM_NONEseviyesiyle (her zaman görünür) ekrana yazdırılır.
Yöntem 2: Modern Handle Tabanlı (Doğrudan Fonksiyon Çağrısı)
Sektörde tavsiye edilen, port kısıtlaması olmayan yöntem. Covergroup argümanları doğrudan fonksiyonla toplanır.
// APPROACH 2: Handle-based using standard uvm_component
class my_handle_coverage extends uvm_component;
`uvm_component_utils(my_handle_coverage)
// Covergroup with arguments passed to its sample function
covergroup cg with function sample(bit w_en);
option.per_instance = 1;
cp_write_en: coverpoint w_en {
bins read_op = {0};
bins write_op = {1};
}
endgroup
function new(string name = "my_handle_coverage", uvm_component parent = null);
super.new(name, parent);
cg = new();
endfunction
// Custom function to be called directly from anywhere
function void sample_cov(bit w_en);
cg.sample(w_en);
`uvm_info("HANDLE_COV", $sformatf("Sampled via Handle. write_en=%0b", w_en), UVM_LOW)
endfunction
// Report phase to print the final coverage at the end of the simulation
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("HANDLE_COV_REPORT", $sformatf("Final Coverage (Handle-Based): %0.2f%%", cg.get_inst_coverage()), UVM_NONE)
endfunction
endclass
Kodun Açıklaması
my_handle_coverage sınıfı, sektörde tavsiye edilen handle tabanlı yöntemi gösterir ve sıradan bir uvm_component'ten türer (TLM portuna gerek yoktur):
covergroup cg with function sample(bit w_en): Bu, kapsama mantığının kalbidir. Geleneksel yöntemden farklı olarak covergroup, parametre alabilen özel birsample(bit w_en)fonksiyonuyla tanımlanır. Böylece bir sınıf üyesi değişkene (cov_trgibi) bağlı kalmadan, dışarıdan gelen değer doğrudan örneklenebilir.coverpoint w_en:cp_write_encoverpoint'i, sınıf değişkeni yerine doğrudan fonksiyon argümanı olanw_en'i izler;bins read_op = {0}vebins write_op = {1}ile değerleri ayrı kovalarda sayar.sample_cov(bit w_en): Bu, herhangi bir yerden (örneğin Monitor'den) doğrudan çağrılabilen özel bir fonksiyondur. İçeridecg.sample(w_en)ile gelen değeri covergroup'a aktarır. Port/bağlantı gerektirmediği için tek bir kaynaktan birden çok yere kolayca dağıtım yapılabilir.report_phase: Geleneksel yöntemde olduğu gibicg.get_inst_coverage()ile nihai kapsama yüzdesi alınıp ekrana yazdırılır.
Lab 3.4: Çevre (Environment) İçinde Bağlantıların Yapılması
Scoreboard ve TLM tabanlı Coverage için geleneksel port bağlantılarını (connect) kullanacağız. Handle tabanlı modern Coverage sınıfı için ise referans ataması (=) yapacağız.
Görev: my_env sınıfını güncelleyerek build_phase ve connect_phase mekanizmalarını kurun.
class my_env extends uvm_env;
`uvm_component_utils(my_env)
// Component handles
my_agent agent_inst;
my_scoreboard sb_inst;
my_tlm_coverage tlm_cov_inst;
my_handle_coverage handle_cov_inst;
// Constructor
function new(string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction
// Build phase
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent_inst = my_agent::type_id::create("agent_inst", this);
sb_inst = my_scoreboard::type_id::create("sb_inst", this);
tlm_cov_inst = my_tlm_coverage::type_id::create("tlm_cov_inst", this);
handle_cov_inst = my_handle_coverage::type_id::create("handle_cov_inst", this);
endfunction
// Connect phase
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// 1. TLM Connections: ap is a broadcast port, it can connect to multiple destinations
agent_inst.mon.ap.connect(sb_inst.ap_imp);
agent_inst.mon.ap.connect(tlm_cov_inst.analysis_export);
// 2. Handle Passing: Pass the coverage collector handle directly to the monitor
agent_inst.mon.cov_handle = this.handle_cov_inst;
`uvm_info("ENV_CONN", "TLM connections and handle passing completed successfully.", UVM_LOW)
endfunction
endclass
Kodun Açıklaması
my_env sınıfı, tüm bileşenleri bir araya getiren ve aralarındaki bağlantıları kuran çevredir:
- Bileşen referansları: Sınıf başında
my_agent,my_scoreboard,my_tlm_coveragevemy_handle_coveragetiplerinde handle'lar tanımlanır. build_phase: Her bileşentype_id::create(...)ile factory üzerinden örneklendirilir.thisparametresi, oluşturulan bileşenin Environment'a child olarak bağlanmasını sağlar.connect_phase– TLM bağlantıları:agent_inst.mon.ap.connect(sb_inst.ap_imp)ile Monitor'ün analiz portu Scoreboard'un implementation portuna bağlanır. Hemen ardından aynıapportutlm_cov_inst.analysis_export'a da bağlanır. Bu, broadcast (yayın) portunun TEK kaynaktan ÇOK hedefe bağlanabildiğini gösterir;ap.write()çağrıldığında her iki bağlantı da tetiklenir.connect_phase– Handle aktarımı:agent_inst.mon.cov_handle = this.handle_cov_instsatırı, herhangi bir port kullanmadan, modern coverage toplayıcının referansını doğrudan Monitor'e atar (=ile). Artık Monitor içindekicov_handleboş değildir vesample_covçağrıları geçerli nesneye ulaşır.
Önemli Noktalar
- TLM port vs Handle yaklaşımı: TLM (
analysis_port/analysis_imp) yaklaşımı standart, çözük (decoupled) ve test edilebilir bir bağlantı sunar; alıcı ile gönderici birbirini doğrudan tanımaz. Handle tabanlı yaklaşım ise daha doğrudan ve hızlıdır, port/bağlantı maliyeti olmadan basit veri aktarımı için idealdir. Genel kural: bileşenler arası standart haberleşmede TLM, hafif/lokal kapsama örneklemede handle tercih edin. - Broadcast portun gücü: Tek bir
uvm_analysis_port,connectçağrısı tekrarlanarak birden fazla hedefe (Scoreboard, Coverage, Logger vb.) bağlanabilir.ap.write(tr)tek çağrıyla tüm dinleyicilere aynı veriyi iletir; bu, yeni dinleyici eklemeyi zahmetsiz kılar. write()metodu zorunludur:uvm_analysis_impveyauvm_subscriberkullanan her sınıf,write()fonksiyonunu tanımlamak zorundadır; aksi halde derleme hatası alınır. Bu metot, gelen transaction geldiğinde TLM tarafından otomatik tetiklenir.uvm_subscribersınırı:uvm_subscriberyalnızca tek biranalysis_exportbarındırır. Birden fazla farklı kaynaktan veri toplamak gerektiğinde yetersiz kalır; bu durumda ayrıuvm_analysis_impportları veya handle tabanlı yaklaşım gerekir.- Covergroup mutlaka
newile başlatılır: Covergroup'lar otomatik oluşturulmaz; constructor içindecg = new()çağrısı unutulursa örnekleme çalışmaz ve kapsama sıfır kalır.option.per_instance = 1ile her örnek için ayrı kapsama izlenir. nullkontrolü güvenliği: Handle tabanlı çağrılarda (cov_handle.sample_cov(...)) öncesindeif (cov_handle != null)kontrolü yapmak, handle henüz atanmadan çalıştırılan senaryolarda simülasyonun çökmesini engeller.
Özet ve Beklenen Sonuç
base_test içerisinde #100 zaman birimi boyunca beklenildiğinde, Monitor (her 20 birimde bir çalıştığı için) 5 adet işlem (transaction) üretecektir.
Simülasyon çalıştırıldığında testin run_phase'i süresince ekranda Scoreboard eşleşmeleri [PASS] ve her iki Coverage örneklendirme [TLM_COV], [HANDLE_COV] logları art arda düşecektir.
Simülasyon kapatılırken (UVM report_phase'e geçtiğinde), terminalde aşağıdaki nihai rapor çıktılarını görmelisiniz:
UVM_INFO @ 100: uvm_test_top.env_inst.tlm_cov_inst [TLM_COV_REPORT] Final Coverage (TLM-Based): 50.00%
UVM_INFO @ 100: uvm_test_top.env_inst.handle_cov_inst [HANDLE_COV_REPORT] Final Coverage (Handle-Based): 50.00%
(Not: Dummy verimiz sürekli write_en = 1 ürettiği için coverpoint içindeki "write" durumu %100, "read" durumu %0 kapsanmıştır. İkisinin ortalaması %50'dir.)
Sıkça Sorulan Sorular (FAQ)
Soru 1: analysis_port ile analysis_imp arasındaki fark nedir?
Cevap: uvm_analysis_port veriyi GÖNDEREN (üreten) taraftadır; Monitor gibi bileşenlerde ap.write(tr) ile veri yayınlamak için kullanılır. uvm_analysis_imp ise veriyi ALAN (tüketen) taraftadır ve içinde write() metodunun gerçek uygulamasını (implementation) barındırır. Kısaca port "çağıran", imp ise "uygulayan" taraftır. Bağlantı her zaman porttan imp'e doğru connect ile yapılır.
Soru 2: write() metodunu neden mutlaka tanımlamak zorundayım?
Cevap: TLM analysis mekanizması, gönderici tarafta ap.write(tr) çağrıldığında, bağlı tüm alıcıların write() metodunu otomatik olarak tetikler. Bu metot, uvm_analysis_imp veya uvm_subscriber arayüzünün bir sözleşmesidir (interface contract). Eğer tanımlanmazsa sınıf soyut kalır ve derleme hatası verir; çünkü TLM altyapısının çağıracağı somut bir fonksiyon bulamaz.
Soru 3: uvm_subscriber her zaman yeterli değil mi, neden başka yöntemlere ihtiyaç var?
Cevap: uvm_subscriber içinde hazır TEK bir analysis_export barındırır, bu da onu basit kapsama toplama için pratik kılar. Ancak bir bileşenin birden fazla farklı kaynaktan (örneğin hem input hem output Monitor'den) veri toplaması gerektiğinde tek port yetersiz kalır. Bu gibi durumlarda ya birden çok uvm_analysis_imp (makrolarla isimlendirilmiş) tanımlanır ya da handle tabanlı yaklaşıma geçilir.
Soru 4: Handle tabanlı kapsama toplamanın avantajı nedir?
Cevap: Handle tabanlı yöntem port, export ve connect zincirine ihtiyaç duymaz; sadece bir referans ataması (cov_handle = handle_cov_inst) ve doğrudan fonksiyon çağrısı (sample_cov(...)) yeterlidir. Bu, daha az kod ve daha hızlı çağrı anlamına gelir. Ayrıca covergroup ... with function sample(...) kullanıldığı için, birden fazla parametre temiz biçimde tek çağrıda örneklenebilir. Hafif ve lokal örneklemelerde ideal bir tekniktir.
Soru 5: Bir analysis_port neden birden fazla yere bağlanabiliyor?
Cevap: uvm_analysis_port tasarım gereği bir "broadcast" (yayın) portudur; tıpkı bir radyo vericisi gibi tek bir veriyi dinleyen herkese aynı anda iletir. Bu yüzden aynı ap portu üzerinde connect'i tekrar tekrar çağırarak Scoreboard, Coverage ve istediğiniz kadar başka dinleyiciyi bağlayabilirsiniz. ap.write(tr) çağrıldığında UVM, bağlı tüm imp/export'ların write() metodunu sırayla tetikler; bu sayede yeni bir dinleyici eklemek mevcut kodu değiştirmeden mümkün olur.