The interviewer asks for a loadable counter with enable and reset, then presses on the corners: load and enable together, the wrap cycle, and reset style. The question fuses two common questions into one whiteboard exercise.
What is really under test is control priority and state coverage. The candidate must fix an order for simultaneous controls and prove every state is reachable.
Priority is reset first, then load, then enable. Simultaneous load and enable loads; reset beats both. Document the order in the code so review never re-derives it.
Reset only what the protocol needs, usually the control state. Datapath registers that are always written before use need no reset, which saves area and eases timing.
Control | Priority | Behaviour |
|---|---|---|
Reset | Highest | Count returns to zero on the next edge (synchronous) or at once (asynchronous assert). |
Load | Middle | Count takes the load value, even with enable high. |
Enable | Lowest | Count advances by one; low holds the value with the clock still running. |
Terminal count | Status, not control | High exactly while count holds all ones, one cycle before the wrap. |
Read right to left from the flop: whatever is closest to D overrides everything to its left.
load = 1: the load mux passes load_val, whatever en chose.
load = 0: the en mux decides between count + 1 and holding count.
rst_n low: clears the flop directly, so reset never sits in the D path.
Either reset style is acceptable if applied per the project's reset policy. This counter takes an active-low asynchronous reset with synchronized release assumed at the domain level, the common reusable form.
Synchronous or asynchronous reset, and why? Async-assert/sync-release works without a running clock and keeps reset off the D-path, but the release must be synchronized to avoid recovery/removal failures. Synchronous reset needs a running clock and adds logic to the D-path, but is simpler for timing and STA and has no recovery/removal checks.
Load and enable arrive together with different values. Which wins? Load, by the documented priority. The testbench below asserts it with enable held high during the load.
How do you prove the wrap is correct? Walk the full state space: sixteen enables from reset must visit every value exactly once, with terminal count high only on fifteen.
A weaker candidate resets every register including the datapath. Unneeded resets add routing and load to the reset tree and block retiming across the boundary.
Another builds the enable by gating the clock. Clock gating needs a glitch-free cell and DFT handling; a synchronous enable on the data path holds state with none of that.
A third flags terminal count one cycle late, on the wrap itself. Terminal count means the last value is present now, so downstream logic can act in the same cycle as the wrap.
Ten. Load outranks enable, so the advance is skipped for that cycle.
Terminal count is high now. The next value is zero; fifteen is the last state, not the wrap itself.
A loadable counter plus the testbench that checks priority, wrap, terminal count, and full-state coverage.
module load_cnt #(parameter int W = 4) (
input logic clk, rst_n, en, load,
input logic [W-1:0] load_val,
output logic [W-1:0] count,
output logic tc
);
assign tc = (count == {W{1'b1}}); // high on the last value, before the wrap
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) count <= '0; // reset wins over everything
else if (load) count <= load_val; // load beats enable
else if (en) count <= count + 1'b1; // enable advances, low holds
endmodule