Electronic – Verilog odd behavior with multiple edge detect

clocksensitivityverilog

So I'm a newbie to Verilog. I decided to purchase a nice board (a Terasic DE0-CV) and teach myself some Verilog. And I'm seeing some strange behavior that I can't explain.

I lifted some code out of an example that drives the 7-seg LEDs. And I have them tied to a register. Whatever the register holds, the LEDs display.

reg [23:0]  mSEG7_DIG;
SEG7_LUT_6          u0  (   .oSEG0(HEX0),
                            .oSEG1(HEX1),
                            .oSEG2(HEX2),
                            .oSEG3(HEX3),
                            .oSEG4(HEX4),
                            .oSEG5(HEX5),
                            .iDIG(mSEG7_DIG) );

I made a simple counter to drive the register, like this:

always @(negedge KEY[3]) begin
    mSEG7_DIG <= mSEG7_DIG + 1;
end

And that works just fine. Click Key[3] on the board, the counter counts up from zero with each key click. Works like a charm. Sees the edge, increments the register, displays on the LEDs. So next, I wanted to make a way to clear the register to start back from zero. This is the idea I was thinking of.

always @(negedge KEY[3]) begin
    mSEG7_DIG <= mSEG7_DIG + 1;
end

always @(negedge KEY[2]) begin
    mSEG7_DIG <= 0;
end

This turns out to not be legal, since I have two always blocks trying to drive the same register. But you get where I'm going with this.

So I figure "Ok, I'll make them into one always block, then check the switch levels to see what we're trying to do, increment or clear."

So I did this to start.

always @(negedge KEY[3], negedge KEY[2]) begin
    mSEG7_DIG <= mSEG7_DIG + 1;
end

For whatever reason when I do this, the counter counts so fast you can't see it. I've tested it for hardware problems by doing this:

always @(negedge KEY[2]) begin
     mSEG7_DIG <= mSEG7_DIG + 1;
end

And that works fine too.

So what am I missing? Why does having two edge dependent signals in my always block's dependency list make it seem to be always true?

Edit: The hint from IanJ – "the synthesis tool doesn't want to make something sensitive to two different edges", and being able to see what synthesis is doing with my code in RTL viewer – this allowed me to solve the problem.

The problem – imagine a CLK line going into a D type flip flop. And you want two signals to control it. But I didn't specify HOW. The synthesis tool would have to be a mind reader to understand my intention. You have to explicitly say how you're triggering. This solves the problem. Full points to IanJ for the tip – that led to this solution.

wire change;

assign change = ~KEY[2] | ~KEY[3];

always @(posedge change) begin
    if(~KEY[2]) begin
        mSEG7_DIG <= mSEG7_DIG + 1;
    end
    else begin
        mSEG7_DIG <= 0;
    end
end

Best Answer

Learning Verilog is one thing. It also good to learn digital design. Modern designs use clocks. The synthesis tool doesn't want to make something sensitive to two different edges.

You should us a clock and either count or reset depending on the input. You should digitally detect the edge to count up.


  always @(posedge clock) begin
    KEY3_d1 <= KEY[3];
    if (!KEY3_d1 && KEY[3]) begin
      mSEG7_DIG <= mSEG7_DIG + 1;
    else if (KEY[2]) then
      mSEG7_DIG <= 0;
    end
  end