Different behavior between ternary operator and if-else inside an always@ block

Today we hit a bug where the following two blocks resulted in different behavior in terms of the whole program:

always@(posedge WF_CLK or negedge WF_BUTTON) begin
    // WF_BUTTON is active low
    if(WF_BUTTON)
        current <= next;
    else
        current <= INIT;
end

vs.

always@(posedge WF_CLK or negedge WF_BUTTON) begin
    // WF_BUTTON is active low
    current <= WF_BUTTON ? next : INIT;
end

I can provide the complete code and pin layout for debugging but I googled around and didn’t see any general Verilog related information saying these should behave different so just want to make sure there isn’t any subtle but known difference between them.

Hi Jling,

I would expect both code snippets to behave the same if you first take care of the contact bounce and synchronize the WF_BUTTON input signal. Running an asynchronous signal into a digital system is fraught with problems and unexpected behavior.

You can google for contact bounce, it is real and must be dealt with when being used with digital logic systems. Here is a good explanation.

Please see the DOs and DON’Ts section here. I will add asynchronous signal handling to the list. WebFPGA_book

Please see the example on the webfpga IDE website, “WF_blinky_with_switch.v”
https://beta.webfpga.io/dashboard

You can download it to your board to experiment. The example uses the WF_BUTTON to change the rate in which the WF_LED blinks. In the example there is a WF_switch_debounce module which cleans up the contact bounce and synchronizes the signal for use in the second always block.

As an experiment you can run the WF_BUTTON directly into that always block and download it to the board and you will see some erratic behavior.

Change line 32, ===> if (~WF_BUTTON).

 // create smaller counter when button pushed
  always @ (posedge WF_CLK)
     if (blink_cnt == 0)
       blink_cnt <= 4'b0001;
     else
        if (~WF_BUTTON)   // For experiment change this line
         blink_cnt <= { blink_cnt[2:0],blink_cnt[3]}; // rotate a one

Contact bounce will take milliseconds to settle. So we must also include some sampling point for the logic to eliminate the effects of the bounce. We use the WF_timer modules for this to create a pulse every 10ms. The debounce module uses this pulse as a sampling signal to clean up the bouncing signal and output a clean signal to be used in the digital logic block.

I would also remove the negedge from your always @ statement. It is not needed, and would not recommended a new user to verilog in using it unless they are fully aware of how the synthesis tools will resolve it. Designers will use that for asynchronous reset of a system and synchronous unreset. Special care is needed to make sure no timing violation can occur by design.

Mick

thx for the tips, as I noted, the whole circuit works in one case but not the other, which indicates the problem is not about debouncing.

This is one of the three always blocks implementing a FSM. (the states diagram of this particular FSM dictates that we don’t care about debouncing).

I’m wondering if there’s a way to dump the synthesized circuit in some way, so we can look at what’s the difference.

Jerry,

Since you are using asynchronous resets in your design I looked for RTL coding guide lines for synthesis of asynchronous resets. I couldn’t find exacting guide lines for LSE (lattice synthesis engine) just examples which all follow what I found from Xilinx.

On page 244,

If optional asynchronous control signals are modeled, the always block is structured as follows:

always @(posedge CLK or posedge ACTRL1)
begin
if (ACTRL1)
<$asynchronous part>
else
<$synchronous_part>
end

All tools vendors follow basic coding styles to infer the asynchronous connections to the flip flops. This is done for portability. The ternary operator isn’t being picked up with the LSE engine. In all my coding experience (from beginning of verilog) we have always used the if/else to infer the async reset functionality. It maybe possible to add a directive to help direct the compiler, but couldn’t find one for LSE. Synopsys compiler has many directives for reset styles and may support the ternary operator.

If you use the command line instead of the website to submit your code to webfpga, you will get back the binary file. There are open source tools which may tell you how it was synthesized. Or download the lattice tool chain and use their GUI. They have a tool which shows the schematic of the design.
So if the tool didn’t pickup that you wanted an asynchronous reset case, it probably made a real mess trying to apply multiple clocks to the flop.

Regarding your FSM handling the contact bounce, I am curious on your handing it. If you want to share it via our support page I would like to see it. If the asynchronous signal (WF_BUTTON) is an input to more than one flip flop then there are hazards such that each flop could acquire a different state due to the varying internal path delays, or timing violations of the setup or hold requirements.

Mick