rand ve randc
Gün 3: Rastgele Üretim ve Kısıtlamalar | Rastgele değişkenler: rand (rastgele) ve randc (döngüsel)
Bu derste SystemVerilog'un kısıtlanmış-rastgele (constrained-random) doğrulama altyapısının temel yapı taşı olan rastgele değişkenleri öğreneceğiz: rand ve randc niteleyicileri ile std::randomize fonksiyonunu, bunların farklarını ve nerede kullanılacağını ele alacağız.
Neden Rastgele Üretim?
Modern donanım doğrulamasında, bir tasarımın tüm köşe durumlarını (corner case) elle yazılmış yönlü (directed) testlerle yakalamak neredeyse imkânsızdır. Bunun yerine kısıtlanmış-rastgele doğrulama yaklaşımı kullanılır: değişkenlere rastgele ama anlamlı (kısıtlanmış) değerler atayarak çok sayıda senaryo otomatik olarak üretilir. Bu sayede mühendisin aklına gelmeyen kombinasyonlar da test edilir.
SystemVerilog'da bir sınıf üyesini rastgele yapmak için iki niteleyici vardır:
rand: Herrandomize()çağrısında değişkene, izin verilen aralık içinden bağımsız ve rastgele bir değer atar. Aynı değer arka arkaya birden fazla kez gelebilir (tekrar olabilir).randc(cyclic random / döngüsel rastgele): Olası tüm değerleri bir kez üretmeden hiçbirini tekrar etmez. Tüm değerler tükendiğinde döngü baştan başlar ve yeni bir rastgele sıra ile devam eder. Genellikle dar bit genişlikli alanlar (örneğin adres seçici, kanal numarası) için kullanılır.
rand vs randc
randdeğişkenleri bellek/performans açısından ucuzdur ve çoğu durumda tercih edilir.randcdeğişkenleri tüm değer uzayını tarama garantisi verir; ancak bu uzayın küçük olması beklenir (örneğin 3 bit = 8 değer). Çok genişrandcalanları kaçınılması gereken bir kalıptır.
std::randomize
Sınıf dışındaki yerel değişkenleri rastgeleleştirmek için sınıfa bağlı olmayan std::randomize() fonksiyonu kullanılır. Buna with { ... } ekleyerek satır içi (inline) kısıtlamalar da verilebilir.
Kaynak Kod
// =============================================================================
// GUN 3 - Konu 1: Rastgele Test ve rand/randc Degiskenleri
// =============================================================================
class Packet;
// rand: Her randomize() cagrisinda rastgele deger uretir
rand bit [7:0] data;
rand bit [3:0] addr;
rand bit [1:0] burst_type;
// randc: Tum olasi degerleri tuketmeden tekrar etmez (cyclic random)
randc bit [2:0] prio; // 0-7 arasi, her biri bir kez, sonra tekrar
// Rastgele olmayan degiskenler
int id;
static int count = 0;
function new();
this.id = count++;
endfunction
function void display();
$display(" Paket #%0d: data=0x%02h, addr=0x%01h, burst=%0d, priority=%0d",
id, data, addr, burst_type, prio);
endfunction
endclass
module rand_ve_randc;
initial begin
Packet pkt;
Packet pkt2;//randc gösterimi için
$display("=== rand ve randc Degiskenleri ===\n");
pkt = new();
pkt2 = new();
// --- rand: Her seferinde rastgele ---
$display("--- rand: 10 rastgele paket ---");
repeat (10) begin
if (!pkt.randomize())
$fatal(1, "Randomize basarisiz!");
pkt.display();
end
// --- randc: Dongusel rastgele ---
$display("\n--- randc: priority (0-7 dongusel) ---");
$display(" Ilk dongu (8 deger):");
repeat (8) begin
void'(pkt2.randomize());
$display(" priority = %0d", pkt2.prio);
end
$display(" Ikinci dongu (tekrar 8 deger, farkli sira):");
repeat (8) begin
void'(pkt2.randomize());
$display(" priority = %0d", pkt2.prio);
end
// --- std::randomize (sinif disinda) ---
$display("\n--- std::randomize (Bagimsiz) ---");
begin
int x, y;
repeat (5) begin
if (!std::randomize(x, y) with { x inside {[1:100]}; y < x; })
$fatal(1, "std::randomize basarisiz!");
$display(" x = %0d, y = %0d", x, y);
end
end
$display("\n=== rand ve randc Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
Packetsınıfında üç adetranddeğişkeni (data,addr,burst_type) ve bir adetrandcdeğişkeni (prio) tanımlanmıştır.idve statikcountise rastgele olmayan, normal üyelerdir.new()kurucusunda her nesne içincount++ile artan biridatanır; bu, üretilen paketleri birbirinden ayırmak için kullanılır.- İlk
repeat (10)döngüsündepkt.randomize()çağrılır. Dönüş değeri kontrol edilir; başarısız olursa$fatalile simülasyon durdurulur. Her çağrıdadata,addrveburst_typebağımsız rastgele değerler alır. - İkinci bölümde
pkt2üzerindekirandcdeğişkenipriogösterilir. İlk 8 çağrıda 0–7 arası her değer tam bir kez üretilir. İkinci 8 çağrıda yine tüm değerler çıkar, ancak farklı bir sırada — döngüsel davranışın kanıtı budur. void'(pkt2.randomize())ifadesindekivoid'(...)cast'i, fonksiyonun dönüş değerini bilinçli olarak yok saymak içindir.- Son bölümde
std::randomize(x, y) with { x inside {[1:100]}; y < x; }kullanılarak sınıfa ait olmayan iki yerelintdeğişkeni satır içi kısıtlamalarla rastgeleleştirilir:x1–100 arasında,yisex'ten küçük olur.
Önemli Noktalar
randomize()dönüş değerini her zaman kontrol edin. Başarısız bir randomize (kısıtlama çakışması) sessizce eski değerleri bırakır; bu yüzdenif (!...)veyaassert(...)kullanmak şarttır.randcalanlarını dar tutun. Geniş bit genişliği, tüm değer uzayının izlenmesi nedeniyle performansı ciddi şekilde düşürür.randc, tüm değerler tükenene kadar tekrar üretmez; bu yüzden "her değeri bir kez gör" tipi senaryolarda idealdir.void'(...)cast'i ile dönüş değerini yok saymak yalnızca randomize'ın başarılı olacağından emin olduğunuz durumlarda tercih edilmelidir.std::randomize, hızlı testlerde veya sınıf tanımlamadan birkaç değişkeni rastgeleleştirmek istediğinizde pratiktir, ancak büyük doğrulama ortamlarında sınıf tabanlı yaklaşım daha sürdürülebilirdir.- Tekrarlanabilirlik (reproducibility): Rastgele üretim aslında sözde rastgeledir; aynı tohum (seed) ile aynı simülasyon birebir aynı değerleri üretir. Bir hata bulduğunuzda simülatörün yazdırdığı seed'i not alıp (
+ntb_random_seed=,-svseed,+UVM_SEEDgibi araca göre değişen seçeneklerle) hatayı yeniden üretebilirsiniz. "Rastgele test tekrar edilemez" düşüncesi bir efsanedir. - Rastgele sayı üreteci (RNG) her nesneye ve her sürece (thread) ayrı bağlıdır. Bu yüzden
pkt.randomize()ilepkt2.randomize()birbirinden bağımsız dizilere sahiptir;pkt2üzerindekirandcdöngüsüpkt'nin çağrılarından etkilenmez. Sınıf tanımına yeni birrandalan eklemek bile dizileri değiştirebilir; bunun içinsrandom(seed)metoduyla nesne tohumunu sabitleyebilirsiniz.
rand ve randc Yan Yana
rand |
randc |
|
|---|---|---|
| Tekrar | Aynı değer art arda gelebilir | Tüm değerler bitmeden tekrar etmez |
| Değer uzayı | Sınırsız (tipin tamamı) | Küçük tutulmalı (tipik ≤ 8–16 bit) |
| Kısıtlama | Her tür constraint | inside gibi basit kısıtlar; dist kullanılamaz |
| Çözüm sırası | Diğer değişkenlerle birlikte | Önce çözülür, sonra rand'lar ona göre ayarlanır |
| Tipik kullanım | Veri, adres, uzunluk | Kanal numarası, port seçici, "her birini bir kez dene" senaryoları |
| Bellek maliyeti | Düşük | Değer uzayı kadar (izleme listesi) |
Benzetme:
randbir zar atmaktır: 6 kez attığınızda üçü 4 gelebilir.randcise 1'den 6'ya numaralı kartları karıştırıp sırayla çekmektir: 6 çekişte her kart bir kez çıkar, deste bitince yeniden karıştırılır.
Sık Yapılan Hatalar
- Dönüş değerini yok saymak:
pkt.randomize();tek başına yazıp başarısızlığı görmezden gelmek, eski değerlerle sessizce devam etmek demektir.if (!pkt.randomize()) $fatal(...)veyaassert(...)kullanın. randcdeğişkenedistyazmak: Derleme hatasıdır;randczaten eşit dağılım garantisi verir.- Geniş
randcalanı:randc bit [31:0] addr;4 milyar değeri izlemeye çalışır; simülasyon belleği ve hızı çöker.randc'yi dar alanlara saklayın. randolmayan alanın rastgele değişmesini beklemek:idvecountgibi alanlarranddeğildir;randomize()onlara dokunmaz. Bu bilinçli bir tercihtir (kimlik rastgele olmamalı).- Her çağrıda yeni nesne yaratmayı unutmak / gereksiz yaratmak: Aynı nesneyi tekrar tekrar
randomize()etmek geçerlidir ve ucuzdur; ancak nesneyi bir kuyruğa koyup sonra tekrarrandomize()ederseniz kuyruktaki "eski" paket de değişir (handle paylaşımı). Kuyruğa koyacaksanız yeni nesne yaratın veya kopya alın.
Kendinizi Deneyin
prioalanınırandc bit [1:0]yapın (4 değer) ve 12 çağrıda üç tam döngü gördüğünüzü doğrulayın.pkt2.randomize()yerinepkt.randomize()ileprioyazdırın;pkt'nin kendirandcdöngüsünün bağımsız ilerlediğini gözlemleyin.- Simülatörünüzün seed parametresini bulun ve aynı seed ile iki kez çalıştırın: çıktılar birebir aynı mı? Seed'i değiştirip tekrar bakın.
std::randomize(x, y) with { x inside {[1:100]}; y < x; }içiney > 0;ekleyin vey'nin artık negatif olamadığını gösterin. Önceki hâldeyneden çok büyük negatif değerler alabiliyordu?
Hızlı Kontrol: randc bit [2:0] prio için 8 ardışık randomize() çağrısında aynı değer iki kez gelebilir mi?
Hayır. randc, 0–7 arasındaki 8 değerin her birini bir kez üretmeden tekrar etmez. Dokuzuncu çağrıda yeni bir rastgele permütasyon başlar; ancak 8. ile 9. değer tesadüfen aynı olabilir (bir döngünün sonu ile diğerinin başı).