EDA Playground'da Dene

Coverage Tanımları

Gün 7: Bitirme Projesi - Bölüm 2 | ALU projesi için covergroup ve cross coverage

Coverage (kapsam), "DUT'u yeterince test ettik mi?" sorusunu ölçen bileşendir. ALU mimarimizde fonksiyonel kapsamı toplar: hangi işlemlerin, hangi operand değerlerinin ve hangi bayrak kombinasyonlarının gerçekten denendiğini sayısal olarak raporlar.

Coverage ve Mimarideki Yeri

Testlerin geçmesi tek başına yeterli değildir; önemli senaryoların uyarılmış olduğundan da emin olmak gerekir. İşte bunu functional coverage ölçer. Bu sınıf, scoreboard tarafından beslenir:

  • Scoreboard her gözlenen transaction için cov.sample(actual) çağırır.
  • sample, ilgili alanları alu_cg covergroup'una iletir ve kapsam noktaları güncellenir.

Sonuçta kapsam yüzdeleri, test setinin ne kadar kapsamlı olduğunu gösterir ve eksik kalan senaryoları ortaya çıkarır.

Coverpoint ve Cross Coverage

alu_cg covergroup'u şu kapsam noktalarını izler:

  • cp_opcode: Sekiz işlem kodunun her birinin denenip denenmediği.
  • cp_op_a / cp_op_b: Operandların köşe değerleri ve aralıkları (sıfır, bir, orta, maksimum vb.).
  • cp_flags: Üretilen bayrak kombinasyonları.
  • cp_result: Sonuç büyüklüğü aralıkları.

Asıl güç cross coverage'tadır: cx_opcode_a, cx_opcode_b ve cx_opcode_flags ile işlem-operand ve işlem-bayrak kombinasyonları ölçülür. Anlamsız kombinasyonlar (ignore_bins) kapsam dışı bırakılır.

Kaynak Kod

// =============================================================================
// GUN 7 - Konu 5: Covergroup - Fonksiyonel Kapsam Tanimlari
// =============================================================================

class ALU_Coverage;

  covergroup alu_cg with function sample(
    ALU_Transaction::opcode_e opcode,
    logic [7:0] a, logic [7:0] b,
    logic [15:0] result, logic [3:0] flags
  );
    option.per_instance = 1;

    // Islem kodu kapsami
    cp_opcode: coverpoint opcode {
      bins add = {ALU_Transaction::OP_ADD};
      bins sub = {ALU_Transaction::OP_SUB};
      bins and_op = {ALU_Transaction::OP_AND};
      bins or_op  = {ALU_Transaction::OP_OR};
      bins xor_op = {ALU_Transaction::OP_XOR};
      bins not_op = {ALU_Transaction::OP_NOT};
      bins shl = {ALU_Transaction::OP_SHL};
      bins shr = {ALU_Transaction::OP_SHR};
    }

    // Operand A kose durumlari
    cp_op_a: coverpoint a {
      bins zero = {0};
      bins one  = {1};
      bins mid  = {8'h7F, 8'h80};
      bins max_minus_one = {8'hFE};
      bins max  = {8'hFF};
      bins low  = {[2:8'h7E]};
      bins high = {[8'h81:8'hFD]};
    }

    // Operand B kose durumlari
    cp_op_b: coverpoint b {
      bins zero = {0};
      bins one  = {1};
      bins mid  = {8'h7F, 8'h80};
      bins max_minus_one = {8'hFE};
      bins max  = {8'hFF};
      bins low  = {[2:8'h7E]};
      bins high = {[8'h81:8'hFD]};
    }

    // Bayrak kapsami: [0]=Z, [1]=N, [2]=C, [3]=P (Ders 36-1 sozlesmesi)
    // Her bayragin tek basina set oldugu durumlar + herhangi birinin set oldugu durumlar
    cp_flags: coverpoint flags {
      bins no_flags    = {4'b0000};
      bins zero_only   = {4'b0001};
      bins neg_only    = {4'b0010};
      bins carry_only  = {4'b0100};
      bins parity_only = {4'b1000};
      wildcard bins zero_any   = {4'b???1};
      wildcard bins neg_any    = {4'b??1?};
      wildcard bins carry_any  = {4'b?1??};
      wildcard bins parity_any = {4'b1???};
    }

    // Sonuc araliklari
    cp_result: coverpoint result {
      bins zero_    = {0};
      bins small_    = {[1:255]};
      bins medium_   = {[256:65534]};
      bins max_val  = {65535};
    }

    // Cross coverage: Islem x Operand araliklari
    cx_opcode_a: cross cp_opcode, cp_op_a;
    cx_opcode_b: cross cp_opcode, cp_op_b;
    // Mantiksal ve kaydirma islemlerinde carry daima 0'dir (DUT sozlesmesi);
    // bu kombinasyonlar hedef degildir, kapsam hesabindan cikarilir.
    cx_opcode_flags: cross cp_opcode, cp_flags {
      ignore_bins no_carry_for_logic =
        binsof(cp_opcode) intersect {ALU_Transaction::OP_AND, ALU_Transaction::OP_OR,
                                     ALU_Transaction::OP_XOR, ALU_Transaction::OP_NOT,
                                     ALU_Transaction::OP_SHL, ALU_Transaction::OP_SHR} &&
        (binsof(cp_flags.carry_only) || binsof(cp_flags.carry_any));
    }
  endgroup


  function new();
    alu_cg = new();
  endfunction

  function void sample(ALU_Transaction txn);
    alu_cg.sample(txn.opcode, txn.operand_a, txn.operand_b, txn.result, txn.flags);
  endfunction

  function void report();
    $display("\n  ========== KAPSAM RAPORU ==========");
    $display("  Toplam Kapsam : %.1f%%", alu_cg.get_coverage());
    $display("  cp_opcode     : %.1f%%", alu_cg.cp_opcode.get_coverage());
    $display("  cp_op_a       : %.1f%%", alu_cg.cp_op_a.get_coverage());
    $display("  cp_op_b       : %.1f%%", alu_cg.cp_op_b.get_coverage());
    $display("  cp_flags      : %.1f%%", alu_cg.cp_flags.get_coverage());
    $display("  cp_result     : %.1f%%", alu_cg.cp_result.get_coverage());
    $display("  =====================================");
  endfunction
endclass

Kodun Açıklaması

  • covergroup alu_cg with function sample(...): Kapsam grubu, dışarıdan parametre alan bir sample fonksiyonuyla tanımlanır. Böylece örnekleme tetiklendiğinde opcode, a, b, result, flags değerleri aktarılır. option.per_instance = 1 her örnek için ayrı kapsam tutar.
  • cp_opcode: Sekiz işlem kodunun her biri ayrı bins'e atanır (add, sub, and_op, ...).
  • cp_op_a / cp_op_b: Operandlar köşe değerlere (zero, one, mid, max_minus_one, max) ve aralıklara (low, high) bölünür.
  • cp_flags: İki tür bin vardır. *_only bin'leri yalnızca tek bayrağın set olduğu desenleri (4'b0100 = sadece carry) yakalar; wildcard bins *_any ise ? joker karakteriyle "diğer bitler ne olursa olsun bu bayrak set" sorusunu yanıtlar (4'b?1?? = carry set). Böylece hem saf hem karışık bayrak durumları ölçülür. Bit sırası DUT sözleşmesini izler: [0]=Z, [1]=N, [2]=C, [3]=P.
  • cp_result: Sonuç zero_, small_, medium_, max_val aralıklarına ayrılır.
  • Cross'lar: cx_opcode_a ve cx_opcode_b, işlem × operand kombinasyonlarını izler. cx_opcode_flags, mantıksal ve kaydırma işlemlerinde DUT'un carry'yi her zaman 0 ürettiğini bildiği için "AND × carry set" gibi asla oluşamayacak kombinasyonları ignore_bins ile hedef listesinden düşürür; aksi halde cross hiçbir zaman %100 olamazdı. binsof(cp_flags.carry_only) söz dizimi, bir coverpoint'in adlandırılmış bin'ine doğrudan atıf yapmanın yoludur.
  • new(): Yapıcı, alu_cg = new() ile covergroup örneğini oluşturur (covergroup'lar açıkça örneklenmelidir).
  • sample(ALU_Transaction txn): Transaction alanlarını ayırıp alu_cg.sample(...)'ı çağırır.
  • report(): get_coverage() ile toplam ve nokta bazlı kapsam yüzdelerini basar.

Önemli Noktalar

  • Covergroup yapıcıda new() ile örneklenmelidir; aksi halde sample çağrıları kapsam toplamaz.
  • ignore_bins ile imkânsız kombinasyonların dışlanması kapsam hedefini gerçekçi tutar; AND/OR/XOR/NOT/SHL/SHR işlemlerinde carry sözleşme gereği hep 0 olduğundan "carry set" kombinasyonlarını hedeflemek, hiç dolmayacak bin'ler yaratırdı.
  • Cross coverage gerçek doğrulama gücüdür: Tekil noktalar %100 olsa bile, kombinasyonlar eksik kalabilir; cross bunu açığa çıkarır.
  • wildcard bins kullanımı, bit maskeli kalıplarla (4'b???1) esnek bayrak yakalama sağlar. ? karakteri hem 0 hem 1 ile eşleşir; x/z ile de eşleştiği için wildcard bin'ler X üreten bir DUT'ta yanlış dolabilir — scoreboard'daki === kontrolü bu yüzden ayrıca gereklidir.
  • Kapsam scoreboard tarafından actual (gözlenen) değerlerle beslenir; bu, gerçekten DUT'a ulaşan senaryoların ölçülmesini garanti eder.
  • Bayrak isimleri DUT sözleşmesiyle eşleşir. Bu dersin önceki sürümünde flags[3] için ovf_flag (overflow) adı kullanılıyordu; DUT bu bitte parity üretir. Yanlış isim kapsam yüzdesini değiştirmez ama raporu okuyan mühendisi yanıltır: "overflow %0 kapsanmış" diye gereksiz test yazdırabilirdi. Kapsam modeli de bir belgedir; isimler doğru olmalı.

Kapsam Modelini Okumak: Hangi Bin Boş Kalır?

Rastgele test (200 transaction) sonrasında tipik olarak dolmayan bin'ler ve nedenleri:

Bin Neden boş kalabilir? Nasıl doldurulur?
cp_op_a.max_minus_one (0xFE) c_corner_cases dağılımında özel ağırlığı yok, 1/254 ihtimal Köşe generator (corners dizisinde var)
cp_result.max_val (0xFFFF) Yalnızca SUB ile a - b = -1 (ör. 0 - 1) üretir Yönlendirilmiş 0 - 1 testi
cp_flags.neg_only N=1 ise SUB ile borrow var → C=1 de olur; saf "sadece N" çok nadir Belki imkânsız: ignore_bins adayı!
cx_opcode_b × SHL/SHR × high/max c_shift_range operand_b'yi 0–7'ye sınırlar Bu kombinasyon kısıtla yasaklı → ignore_bins ile dışlanmalı

Son iki satır kapsam mühendisliğinin özüdür: boş bir bin ya "daha fazla test yaz" ya da "bu kombinasyon imkânsız, modeli düzelt" demektir. Hangisi olduğuna karar vermek için DUT sözleşmesini ve kısıtları birlikte okumalısınız.

Benzetme: Kapsam modeli, bir haritadaki ziyaret edilecek yerler listesidir. Regresyon bittiğinde boş kalan yerler iki şey söyler: ya oraya gitmediniz (test ekle) ya da oraya gitmek zaten imkânsız (haritayı düzelt). Haritada olmayan bir yere ise asla gitmezsiniz; bu yüzden listeyi plan aşamasında, DUT'u görmeden yazmak en iyisidir.

Sık Yapılan Hatalar

  • Bayrak bitlerine yanlış isim vermek: Yüzdeyi bozmaz ama raporu yanlış okutur.
  • İmkânsız kombinasyonları ignore_bins ile dışlamamak: Cross hiçbir zaman %100 olmaz ve ekip "neden bitmiyor?" diye zaman kaybeder.
  • illegal_bins'i ignore_bins yerine kullanmak: "Carry mantıksal işlemde set" durumu DUT'ta oluşursa bu bir hatadır; aslında illegal_bins daha uygun olabilir — ama o zaman scoreboard ile iş bölümünü netleştirin: hata yakalama scoreboard'un, ölçme coverage'ın işidir. Bu projede yakalamayı scoreboard yapar (flags karşılaştırması), coverage yalnızca ölçer.
  • per_instance olmadan birden fazla env örneği: İki environment aynı covergroup tipini kullanırsa kapsam birleşir; option.per_instance = 1 ayrı tutar.
  • Kapsamı stimulus tarafında toplamak: Driver'ın gönderdiği ≠ DUT'un gördüğü. Bu projede scoreboard actual ile besler; doğru yer orasıdır.

Kendinizi Deneyin

  1. Random_Test (200 txn) ile Corner_Test'i ayrı ayrı koşturup cp_op_a, cp_result ve cross yüzdelerini tabloya yazın. Hangi test hangi bin'leri doldurdu?
  2. cx_opcode_b'ye SHL/SHR × high/max_minus_one/max kombinasyonlarını dışlayan bir ignore_bins ekleyin. Cross yüzdesi ne oldu?
  3. cp_flags.neg_only'nin gerçekten imkânsız olup olmadığını araştırın: N=1 iken C=0 ve Z=0 ve P=0 olan bir (opcode, a, b) var mı? (İpucu: yalnızca SUB N=1 üretebilir; 0x00 - 0x01 = 0xFFFF için P'yi hesaplayın.)
  4. cp_result'a bins eight_bit_max = {16'h00FF}; ekleyin (8-bit'e tam sığan en büyük sonuç). Hangi işlemler bu bin'i doldurabilir?
Hızlı Kontrol: cx_opcode_flags'ta mantıksal işlemler için carry neden ignore_bins ile dışlanır?

DUT sözleşmesi gereği AND/OR/XOR/NOT/SHL/SHR işlemlerinde carry bayrağı her zaman 0'dır; "AND × carry set" kombinasyonu hiçbir doğru DUT'ta oluşmaz. Bu kombinasyonu hedefte tutmak cross'un %100'e ulaşmasını imkânsız kılar ve ekibi yanlış yönlendirir. ignore_bins onu hedef listesinden düşürür; DUT yine de carry üretirse bunu scoreboard'un bayrak karşılaştırması yakalar.