EDA Playground'da Dene

Lab 5: Interface + Coverage

Gün 5: Arayüzler, Assertion ve Coverage | Interface, Virtual Interface, Driver ve Coverage entegrasyonu

Kaynak Kod

// =============================================================================
// GÜN 5 - Lab 5: Interface + Virtual Interface + Driver + Coverage (Kapsam)
// =============================================================================

// Arayüz (Interface) tanımı
interface mem_if(input logic clk);
  logic        rst_n;
  logic        cs;       // çip seçme (chip select)
  logic        we;       // yazma yetkisi (write enable)
  logic [7:0]  addr;
  logic [31:0] wdata;
  logic [31:0] rdata;
  logic        ack;

  clocking cb @(posedge clk);
    default input #1step output #0;
    output cs, we, addr, wdata;
    input  rdata, ack;
  endclocking

  modport tb  (clocking cb, output rst_n, input clk);
  modport dut (input clk, rst_n, cs, we, addr, wdata, output rdata, ack);
endinterface

// Basit Bellek Tasarımı (DUT)
module memory_dut(mem_if.dut mif);
  logic [31:0] mem [256];

  always @(posedge mif.clk or negedge mif.rst_n) begin
    if (!mif.rst_n) begin
      mif.rdata <= 0;
      mif.ack   <= 0;
    end else if (mif.cs) begin
      if (mif.we) begin
        mem[mif.addr] <= mif.wdata;
      end else begin
        mif.rdata <= mem[mif.addr];
      end
      mif.ack <= 1;
    end else begin
      mif.ack <= 0;
    end
  end
endmodule

// İşlem (Transaction) sınıfı
class MemTransaction;
  rand bit         we;
  rand bit [7:0]   addr;
  rand logic [31:0] wdata;
  logic [31:0]    rdata;
  int              id;
  static int       count = 0;

  constraint c_addr {
    addr inside {[0:63]};
  }

  //
  constraint c_data {
    solve we before wdata; // ÖNEMLİ: Önce we sinyalini %50 olasılıkla hesaplasın diye koyduk
    we -> wdata != 0;
    !we -> wdata == 0;
  }

  function new();
    id = count++;
  endfunction

  function void display(string prefix = "");
    $display("  %sTXN#%0d | %s | Addr=0x%02h | WData=0x%08h | RData=0x%08h",
             prefix, id, we ? "WR" : "RD", addr, wdata, rdata);
  endfunction
endclass

// Driver sınıfı (sanal arayüz - virtual interface kullanarak)
class MemDriver;
  virtual mem_if.tb vif;
  mailbox #(MemTransaction) mbx;
  int driven_count = 0;

  function new(virtual mem_if.tb vif, mailbox #(MemTransaction) mbx);
    this.vif = vif;
    this.mbx = mbx;
  endfunction

  task run();
    MemTransaction txn;
    forever begin
      mbx.get(txn);
      @(vif.cb);
      vif.cb.cs    <= 1;
      vif.cb.we    <= txn.we;
      vif.cb.addr  <= txn.addr;
      vif.cb.wdata <= txn.wdata;
      
      // Onay (ack) sinyalini senkronize şekilde bekle (simülatörün asılı kalmasını önler)
      do begin
        @(vif.cb);
      end while (vif.cb.ack == 0);

      if (!txn.we)
        txn.rdata = vif.cb.rdata;
        
      vif.cb.cs <= 0;
      
      // Tasarımın (DUT) CS=0 sinyalini görmesi ve ACK'yı düşürmesi için boş (idle) döngü
      @(vif.cb);
      
      driven_count++;
      txn.display("[Driver] ");
    end
  endtask

  task reset();
    vif.rst_n = 0;
    vif.cb.cs    <= 0;
    vif.cb.we    <= 0;
    vif.cb.addr  <= 0;
    vif.cb.wdata <= 0;
    repeat(5) @(vif.cb);
    vif.rst_n = 1;
    @(vif.cb);
    $display("  [Driver] Reset tamamlandı");
  endtask
endclass

// Kapsam (Coverage) sınıfı
class MemCoverage;
  covergroup mem_cg with function sample(bit we, bit [7:0] addr, logic [31:0] data);
    cp_we: coverpoint we {
      bins read  = {0};
      bins write = {1};
    }

    cp_addr: coverpoint addr {
      bins low   = {[0:15]};
      bins mid   = {[16:31]};
      bins high  = {[32:47]};
      bins upper = {[48:63]};
    }

    cp_data: coverpoint data {
      bins zero    = {0};
      bins nonzero = default; // Hazırlık (elaboration) sırasında aracın asılı kalmasını önler
    }

    cx_we_addr: cross cp_we, cp_addr;
  endgroup

  // Covergroup, içine gömüldüğünde kendi örnek tipi (instance type) gibi davranır
  function new();
    mem_cg = new();
  endfunction

  function void sample(MemTransaction txn);
    mem_cg.sample(txn.we, txn.addr, txn.wdata);
  endfunction

  function void report();
    $display("\n  --- Kapsam Raporu (Coverage Report) ---");
    $display("  Toplam      : %.1f%%", mem_cg.get_coverage());
    $display("  cp_we       : %.1f%%", mem_cg.cp_we.get_coverage());
    $display("  cp_addr     : %.1f%%", mem_cg.cp_addr.get_coverage());
    $display("  cp_data     : %.1f%%", mem_cg.cp_data.get_coverage());
    $display("  cx_we_addr : %.1f%%", mem_cg.cx_we_addr.get_coverage());
  endfunction
endclass

// Üst seviye test ortamı (Testbench)
module lab5_interface_coverage;
  logic clk = 0;
  always #5 clk = ~clk;

  mem_if mif(clk);
  memory_dut dut(mif);

  mailbox #(MemTransaction) mbx;
  MemDriver    drv;
  MemCoverage cov;

  initial begin
    // Örtük statik değişken hatalarını önlemek için döngü dışında tanımlanmıştır
    MemTransaction txn;

    $display("============================================================");
    $display("  LAB 5: Interface + Driver + Coverage");
    $display("============================================================\n");

    mbx = new();
    drv = new(mif, mbx);
    cov = new();

    // Reset dizisi
    drv.reset();

    // Driver'ı arka planda çalıştır
    fork
      drv.run();
    join_none

    // İşlemleri (transaction) oluştur ve kuyruğa (mailbox) koy
    repeat (50) begin
      txn = new(); // Nesneyi burada başlat
      assert(txn.randomize()) else $fatal(1, "Randomize başarısız!");
      cov.sample(txn);
      mbx.put(txn);
    end

    // Tüm işlemlerin tamamen sürülmesini bekle
    wait(drv.driven_count == 50);
    #100;

    // Raporlama
    $display("\n  --- İstatistikler ---");
    $display("  Toplam sürülen: %0d", drv.driven_count);
    cov.report();

    $display("\n============================================================");
    $display("  LAB 5 TAMAMLANDI");
    $display("============================================================");
    $finish;
  end
endmodule