Blok Dasar: D Flip-Flop
Semua hal di desain sinkron kembali ke satu potong hardware kecil: D flip-flop. Ia punya input clock, input data, dan output data. Di setiap tepi naik clock, ia menangkap nilai D dan menahannya sampai tepi berikutnya. Itu saja.
Di Verilog:
always @(posedge clk) begin
q <= d;
end
Tiga baris, satu flip-flop. <= adalah non-blocking, persis seperti perilaku flip-flop sungguhan (dibahas di Blocking vs Non-blocking). Sensitivity posedge clk-lah yang membuatnya sekuensial.
Tumpuk banyak flip-flop ini dengan logika kombinasional di antaranya, dan kamu bisa membangun rangkaian sinkron apa pun yang kamu inginkan.
Register dengan Reset
Desain sungguhan selalu butuh reset, yaitu cara untuk menaruh sistem ke state yang jelas saat dinyalakan atau saat diminta:
Itu adalah reset sinkron: kondisi reset dievaluasi di tepi clock seperti input lainnya. Synthesizer menghasilkan flip-flop dengan mux 2-ke-1 di input datanya: saat reset bernilai high, mux mengisi nol; selain itu ia mengisi d.
Untuk kebanyakan desain, reset sinkron adalah pilihan yang tepat. Timing-nya lebih sederhana, cocok untuk FPGA, dan sinyal reset bisa datang dari mana saja, tanpa butuh sumber sendiri yang selaras dengan clock.
Register dengan Enable
Sering kali kamu ingin register yang hanya diperbarui saat ada yang menyuruhnya. Pakai if di dalam blok yang dikendalikan clock, dan andalkan perilaku implisit "tahan nilai sebelumnya" dari else yang tidak ditulis:
Inilah "register dengan load enable" yang baku. Ia muncul di mana-mana: register konfigurasi, tahap pipeline yang hanya maju saat tahap berikutnya siap, counter yang bisa dijeda dan dilanjutkan. Tidak menulis else terakhir itu disengaja dan aman di dalam blok yang dikendalikan clock: flip-flop sudah mengingat nilai sebelumnya, jadi "tidak melakukan apa-apa" berarti "tahan".
Di blok kombinasional, kode yang sama akan menghasilkan latch. Aturannya berbeda, sintaksnya sama.
Counter
Counter adalah register yang nilai berikutnya adalah nilai saat ini ditambah satu:
Counter bertambah di setiap siklus clock saat enable bernilai high. Setelah 16 siklus, nilainya kembali ke 0 (karena kita mendeklarasikan 4 bit dan 15 + 1 overflow menjadi 0). Perputaran itu bawaan aritmetika N-bit dan persis seperti perilaku counter di hardware sungguhan.
Shift Register
Menumpuk flip-flop menghasilkan shift register. Triknya, lakukan semua pergeseran dalam satu pernyataan non-blocking:
Isinya adalah out <= {out[WIDTH-2:0], in}: sambungkan bit-bit bawah dari out saat ini dengan in yang baru, lalu berikan semuanya sekaligus. Karena non-blocking, RHS membaca out yang lama sebelum LHS diperbarui. Hasilnya adalah pergeseran N-bit yang bersih dalam satu siklus clock.
Inilah pola shift register dalam bentuk terkecilnya. Pola ini bisa dikembangkan menjadi LFSR, transmitter serial, deserializer, desain apa pun di mana data bergerak melewati rantai flip-flop di setiap clock.
Pipeline
Pipeline adalah rantai register yang dipisahkan oleh logika kombinasional. Setiap tahap memproses data dari tahap sebelumnya dan mengisi tahap berikutnya:
Tiga tahap, latensi tiga siklus, tapi ada hasil baru di setiap siklus begitu pipeline-nya penuh. Latensinya 3 clock karena data melewati tiga flip-flop; throughput-nya 1 operasi per clock karena ketiga tahap bekerja bersamaan pada input yang berbeda.
Begitulah desain berperforma tinggi mencapai target throughput-nya: buat tahap-tahapnya pendek, perdalam pipeline-nya, dan biarkan paralelisme yang bekerja.
Reset Asinkron (Saat Kamu Membutuhkannya)
Kadang kamu tidak bisa menunggu tepi clock untuk mengaktifkan reset: chip sedang dimatikan, clock-nya di-gate, watchdog eksternal sedang menarik jalurnya. Dalam kasus seperti itu:
always @(posedge clk or negedge reset_n) begin
if (~reset_n) q <= 0;
else q <= d;
end
Sekarang sensitivity list-nya punya tepi clock dan tepi reset. Flip-flop langsung merespons salah satunya. Menurut konvensi, reset_n aktif low (aktif saat bernilai low), itulah kenapa pengujiannya ~reset_n.
Reset asinkron punya konsekuensi: lebih sulit dianalisis timing-nya, bisa menyebabkan metastabilitas kalau pelepasannya tidak ditangani dengan hati-hati, dan tidak portabel di semua arsitektur FPGA. Pakai reset sinkron secara default, dan reset asinkron hanya kalau desainnya memang membutuhkannya.
Selanjutnya
Sekarang kamu bisa membangun jalur data sinkron apa pun. Dokumen berikutnya, Finite State Machine, menggabungkan register yang dikendalikan clock dengan pernyataan case untuk menghasilkan idiom FSM yang standar: andalan utama setiap kontroler, mesin protokol, dan blok pengambil keputusan di desain digital.
Pertanyaan yang sering diajukan
Apa itu logika clocked di Verilog?
Logika yang pembaruannya diatur oleh sinyal clock. Idiom standarnya adalah always @(posedge clk) target <= next_value;: di setiap tepi naik clk, target menangkap next_value. Satu baris itu menggambarkan sebuah flip-flop di hardware. Menumpuk banyak flip-flop dengan logika kombinasional di antaranya akan membangun counter, shift register, pipeline, pokoknya semua yang sinkron.
Apa perbedaan reset sinkron dan asinkron di Verilog?
Reset sinkron memakai always @(posedge clk) if (reset) ...: reset diambil sampelnya di tepi clock seperti input lainnya. Reset asinkron memakai always @(posedge clk or negedge reset_n) if (~reset_n) ...: blok terpicu oleh tepi clock atau saat reset diaktifkan, sehingga reset langsung berlaku. Reset sinkron adalah pilihan default; reset asinkron dipakai saat reset harus dijamin berlaku meskipun clock-nya mati.
Bagaimana cara membangun pipeline di Verilog?
Tumpuk beberapa tahap always @(posedge clk) stageN_reg <= stageN_combinational;: logika kombinasional setiap tahap mengisi register tahap berikutnya, dan setiap register menangkap di tepi clock yang sama. Hasilnya adalah pipeline di mana data baru masuk setiap siklus dan keluar N siklus kemudian, dengan throughput satu hasil per clock.
Apa itu shift register di Verilog?
Rantai flip-flop di mana output setiap flip-flop mengisi input flip-flop berikutnya. Setiap tepi clock menggeser semua bit satu posisi. Versi Verilog yang baku memakai penugasan non-blocking: out <= {out[N-2:0], in};. Satu baris itu membuat shift register N-bit yang mengambil satu bit per siklus dari in.