0
\$\begingroup\$

I'm getting started with Verilog, using FSM patterns. I'm finding that something I keep needing to do is to wait in a particular state until I see a leading edge of a (much slower) signal.

What I always end up doing is something like this:

module (... syncsignal input ...)
reg oldsyncsignal;

case (state)
  STATE_WAIT:
  begin
    if (syncsignal && !oldsyncsignal)
    begin
      state <= STATE_CONTINUE;
    end
    oldsyncsignal <= syncsignal;
  end
end

I'm becoming less and less convinced this is a good idea, partly due to various edge cases (what happens if syncsignal goes high when I'm not in STATE_WAIT?), and partly because I haven't seen anything like it in other code I've seen.

This must be a requirement which comes up a lot. What's the standard way to do this?

I'm using the Warp dialect of Verilog for the Cypress PSoC devices.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

(what happens if syncsignal goes high when I'm not in STATE_WAIT?)

That is a more matter of specification. What are you building? What do you want to happen when?

What you have build is a that the rising edge is only detected when you are in the WAIT state.

If that is not what you want you should make an independent edge detection which remembers that there was at some time a rising edge. You then also have to somehow signal that, yes you have seen the rising edge and the 'there-was-a-rising-edge' flag can be cleared again:

reg there_was_a_rising_edge;
always @(posedge clock or negedge reset_n)
begin
   if (!reset_n)
      there_was_a_rising_edge <= 1'b0;
   else
   begin
      if (syncsignal & !oldsyncsignal)
         there_was_a_rising_edge <= 1'b1;
      else
         if (state==STATE_WAIT)
            there_was_a_rising_edge <= 1'b0;
   end // clocked 
end // always

....
   case (state)
   STATE_WAIT:
      if (there_was_a_rising_edge  )
         state <= STATE_CONTINUE;
   ...

But that is not the end of it.
(Here is where a experienced HDL designer earns her/his pay) The above code is such that if a new rising edge comes just when the FSM is in the WAIT_STATE, then there_was_a_rising_edge signal does not get cleared. Thus it will remain set for the next time you get into that wait state.

If that is NOT what you want you have to change the order of the conditions:

      if (state==STATE_WAIT)
         there_was_a_rising_edge <= 1'b0;
      else
         if (syncsignal & !oldsyncsignal)
            there_was_a_rising_edge  <= 1'b1;

Now the there_was_a_rising_edge signal always gets cleared in the WAIT_STATE and any rising edge arriving just at that time is ignored/lost.

\$\endgroup\$
1
\$\begingroup\$

Normally, you wouldn't do that kind of edge detection directly inside the state machine. Instead, you'd have a separate process for that:

reg oldsyncsignal;
always @(posedge clock) oldsyncsignal <= syncsignal;
wire syncsignal_edge = syncsignal & !oldsyncsignal;

Then, in your state machine:

STATE_WAIT: begin
  if (syncsignal_edge) begin
    ...
  end
end

Of course, if an edge comes along when you're not in STATE_WAIT, you'll miss it. Sometimes that's important, sometimes not so much. It depends on the specific application.

\$\endgroup\$
2
  • \$\begingroup\$ I had completely not thought of doing that! That's a big improvement, thanks. \$\endgroup\$ Nov 25, 2019 at 18:33
  • \$\begingroup\$ If it IS important to react to every incoming edge, you may want something called a "counting semaphore". I can add that to my answer if you need it. \$\endgroup\$
    – Dave Tweed
    Nov 25, 2019 at 19:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.