Synthesizable vs non-synthesizable constructs in Verilog - Performance Comparison
When writing Verilog code, some parts can be turned into hardware, and some cannot.
We want to see how the time to run the code grows depending on which parts are synthesizable or not.
Analyze the time complexity of the following Verilog code snippet.
module example(input clk, input [3:0] in, output reg [3:0] out);
integer i;
always @(posedge clk) begin
for (i = 0; i < 4; i = i + 1) begin
out[i] <= in[i];
end
end
endmodule
This code copies each bit of input to output on every clock cycle using a loop inside a clocked block.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The for-loop unrolls into 4 parallel assignments every clock cycle.
- How many times: Once per clock cycle, repeating indefinitely.
Explain the growth pattern intuitively.
| Input Size (n) | Approx. Operations |
|---|---|
| 4 | 4 operations per clock |
| 8 | 8 operations per clock |
| 16 | 16 operations per clock |
Pattern observation: The number of operations grows linearly with input size.
Time Complexity: O(1)
This means the time to process is constant (one clock cycle) regardless of the input size.
[X] Wrong: "All Verilog loops run step-by-step in hardware like software loops."
[OK] Correct: Synthesizable loops are unrolled into parallel hardware, so they don't take extra time per iteration at runtime.
Understanding which Verilog constructs become hardware and how they affect timing is a key skill for designing efficient circuits.
"What if we replaced the for-loop with a while-loop that depends on a variable count? How would the time complexity change?"
Practice
non-synthesizable and used only for simulation?Solution
Step 1: Understand the role of
initialblocksinitialblocks run once at simulation start and are not synthesizable into hardware.Step 2: Compare with synthesizable constructs
alwaysblocks,assignstatements, and module instantiations describe hardware and are synthesizable.Final Answer:
Initial blocks (initial) -> Option BQuick Check:
initial= Non-synthesizable [OK]
initial blocks only simulate, never synthesize [OK]- Confusing
alwayswithinitial - Thinking
assignis non-synthesizable - Assuming module instantiations are simulation-only
always block sensitive to the positive edge of clock clk?Solution
Step 1: Identify correct edge sensitivity
Synthesizable clocked logic usesposedge clkornegedge clkto trigger on clock edges.Step 2: Check syntax correctness
always @(posedge clk)is correct;always @(clk)triggers on any change, which is not typical for clocked logic.Final Answer:
always @(posedge clk) begin ... end -> Option DQuick Check:
Useposedge clkfor clock edge sensitivity [OK]
posedge or negedge for clock edges [OK]- Using level sensitivity instead of edge sensitivity
- Mixing clock and reset signals incorrectly
- Omitting
beginandendfor multiple statements
reg [3:0] count; initial begin count = 0; #5 count = 4'b1010; end always @(posedge clk) begin count <= count + 1; end
Assuming
clk toggles every 1 time unit starting at 0.Solution
Step 1: Analyze
Theinitialblock effectinitialblock setscountto 0 at time 0, then to 10 at time 5 (delay #5).Step 2: Analyze
Thealwaysblock behavioralwaysblock incrementscounton every positive clock edge, but the non-blocking assignment means the update happens after the clock edge, so the delayed assignment at time 5 overrides the count value at that time.Final Answer:
Count stays 0 until time 5, then becomes 10 and increments every clock cycle -> Option AQuick Check:
initial#delay executes after clock edges [OK]
initial affects count after clock increments [OK]- Ignoring the
initialblock's delayed assignment - Assuming
countincrements from 10 immediately - Confusing blocking and non-blocking assignments
always @(posedge clk) begin
integer i;
for (i = 0; i < 4; i = i + 1) begin
data[i] = i;
end
endSolution
Step 1: Check variable declarations and loop usage
Declaringinteger iinside thealwaysblock is allowed for synthesis as loop variable.Step 2: Identify assignment type issue
Using blocking assignment=inside clockedalwaysblock causes unintended behavior; non-blocking<=is required for registers.Final Answer:
Using blocking assignment (=) instead of non-blocking (<=) inside always block -> Option CQuick Check:
Use non-blocking (<=) in clocked always blocks [OK]
- Thinking loop variables cannot be declared inside always blocks
- Believing for loops are non-synthesizable
- Ignoring difference between blocking and non-blocking assignments
sig is high during clock cycles, but also print the count value for debugging during simulation. Which approach correctly separates synthesizable and non-synthesizable constructs?Solution
Step 1: Identify synthesizable counting logic
Counting on clock edges must be in analways @(posedge clk)block using non-blocking assignments.Step 2: Identify non-synthesizable debugging code
Printing with$displayis simulation-only and must be placed in aninitialor separate simulation-only block.Final Answer:
Use analways @(posedge clk)block to increment count and aninitialblock with$displayto print count -> Option AQuick Check:
Separate hardware logic and simulation prints [OK]
initial, counting in clocked always [OK]- Trying to print inside synthesizable
alwaysblock - Using continuous assignment for counting
- Confusing tasks/functions for simulation prints
Ready to prove it?
Pass the test and this pattern moves unknown
