EDA Playground'da Dene

Lab 1: Veri Paketleri

Gün 1: SystemVerilog'a Giriş ve Veri Tipleri | Ağ paketi oluşturma ve dizi metodlarını test etme

Aşağıdaki kod, egzersizin tamamlanmış halidir. Önce EDA Playground'da kendinizi deneyin. Sonrasında buradakiyle karşılaştırabilirsiniz. Başarılar!

Adım Adım Görev Listesi

Çözüme bakmadan önce aşağıdaki adımları kendiniz tamamlamayı deneyin. Her adım bir öncekinin üzerine kurulur:

  1. Veri modeli: PKT_DATA, PKT_ACK, PKT_NACK, PKT_CTRL değerlerini taşıyan 2-bitlik bir enum ve id, pkt_type, src_addr, dst_addr, payload[$], length, valid alanlarını içeren bir struct tanımlayın.
  2. Üretici fonksiyon: Parametrelerden bir paket üretip döndüren create_packet(...) fonksiyonu yazın. Payload'u payload_len kadar push_back ile doldurun.
  3. Yazdırıcı fonksiyon: Tek satırda özet basan void bir print_packet(...) yazın; enum ismi için .name() kullanın.
  4. Kuyruğu doldurun: En az 8 paket üretin; birkaçını bilinçli olarak valid = 0 yapın.
  5. Filtreleyin: find with kullanarak (a) yalnızca DATA paketleri, (b) geçerli paketler, (c) payload'u 50 bayttan büyük paketler, (d) hedef adresi 0x0002 olan paketleri ayrı ayrı bulun.
  6. İstatistik: Payload uzunluklarını ayrı bir kuyruğa toplayıp sum, min, max ve sort uygulayın.
  7. Tüketin: Kuyruğu while (size() > 0) + pop_front() ile boşaltın.

İpucu: Takıldığınız adımda yalnızca o adımın çözümüne bakın; tüm kodu bir anda kopyalamak öğrenmeyi kısa devre yapar.

Bu laboratuvarda Gün 1 boyunca öğrendiğimiz tüm parçaları tek bir uygulamada birleştiriyoruz: enum, struct, kuyruklar, fonksiyonlar ve dizi metodları ile gerçekçi bir ağ paketi (network packet) modeli kuracağız.

Lab'ın Amacı

Bu egzersiz, ayrı ayrı öğrenilen kavramların gerçek bir doğrulama senaryosunda nasıl bir araya geldiğini göstermeyi hedefler. Adım adım:

  • Bir paket tipini enum (pkt_type_e) ve struct (network_packet_t) ile modelleyeceğiz.
  • Paketleri yeniden kullanılabilir bir fonksiyon (create_packet) ile üretip bir kuyruğa ekleyeceğiz.
  • find with filtreleriyle paketleri tipe, geçerliliğe, boyuta ve hedef adrese göre süzeceğiz.
  • Dizi metodlarıyla (sum, min, max, sort) istatistik çıkaracağız.
  • Kuyruğu FIFO mantığıyla işleyerek tüketeceğiz.

Kullanılan Kavramlar

  • enum + struct: Paket tipini ve alanlarını anlamlı isimlerle gruplamak.
  • Kuyruk içinde kuyruk: network_packet_t içindeki byte payload [$] alanı, struct'ın içinde dinamik bir veri tutar.
  • Fonksiyonlar: create_packet bir struct döndürürken, print_packet void döndürerek yalnızca çıktı üretir.
  • find with ve item: Karmaşık elemanlarda item.pkt_type, item.valid, item.length, item.dst_addr gibi alanlar üzerinden filtreleme.

Kaynak Kod

// =============================================================================
// GUN 1 - Lab 1: Veri Paketleri Olusturma ve Dizi Metodlarini Test Etme
// =============================================================================
// Bu laboratuvar calismasinda:
// - Cesitli veri tipleri kullanarak bir ag paketi modeli olusturulacak
// - Paketler dizilere ve kuyruklara eklenecek
// - Dizi metodlari ile filtreleme, siralama yapilacak
// =============================================================================

module lab1_veri_paketleri;

  // Paket tipi enum
  typedef enum logic [1:0] {
    PKT_DATA   = 2'b00,
    PKT_ACK    = 2'b01,
    PKT_NACK   = 2'b10,
    PKT_CTRL   = 2'b11
  } pkt_type_e;

  // Paket yapisi
  typedef struct {
    int          id;
    pkt_type_e   pkt_type;
    logic [15:0] src_addr;
    logic [15:0] dst_addr;
    byte         payload [$];
    int          length;
    bit          valid;
  } network_packet_t;

  // Paket kuyruklari
  network_packet_t pkt_queue [$];
  network_packet_t filtered  [$];

  // Paket olusturma fonksiyonu
  function network_packet_t create_packet(
    int id, pkt_type_e ptype, 
    logic [15:0] src, logic [15:0] dst,
    int payload_len, bit valid_flag
  );
    network_packet_t pkt;
    pkt.id       = id;
    pkt.pkt_type = ptype;
    pkt.src_addr = src;
    pkt.dst_addr = dst;
    pkt.length   = payload_len;
    pkt.valid    = valid_flag;
    
    // Payload olustur
    for (int i = 0; i < payload_len; i++)
      pkt.payload.push_back(byte'(i + 'hA0));
    
    return pkt;
  endfunction

  // Paket yazdirma fonksiyonu
  function void print_packet(network_packet_t pkt);
    $display("  Paket #%0d | Tip: %-4s | Src: 0x%04h | Dst: 0x%04h | Len: %0d | Gecerli: %0b",
             pkt.id, pkt.pkt_type.name(), pkt.src_addr, pkt.dst_addr, pkt.length, pkt.valid);
  endfunction

  initial begin
    $display("============================================================");
    $display("  LAB 1: Ag Paketi Olusturma ve Dizi Metodlari Testi");
    $display("============================================================\n");

    // --- Paketleri olustur ve kuyruga ekle ---
    $display("--- 1. Paket Olusturma ---");
    pkt_queue.push_back(create_packet(1, PKT_DATA, 16'h0001, 16'h0002, 64, 1));
    pkt_queue.push_back(create_packet(2, PKT_ACK,  16'h0002, 16'h0001, 0,  1));
    pkt_queue.push_back(create_packet(3, PKT_DATA, 16'h0003, 16'h0002, 128,1));
    pkt_queue.push_back(create_packet(4, PKT_NACK, 16'h0002, 16'h0003, 0,  1));
    pkt_queue.push_back(create_packet(5, PKT_DATA, 16'h0001, 16'h0004, 32, 0)); // Gecersiz
    pkt_queue.push_back(create_packet(6, PKT_CTRL, 16'h0001, 16'hFFFF, 8,  1));
    pkt_queue.push_back(create_packet(7, PKT_DATA, 16'h0005, 16'h0002, 256,1));
    pkt_queue.push_back(create_packet(8, PKT_DATA, 16'h0003, 16'h0001, 16, 0)); // Gecersiz

    $display("  Toplam %0d paket olusturuldu.\n", pkt_queue.size());
    foreach (pkt_queue[i]) print_packet(pkt_queue[i]);

    // --- Filtreleme: Sadece DATA paketleri ---
    $display("\n--- 2. Filtreleme: Sadece DATA Paketleri ---");
    filtered = pkt_queue.find with (item.pkt_type == PKT_DATA);
    $display("  DATA paketi sayisi: %0d", filtered.size());
    foreach (filtered[i]) print_packet(filtered[i]);

    // --- Filtreleme: Gecerli paketler ---
    $display("\n--- 3. Filtreleme: Gecerli Paketler ---");
    filtered = pkt_queue.find with (item.valid == 1);
    $display("  Gecerli paket sayisi: %0d", filtered.size());
    foreach (filtered[i]) print_packet(filtered[i]);

    // --- Filtreleme: Buyuk paketler (len > 50) ---
    $display("\n--- 4. Filtreleme: Payload > 50 Byte ---");
    filtered = pkt_queue.find with (item.length > 50);
    $display("  Buyuk paket sayisi: %0d", filtered.size());
    foreach (filtered[i]) print_packet(filtered[i]);

    // --- Filtreleme: Belirli hedef adres ---
    $display("\n--- 5. Filtreleme: Hedef = 0x0002 ---");
    filtered = pkt_queue.find with (item.dst_addr == 16'h0002);
    $display("  Eslesen paket sayisi: %0d", filtered.size());
    foreach (filtered[i]) print_packet(filtered[i]);

    // --- Istatistikler ---
    $display("\n--- 6. Istatistikler ---");
    begin
      int lengths [$];
      foreach (pkt_queue[i]) lengths.push_back(pkt_queue[i].length);
      $display("  Toplam payload  = %0d byte", lengths.sum());
      $display("  Min payload     = %0d byte", lengths.min());
      $display("  Max payload     = %0d byte", lengths.max());
      
      lengths.sort();
      $display("  Sirali payload  = %p", lengths);
    end

    // --- FIFO Isleme ---
    $display("\n--- 7. FIFO Isleme ---");
    $display("  Kuyruk boyutu = %0d", pkt_queue.size());
    while (pkt_queue.size() > 0) begin
      network_packet_t p = pkt_queue.pop_front();
      $display("  Islendi: Paket #%0d (%s)", p.id, p.pkt_type.name());
    end
    $display("  Kuyruk boyutu = %0d (Tumu islendi)", pkt_queue.size());

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

endmodule

Kodun Açıklaması

  • pkt_type_e enum'u dört paket tipini (PKT_DATA, PKT_ACK, PKT_NACK, PKT_CTRL) logic [1:0] tabanında tanımlar.
  • network_packet_t struct'ı bir paketin tüm alanlarını taşır: id, pkt_type, src_addr, dst_addr, dinamik byte payload [$], length ve valid. Burada bir struct'ın içinde kuyruk barındırabildiğini görüyoruz.
  • pkt_queue ve filtered, network_packet_t tipinde iki struct kuyruğudur.
  • create_packet(...) fonksiyonu bir network_packet_t döndürür. Alanları parametrelerden doldurur ve for (int i = 0; i < payload_len; i++) pkt.payload.push_back(byte'(i + 'hA0)); döngüsüyle payload'ı üretir; byte'(...) casting ile değer 8-bit'e sığdırılır.
  • print_packet(...) void döndüren bir fonksiyondur; pkt.pkt_type.name() ile enum ismini ve %-4s gibi hizalı biçim belirteçleriyle tek satırlık özet basar.
  • initial bloğunda pkt_queue.push_back(create_packet(...)) ile 8 paket üretilir; bazıları (#5, #8) bilinçli olarak valid = 0 (geçersiz) işaretlenir.
  • Filtreleme adımları find with kullanır: item.pkt_type == PKT_DATA (yalnızca DATA), item.valid == 1 (geçerliler), item.length > 50 (büyük payload'lar) ve item.dst_addr == 16'h0002 (belirli hedef). Her biri sonucu filtered kuyruğuna yazar.
  • İstatistik bölümünde lengths adlı yardımcı kuyruğa tüm length değerleri toplanır; lengths.sum(), min(), max() ve sort() ile özet üretilir.
  • FIFO işleme bölümünde while (pkt_queue.size() > 0) döngüsü pkt_queue.pop_front() ile paketleri ekleniş sırasıyla tüketir; sonunda kuyruk boşalır.

Önemli Noktalar

  • Bir struct içinde dinamik diziler ve kuyruklar (byte payload [$]) barındırabilirsiniz; bu, değişken uzunluklu paketleri modellemenin doğal yoludur.
  • Paket üretimini bir fonksiyona (create_packet) ayırmak, kodu tekrar etmeden çok sayıda paket oluşturmayı sağlar; bu, doğrulamada yeniden kullanılabilirliğin temelidir.
  • find with (item.alan == deger) ifadesinde item her elemanı temsil eder ve struct alanlarına nokta ile erişilir; bu, koleksiyonları okunaklı biçimde filtrelemenin güçlü bir yoludur.
  • İstatistik almak için verileri ayrı bir kuyruğa toplayıp sum/min/max/sort uygulamak, struct kuyruğunun tamamını sıralamaktan daha pratiktir.
  • byte'(i + 'hA0) gibi açık casting, değerin hedef tipe (8-bit) güvenle sığmasını sağlar ve istenmeyen genişlik uyarılarını önler.
  • FIFO tüketiminde her zaman size() > 0 koşulunu kontrol edin; boş kuyrukta pop_front çağırmaktan kaçının.
  • create_packet bir struct döndürür ve struct'lar değer tipidir: pkt_queue.push_back(create_packet(...)) çağrısında paketin tüm alanları (payload kuyruğu dahil) kuyruğa kopyalanır. Gün 2'de sınıflara geçince aynı satır bir handle kopyalayacak; bu fark derin/sığ kopya dersinde kritik olacak.
  • Lab'daki filtered = pkt_queue.find with (...) satırı her çağrıda filtered'ın eski içeriğini tamamen değiştirir. Sonuçları biriktirmek isterseniz filtered = {filtered, pkt_queue.find with (...)} biçiminde birleştirmeniz gerekir.

Çıktıyı Okuma Rehberi

Çalıştırdığınızda şu kontrolleri yapın:

Bölüm Beklenen sonuç Neden
2. Filtre (DATA) 5 paket (#1, #3, #5, #7, #8) 8 paketin 5'i PKT_DATA
3. Filtre (Geçerli) 6 paket #5 ve #8 valid = 0
4. Filtre (Len > 50) 3 paket (#1, #3, #7) 64, 128, 256 > 50
5. Filtre (Dst = 0x0002) 3 paket (#1, #3, #7) Hedef alanı 16'h0002 olanlar
6. İstatistik Toplam 504, min 0, max 256 64+0+128+0+32+8+256+16

Sayılarınız tabloyla uyuşmuyorsa önce create_packet çağrılarındaki argüman sırasını kontrol edin; en sık hata src/dst adreslerinin yer değiştirmesidir.

Ek Görevler (Zorluk Artan)

  1. Kolay: Paketleri length değerine göre azalan sırada listeleyin (rsort with (item.length) ya da ayrı kuyruk).
  2. Orta: Her pkt_type için kaç paket olduğunu int count [pkt_type_e] ilişkisel dizisiyle sayın ve .name() ile yazdırın.
  3. Orta: Payload'un basit bir sağlama toplamını (checksum) hesaplayan function byte calc_checksum(network_packet_t p) yazın (tüm baytların XOR'u) ve struct'a checksum alanı ekleyin.
  4. Zor: src_addr == dst_addr olan paketleri geçersiz işaretleyen bir validate_packets(ref network_packet_t q[$]) fonksiyonu yazın. ref argümanın neden gerektiğini açıklayın (Gün 2'de ref'i ayrıntılı göreceksiniz).
Hızlı Kontrol: find with (item.valid == 1) ifadesindeki item nereden geliyor?

item, with yan tümcesinde SystemVerilog'un örtük olarak tanımladığı yineleme değişkenidir; kuyruğun o anki elemanını temsil eder. İsterseniz find(p) with (p.valid == 1) yazarak kendi adınızı da verebilirsiniz.