4

I am implementing an FSM controller for a datapath circuit. The controller increments a counter internally. When I simulated the program below, the counter was never updated.

reg[3:0] counter;

//incrementing counter in combinational block
counter = counter + 4'b1;

However, on creating an extra variable, counter_next, as described in Verilog Best Practice - Incrementing a variable and incrementing the counter only in the sequential block, the counter gets incremented.

reg[3:0] counter, counter_next;

//sequential block
always @(posedge clk) 
   counter <= counter_next;

//combinational block
counter_next = counter + 4'b1;

Why doesn't the counter get incremented in the previous case? Anything I'm missing?

Community
  • 1
  • 1
SleepingSpider
  • 1,168
  • 4
  • 19
  • 35

1 Answers1

10

Ok. I'm assuming that you left some code out of your first example since that shouldn't even compile. However, I think I can elucidate the issue for you anyway.

In a block that looks like this:

always @(*) begin // or always @(counter)
    counter = counter + 4'b1;
end

there are two problems.

1) counter is never initialized. All 'reg' type variables are X at the start of simulation time so adding 1 to X is X.

2) This is what is considered a combinational loop. The block is sensitive to changes in 'counter' so even assuming that 'counter' was initialized to 0 the simulator would loop forever updating 'counter' and simulation time will never advance. i.e.

always block executes -> counter = 1
counter has changed
always block executes -> counter = 2
counter has changed
and so on...

If you were to put a $display statement in there you could see this loop occurring. Otherwise it'll just appear that the simulator is hung and no waves will be written.

The reason the 2nd example works is that you have a flip-flop breaking the combinational loop. At each clock edge 'counter' gets updated with the current value of 'counter_next'. Then the combinational block executes once (and only once) to calculate the new version of 'counter_next'.

You're still lacking an initialization of 'counter' through a reset clause or initial statement so for completeness.

reg [3:0] counter;
reg [3:0] counter_next;

always @(*) begin
   counter_next = counter + 1;
end

always @(posedge clk or negedge rst_l) begin
   if (!rst_l)
      counter <= 4'b0;
   else
      counter <= counter_next;
end
Brian Magnuson
  • 1,487
  • 1
  • 9
  • 15
  • Obviously, none of the codes will compile. I have left out a huge chunk of code as the only change I made to get it working was changing 'counter' on an edge-trigger. The register, 'counter' was initialized to 0 in both cases. – SleepingSpider Dec 29 '12 at 21:48
  • I didn't experience any 'loop' in my simulation incidentally. The 'counter' variable was never even updated. It was just fixed at 0. A loop would be unlikely as I compare the value of 'counter' against constant. – SleepingSpider Dec 29 '12 at 22:01
  • 1
    Fair enough. I'm not sure what a comparison elsewhere has to do with it. I don't have ready access to a simulator at the moment to confirm the (simulator) loop behavior, but the fact remains that the combinational loop is the problem with your first example. It basically states 'this set of wires represents X and X+1 simultaneously' There is no realizable hardware that can do this. Remember that Verilog is not imperative programming. You're describing hardware here. – Brian Magnuson Dec 29 '12 at 22:31
  • counter = counter + 4'b1; in a combinatorial statement is a loop. Likely never updated because the sensitivity list never triggered. This would be fine: always @(posedge clk) counter <= counter + 4'b1; – Morgan Jan 03 '13 at 14:44