Scoreboard
Gün 7: Bitirme Projesi - Bölüm 2 | Referans model ile DUT çıkışını karşılaştırma
Scoreboard (puan tablosu), doğrulamanın "hakem"idir. ALU mimarimizde driver'ın gönderdiği beklenen işlemle, monitor'ün gözlediği gerçek DUT çıkışını karşılaştırır ve bir bağımsız referans modeliyle DUT'un doğru çalışıp çalışmadığına karar verir.
Scoreboard ve Mimarideki Yeri
Scoreboard iki mailbox'tan beslenir:
drv2scb: Driver'dan gelen beklenen (uygulanan) transaction.mon2scb: Monitor'den gelen gerçek (gözlenen) DUT çıkışı.
Her iki kaynaktan birer transaction alıp eşleştirir. Beklenen değer DUT'tan değil, scoreboard'un kendi içindeki referans modelinden (compute_reference) hesaplanır. Bu sayede DUT'un çıkışı bağımsız bir "altın model"e (golden model) karşı sınanır.
Referans Model (Golden Model)
compute_reference, ALU davranışını saf yazılımda yeniden modeller. Donanımdan tamamen bağımsız bu hesap, "doğru cevabın" ne olması gerektiğini söyler. DUT'un sonucu bu beklenen değerle uyuşmazsa hata raporlanır.
Referans modelin uyması gereken sözleşme Ders 36-1'de tanımlanmıştır: Z (result == 0), N (result[15]), C (ADD/SUB için result[8], diğerlerinde 0), P (^result). Model bu sözleşmeyi DUT koduna bakmadan, spesifikasyondan yazmalıdır; aksi halde aynı yorum hatasını iki yerde tekrarlayıp görünmez kılarsınız.
Kapsamla (Coverage) İşbirliği
Scoreboard, gözlenen her transaction'ı (actual) cov.sample(actual) ile coverage bileşenine de iletir. Böylece hangi senaryoların gerçekten DUT'a ulaştığı ölçülür. cov nesnesi scoreboard tarafından yaratılmaz; environment tek bir ALU_Coverage örneği oluşturur ve yapıcı argümanıyla scoreboard'a verir (dependency injection). Böylece environment'ın raporladığı kapsam ile scoreboard'un beslediği kapsam aynı nesnedir.
Kaynak Kod
// =============================================================================
// GUN 7 - Konu 2: Scoreboard - Referans Model ile DUT Karsilastirmasi
// =============================================================================
class ALU_Scoreboard;
mailbox #(ALU_Transaction) drv2scb; // Driver'dan beklenen (girisler)
mailbox #(ALU_Transaction) mon2scb; // Monitor'den gercek (DUT cikisi)
int pass_count = 0;
int fail_count = 0;
int total_count = 0;
ALU_Coverage cov; // Environment tarafindan verilir (paylasilan tek ornek)
function new(mailbox #(ALU_Transaction) drv_mbx,
mailbox #(ALU_Transaction) mon_mbx,
ALU_Coverage cov);
this.drv2scb = drv_mbx;
this.mon2scb = mon_mbx;
this.cov = cov;
endfunction
task run();
ALU_Transaction expected, actual;
logic [15:0] ref_result;
logic [3:0] ref_flags;
$display("[Scoreboard] Baslatildi");
forever begin
drv2scb.get(expected);
mon2scb.get(actual);
total_count++;
// Referans model: beklenen sonucu ve bayraklari girislerden hesapla
compute_reference(expected.opcode, expected.operand_a, expected.operand_b,
ref_result, ref_flags);
// Karsilastirma: hem sonuc hem bayraklar (=== ile x/z de yakalanir)
if (actual.result === ref_result && actual.flags === ref_flags) begin
pass_count++;
end else begin
fail_count++;
$display("[Scoreboard] HATA #%0d!", total_count);
$display(" Islem : %s", expected.opcode.name());
$display(" A=0x%02h, B=0x%02h", expected.operand_a, expected.operand_b);
$display(" Beklenen : result=0x%04h, flags=%04b (P C N Z)", ref_result, ref_flags);
$display(" Gercek : result=0x%04h, flags=%04b (P C N Z)", actual.result, actual.flags);
end
// Gozlenen (actual) degerler kapsam toplayiciya gonderilir
cov.sample(actual);
end
endtask
// Referans model: ALU'nun SPESIFIKASYONUNU yazilimda modelle
// Bayrak sozlesmesi (Ders 36-1): [0]=Z, [1]=N, [2]=C (yalnizca ADD/SUB), [3]=P
function void compute_reference(
ALU_Transaction::opcode_e opcode,
logic [7:0] a, logic [7:0] b,
output logic [15:0] result,
output logic [3:0] flags
);
case (opcode)
ALU_Transaction::OP_ADD: result = {8'h00, a} + {8'h00, b}; // 16-bit toplam, bit 8 = carry
ALU_Transaction::OP_SUB: result = {8'h00, a} - {8'h00, b}; // a<b ise 16'hFFxx (borrow)
ALU_Transaction::OP_AND: result = {8'h00, a & b};
ALU_Transaction::OP_OR: result = {8'h00, a | b};
ALU_Transaction::OP_XOR: result = {8'h00, a ^ b};
ALU_Transaction::OP_NOT: result = {8'h00, ~a};
ALU_Transaction::OP_SHL: result = {8'h00, a} << b[2:0];
ALU_Transaction::OP_SHR: result = {8'h00, a} >> b[2:0];
default: result = 16'h0000;
endcase
flags = 4'b0000;
flags[0] = (result == 16'h0000); // Z: sifir
flags[1] = result[15]; // N: isaret biti
flags[2] = (opcode inside {ALU_Transaction::OP_ADD,
ALU_Transaction::OP_SUB}) ? result[8] : 1'b0; // C: carry/borrow
flags[3] = ^result; // P: parite
endfunction
function void report();
$display("\n ========== SCOREBOARD RAPORU ==========");
$display(" Toplam : %0d", total_count);
$display(" Basarili: %0d", pass_count);
$display(" Basarisiz: %0d", fail_count);
if (fail_count == 0)
$display(" *** TUM TESTLER BASARILI! ***");
else
$display(" !!! %0d HATA TESPIT EDILDI !!!", fail_count);
$display(" ========================================");
endfunction
endclass
Kodun Açıklaması
- Üyeler:
drv2scbvemon2scbmailbox'ları beklenen ve gerçek transaction'ları taşır.pass_count,fail_count,total_countistatistikleri tutar.covcoverage nesnesidir; yapıcıya dışarıdan verilir, scoreboard kendisi yaratmaz. new(...): İki mailbox ve coverage handle'ını alır. Coverage'ın enjekte edilmesi, environment'ıncov.report()ile raporladığı nesnenin scoreboard'uncov.sample()ile beslediği nesneyle aynı olmasını garanti eder.run(): Sonsuz döngüdedrv2scb.get(expected)vemon2scb.get(actual)ile bir beklenen ve bir gerçek transaction'ı eşleştirir,total_count++yapar.- Referans hesabı:
compute_reference(...)ile beklenenref_resultveref_flagshesaplanır. - Karşılaştırma:
actual.result === ref_result && actual.flags === ref_flagsile hem sonuç hem bayraklar denetlenir.===operatörüx/zdurumlarını da kesin karşılaştırır. Eşleşmezse hata detayları (opcode.name(), operandlar, beklenen ve gerçek değerler) basılır vefail_count++yapılır. cov.sample(actual): Gözlenen değerler her işlemde kapsam toplamaya gönderilir.compute_reference(...): Önceresulthesaplanır:ADD/SUBiçin operandlar 16-bit'e genişletilerek toplanır/çıkarılır (böylece 9. bit carry/borrow'u taşır), mantıksal ve kaydırma işlemleri{8'h00, ...}ile 16-bit'e tamamlanır. Ardından bayraklar sözleşmeye göre üretilir:Z = (result == 0),N = result[15],C = ADD/SUB ise result[8] değilse 0,P = ^result. Model yalnızca spesifikasyonu bilir; DUT'unalways_combbloğunu kopyalamaz.report(): Toplam/başarılı/başarısız sayıları ve geçti/kaldı özetini ekrana basar.
Önemli Noktalar
- Referans modelin DUT'tan bağımsız olması esastır; aksi halde aynı hatayı iki yerde yapıp hatayı göremezsiniz. Model, "spesifikasyon ne diyor?" sorusuna göre yazılır; "DUT ne yapıyor?" sorusuna göre değil.
- Karşılaştırmada
===kullanımı, sonuçtaki olasıx/zdurumlarını da yakalar;==bu durumlarda yanıltıcı olabilir. - Bayraklar da karşılaştırılır. Bir ALU'da bayraklar çoğu zaman sonuçtan daha hataya açıktır (carry'nin yanlış işlemde set olması, zero'nun 16-bit yerine 8-bit'e bakması gibi). Yalnızca
result'ı kontrol eden bir scoreboard bu hataları hiç görmez. - Bayrak sözleşmesini yanlış yorumlamak klasik bir tuzaktır. Bu dersin önceki sürümünde referans model
flags[3]'ü overflow olarak hesaplıyordu, oysa DUT bu bitte parity üretir; modelNbayrağını daresult[7]'ye bakarak hesaplıyordu. Bayraklar karşılaştırılmadığı için bu uyuşmazlık görünmüyordu. Ders şimdi hem sözleşmeyi (Ders 36-1) hem modeli hizalar; bu hikâye, "karşılaştırmadığınız alan size hata vermez ama doğru da değildir" ilkesinin canlı örneğidir. - Driver ve monitor akışlarının hizalı kalması kritiktir; her beklenen transaction tam bir gözlemle eşleşmelidir, yoksa
getçağrıları kayar ve tüm sonraki karşılaştırmalar bozulur. compute_referencebirfunction'dır, zaman tüketmez; referans model karmaşıklaşsa (bellek, durum makinesi) bilefunctionkalması, scoreboard'ungetritmini bozmamasını sağlar.
Scoreboard Mimarisi Seçenekleri
Bu derste "sıralı eşleştirme" (in-order) kullanılır: her beklenenle bir gözlenen, geliş sırasına göre eşleşir. Gerçek projelerde başka desenler de görürsünüz:
| Desen | Nasıl çalışır? | Ne zaman? |
|---|---|---|
| Sıralı (bu ders) | get(expected); get(actual); compare |
DUT sırayı korur, her girişe tam bir çıkış |
| Kuyruk tabanlı | Beklenenler kuyruğa; gözlenen gelince pop_front ile eşleştir |
Driver monitor'den hızlıysa (geri-basınç) |
| Sırasız (out-of-order) | Beklenenler ilişkisel dizide (id/adres anahtarlı); gözlenen gelince anahtarla ara |
DUT çıkışları farklı sırada üretebiliyorsa (çok kanallı, önbellekli) |
| Tahmin edici (predictor) | Monitor girişi gözler → model çıktıyı tahmin eder → çıkış monitor'ü ile karşılaştır | Driver'dan hiç veri almak istemiyorsanız (UVM'in önerdiği yapı) |
Son satır önemlidir: UVM'de scoreboard genellikle driver'dan değil, giriş monitor'ünden beslenir; böylece doğrulama tamamen pin seviyesinde gözlenen gerçeklere dayanır. Bu projede driver kopyası kullanmak öğrenmeyi kolaylaştırır; UVM derslerinde "predictor" yaklaşımını göreceksiniz.
Benzetme: Scoreboard bir sınav gözetmenidir: elinde cevap anahtarı (referans model) vardır, öğrencinin kâğıdını (DUT çıkışı) sorularla (driver'ın gönderdiği girişler) eşleştirir ve puanlar. Cevap anahtarını öğrencinin kâğıdından kopyalarsanız (DUT koduna bakarak model yazmak) herkes 100 alır.
Sık Yapılan Hatalar
- Referans modeli DUT kodunu kopyalayarak yazmak: Aynı hata iki yerde → her zaman PASS.
- Yalnızca
resultkarşılaştırmak: Bayrak hataları görünmez kalır. getsırasını değiştirmek:mon2scb.getönce yazılırsa monitor henüz veri üretmediği için scoreboard bloklanır; sonuç yine doğru ama hata ayıklama zorlaşır. Sıra "beklenen → gözlenen" kalsın.- Coverage'ı scoreboard içinde
new()etmek: Environment'ın raporladığı nesneyle farklı olur; rapor hep %0 gösterir. Bu dersin önceki sürümündeki hata tam olarak buydu. - Hata mesajında yalnızca "FAIL" basmak: Girişler, beklenen ve gözlenen değerler olmadan hata ayıklanamaz; mesaj her zaman bu üçünü içermeli.
Kendinizi Deneyin
- DUT'ta bilinçli hata yapın:
always_combiçindenext_flags[FLAG_C]'yi her işlem içinnext_result[8]yapın. Scoreboard hangi işlemlerde hata verdi? Yalnızcaresultkarşılaştıran eski sürüm bunu yakalar mıydı? compute_referenceiçindeflags[1] = result[15] | result[7];yazın (eski hatalı sürüm). Hangi işlemlerde ve hangi operand değerlerinde uyuşmazlık çıktı?0x80 + 0x00örneğini elle hesaplayın.- Scoreboard'u kuyruk tabanlı hale getirin:
drv2scb.getile gelenleriexpected_qkuyruğuna koyan ayrı bir süreç,mon2scb.getile gelenleripop_frontile eşleştiren başka bir süreç yazın (forkiçinde ikiforever). compare()metodunuALU_Transaction'a taşıyın (Ders 37 ek görevi) ve scoreboard'daif (actual.compare(expected_with_ref))biçiminde kullanın. Hangi tasarım daha temiz?
Hızlı Kontrol: Referans model neden DUT'un RTL koduna bakılarak yazılmamalıdır?
Çünkü o zaman DUT'taki bir yorum hatası modele de aynen geçer; iki taraf aynı yanlış cevabı üretir ve scoreboard her şeyi PASS sayar. Referans model spesifikasyondan yazılmalı, DUT'tan bağımsız ikinci bir "görüş" olmalıdır. Bu bağımsızlık, doğrulamanın tasarımdan ayrı bir disiplin olmasının temel nedenidir.