EDA Playground'da Dene

Diziler (Arrays)

Gün 1: SystemVerilog'a Giriş ve Veri Tipleri | Sabit boyutlu, dinamik ve ilişkisel diziler

Bu derste SystemVerilog'un üç temel dizi türünü, hangi senaryoda hangisinin uygun olduğunu ve yerleşik dizi metodlarını (sum, min, max, sort, unique...) öğreneceğiz.

01 — ÜÇ DİZİ, ÜÇ BELLEK MODELİ

Aynı köşeli parantez, farklı kader.

Boyutu ne zaman bilirsin? Anahtarların ne kadar seyrek? Cevap hangi diziyi seçeceğini söyler.

DERLEME ZAMANI

fixed

int a [5]; Bellek baştan ayrılır, hepsi yan yana. Hızlı ama sabit.

ÇALIŞMA ZAMANI

dynamic

new[N] ile boyut belirlenir; new[M](eski) ile büyürken veriyi taşır.

SEYREK & ANAHTARLI

associative

int a [string]; Sadece yazılan girdiler bellek harcar.

02 — DİZİ LABORATUVARI

Belleği ve metodları canlı izle.

Bir sekme seç, altındaki düğmelerle sırayla komut çalıştır. Sol tarafta hücrelerin gerçekte ne yaptığını gör.

DİZİ

KAYNAK KODfixed_arr

DURUMcanlı

SİMÜLATÖR ÇIKTISI$display

03 — MEYDAN OKUMA

Hangi diziyi seçmelisin?

SERİ 0 · EN İYİ 0
04 — ÖZET

Aklında kalsın.

Boyut sabitse → fixedBellek derleme zamanında ayrılır; hücreler yan yana, erişim en hızlısı.
Boyut sonradan belli olursa → dynamicnew[N] ile ayır; büyütürken new[M](eski) yaz, yoksa veri sıfırlanır.
Anahtar–değer / seyrek → associative64K adres uzayında 3 girdi yazıyorsan yalnızca 3 girdilik bellek harcarsın.
Metodlar tüm türlerdesum, min, max, sort, rsort, shuffle, unique — hazır kullan.
foreach her türü gezerÇok boyutlu dizide foreach (m[i, j]) ile tüm indeksler tek satırda.
Sınır dışı indeks sessizce geçmezSabit dizide a[5] (5 elemanlı) yazmak hata/uyarı üretir; her zaman size()'a güven.

Üç Dizi Türü

SystemVerilog, ihtiyaca göre seçilen üç ana dizi türü sunar:

  • Sabit boyutlu (fixed-size) diziler: Boyut derleme zamanında bellidir (int fixed_arr [5];). Çok boyutlu olabilirler (int matrix [3][4];). Bellek önceden ayrılır, erişim hızlıdır.
  • Dinamik (dynamic) diziler: Boyut çalışma zamanında new[] ile belirlenir (int dyn_arr [];). Gerektiğinde yeniden boyutlandırılabilir ve delete() ile temizlenebilir.
  • İlişkisel (associative) diziler: Anahtar–değer eşlemesi yapar (int assoc_arr [string];). Çok büyük ama seyrek (sparse) adres uzaylarını modellemek için idealdir; yalnızca yazılan girdiler için bellek harcar.

Dizi Metodları

SystemVerilog dizilerde doğrudan kullanılabilen güçlü metodlar sağlar:

  • İndirgeme: sum(), min(), max()
  • Sıralama/düzen: sort(), rsort() (tersten), shuffle() (karıştır)
  • Filtreleme: unique() ile tekrarsız elemanlar
  • Sorgu (associative): num() eleman sayısı, exists() anahtar var mı

foreach döngüsü, indeksleri elle yönetmeden tüm dizi türlerini gezmenin en pratik yoludur; çok boyutlu dizilerde foreach (matrix[i, j]) gibi birden çok indeks kullanılabilir.

Kaynak Kod

// =============================================================================
// GUN 1 - Konu 5: Diziler (Arrays): Sabit, Dinamik, Iliskisel
// =============================================================================

module diziler;

  // -------------------------------------------------------------------------
  // SABIT BOYUTLU DIZILER (Fixed-Size Arrays)
  // -------------------------------------------------------------------------
  int fixed_arr [5];                    // 5 elemanli unpacked dizi
  logic [7:0] vec_arr [3];              // 3 elemanli unpacked dizi; her eleman 8-bit packed vektor
  int matrix [3][4];                    // 2 boyutlu dizi (3 satir, 4 sutun)

  // -------------------------------------------------------------------------
  // DINAMIK DIZILER (Dynamic Arrays)
  // -------------------------------------------------------------------------
  int dyn_arr [];                       // Boyut calisma zamaninda belirlenir

  // -------------------------------------------------------------------------
  // ILISKISEL DIZILER (Associative Arrays)
  // -------------------------------------------------------------------------
  int assoc_arr [string];              // String anahtarli
  int sparse_mem [int];                // Int anahtarli

  initial begin
    $display("=== SystemVerilog Diziler ===\n");

    // --- Sabit Boyutlu Dizi ---
    $display("--- Sabit Boyutlu Diziler ---");
    fixed_arr = '{10, 20, 30, 40, 50};
    foreach (fixed_arr[i])
      $display("  fixed_arr[%0d] = %0d", i, fixed_arr[i]);

    // 2D dizi
    $display("\n  2D Matris (3x4):");
    foreach (matrix[i, j])
      matrix[i][j] = (i * 4) + j;
    foreach (matrix[i]) begin
      $write("  Satir %0d: ", i);
      foreach (matrix[i][j])
        $write("%3d ", matrix[i][j]);
      $display();
    end

    // Dizi metodlari
    $display("\n  Dizi Metodlari:");
    $display("  sum     = %0d", fixed_arr.sum());
    $display("  min     = %0d", fixed_arr.min());
    $display("  max     = %0d", fixed_arr.max());
    $display("  unique  = %p",  fixed_arr.unique());

    // Siralama
    fixed_arr.rsort();
    $display("  rsort   = %p", fixed_arr);
    fixed_arr.shuffle();
    $display("  shuffle = %p", fixed_arr);

    // --- Dinamik Dizi ---
    $display("\n--- Dinamik Diziler ---");
    dyn_arr = new[5];
    foreach (dyn_arr[i]) dyn_arr[i] = (i + 1) * 100;
    $display("  Baslangic boyut = %0d", dyn_arr.size());
    foreach (dyn_arr[i]) $display("  dyn_arr[%0d] = %0d", i, dyn_arr[i]);

    // Boyut degistirme (mevcut veriyi koruyarak)
    dyn_arr = new[8](dyn_arr);
    dyn_arr[5] = 600; dyn_arr[6] = 700; dyn_arr[7] = 800;
    $display("\n  Yeni boyut = %0d", dyn_arr.size());
    foreach (dyn_arr[i]) $display("  dyn_arr[%0d] = %0d", i, dyn_arr[i]);

    // Silme
    dyn_arr.delete();
    $display("  Silme sonrasi boyut = %0d", dyn_arr.size());

    // --- Iliskisel Dizi ---
    $display("\n--- Iliskisel Diziler ---");
    
    // String anahtarli
    assoc_arr["sifirlama"]  = 0;
    assoc_arr["baslat"]     = 1;
    assoc_arr["durdur"]     = 2;
    assoc_arr["hata"]       = 99;

    $display("  Eleman sayisi = %0d", assoc_arr.num());
    if (assoc_arr.exists("hata"))
      $display("  'hata' anahtari mevcut, deger = %0d", assoc_arr["hata"]);

    // foreach ile gezinme
    foreach (assoc_arr[key])
      $display("  assoc_arr[\"%s\"] = %0d", key, assoc_arr[key]);

    // Seyrek bellek modeli
    $display("\n  Seyrek Bellek Modeli:");
    sparse_mem[32'h0000] = 'hAA;
    sparse_mem[32'h1000] = 'hBB;
    sparse_mem[32'hFFFF] = 'hCC;
    foreach (sparse_mem[addr])
      $display("  MEM[0x%04h] = 0x%02h", addr, sparse_mem[addr]);

    $display("\n=== Diziler Sonu ===");
    $finish;
  end

endmodule

Kodun Açıklaması

  • fixed_arr = '{10, 20, 30, 40, 50}; ile sabit boyutlu dizi toplu atama (array literal) ile doldurulur ve foreach (fixed_arr[i]) ile yazdırılır.
  • int matrix [3][4]; iki boyutlu dizidir. foreach (matrix[i, j]) matrix[i][j] = (i * 4) + j; tek satırda her iki indeksi birden gezer; ardından satır satır basılır.
  • Dizi metodları fixed_arr üzerinde gösterilir: sum() toplamı, min()/max() uç değerleri, unique() tekrarsız elemanları döndürür. rsort() diziyi büyükten küçüğe sıralar, shuffle() rastgele karıştırır (yerinde değiştirir).
  • dyn_arr = new[5]; dinamik diziyi 5 elemanlı oluşturur; foreach ile (i + 1) * 100 değerleri atanır. dyn_arr.size() anlık boyutu verir.
  • dyn_arr = new[8](dyn_arr); diziyi 8 elemana büyütürken mevcut veriyi korur (parantez içindeki dyn_arr eski içeriği taşır); ardından dyn_arr[5], [6], [7] elle doldurulur.
  • dyn_arr.delete(); diziyi tamamen boşaltır, boyut 0 olur.
  • İlişkisel dizide assoc_arr["hata"] = 99; gibi string anahtarlı girdiler eklenir; assoc_arr.num() eleman sayısını, assoc_arr.exists("hata") anahtarın varlığını sorgular. foreach (assoc_arr[key]) ile anahtar üzerinden gezilir.
  • sparse_mem[32'h0000], [32'h1000], [32'hFFFF] girdileri seyrek bellek modelini gösterir: 64K'lık bir adres uzayında yalnızca 3 girdi için bellek harcanır.

Önemli Noktalar

  • Doğru türü seçin: Boyut sabitse fixed-size, çalışma zamanında değişiyorsa dynamic, anahtar–değer veya seyrek adresleme gerekiyorsa associative dizi kullanın.
  • Dinamik diziyi büyütürken eski veriyi korumak için new[N](eski_dizi) biçimini kullanın; aksi hâlde dizi yeni boyutta sıfırlanır.
  • İlişkisel diziler bellek açısından verimlidir ama erişim sırası tanımlı değildir; sırayı önemsiyorsanız anahtarları ayrıca sıralayın.
  • unique(), min(), max() gibi metodlar yeni bir kuyruk (queue) döndürür; orijinal diziyi değiştirmezler. Buna karşılık sort(), rsort(), shuffle() diziyi yerinde değiştirir.
  • Çok boyutlu dizilerde foreach (matrix[i, j]) sözdizimi, iç içe döngülere göre daha kısa ve okunaklıdır.
  • İlişkisel bir anahtara erişmeden önce exists() ile kontrol etmek, tanımsız girdi okumaktan kaynaklı sürprizleri önler. Daha da önemlisi: var olmayan bir anahtarı okumak (x = assoc_arr["yok"]) çoğu simülatörde uyarı verip varsayılan değer döndürürken, ona yazmak o anahtarı sessizce oluşturur. if (sparse_mem[addr] == 0) gibi masum bir okuma bile bazı araçlarda girdi yaratabilir; exists() bu belirsizliği ortadan kaldırır.
  • logic [7:0] vec_arr [3] bildiriminde köşeli parantezlerin yeri anlamı belirler: tip adının solundaki [7:0] packed boyuttur (bit vektörü), değişken adının sağındaki [3] unpacked boyuttur (eleman sayısı). vec_arr[1][3] ifadesi 1. elemanın 3. bitini verir.

Packed ve Unpacked Boyutlar

Packed (logic [7:0] x) Unpacked (logic x [8])
Nerede yazılır? Tip ile değişken adı arasında Değişken adının sağında
Bellekte Bitişik tek vektör Ayrı elemanlar
Tek parça atama x = 8'hFF; x = '{default: 1};
Dilimleme x[3:0] geçerli x[3:0] → 4 elemanlı alt dizi
Eleman tipi Yalnızca bit türleri Her tip (int, string, struct, class...)
Tipik kullanım Sinyaller, bus veri yolu Bellek modelleri, transaction listeleri

Hangi Dizi Türü, Hangi Senaryo?

Senaryo Tür Gerekçe
256 girdili register dosyası modeli Sabit boyutlu logic [31:0] rf [256] Boyut bellidir, erişim en hızlı
Payload uzunluğu transaction'a göre değişen paket Dinamik byte payload [] Boyut new[n] ile çalışma zamanında
4 GB adres uzayında yalnızca birkaç KB yazılan bellek İlişkisel logic [7:0] mem [logic [31:0]] Yalnızca kullanılan adresler için bellek
İsimle eşleşen konfigürasyon değerleri İlişkisel int cfg [string] Anahtar metin olabilir
Scoreboard'da beklenen değer listesi (ekle/çıkar) Kuyruk (sonraki ders) İki uçtan verimli ekleme/çıkarma

Sık Yapılan Hatalar

  • new[N] ile veriyi kaybetmek: dyn_arr = new[8]; eski içeriği siler. Korumak için new[8](dyn_arr) yazın.
  • İlişkisel dizide sıra beklemek: foreach ilişkisel diziyi anahtar sırasına göre gezer (sayısal anahtarlarda artan, string'lerde sözlük sırası); ekleme sırası korunmaz. Ekleme sırası önemliyse kuyruk kullanın.
  • sum()'ın genişliğini unutmak: bit [7:0] arr [4] için arr.sum() 8-bit'te toplanır ve taşabilir. arr.sum() with (int'(item)) ile genişletin (bu konu Gün 3'teki kısıtlamalarda tekrar karşımıza çıkacak).
  • unique() sonucunu diziye atamak: unique(), find() gibi metodlar kuyruk döndürür; sonucu int q[$] tipinde bir değişkene alın.

Kendinizi Deneyin

  1. matrix dizisini foreach (matrix[i, j]) ile gezerken yalnızca köşegen elemanları (i == j) 99 yapın ve yazdırın.
  2. dyn_arr'ı new[3](dyn_arr) ile küçültün. Hangi elemanlar kaldı? Bu davranışı açıklayın.
  3. sparse_mem'e foreach ile girmeden önce sparse_mem.first(addr) / sparse_mem.next(addr) metodlarını araştırın ve aynı çıktıyı do...while döngüsüyle üretin.
  4. fixed_arr.find with (item > 25) ifadesini kullanıp sonucu bir kuyruğa alın; %p ile yazdırın.
Hızlı Kontrol: int a [int]; ile int a []; arasındaki fark nedir?

int a [int] ilişkisel dizidir: anahtar tipi int, yalnızca yazılan anahtarlar için yer ayrılır, new[] gerekmez. int a [] ise dinamik dizidir: indeksler 0'dan size()-1'e kadar bitişiktir ve kullanmadan önce a = new[N] ile boyutlandırılmalıdır.