EDA Playground'da Dene

Environment Sınıfı

Gün 7: Bitirme Projesi - Bölüm 2 | Tüm bileşenleri kapsayan ortam sınıfı

Environment (ortam), tüm doğrulama bileşenlerini tek bir çatı altında toplayan, onları oluşturan ve birbirine bağlayan üst düzey kapsayıcıdır. ALU mimarimizde generator, driver, monitor, scoreboard ve coverage'ı barındırır; mailbox'ları üretip aralarındaki tüm bağlantıyı kurar.

Environment ve Mimarideki Yeri

Environment, testbench'in "montaj fabrikası"dır. Tek tek bileşenleri tanımak yerine, test sınıfı yalnızca environment ile konuşur. Sorumlulukları:

  • Mailbox'ları oluşturmak: gen2drv_mbx, drv2scb_mbx, mon2scb_mbx kanallarını üretir.
  • Bileşenleri bağlamak: Her bileşene doğru mailbox ve virtual interface referanslarını verir.
  • Yaşam döngüsünü yönetmek: build → reset → run → report aşamalarını sıralı yürütür.

Mailbox Bağlantı Topolojisi

Bileşenler arası veri akışı şu mailbox'larla kurulur:

  • gen2drv_mbx: Generator → Driver
  • drv2scb_mbx: Driver → Scoreboard (beklenen)
  • mon2scb_mbx: Monitor → Scoreboard (gerçek)

Bu topoloji sayesinde bileşenler birbirine doğrudan değil, yalnızca mailbox'lar üzerinden bağlıdır (gevşek bağlılık / loose coupling).

Paralel Çalışma ve Senkronizasyon

run() task'ı dört bileşeni (gen, drv, mon, scb) fork...join_any ile paralel başlatır. Generator işini bitirip done event'ini tetikleyince, pipeline'ın her katmanı kendi sayacıyla boşalmayı onaylar: driver tüm işlemleri sürdü mü, scoreboard hepsini karşılaştırdı mı? Bu "boşaltma" (drain) kontrolü sabit bir bekleme süresine değil, sayaç eşitliklerine dayanır.

Tek Coverage, İki Kullanıcı

Coverage nesnesi environment'ta bir kez yaratılır ve scoreboard'a yapıcı argümanıyla verilir. Scoreboard onu sample() ile besler, environment report() ile okur. İki ayrı nesne olsaydı environment'ın raporu hep %0 gösterirdi; bu, handle paylaşımının (Gün 2) doğrulama ortamındaki en somut uygulamasıdır.

Kaynak Kod

// =============================================================================
// GUN 7 - Konu 3: Environment - Tum Bilesenleri Kapsayan Sinif
// =============================================================================

class ALU_Environment;
  // Bilesenler
  ALU_Generator   gen;
  ALU_Driver      drv;
  ALU_Monitor     mon;
  ALU_Scoreboard  scb;
  ALU_Coverage    cov;  // 7_5_coverage.sv'de tanimli

  // Mailbox'lar
  mailbox #(ALU_Transaction) gen2drv_mbx;
  mailbox #(ALU_Transaction) drv2scb_mbx;
  mailbox #(ALU_Transaction) mon2scb_mbx;

  // Virtual interface
  virtual alu_if.driver  drv_vif;
  virtual alu_if.monitor mon_vif;

  // Konfigurasyon
  int num_transactions;

  function new(virtual alu_if.driver drv_vif, virtual alu_if.monitor mon_vif, int num_txn = 100);
    this.drv_vif = drv_vif;
    this.mon_vif = mon_vif;
    this.num_transactions = num_txn;
  endfunction

  // Tum bilesenleri olustur ve bagla
  function void build();
    $display("[Environment] Build asamasi");

    // Mailbox'lari olustur
    gen2drv_mbx = new();
    drv2scb_mbx = new();
    mon2scb_mbx = new();

    // Bilesenleri olustur
    // Coverage once yaratilir ve scoreboard'a verilir: tek ornek, iki kullanici
    cov = new();
    gen = new(gen2drv_mbx, num_transactions);
    drv = new(drv_vif, gen2drv_mbx, drv2scb_mbx);
    mon = new(mon_vif, mon2scb_mbx);
    scb = new(drv2scb_mbx, mon2scb_mbx, cov);

    $display("[Environment] Tum bilesenler olusturuldu");
  endfunction

  // Reset
  task reset();
    $display("[Environment] Reset");
    drv.reset();
  endtask

  // Tum bilesenleri calistir
  task run();
    $display("[Environment] Calistiriliyor...\n");

    fork
      gen.run();
      drv.run();
      mon.run();
      scb.run();
    join_any

    // 1) Generator bitene kadar bekle
    wait(gen.done.triggered);
    // 2) Driver tum transaction'lari surmus olsun
    wait(drv.driven_count == gen.generated_count);
    // 3) Scoreboard hepsini karsilastirmis olsun (pipeline bosaldi)
    wait(scb.total_count == gen.generated_count);
    // 4) Dalga formunda son islemin gorunmesi icin birkac cevrim
    repeat (5) @(drv_vif.drv_cb);
  endtask

  // Rapor
  function void report();
    $display("\n[Environment] Final Rapor");
    $display("  Generator : %0d uretildi", gen.generated_count);
    $display("  Driver    : %0d suruldu", drv.driven_count);
    $display("  Monitor   : %0d izlendi", mon.monitored_count);
    if (drv.driven_count != mon.monitored_count)
      $display("  [UYARI] Surulen ve izlenen sayilari farkli! Monitor/driver hizalamasini kontrol edin.");
    scb.report();
    cov.report();
  endfunction
endclass

Kodun Açıklaması

  • Bileşen üyeleri: gen, drv, mon, scb, cov referansları tüm doğrulama bileşenlerini tutar.
  • Mailbox üyeleri: gen2drv_mbx, drv2scb_mbx, mon2scb_mbx bileşenler arası veri kanallarıdır.
  • Virtual interface'ler: drv_vif ve mon_vif, driver ve monitor'ün DUT'a bağlanmasını sağlar; yapıcıda dışarıdan alınır.
  • build(): Önce üç mailbox new() ile oluşturulur. Ardından cov yaratılır ve bileşenler doğru bağlantılarla kurulur: drv hem gen2drv_mbx'ten okur hem drv2scb_mbx'e yazar, mon mon2scb_mbx'e yazar, scb her iki giriş mailbox'ını ve cov handle'ını alır.
  • reset(): Driver'ın reset() task'ını çağırarak DUT'u başlangıç durumuna getirir.
  • run(): fork ... join_any ile dört bileşeni paralel başlatır. Sonra üç aşamalı boşaltma yapar: wait(gen.done.triggered) üretim bitti, wait(drv.driven_count == gen.generated_count) hepsi sürüldü, wait(scb.total_count == gen.generated_count) hepsi karşılaştırıldı. Son repeat (5) @(drv_vif.drv_cb) yalnızca dalga formu içindir.
  • report(): Generator/driver/monitor sayaçlarını basar; sürülen ile izlenen sayıları farklıysa bir uyarı ekler, ardından scb.report() ve cov.report() ile detaylı raporları tetikler. gen.generated_count kullanılması, köşe üreticide (num_transactions'ı yok sayar) doğru sayının görünmesini sağlar.

Önemli Noktalar

  • Bağlantı (connection) tek noktadan yapılır: Mailbox eşleştirmelerinin ve handle enjeksiyonlarının tamamı build() içindedir; bu, hata ayıklamayı ve bakımı kolaylaştırır.
  • join_any kullanımı bilinçlidir: Bileşenlerin çoğu sonsuz döngüdür; tümünün bitmesini beklemek (join) yerine, generator bitince kontrol akışı devam eder.
  • Boşaltma sayaçla yapılır, zamanla değil: #100 gibi sabit bir bekleme, transaction sayısı ya da driver hızı değişince ya yetersiz kalır ya gereksiz uzar. Her katmanın sayacını beklemek bu kırılganlığı ortadan kaldırır. Bu dersin önceki sürümü wait(gen2drv_mbx.num() == 0); #100; kullanıyordu; mailbox'ın boşalması yalnızca driver'ın almaya başladığını gösterir, sürmeyi bitirdiğini değil.
  • Tek cov nesnesi: Environment yaratır, scoreboard besler, environment raporlar. Dersin önceki sürümünde scoreboard kendi cov'unu yaratıyordu ve environment'ın raporu hiç örneklenmemiş ikinci bir nesneyi okuyordu (her zaman %0). Handle paylaşımı bu hatayı yapısal olarak imkânsız kılar.
  • Virtual interface'lerin dışarıdan enjekte edilmesi, aynı environment'ın farklı top/test yapılarında yeniden kullanılabilmesini sağlar.
  • drv_vif.drv_cb environment'tan erişilebilir: Environment driver modport'una sahip olduğu için clocking olayını bekleyebilir; ama sinyal sürmemelidir — sürüş yalnızca driver'ın işidir.

Bu Environment'ın UVM Karşılığı

Bu ders UVM
ALU_Environment sınıfı uvm_env
build() fonksiyonu build_phase() (top-down, factory ile create)
Mailbox eşleştirmeleri connect_phase() (TLM port/export connect)
reset() + run() task'ları run_phase() ve reset_phase/main_phase alt fazları
wait(...) boşaltma zinciri phase.raise_objection / drop_objection
report() report_phase()
Yapıcıya verilen vif uvm_config_db#(virtual alu_if)::get
Yapıcıya verilen cov Analysis port'tan uvm_subscriber ya da handle enjeksiyonu (UVM dersi 3)

UVM, bu derste elle yaptığınız her adımı bir faz olarak standartlaştırır. "Hangi sırayla kur, bağla, çalıştır, raporla?" sorusunun cevabı artık metodolojinin kendisindedir.

Benzetme: Environment bir film setinin yapım amiridir: oyuncuları (bileşenleri) işe alır (build), aralarındaki iletişimi kurar (mailbox'lar), "motor" der (run), çekim bitince herkesin işini bitirmesini bekler (boşaltma) ve günün raporunu yazar (report). Yönetmen (test) yalnızca "bugün hangi sahne?" kararını verir.

Sık Yapılan Hatalar

  • Scoreboard'da ikinci bir cov yaratmak: Rapor %0 gösterir; kapsam toplanıyor sanırsınız ama okuduğunuz nesne boştur.
  • Boşaltmayı mailbox'ın boşalmasıyla ölçmek: gen2drv_mbx.num() == 0 yalnızca driver'ın son transaction'ı aldığını söyler; sürme, gözlem ve karşılaştırma hâlâ devam ediyor olabilir.
  • join kullanmak: drv, mon, scb sonsuz döngülüdür; join asla dönmez.
  • build()'i task, run()'ı function yapmak: build() zaman tüketmez (function doğru); run() zaman tüketir (task zorunlu). UVM'de de build_phase function, run_phase task'tır.
  • Bileşenleri build() dışında yaratmak: Test sınıfı env.gen'i değiştirmek için build()'in bitmiş olmasına güvenir (Ders 44); yaratma yerleri dağılırsa bu sözleşme bozulur.

Kendinizi Deneyin

  1. scb = new(drv2scb_mbx, mon2scb_mbx, cov); satırını eski haline (cov olmadan, scoreboard içinde new()) döndürün ve cov.report() çıktısını gözlemleyin. Hatayı kendi gözünüzle görün.
  2. wait(scb.total_count == gen.generated_count) satırını silip yerine #100 koyun; sonra num_transactions'ı 2000 yapın. Scoreboard raporundaki Toplam ile Generator sayısı tutuyor mu?
  3. Environment'a int timeout_cycles = 10000; ekleyin ve run()'daki boşaltma zincirini Gün 4'teki izole timeout kalıbı ile sarmalayın: süre aşılırsa $fatal("Simulation hang!").
  4. report()'a toplam simülasyon süresini ($time) ve "transaction/çevrim" verimliliğini (generated_count / çevrim sayısı) ekleyin.
Hızlı Kontrol: Environment neden cov nesnesini yaratıp scoreboard'a veriyor; scoreboard kendisi yaratsa ne olurdu?

Scoreboard kendi cov'unu yaratsaydı environment'ın cov handle'ı farklı bir nesneyi gösterirdi; scoreboard örnekler, environment ise hiç beslenmemiş nesneyi raporlardı (%0). Tek nesneyi yaratıp paylaştırmak (dependency injection), "besleyen" ile "raporlayan"ın aynı veriye bakmasını yapısal olarak garanti eder.