EDA Playground'da Dene

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 ---
    cp_corner: coverpoint a {
      bins boundaries[] = {0, 1, 127, 128, 254, 255};
      illegal_bins never = default;
    }

    // --- 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_Transaction sınıfı: op_e enum'u (ADD, SUB, AND, OR, XOR, SHL, SHR) ile işlem türünü tanımlar. operation, operand_a, operand_b rastgele üretilir; c_operands kısıtı operandları 0-255 aralığına çeker. compute fonksiyonu seçilen işleme göre result'u hesaplar (kaydırmalarda operand_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ı bir bin ile izler.
    • cp_op_a / cp_op_b: Operandları anlamlı aralıklara böler: zero, low [1:63], mid [64:191], high [192:254], max_val 255.
    • cp_result: Sonucu zero, small, medium, overflow aralıklarına ayırır.
    • cp_corner: bins boundaries[] = {0, 1, 127, 128, 254, 255} ile köşe değerleri tek tek izler; illegal_bins never = default bu altı değer dışındaki her durumu yasadışı sayar.
    • cx_op_a / cx_op_b: İşlem ile operand aralıklarının çapraz kombinasyonlarını (cross) ölçer.
  • functional_coverage (top): txn ve cg nesneleri oluşturulur. Önce repeat(200) döngüsünde rastgele işlemler üretilip compute edilir ve cg.sample(...) ile örneklenir. Ardından corners dizisi üzerinde iç içe foreach ile yönlendirilmiş köşe testleri çalıştırılır (randomize() with {...} ile operandlar sabitlenir). Son olarak get_coverage() çağrılarıyla toplam ve coverpoint bazlı kapsam raporlanır.

Önemli Noktalar

  • illegal_bins ile geçersiz değerleri yakalayın: cp_corner içindeki illegal_bins never = default, beklenmedik bir değer örneklenirse simülasyonda hata üretir; bu, sessiz kapsam boşluklarını engeller.
  • default bin tuzağı: default bin, kalan tüm değerleri toplar ama genellikle kapsam yüzdesine sayılmaz. Anlamlı senaryoları açıkça adlandırılmış bins'lerle ifade edin.
  • cross ile 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 255 veya 0 gibi köşe değerlerine nadiren isabet eder. Kod, bunları randomize() with ile bilinçli olarak zorlar.
  • sample()'ı doğru anda çağırın: Kapsam ancak sample çağrıldığında güncellenir; veri kararlıyken (burada compute sonrası) örneklemek doğru sonuç verir.
  • Anlamlı bins aralı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çin overflow = {65535}) kapsamı anlamlı kılar.