Enables and Loads
Most registers spend most cycles holding still. A UART’s shift register only shifts when a bit has arrived; an accumulator only adds when told to. Verilog has a pattern for that, and it is one you have already seen.
The pattern
always @(posedge clk) begin
if (en)
q <= d;
// en low: q holds
end
No else, and in a clocked block that is correct: holding is what a flip-flop does. It is not a latch. Most library and FPGA flip-flops have a clock-enable pin, and the synthesiser wires en to it rather than building a mux in front of D, which is smaller and uses less power.
Reset first
With both a reset and an enable, reset wins:
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else if (en)
q <= d;
end
Reset, then enable. A register that could load data on the cycle it is being cleared is a bug waiting for its moment.
Load
A parallel-load register is the enable pattern with a name that says what the enable means:
always @(posedge clk) begin
if (load)
data_reg <= data_in;
end
Clear is not reset
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0; // reset: power-up or an external event
else if (clear)
q <= 1'b0; // clear: the design's own command during operation
else if (en)
q <= d;
end
Same destination, different roles. Reset establishes a known state once. Clear is a state machine zeroing a counter mid-run. Keep the order: reset, clear, enable.
Do not gate the clock
assign gated_clk = clk & en; // do not
It looks like it should work. It is an AND gate in a clock path, and section 2.4 explained what that does: if en changes while the clock is high, the gate makes a runt pulse that every downstream flip-flop takes as an edge, and static timing analysis, which assumes clocks are clean, cannot see it. Write the enable in the D-input logic as above. When power matters, the synthesis tool recognises the pattern and inserts an integrated clock-gating cell, the latch-and-AND cell built for the job, and that is the only gated clock that should exist in a design.
Practice Exercise
A 4-bit accumulator that adds data_in to its total when acc_en is high, clears when clear is high, and otherwise holds. Write it with a self-checking testbench.
Solution
`timescale 1ns / 1ps
`default_nettype none
module accumulator (
input wire clk, rst_n,
input wire clear, acc_en,
input wire [3:0] data_in,
output reg [3:0] acc
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) acc <= 4'b0000;
else if (clear) acc <= 4'b0000;
else if (acc_en) acc <= acc + data_in;
end
endmodule
module tb_accumulator;
reg clk = 0, rst_n, clear, acc_en;
reg [3:0] data_in;
wire [3:0] acc;
accumulator dut (.clk(clk), .rst_n(rst_n), .clear(clear), .acc_en(acc_en), .data_in(data_in), .acc(acc));
always #5 clk = ~clk;
initial begin
$dumpfile("tb.vcd");
$dumpvars(0, tb_accumulator);
rst_n = 0; clear = 0; acc_en = 0; data_in = 0;
@(negedge clk); rst_n = 1;
@(negedge clk);
if (acc !== 4'd0) $display("FAIL: expected hold at 0, got %0d", acc);
acc_en = 1; data_in = 4'd3; @(negedge clk);
data_in = 4'd2; @(negedge clk);
if (acc !== 4'd5) $display("FAIL: expected 5, got %0d", acc);
else $display("PASS: acc=%0d after 3+2", acc);
clear = 1; @(negedge clk); // clear with acc_en still high
if (acc !== 4'd0) $display("FAIL: expected 0 after clear, got %0d", acc);
else $display("PASS: cleared");
$finish;
end
endmodule
The test that matters is the last one: with clear and acc_en both high, clear wins, as the order of the else ifs says.
The next article is a catalogue: six register shapes, counters, shift registers and an accumulator, that make up most of the sequential logic in any design.