Menu

Verilog Module Instantiation: Alt Modülleri Bağlama

Bir modülü diğerinin içinde nasıl instantiate edilir, adlandırılmış ve positional port bağlantıları arasındaki fark ve gerçek tasarımları kurmak için kullanacağın çoklu instance kalıpları.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Modüller İçinde Modüller

Bir Verilog tasarımı bir modüller ağacıdır. Üst seviye modül (testbench'in veya bir chip'in üst seviye sarmalayıcısı) daha düşük seviye modülleri instantiate eder, onlar da daha düşük seviyeleri instantiate eder, ta vendor tarafından sağlanan gate primitive'lerine kadar. Instantiation bu iç içe geçirme için sözdizimidir.

Şekli zaten gördün - Your First Module bölümünde kullandık:

and_gate dut(.a(a), .b(b), .y(y));

Bu satır and_gate'in tek bir instance'ını çıkarır, dut olarak adlandırır ve portlarını yerel sinyallere bağlar. Her parçayı açalım.

Bir Instantiation'un Şekli

module_name instance_name (port_connections);
  • module_name, projedeki bir yerdeki module tanımlamasındaki adla eşleşmelidir. Verilog büyük/küçük harf duyarlıdır.
  • instance_name, seçtiğin bir etikettir - genellikle bu instance'ın oynadığı rolü tanımlayıcıdır. Onu hiyerarşik path'lerde ve waveform görünümlerinde kullanacaksın.
  • port_connections, instance'ın portlarını yerel sinyallere bağlar. Bunu yazmanın iki yolu vardır.

Adlandırılmış Port Bağlantıları (Bunları Kullan)

Adlandırılmış form şöyle görünür:

my_module instance_name(
    .clk    (clk),
    .reset  (reset_n),
    .data_in(in_bus),
    .data_out(out_bus),
    .valid  (out_valid)
);

Her .port(signal) çifti "bu instance'ın port adlı portunu signal adlı yerel sinyale bağla" der. Sıra önemli değildir. Modülün tanımına yeni bir port eklersen, mevcut instantiation'lar yeni porta bir varsayılan verdiğin veya her siteyi güncellediğin sürece bozulmaz.

İki pratik not:

  • Port adı (parantezlerin solu) modülün tanımıyla tam olarak eşleşmelidir.
  • Sinyal adı (parantezlerin içi) instantiation'un yaşadığı her yere yereldir - genellikle üst modül.

Bir port bağlı değilse, içini boş bırak: .optional_port(). Sinyal instance içinde dalgalanır (z). Birkaç sentez aracı uyarır; çoğu kabul eder.

Positional Port Bağlantıları (Bunlardan Kaçın)

Daha kısa form sinyalleri port liste sırasında listeler:

my_module instance_name(clk, reset_n, in_bus, out_bus, out_valid);

Bu daha kısadır ama kırılgandır. Modülün port listesini yeniden sırala (gerçek bir refactor) ve her positional instantiation sessizce yanlış bağlanır. Port listesi tam olarak bir veya iki üyeye sahip değilse ve değişmesi olası değilse positional'a uzanma.

Hala kabul edilebilir olduğu yer: port sırasının API'nin bir parçası olduğu küçük yardımcı modüller. İki girişli bir gate positional olarak iyidir. 30 portlu bir memory kontrolörü sorun istiyor.

Tam Bir Hiyerarşik Örnek

Bu gerçek bir üç seviyeli hiyerarşi: testfull_adder → iki half_adder instance. Her instance, half_adder içindeki gate'lerin kendi kopyasına sahiptir; sentez aracı instantiation başına bir devre çıkaracaktır.

Aynı Modülün Birden Çok Instance'ı

Aynı modülü birkaç kez instantiate ettiğinde, her instance bağımsız donanım'dır. State paylaşmazlar. Gate paylaşmazlar. Her instance'ı tasarımdan çıkarılmış yeni bir kopya olarak hayal et.

adder add0(.a(a0), .b(b0), .sum(s0));
adder add1(.a(a1), .b(b1), .sum(s1));
adder add2(.a(a2), .b(b2), .sum(s2));
adder add3(.a(a3), .b(b3), .sum(s3));

Bunlar paralel çalışan dört ayrı adder. adder bir register içerseydi, her instance kendi state'i ile o register'ın kendi kopyasına sahip olurdu.

generate Döngüleri: Tekrarlanan Donanım Çıkarma

Dört instance'ı elle yazmak iyidir. 64 yazmak sıkıcıdır. generate bloğu, elaborator'ın yazımı senin için yapmasına izin verir:

Üç yeni sözdizimi parçası:

  • genvar i, generate'te kullanılabilen bir döngü değişkeni tanımlar. Bir çalışma zamanı sinyali değildir - yalnızca elaboration zamanında var olur.
  • generate ... endgenerate döngüyü sarar. Bazı araçlar açık generate anahtar sözcüğü olmadan generate döngülerini kabul eder, ama yazmak niyeti açık hale getirir.
  • begin : invert_loop, generate scope'unu etiketler. Etiket her üretilen instance'ın hiyerarşik adının bir parçası olur (dut.invert_loop[0].u_inv, dut.invert_loop[1].u_inv vb.).

Sentezleyici döngüyü açar ve bit_inverter'ın WIDTH kopyasını üretir. Her kopya bağımsız donanımdır.

Instantiation'da Parametre Override'ları

Modülün parametreleri varsa, modül adı ile instance adı arasında #(.PARAM(value)) ile override edebilirsin:

counter #(.WIDTH(16)) c16 (.clk(clk), .count(out16));
counter #(.WIDTH(32)) c32 (.clk(clk), .count(out32));

İki instance da aynı counter kaynağını kullanır ama farklı genişliklere sahiptir. Sözdizimini Parameters bölümünde kapsadık; temiz şekilde instantiation'a oturur.

Hiyerarşik Adlar

Bir hiyerarşin olduğunda, her sinyal bir hiyerarşik path'e sahiptir:

test.dut.ha0.sum

Bu şöyle okunur: test modülünde, dut instance'ının içinde, ha0 instance'ının içinde, sum adlı sinyal. Bu path'leri waveform viewer'larda, hata mesajlarında ve ara sıra bir testbench'ten bir alt modüle derinden uzanan $display çağrısında göreceksin:

$display("dahili carry1 = %b", dut.carry1);

Bu tür hiyerarşik referanslar yalnızca testbench'ler ve debug içindir - sentezlenebilir RTL diğer modüllere uzanmaz.

Yaygın Hatalar

Port adı uyuşmazlığı. .clk_in(clk), yerel clk'yi clk_in adlı bir porta bağlar. Modülün portu aslında clk ise, parser sana söyleyecektir (bazı araçlar diğerlerinden daha net).

Bir porta genişlik uyuşmazlığı. 4 bitlik bir sinyali 8 bitlik bir porta bağlamak sessizce zero-extend eder; tersi sessizce truncate eder. Çoğu araç uyarır; uyarıları görmüyorsan, daha sıkı bak.

Parametre # unutmak. counter (.WIDTH(8)) c(.clk(clk)) bir override gibi görünür ama değildir - parser (.WIDTH(8))'ı bir port bağlantısı olarak ele almaya çalışır ve başarısız olur. Doğru: counter #(.WIDTH(8)) c(.clk(clk)).

Instance adını yeniden kullanmak. Aynı scope'ta iki instance aynı ada sahip olamaz. Hata mesajı genellikle nettir; seni yakalayan kopyala-yapıştır cazibesidir.

Sırada Ne Var

Artık modülleri gerçek bir hiyerarşiye bağlayabilirsin. Bir sonraki doc Verilog'un yapısal tarafını tamamlar - Continuous Assignment - ve ilk bölümden beri özgürce kullandığımız assign ifadesine daha derinden iner.

Sıkça Sorulan Sorular

Verilog'da bir modül nasıl instantiate edilir?

Modül adını, sonra bir instance adını, sonra parantezli bir port bağlantı listesini yaz: my_module instance_name(.port(signal), ...);. En yaygın stil adlandırılmış bağlantıları kullanır (.port(signal)), sıralamadan bağımsız olarak port adına göre eşleşir. Daha kısa positional stil (my_module instance(signal1, signal2)) port liste sırasına bağlıdır ve sürdürmek tehlikelidir.

Adlandırılmış ve positional port bağlantıları arasındaki fark nedir?

Positional bağlantılar sinyalleri modülün port listesiyle aynı sırada listeler - ilk sinyal ilk porta, ikinci ikinciye ve böyle devam eder. Adlandırılmış bağlantılar .port_name(signal_name) kullanır, ada göre eşleşir. Adlandırılmış uzundur ama port yeniden sıralamaya karşı bağışıktır ve çağrı sitesinde kendisini belgeler. İki veya üç port ötesindeki her şey için adlandırılmış kullan.

Aynı Verilog modülünü birden çok kez instantiate edebilir misin?

Evet - tüm mesele bu. Her instance kendi state'ine sahip bağımsız donanımdır. Bir adder modülün varsa, onu bir SIMD biriminde 64 kez instantiate edebilirsin, her biri farklı girişlerle. generate döngüsü, instance'lar benzer ve indekslendiğinde kanonik sözdizimidir.

Verilog'da generate block nedir?

generate ... endgenerate, tekrarlanan donanımı çıkaran bir derleme zamanı yapısıdır. generate içindeki bir for döngüsü, gövdesindeki her ne ise onun N instance'ını oluşturur. generate elaboration zamanında, simülasyon başlamadan önce çalışır - çalışma zamanı döngüsü değil, sentezleyici için bir kod üreticidir.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA