EDA Playground'da Dene

Clocking Block

Gün 5: Arayüzler, Assertion ve Coverage | Zamanlama blokları, setup/hold yönetimi, APB protokolü

🌟🌟Okuma Önerisi: Taming Testbench Timing

Bu derste clocking block (zamanlama bloğu) yapısını ve test ortamı ile DUT arasındaki yarış koşullarını (race condition) nasıl ortadan kaldırdığını öğreneceğiz. Örnek olarak basit bir APB protokolünü clocking block üzerinden süreceğiz.

Clocking Block Neden Gerekli?

Bir test ortamı (testbench), DUT ile aynı saat kenarında sinyal sürerse iki kritik problem ortaya çıkar:

  • Yarış koşulu (race condition): TB sinyali tam saat kenarında değiştirir, DUT da aynı kenarda örnekler. Hangisinin önce çalışacağı belirsizdir; simülatöre göre sonuç değişebilir.
  • Setup/Hold belirsizliği: Gerçek donanımda sinyaller saat kenarından belirli bir süre önce kararlı olmalı (setup) ve kenardan sonra bir süre korunmalıdır (hold). Bunu modellemeden yapılan testler gerçekçi değildir.

clocking block, bir saat olayına (@(posedge clk)) göre sinyallerin ne zaman örnekleneceğini (input) ve ne zaman sürüleceğini (output) merkezi olarak tanımlar. Böylece testbench, DUT'tan otomatik olarak güvenli bir zaman aralığında ayrılır.

Input/Output Skew ve 1step

Koddaki default input #1step output #1 satırı zamanlamanın kalbidir:

  • input #1step: Girişler, saat kenarından bir simülasyon adımı önce (yani Preponed bölgesinde) örneklenir. Bu, DUT henüz aynı kenarda değer değiştirmeden, kararlı eski değeri okumayı garanti eder; böylece yarış koşulu yok olur.
  • output #1: Çıkışlar, saat kenarından 1 zaman birimi sonra sürülür. Bu, DUT'un girişleri kenarın hemen üzerinde değil, küçük bir gecikmeyle görmesini sağlar (hold benzeri davranış).

Test kodunda aif.cb.psel <= 1 gibi atamalar ve @(aif.cb) ile beklemeler, doğrudan ham sinyallere değil clocking block üzerinden yapıldığı için bu zamanlama kuralları otomatik uygulanır.

Kaynak Kod

// =============================================================================
// GUN 5 - Konu 2: Clocking Blocks (Zamanlama Bloklari)
// =============================================================================

interface apb_if(input logic clk);
  logic        psel;
  logic        penable;
  logic        pwrite;
  logic [7:0]  paddr;
  logic [31:0] pwdata;
  logic [31:0] prdata;
  logic        pready;

  // Clocking Block: Testbench zamanlama yonetimi
  // Sinyallerin ornekleme ve surme zamanlarini tanimlar
  clocking cb @(posedge clk);
    default input #1step output #1;   // Input output skew
    
    output psel;
    output penable;
    output pwrite;
    output paddr;
    output pwdata;
    input  prdata;
    input  pready;
  endclocking

  modport dut (
    input  clk, psel, penable, pwrite, paddr, pwdata,
    output prdata, pready
  );
endinterface

// Basit APB Slave (DUT rolunde)
module apb_slave(apb_if.dut apb);
  logic [31:0] reg_file [256];

  always @(posedge apb.clk) begin
    apb.pready <= 0;
    if (apb.psel && apb.penable) begin
      if (apb.pwrite)
        reg_file[apb.paddr] <= apb.pwdata;
      else
        apb.prdata <= reg_file[apb.paddr];
      apb.pready <= 1;
    end
  end
endmodule

module clocking_block;
  logic clk = 0;
  always #5 clk = ~clk;

  apb_if aif(clk);
  apb_slave dut(aif);

  // Clocking block ile surme
  task apb_write(input logic [7:0] addr, input logic [31:0] data);
    // Setup phase
    aif.cb.psel    <= 1;
    aif.cb.pwrite  <= 1;
    aif.cb.paddr   <= addr;
    aif.cb.pwdata  <= data;
    aif.cb.penable <= 0;
    @(aif.cb);

    // Access phase
    aif.cb.penable <= 1;
    @(aif.cb);
    wait(aif.cb.pready);

    // Idle
    aif.cb.psel    <= 0;
    aif.cb.penable <= 0;
    $display("  [%0t] APB Write: Addr=0x%02h, Data=0x%08h", $time, addr, data);
  endtask

  task apb_read(input logic [7:0] addr, output logic [31:0] data);
    aif.cb.psel    <= 1;
    aif.cb.pwrite  <= 0;
    aif.cb.paddr   <= addr;
    aif.cb.penable <= 0;
    @(aif.cb);

    aif.cb.penable <= 1;
    @(aif.cb);
    wait(aif.cb.pready);
    data = aif.cb.prdata;

    aif.cb.psel    <= 0;
    aif.cb.penable <= 0;
    $display("  [%0t] APB Read:  Addr=0x%02h, Data=0x%08h", $time, addr, data);
  endtask
  
  initial begin
    $dumpfile("dump.vcd"); 
    $dumpvars;
  end

  initial begin
    logic [31:0] rdata;
    $display("=== Clocking Block ===\n");

    // Baslangic degerleri
    aif.cb.psel    <= 0;
    aif.cb.penable <= 0;
    #20;

    // Yazma
    apb_write(8'h00, 32'hAAAA_BBBB);
    apb_write(8'h04, 32'hCCCC_DDDD);

    // Okuma
    apb_read(8'h00, rdata);
    apb_read(8'h04, rdata);

    #50;
    $display("\n=== Clocking Block Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • interface apb_if(input logic clk): APB sinyallerini (psel, penable, pwrite, paddr, pwdata, prdata, pready) gruplar.
  • clocking cb @(posedge clk): Tüm testbench zamanlamasını yöneten bloktur. default input #1step output #1 ile varsayılan örnekleme/sürme gecikmeleri belirlenir. TB'nin sürdüğü sinyaller (psel, penable, pwrite, paddr, pwdata) output; DUT'tan okunanlar (prdata, pready) input olarak tanımlanır.
  • modport dut: DUT'un bakış açısını verir; TB'nin sürdüğü sinyalleri input, ürettiklerini output alır. DUT clocking block'u kullanmaz, doğrudan ham sinyalleri kullanır.
  • apb_slave modülü: posedge apb.clk ile çalışır. Her saatte pready'yi varsayılan 0 yapar; apb.psel && apb.penable (APB access fazı) doğruysa pwrite'a göre reg_file'a yazar ya da oradan prdata'ya okur ve pready'yi 1 yapar.
  • apb_write task'i: APB'nin iki fazını uygular. Önce setup faz (psel=1, penable=0) kurulup @(aif.cb) ile bir saat beklenir; sonra access faz (penable=1) sürülür ve wait(aif.cb.pready) ile DUT onayı beklenir. Tüm atamalar aif.cb.* üzerinden, yani clocking block aracılığıyla yapılır.
  • apb_read task'i: Aynı setup/access akışını okuma için uygular ve data = aif.cb.prdata ile veriyi clocking block'tan örneklenmiş olarak alır.
  • clocking_block (top): clk üretir, apb_if aif(clk) ve apb_slave dut(aif) örneklenir. Başlangıç değerleri verildikten sonra iki yazma ve iki okuma işlemi yürütülür.

Önemli Noktalar

  • @(aif.cb) ile @(posedge clk) farkı: Clocking block olayını (@(aif.cb)) beklemek, ham saat kenarını beklemekten farklı olarak skew kurallarını devreye sokar ve yarış koşullarını engeller.
  • Clocking block sinyaline <= ile yazın: aif.cb.psel <= 1 gibi non-blocking atamalar, çıkış skew'ine (output #1) uygun şekilde değeri planlı zamanda sürer. Blocking atama (=) clocking block çıkışlarında uygun değildir.
  • 1step özel bir değerdir: #1step, zaman birimine bağlı olmayan en küçük adımdır ve Preponed bölgesinde örnekleme yaparak en güvenli giriş okumasını sağlar.
  • DUT clocking block kullanmaz: Zamanlama bloğu yalnızca testbench tarafının senkronizasyon aracıdır; DUT sentezlenebilir mantıkla ham sinyalleri kullanır.
  • Tek merkezden zamanlama: Tüm gecikme kararlarının clocking block'ta toplanması, dağınık #delay ifadelerine kıyasla bakımı kolaylaştırır ve hata riskini azaltır.