Fonksiyonel Kapsam
Gün 5: Arayüzler, Assertion ve Coverage | covergroup, coverpoint, bins, cross coverage
Bu derste fonksiyonel kapsam (functional coverage) ile testlerimizin tasarımın ne kadarını gerçekten denediğini nasıl ölçeceğimizi öğreneceğiz. covergroup, coverpoint, bins ve cross yapılarını bir ALU örneği üzerinden inceleyeceğiz.
Fonksiyonel Kapsam Neden Önemli?
Rastgele test (constrained random) ile binlerce işlem üretebilirsiniz, ama "her senaryoyu denedim mi?" sorusunun cevabı belirsiz kalır. Fonksiyonel kapsam, doğrulama planındaki senaryoların gerçekten gerçekleşip gerçekleşmediğini sayısal olarak ölçer:
- Doğrulamanın bittiğine karar verdirir: Kapsam %100'e ulaşmadan doğrulama tamamlanmış sayılmaz.
- Test boşluklarını gösterir: Hangi
bin'in hiç dolmadığını görüp eksik senaryoları belirlersiniz. - Kod kapsamından farklıdır: Kod kapsamı "kod satırı çalıştı mı?" sorusunu, fonksiyonel kapsam ise "anlamlı senaryo test edildi mi?" sorusunu yanıtlar.
Temel Kavramlar
covergroup: Birbiriyle ilişkili kapsam noktalarını gruplayan yapıdır.with function sample(...)ile dışarıdan veri alarak örnekleme yapabilir.coverpoint: İzlenecek bir değişkeni/ifadeyi temsil eder.bins: Bir coverpoint'in değer aralıklarını kovalara böler. Her kovanın en az bir kez dolması beklenir.illegal_bins: Asla oluşmaması gereken değerleri işaretler; oluşursa hata verir.cross: İki coverpoint'in kombinasyonlarını ölçer (örn. her işlem × her operand aralığı). Köşe durumlarını yakalamada güçlüdür.
Kaynak Kod
// =============================================================================
// GUN 5 - Konu 5: Fonksiyonel Kapsam (Functional Coverage)
// =============================================================================
class ALU_Transaction;
typedef enum logic [2:0] {ADD, SUB, AND, OR, XOR, SHL, SHR} op_e;
rand op_e operation;
rand logic [7:0] operand_a;
rand logic [7:0] operand_b;
logic [15:0] result;
constraint c_operands {
operand_a inside {[0:255]};
operand_b inside {[0:255]};
}
function void compute();
case (operation)
ADD: result = operand_a + operand_b;
SUB: result = operand_a - operand_b;
AND: result = operand_a & operand_b;
OR: result = operand_a | operand_b;
XOR: result = operand_a ^ operand_b;
SHL: result = operand_a << operand_b[2:0];
SHR: result = operand_a >> operand_b[2:0];
endcase
endfunction
endclass
module functional_coverage;
ALU_Transaction txn;
// =========================================================================
// COVERGROUP: Kapsam grubu tanimi
// =========================================================================
covergroup alu_cg with function sample(
ALU_Transaction::op_e op,
logic [7:0] a, logic [7:0] b, logic [15:0] res
);
// --- COVERPOINT: Islem kapsanmasi ---
cp_operation: coverpoint op {
bins add_bin = {ALU_Transaction::ADD};
bins sub_bin = {ALU_Transaction::SUB};
bins and_bin = {ALU_Transaction::AND};
bins or_bin = {ALU_Transaction::OR};
bins xor_bin = {ALU_Transaction::XOR};
bins shl_bin = {ALU_Transaction::SHL};
bins shr_bin = {ALU_Transaction::SHR};
}
// --- COVERPOINT: Operand A araliklari ---
cp_op_a: coverpoint a {
bins zero = {0};
bins low = {[1:63]};
bins mid = {[64:191]};
bins high = {[192:254]};
bins max_val = {255};
}
// --- COVERPOINT: Operand B araliklari ---
cp_op_b: coverpoint b {
bins zero = {0};
bins low = {[1:63]};
bins mid = {[64:191]};
bins high = {[192:254]};
bins max_val = {255};
}
// --- COVERPOINT: Sonuc araliklari ---
cp_result: coverpoint res {
bins zero = {0};
bins small = {[1:255]};
bins medium = {[256:65534]};
bins overflow = {65535};
}
// --- COVERPOINT: Kose durumlari ---
// Yalnizca alti sinir degeri ayri ayri izlenir; diger degerler kapsam
// hesabina katilmaz (ignore). DIKKAT: burada illegal_bins KULLANILMAZ,
// cunku rastgele testte a'nin bu alti deger disinda olmasi tamamen normaldir.
cp_corner: coverpoint a {
bins boundaries[] = {0, 1, 127, 128, 254, 255};
ignore_bins others = default;
}
// --- COVERPOINT: illegal_bins ornegi ---
// Kaydirma islemlerinde b[7:3] kullanilmaz; bu bitlerin set olmasi
// "DUT'un asla gormemesi gereken" bir durum olsaydi hata uretirdi.
// (Buradaki rastgele uretim b'yi sinirlamadigi icin yalnizca GOSTERIM amaclidir
// ve bins yorumda birakilmistir; acarsaniz her ihlalde simulator hata basar.)
cp_shift_amt: coverpoint b[2:0] iff (op inside {ALU_Transaction::SHL, ALU_Transaction::SHR}) {
bins amt[] = {[0:7]};
// illegal_bins bad = {[8:$]}; // b[2:0] zaten 0-7 arasi; ornek amacli
}
// --- CROSS: Islem x Operand A araligi ---
cx_op_a: cross cp_operation, cp_op_a;
// --- CROSS: Islem x Operand B araligi ---
cx_op_b: cross cp_operation, cp_op_b;
endgroup
// =========================================================================
alu_cg cg;
initial begin
$display("=== Fonksiyonel Kapsam (Functional Coverage) ===\n");
txn = new();
cg = new();
// Rastgele test dongusu
$display("--- 200 Rastgele Islem ---");
repeat (200) begin
assert(txn.randomize()) else $fatal(1, "Randomize hata!");
txn.compute();
cg.sample(txn.operation, txn.operand_a, txn.operand_b, txn.result);
end
// Yonlendirilmis testler (kose durumlari)
$display("--- Yonlendirilmis Kose Testleri ---");
begin
logic [7:0] corners [] = '{0, 1, 127, 128, 254, 255};
foreach (corners[i]) begin
foreach (corners[j]) begin
assert(txn.randomize() with {
operand_a == corners[i];
operand_b == corners[j];
}) else continue;
txn.compute();
cg.sample(txn.operation, txn.operand_a, txn.operand_b, txn.result);
end
end
end
// Kapsam raporu
$display("\n--- Kapsam Raporu ---");
$display(" Toplam Kapsam : %.1f%%", cg.get_coverage());
$display(" cp_operation : %.1f%%", cg.cp_operation.get_coverage());
$display(" cp_op_a : %.1f%%", cg.cp_op_a.get_coverage());
$display(" cp_op_b : %.1f%%", cg.cp_op_b.get_coverage());
$display(" cp_result : %.1f%%", cg.cp_result.get_coverage());
$display("\n=== Fonksiyonel Kapsam Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
ALU_Transactionsınıfı:op_eenum'u (ADD,SUB,AND,OR,XOR,SHL,SHR) ile işlem türünü tanımlar.operation,operand_a,operand_brastgele üretilir;c_operandskısıtı operandları 0-255 aralığına çeker.computefonksiyonu seçilen işleme göreresult'u hesaplar (kaydırmalardaoperand_b[2:0]kullanılır).covergroup alu_cg:with function sample(...)ile işlem, iki operand ve sonucu dışarıdan alır.cp_operation: Yedi ALU işleminin her birini ayrı birbinile izler.cp_op_a/cp_op_b: Operandları anlamlı aralıklara böler:zero,low[1:63],mid[64:191],high[192:254],max_val255.cp_result: Sonucuzero,small,medium,overflowaralıklarına ayırır.cp_corner:bins boundaries[] = {0, 1, 127, 128, 254, 255}ile köşe değerleri tek tek izler ([]köşeli parantez, her değer için ayrı bin üretir);ignore_bins others = defaultise bu altı değer dışındaki örnekleri kapsam hesabından düşer. Böylececp_corner'ın yüzdesi yalnızca "altı sınır değerinden kaçı görüldü?" sorusunu yanıtlar.cp_shift_amt:iff (...)koşulu ile yalnızca kaydırma işlemlerinde örneklenir;b[2:0]kaydırma miktarının 0–7 arası her değerini izler. Yorumdakiillegal_binssatırı, "oluşması yasak" değerlerin nasıl işaretleneceğini gösterir.cx_op_a/cx_op_b: İşlem ile operand aralıklarının çapraz kombinasyonlarını (cross) ölçer.
functional_coverage(top):txnvecgnesneleri oluşturulur. Öncerepeat(200)döngüsünde rastgele işlemler üretilipcomputeedilir vecg.sample(...)ile örneklenir. Ardındancornersdizisi üzerinde iç içeforeachile yönlendirilmiş köşe testleri çalıştırılır (randomize() with {...}ile operandlar sabitlenir). Son olarakget_coverage()çağrılarıyla toplam ve coverpoint bazlı kapsam raporlanır.
Önemli Noktalar
ignore_binsileillegal_binsfarklı amaçlara hizmet eder:ignore_binsdeğeri kapsam hesabından çıkarır ("bu değerler beni ilgilendirmiyor");illegal_binsise değer örneklendiğinde hata üretir ("bu değer asla oluşmamalı").cp_corner'da rastgele operandların altı sınır değeri dışında kalması tamamen normaldir; bu yüzdenignore_binsdoğru seçimdir.illegal_binsyalnızca protokol ihlali anlamına gelen değerler için (örneğin tanımsız bir opcode) kullanılmalıdır; aksi halde her rastgele örnekte sahte hata alırsınız.defaultbin tuzağı:defaultbin, kalan tüm değerleri toplar ama kapsam yüzdesine sayılmaz. Anlamlı senaryoları açıkça adlandırılmışbins'lerle ifade edin; "diğerleri" içinignore_bins ... = defaultyazmak niyeti netleştirir.iffile koşullu örnekleme:coverpoint b[2:0] iff (op inside {SHL, SHR})yalnızca koşul doğruyken örnek alır. Bu, "AND işleminde kaydırma miktarı" gibi anlamsız kombinasyonların kapsamı kirletmesini önler vecross'takiignore_bins'e alternatiftir.crossile köşe kombinasyonları: Tek tek coverpoint'ler %100 olsa bile,cx_op_a/cx_op_bçaprazları "her işlemi her aralıkla denedim mi?" sorusunu yanıtlar; gerçek boşluklar çoğunlukla burada görünür.- Rastgele + yönlendirilmiş birlikte: Rastgele test geniş alanı tarar, ama
255veya0gibi köşe değerlerine nadiren isabet eder. Kod, bunlarırandomize() withile bilinçli olarak zorlar. sample()'ı doğru anda çağırın: Kapsam ancaksampleçağrıldığında güncellenir; veri kararlıyken (buradacomputesonrası) örneklemek doğru sonuç verir.- Anlamlı
binsaralıkları seçin: Kovaları rastgele değil, tasarımın davranışının değiştiği sınırlara göre bölmek (örn. taşma içinoverflow = {65535}) kapsamı anlamlı kılar. bins x[] = {...}ilebins x = {...}farkı: Köşeli parantez her değere ayrı bin açar (altı değer → altı bin, her biri ayrı ayrı dolmalı); parantezsiz yazım tüm değerleri tek bin'de toplar (altı değerden herhangi biri görülünce bin dolar). Köşe değerlerin her birini ayrı ayrı görmek istiyorsanız[]şarttır.- Otomatik bin'ler ve
auto_bin_max: Hiçbinsyazmazsanız SystemVerilog değer uzayını otomatik olarak en fazla 64 bin'e böler (option.auto_bin_max). 32-bit bir veri için bu, her biri ~67 milyon değerlik 64 anlamsız kova demektir; her zaman kendi bin'lerinizi tanımlayın.
Kapsam Türleri Bir Bakışta
| Tür | Soru | Kim toplar? | Sınırı |
|---|---|---|---|
| Kod kapsamı (line/branch/toggle) | "Hangi RTL satırları çalıştı?" | Simülatör otomatik | Satır çalışmış ama doğru çalışmış mı bilmez |
Fonksiyonel kapsam (covergroup) |
"Plandaki senaryolar denendi mi?" | Sizin yazdığınız coverpoint/cross |
Yalnızca yazdığınız kadar ölçer; eksik bin = kör nokta |
Assertion kapsamı (cover property) |
"Bu zamansal senaryo yaşandı mı?" | SVA | Protokol düzeyi, veri değerlerine kör |
Doğrulama "bitti" demek için üçü de hedefe ulaşmalı ve regresyon hatasız olmalıdır. Yalnızca fonksiyonel kapsamı %100 yapmak, planda unuttuğunuz senaryoları görünmez kılar.
Benzetme: Fonksiyonel kapsam, bir sınavın konu listesidir: "integral çıktı mı, türev çıktı mı, limit çıktı mı?" Her konu bir
bin'dir.crossise "integral ve zor seviye aynı soruda çıktı mı?" sorusudur. Sınavdan 100 almak (testlerin geçmesi) yetmez; soruların konuları kapsamadığını fark etmezseniz öğrenci bilmediği konudan hiç sınanmamış olur.
Sık Yapılan Hatalar
- Rastgele değişkende
illegal_bins = default: Her rastgele örnek hata üretir;ignore_binskullanın. sample()'ı yanlış anda çağırmak:compute()öncesinde örneklersenizresulteski değeri taşır vecp_resultyanlış dolar.cross'ta anlamsız kombinasyonları dışlamamak: "NOTişlemi ×operand_b = 255" gibi kombinasyonlarNOTtek operandlı olduğu için anlamsızdır;ignore_binsile dışlanmazsa %100'e ulaşmak imkânsız görünür ya da gereksiz test yazarsınız.- Covergroup'u
new()etmemek:cg = new();olmadancg.sample()çalışma zamanı hatası verir. - Kapsamı yalnızca stimulus'tan toplamak: Driver'ın gönderdiğini örneklemek "ne gönderdik?" sorusunu yanıtlar, "DUT ne gördü?" sorusunu değil. Gerçek projelerde kapsam monitor gözleminden toplanır (Gün 7 projesinde scoreboard
actualile besler).
Kendinizi Deneyin
cp_corner'dakiignore_bins'iillegal_binsyapın ve çalıştırın. Kaç hata mesajı aldınız? Neden bu kullanım yanlıştır?bins boundaries[]'den[]'yi silin;cp_cornerkapsamı tek bir örnekten sonra kaç yüzde oldu?cx_op_across'unaignore_bins not_a = binsof(cp_operation) intersect {ALU_Transaction::NOT} && binsof(cp_op_a.zero);benzeri bir dışlama ekleyin ve kapsam yüzdesinin nasıl değiştiğini gözlemleyin (ipucu:NOTiçinoperand_banlamsızdır;cx_op_b'de dışlamak daha mantıklı).repeat (200)'ürepeat (20)yapın. Hangi bin'ler boş kaldı?cg.cp_op_a.get_coverage()ile yönlendirilmiş köşe testleri olmadan ve olduğu şekilde karşılaştırın.
Hızlı Kontrol: ignore_bins ile illegal_bins arasındaki fark nedir?
ignore_bins değerleri kapsam hesabından çıkarır; örneklendiğinde hiçbir şey olmaz. illegal_bins ise değer örneklendiğinde hata üretir: "bu asla olmamalıydı" demektir. Rastgele üretimde doğal olarak oluşabilen değerler için ignore_bins, gerçek protokol ihlalleri için illegal_bins kullanılır.