EDA Playground'da Dene

typedef, enum ve struct

Gün 1: SystemVerilog'a Giriş ve Veri Tipleri | Kullanıcı tanımlı tipler: typedef, enum, struct, union

Bu derste kendi veri tiplerimizi nasıl tanımlayacağımızı öğreniyoruz: typedef ile takma adlar, enum ile anlamlı isimli sabitler, struct ile ilişkili verilerin gruplanması ve union ile aynı belleğin farklı görünümleri.

typedef: Tiplere İsim Vermek

typedef, mevcut bir tipe yeni bir ad vererek kodu hem okunabilir hem de bakımı kolay hâle getirir. Örneğin bir adres tipini her yerde logic [15:0] yazmak yerine addr_t olarak tanımlarsanız, genişliği değiştirmeniz gerektiğinde tek bir yeri düzenlemeniz yeter.

enum: Anlamlı Sabitler

enum, bir değişkenin alabileceği değerleri isimli sabitlerle sınırlar. Durum makineleri (FSM) ve opcode'lar için idealdir; 2'b01 yerine READ yazmak hem okunur hem de hataya kapalıdır. Enum tipleri zengin yerleşik metodlar sunar:

  • .name() → değerin metin karşılığını verir
  • .first() / .last() → ilk ve son elemanı verir
  • .next() / .prev() → komşu elemanlara geçer
  • .num() → eleman sayısını verir

struct ve packed struct

struct, farklı tiplerdeki ilişkili alanları tek bir yapıda toplar (örneğin bir transaction'ın adres, veri ve geçerlilik bilgisi). Varsayılan struct unpacked'tir. struct packed ise alanları bitişik bitler halinde saklar; tek bir vektör gibi davrandığı için sentezlenebilir ve bit düzeyinde erişilebilir.

union: Aynı Belleğin Farklı Görünümleri

union packed, aynı bellek alanını farklı tipler üzerinden yorumlamaya izin verir; örneğin 16-bit bir değere hem tek vektör hem de iki ayrı byte olarak erişmek gibi.

Kaynak Kod

// =============================================================================
// GUN 1 - Konu 4: Kullanici Tanimli Tipler: typedef, enum, struct
// =============================================================================

module typedef_enum_struct;

  // -------------------------------------------------------------------------
  // TYPEDEF: Mevcut tiplere yeni isim verme
  // -------------------------------------------------------------------------
  typedef logic [7:0]  byte_t;
  typedef logic [31:0] word_t;
  typedef logic [15:0] addr_t;

  // -------------------------------------------------------------------------
  // ENUM: Numaralandirilmis tipler
  // -------------------------------------------------------------------------
  typedef enum logic [1:0] {
    IDLE   = 2'b00,
    READ   = 2'b01,
    WRITE  = 2'b10,
    ERROR  = 2'b11
  } state_e;

  typedef enum logic [2:0] {
    ADD, SUB, AND, OR, XOR, NOT, SHL, SHR
  } opcode_e;

  // -------------------------------------------------------------------------
  // STRUCT: Yapilar - iliskili verileri gruplar
  // -------------------------------------------------------------------------
  typedef struct {
    addr_t     addr;
    word_t     data;
    opcode_e   op;
    bit        valid;
  } transaction_t;

  // PACKED STRUCT: Bitler ardisik saklanir, sentezlenebilir
  typedef struct packed {
    logic [3:0] tag;
    logic [7:0] payload;
    logic       parity;
  } packet_t;

  // -------------------------------------------------------------------------
  // UNION: Ayni bellegi paylasan tipler
  // -------------------------------------------------------------------------
  typedef union packed {
    logic [15:0] word;
    struct packed {
      logic [7:0] high;
      logic [7:0] low;
    } bytes;
  } reg16_t;

  // Degisken tanimlamalari
  state_e       current_state;
  opcode_e      alu_op;
  transaction_t txn;
  packet_t      pkt;
  reg16_t       register_val;

  initial begin
    $display("=== typedef, enum, struct Ornekleri ===\n");

    // --- ENUM kullanimi ---
    current_state = IDLE;
    $display("[ENUM] Durum  = %s (deger=%0b)", current_state.name(), current_state);
    
    current_state = READ;
    $display("[ENUM] Durum  = %s (deger=%0b)", current_state.name(), current_state);

    // Enum metodlari
    alu_op = ADD;
    $display("\n[ENUM Metodlari]");
    $display("  Ilk    = %s", alu_op.first().name());
    $display("  Son    = %s", alu_op.last().name());
    $display("  Sonraki= %s", alu_op.next().name());
    $display("  Eleman = %0d", alu_op.num());

    // --- STRUCT kullanimi ---
    txn.addr  = 16'hABCD;
    txn.data  = 32'hDEAD_BEEF;
    txn.op    = ADD;
    txn.valid = 1;
    $display("\n[STRUCT] Transaction:");
    $display("  Adres = 0x%04h", txn.addr);
    $display("  Veri  = 0x%08h", txn.data);
    $display("  Islem = %s", txn.op.name());
    $display("  Gecerli = %0b", txn.valid);

    // Struct literal (toplu atama)
    txn = '{addr: 16'h0010, data: 32'hCAFE_BABE, op: SUB, valid: 1};
    $display("\n[STRUCT Literal] Transaction:");
    $display("  Adres = 0x%04h, Veri = 0x%08h", txn.addr, txn.data);

    // --- PACKED STRUCT ---
    pkt = '{tag: 4'hA, payload: 8'hFF, parity: 1'b1};
    $display("\n[PACKED STRUCT] Paket:");
    $display("  Toplam bit = %0d", $bits(pkt));
    $display("  Bit dizisi = %013b", pkt);
    $display("  Tag=%h, Payload=%h, Parity=%b", pkt.tag, pkt.payload, pkt.parity);

    // --- UNION ---
    register_val.word = 16'hA5F0;
    $display("\n[UNION] Register:");
    $display("  Word = 0x%04h", register_val.word);
    $display("  High = 0x%02h, Low = 0x%02h", register_val.bytes.high, register_val.bytes.low);

    $display("\n=== typedef, enum, struct Sonu ===");
    $finish;
  end

endmodule

Kodun Açıklaması

  • typedef logic [7:0] byte_t;, word_t ve addr_t satırları sık kullanılan genişlikler için takma adlar üretir. Bu adlar daha sonra transaction_t içinde alan tipi olarak kullanılır.
  • state_e enum'u IDLE, READ, WRITE, ERROR durumlarını logic [1:0] tabanında açık değerlerle (2'b00...2'b11) tanımlar. opcode_e ise değer atamadan tanımlandığı için elemanlar 0'dan başlayarak otomatik numaralandırılır (ADD=0, SUB=1 ...).
  • transaction_t bir unpacked struct'tır ve addr, data, op, valid alanlarını birleştirir. packet_t ise struct packed olduğu için tag, payload, parity alanları toplam 13 bit olarak bitişik saklanır.
  • reg16_t bir union packed'tir: word (16-bit) ile içteki bytes yapısının high/low alanları aynı belleği paylaşır.
  • initial bloğunda önce current_state = IDLE; ve READ atanır, current_state.name() ile durumun ismi yazdırılır.
  • Enum metodları alu_op = ADD; üzerinde gösterilir: alu_op.first().name(), .last().name(), .next().name() ve .num() sırasıyla ilk/son elemanı, sonrakini ve toplam eleman sayısını verir.
  • txn.addr = 16'hABCD; gibi alan alan atama ile txn = '{addr: ..., data: ..., op: SUB, valid: 1}; şeklindeki struct literal (toplu atama) karşılaştırılır.
  • pkt = '{tag: 4'hA, payload: 8'hFF, parity: 1'b1}; packed struct'a değer atar; $bits(pkt) toplam genişliği (13) verir ve %013b ile bit dizisi basılır.
  • register_val.word = 16'hA5F0; union'a yazıldığında, aynı bellek register_val.bytes.high (A5) ve register_val.bytes.low (F0) olarak okunabilir.

Önemli Noktalar

  • enum değerlerini .name() ile yazdırmak, dökümlerde 2'b10 yerine WRITE görmenizi sağlar; bu da hata ayıklamayı çok kolaylaştırır.
  • Sentezlenebilir ve bit düzeyinde paketlenmiş veri için struct packed kullanın; salt testbench/modelleme amaçlı esnek gruplamalarda unpacked struct yeterlidir.
  • struct literal ('{...}) ile toplu atama, alanları tek tek atamaktan daha okunaklı ve hataya kapalıdır; alan adlarını yazmak sıralama hatalarını önler.
  • union packed güçlüdür ama dikkatli kullanılmalıdır: aynı belleğin farklı yorumları, alan genişlikleri ve bit sıralaması (endianness) konusunda net olmayı gerektirir.
  • typedef'leri tek bir paket (package) ya da ortak dosyada toplamak, takım genelinde tutarlı tip kullanımını sağlar ve genişlik değişikliklerini tek noktadan yönetir.
  • Enum metodlarında .next() son elemandayken başa döner (sarmalama); bu davranışı durum makinelerinde göz önünde bulundurun.