I will demonstrate how NOT to implement edge detection and asynchronous inputs to a state machine (e.g. 'FDPORT' in the code snippet from post #3 in this thread), with three examples written in (pseudo) Verilog. The fourth example is a design which successfullyaddresses the problem.
Example #1: Straight input to a state machine using states for edge detection.
input clock, async_in;
reg [3..0] state = idle;
reg in_dly1, in_dly2, in_dly3, edge_flag;
reg edge_flag;
wire edge_detected;
always @(posedge clock)
case (state)
idle: begin // wait for a low level on asynchronous input async_in
edge_flag <= 0;
if (async_in == 0) state <= found_zero;
end
found_zero: if (async_in == 1) state <= found_edge;
found_edge: begin // rising edge found, set a flag, loop back to idle state
edge_flag <= 1;
state <= idle;
end
endcase
In example #1, the async_in input signal will propagate through to the inputs of the 4 bits of state registers at different times. This is because the interconnect delays are not matched -- and they cannot be matched. The delay mis-match can be more than a nano-second. If a transition of async_in occurs as the signal is being sampled, the odds are very very good that the various state machine bits will sample different values of async_in. If at least one state bit will see a '0' and at least one state bit sees a '1', then the state machine will fail. The normal symptom is 'lockup'.
我: 检测 if (async_in == 0) 时出现 async_in 的跳变, 导致state赋奇怪的值.
Example #2: Single-register logic for edge detection.
input clock, async_in;
reg [3..0] state = idle;
reg in_dly1, in_dly2, in_dly3, edge_flag;
reg edge_flag;
wire edge_detected;
always @(posedge clock) in_dly1 <= async_in; // delay input one clock cycle
assign edge_detected = async_in && !in_dly1; // async_in was '0', now '1', rising edge detected
always @(posedge clock)
case (state)
idle: begin // wait for a low level on asynchronous input async_in
edge_flag <= 0;
if (edge_detected == 1) state <= found_edge; // edge found!
end
found_edge: begin // rising edge found, set a flag, loop back to idle state
edge_flag <= 1;
state <= idle;
end
endcase
In example #2, the async_in input signal has the same problem as in example #1. It will still propagate through to the inputs of the 4 bits of state registers at different times, through the signal edge_detected. The single delay register for async_in is not sufficient.
Example #3: Two-register logic for edge detection.
input clock, async_in;
reg [3..0] state = idle;
reg in_dly1, in_dly2, in_dly3, edge_flag;
reg edge_flag;
reg edge_detected;
always @(posedge clock) in_dly1 <= async_in; // delay input one clock cycle
always @(posedge clock) edge_detected = async_in && !in_dly1; // async_in was '0', now '1', rising edge detected
always @(posedge clock)
case (state)
idle: begin // wait for a low level on asynchronous input async_in
edge_flag <= 0;
if (edge_detected == 1) state <= found_edge; // edge found!
end
found_edge: begin // rising edge found, set a flag, loop back to idle state
edge_flag <= 1;
state <= idle;
end
endcase
In example #3, it looks like the input to the state machine is properly registered and problem-free. Yes, but a slightly different problem has been created. async_in is still sampled at two different registers. As before, the propagation delay mismatch to these two registers will inevitably result in either missed edges (register in_dly1 sees async_in transition a cycle earlier than registeredge_detected) or double triggers (register edge_detected sees async_in transition a cycle earlier than register in_dly1). This two-register solution for async_in is not sufficient.
Example #4: Another two-register logic solution for edge detection.
input clock, async_in;
reg [3..0] state = idle;
reg in_dly1, in_dly2, in_dly3, edge_flag;
reg edge_flag;
wire edge_detected;
always @(posedge clock) in_dly1 <= async_in; // delay input one clock cycle
always @(posedge clock) in_dly2 <= in_dly1; // delay input a second clock cycle
assign edge_detected = in_dly1 && !in_dly2; // async_in was '0', now '1', rising edge detected
always @(posedge clock)
case (state)
idle: begin // wait for a low level on asynchronous input async_in
edge_flag <= 0;
if (edge_detected == 1) state <= found_edge; // edge found!
end
found_edge: begin // rising edge found, set a flag, loop back to idle state
edge_flag <= 1;
state <= idle;
end
endcase
In example #4, async_in is sampled at a single register: in_dly1, eliminating the problem of mismatched delays and disagreement between multiple sampling points. The output of in_dly1 is aligned to the clock, and is safe for distribution to clocked logic in the design -- including the multi-bit state machine. The standard static timing analysis tools apply for the output of register in_dly1. This two-register solution for async_in is sufficient for aligning this asynchronous input to the synchronous system.