# CPLD is (sometimes) not incrementing counter

I have this simple program running on the Altera EPM240 that’s sometimes not doing the counter increment.

reg trigger_state;
reg [7:0] trigger_count;

always @ (posedge clk)
begin
if (trigger != trigger_state) begin
trigger_state <= !trigger_state;
trigger_count <= trigger_count + 1;
end
end


clk is running at 50 MHz, and the trigger signal in the picture at 1-2 kHz.

In the picture (trigger=6, trigger_state=4, trigger_count[1]=1, trigger_count[0]=2)

As you can see, trigger_state always follows the input signal (trigger), but the counter (2) is sometimes not incremented.

What could explain such a behavior?

• Where is "Trigger" coming from? Is it from an external signal or switch? If so, have you put any synchronisation on it? – Tom Carpenter Jan 21 '18 at 14:52

Fist of all:
Your trigger is coming in a-synchronous to the clock. You must first synchronize it before you can safely use it. The code for that is:

reg sync_trigger,safe_trigger;
always @(posedge clk)
begin
sync_trigger <= trigger;
safe_trigger <= sync_trigger;
end


The behavior your are seeing is because the hardware will more look like this:

if (trigger != trigger_state)
trigger_state <= !trigger_state;

if (trigger != trigger_state)
trigger_count <= trigger_count + 1;


As the trigger is a-synchronous you can have that one if is true and the other is false. Thus one is acted up and the other not. In fact it is implemented for every BIT of your counter so some bits may increment and others not.

Post thought:
Your trigger must be slower then your clock otherwise you will miss triggers!

Is the trigger input completely asynchronous to the 50MHz clock? If so, it probably violates the setup and hold time requirements from time to time. You need to synchronize it to the clock through at least two FFs before you use it in any decision making.

I can hear you saying, "But it's only used in one if statement. If the trigger_state gets updated, doesn't that guarantee that trigger_count gets updated, too?"

trigger_state and trigger_count represent a total of nine separate FFs, and each one of them has its own combinatorial logic to determine what its next state will be. Each one takes trigger as an input, and they each make an independent decision about what the value of trigger is. If you violate the setup/hold time requirements, you can no longer guarantee that those independent decisions will be consistent, leading to the behavior that you observe.

Note that this has nothing to do with metastability, although synchronizing asynchronous inputs mitigates that problem, too.

The problem is most likely meta-stability caused by using an asynchronous signal (your input trigger) to control your counter.

When bringing any signal in, you need to make sure it is synchronised to the clock signal using a multi-register synchroniser chain. Something like:

reg [1:0] triggerSync;
always @ (posedge clk) begin
triggerSync <= {triggerSync[0],trigger}
end


Then use triggerSync[1] in your code. The first register triggerSync[0] will synchronise the signal but may go metastable if the input changes as the clock is changing (setup/hold violation). The second register triggerSync[1] catches the metastable state and stops it propagating through the rest of your logic causing weird glitches.

Your logic itself also seems weird. I would suggest splitting it up into your edge detector logic, and your counter:

reg triggerDly;
wire triggerEdge;
always @ (posedge clk) begin
triggerDly <= triggerSync[1];
end
assign triggerEdge = (triggerDly != triggerSync[1]);

always @ (posedge clk) begin
if (triggerEdge) begin
trigger_count <= trigger_count + 1;
end
end


Splitting everything up makes the code easier to follow and debug. You can also easily modify the edge detector logic to be rising edge, falling edge, or both. You might as well also make the edge detector a submodule as its a useful bit of code to reuse.

• It basically impossible to observe metastability events at 50 MHz on modern technology unless you've got a congested design with no slack. As Dave elucidates, this is probably a coherency issue. The solution is the same (sync FFs), but the problem is different. – jalalipop Jan 22 '18 at 14:41