Module Adalah Satuan Dasar Verilog
Segala sesuatu di Verilog berada di dalam sebuah module. Module membungkus sepotong rangkaian: ia mendeklarasikan sinyal apa yang masuk, sinyal apa yang keluar, dan kabel/register/logika apa yang ada di antaranya. Setiap chip yang pernah kamu lihat adalah pohon module yang membuat instance module lain, terus sampai ke primitif gerbang yang disediakan vendor.
Bentuknya selalu sama:
module name(port_list);
// declarations: wire, reg, parameter
// body: assigns, instantiations, always blocks
endmodule
Kita akan membangun module lengkap secara bertahap.
Module Berguna Terkecil: Gerbang AND Dua Input
Kita butuh sesuatu yang sederhana dan berdiri sendiri. Gerbang AND dua input sangat cocok: dua input, satu output, satu baris logika.
Tekan Jalankan. Kamu akan melihat empat baris tabel kebenaran AND. Mari kita bahas setiap bagiannya.
Membaca Deklarasi Module
module and_gate(
input wire a,
input wire b,
output wire y
);
module and_gatemendeklarasikan module bernamaand_gate. Nama inilah yang dipakai module lain untuk membuat instance-nya.- Daftar di dalam tanda kurung adalah daftar port: sinyal-sinyal yang terlihat dari luar.
input wire a:aadalah port input; tipenyawire(digerakkan dari luar).output wire y:yadalah port output yang digerakkan oleh sesuatu di dalam module.
Kalau ingin ringkas, kamu bisa menulis input a alih-alih input wire a, karena arah port saja sudah membuat tipe default-nya wire. Tapi menulis secara eksplisit adalah kebiasaan yang layak dibangun. Port Module membahas semua bentuk port.
Isi Module
assign y = a & b;
Itu adalah penugasan kontinu. Artinya "setiap kali a atau b berubah, hitung ulang y sebagai AND bitwise dari keduanya". Tidak ada clock, tidak ada timing: hubungan itu selalu berlaku. Itulah logika kombinasional murni.
endmodule menutup blok. Module-nya selesai.
Testbench
Kamu tidak bisa menjalankan and_gate sendirian. Kamu butuh module kedua yang menggerakkan input-nya dan mengamati output-nya. Itulah testbench, dan menurut konvensi kita menamainya test, tb, atau <design>_tb.
module test;
reg a, b;
wire y;
and_gate dut(.a(a), .b(b), .y(y));
...
endmodule
Tiga hal yang perlu diperhatikan:
- Tidak ada daftar port. Testbench adalah puncak simulasi; tidak ada apa pun di luarnya.
reg a, bdanwire y. Input untuk design under test (DUT) bertiperegdi testbench karena kita menggerakkannya dari blok prosedural. Output-nya bertipewirekarena DUT yang menggerakkannya.and_gate dut(.a(a), .b(b), .y(y)). Ini adalah instantiasi. Kita membuat salinanand_gatedan menamainyadut(nama yang umum, dari "design under test"). Sintaks.a(a)artinya "hubungkan port bernamaapada instance ke sinyal lokal bernamaa". Instantiasi Module membahasnya lebih dalam.
Stimulus
initial begin
a = 0; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
a = 0; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
a = 1; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
a = 1; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
$finish;
end
Blok initial berjalan sekali saat simulasi dimulai. Di dalamnya:
a = 0; b = 0;menggerakkan input. Ini adalah penugasan blocking: urutannya penting, dan masing-masing terjadi sebelum yang berikutnya.#1memajukan waktu simulasi sebanyak 1 satuan. Kita butuh ini agarypunya waktu untuk stabil setelah input berubah. Tanpa#1,$displayakan mencetak nilaiyyang lama.$display(...)mencetak ke konsol simulasi. Format string-nya bekerja sepertiprintfdi C:%buntuk biner,%duntuk desimal,%huntuk heksadesimal,%tuntuk waktu simulasi.$finishmengakhiri simulasi. Tanpanya, simulator akan terus memajukan waktu selamanya sambil menunggu event yang tidak pernah datang.
Coba Rusak Kodenya
Ubah module menjadi gerbang OR (ganti & dengan |) lalu jalankan lagi. Tabel kebenarannya berubah. Sekarang coba XOR:
Kerangkanya sama. Operatornya berbeda. Itulah inti logika kombinasional: deklarasikan port, tulis assign, coba semua kombinasi input, amati output-nya.
Module dengan Dua Output
Module bisa punya lebih dari satu output. Ini half-adder: dua input, sum dan carry-out:
Dua pernyataan assign itu berdampingan, tapi terjadi secara paralel: tidak ada "hitung sum dulu, lalu hitung carry". Keduanya selalu berlaku. Inilah konkurensi yang kita bahas di Hardware vs Software, dalam bentuk nyata.
Yang Sekarang Kamu Ketahui
Kamu sudah melihat seluruh kerangka file Verilog: module desain dengan port yang dideklarasikan dan isinya, ditambah module testbench yang membuat instance-nya, menggerakkan input-nya di blok initial, dan melaporkan hasilnya. Hampir setiap file sumber Verilog yang akan kamu baca mengikuti templat ini. Sisa bahasanya hanyalah mengisi logika yang lebih kaya, sinyal multi-bit, perilaku yang dikendalikan clock, dan testbench yang lebih besar.
Berikutnya: komentar dan gaya kode, agar module-mu tetap mudah dibaca saat makin besar.
Pertanyaan yang sering diajukan
Apa module Verilog yang paling sederhana?
Module Verilog terkecil yang sah hanyalah module name; endmodule, tanpa port dan tanpa isi. Module terkecil yang berguna adalah module dengan satu output: module and_gate(input wire a, input wire b, output wire y); assign y = a & b; endmodule. Itu sepotong logika kombinasional sungguhan yang bisa kamu masukkan ke desain apa pun yang lebih besar.
Bagaimana cara menjalankan module Verilog?
Kamu tidak bisa menjalankan module sendirian, karena module adalah deskripsi rangkaian, bukan program. Kamu menulis module testbench yang membuat instance desainmu dan menggerakkan input-nya, lalu compile keduanya dengan iverilog -o sim design.v test.v dan menjalankan vvp sim. Editor browser di halaman ini melakukan kedua langkah itu untukmu saat kamu menekan Jalankan.
Apa itu testbench di Verilog?
Testbench adalah module kedua, biasanya tanpa port, yang tugasnya menguji desainmu. Ia membuat instance desain, menggoyang input-nya lewat blok initial, mengamati output dengan $display atau $monitor, dan memanggil $finish saat selesai. Testbench tidak bisa disintesis; ia hanya ada untuk memverifikasi perilaku.
Kenapa kode Verilog-ku butuh $finish?
Karena hardware tidak pernah berhenti. Simulator berpura-pura waktu sedang berjalan, dan tanpa $finish yang eksplisit ia akan terus memajukan waktu selamanya sambil menunggu event baru. $finish memberi tahu simulator 'kita sudah selesai, keluar dengan rapi'. Di testbench, ini adalah baris terakhir blok initial: jalankan pengujiannya, lalu selesai.