In many (older) serial communications protocols, each data byte is sent along with a start bit and a stop bit, to help the receiver delimit bytes from the stream of bits. One common scheme is to use one start bit (0), 8 data bits, and 1 stop bit (1). The line is also at logic 1 when nothing is being transmitted (idle).
Design a finite state machine that will identify when bytes have been correctly received when given a stream of bits. It needs to identify the start bit, wait for all 8 data bits, then verify that the stop bit was correct. If the stop bit does not appear when expected, the FSM must wait until it finds a stop bit before attempting to receive the next byte.
分析题目可知,我们需要设计的电路应满足以下特征:
1)功能:实现串行接收,分为等待开始(idle)、接收(byte_receive)、停止(stop)、错误(error)四个过程。
2)端口:输入时钟clk、复位信号reset、串行接收输入in,输出接收成功信号done。
3)触发方式:时钟上升沿触发,reset同步且高电平有效。
4)时序解释说明:
1>时钟上升时触发电路复位后,进入等待开始状态(idle)。
2>时钟上升时若处于等待开始状态(idle),则检测当前输入in,如果in为低则进入接收(byte_receive),否则仍在等待开始状态。
3>时钟上升时若处于接收(byte_receive)状态,则接下来8个时钟周期内的输入in为需要接收内容,8个时钟周期结束后若当前输入为高进入停止状态(stop),若当前输入in是低则进入错误(error)状态。
4>时钟上升时若处于停止(stop)状态,则检测输入in是否为低,若是则进入接收(byte_receive)状态;若否,则进入等待开始状态(idle)
5>时钟上升时若处于错误(error)状态,则检测输入in是否为高,若是则进入等待开始状态(idle);若否,则仍为错误(error)状态。
5>无论何时,只要处于停止状态,就输出done为高。
状态转移图如下:
这里需要注意的是,在接收状态,我们通常将八位比特的接收看作是八个不同的状态bit_0、bit_1、bit_2、...、bit_7,严格来说,他们确实是不同的状态,只是他们的转移关系只与时钟周期有关,与输入in无关。如果是八位比特的接收,我们可以写出八种状态的转移,但对于中间过程很多的情况,我们无法全部写出,这时候可以使用循环整型变量来计数当前的时钟周期,从而描述复杂状态的转移。以下代码采用变量i记录过去的个时钟周期数,而不是使用八个状态描述接收过程。
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
reg [1:0] state;
localparam idle=0,byte_receive=1,stop=2,error=3;
integer i;
always@(posedge clk)begin
if(reset)
state<=idle;
else begin
case(state)
idle:begin
state<=in?idle:byte_receive;
i<=0;end
byte_receive:begin
if(i==8)begin
state<=in?stop:error;i<=0;end
else
i<=i+1;end
stop:state<=in?idle:byte_receive;
error:state<=in?idle:error;
endcase
end
end
assign done = (state==stop);
endmodule
这里容易错误的是判断跳出的条件,下图为时序图,我们可以分析一下这一整个过程。
这里使用的是非阻塞赋值语句,这会产生一个类似滞后的效果。具体来讲,如果在当前上升沿触发对i累加,那这个被更新的i只能在下次上升沿触发时被使用,当前时刻被使用的i是上一次触发更新后的值(旧值)。这和阻塞赋值不同的是,每条语句都是并行执行的,没有先后的顺序,所有语句使用的都是旧值,本次更新的值会在下次触发时生效。
事实上,若在某时刻触发,那所有的值都会在当前时刻被更新,但是这一时刻做的事已经做过了,若想要这次更新的值被使用,只能等到下次触发。但是,因为所有的值都已经在这个时刻更新,所以所有类似于assign的赋值语句在这一个时刻就会生效,而不是等到下一个时钟上升沿。因为assign语句只要右侧值发生变化就会执行,也可以理解是所有时刻都触发的语句。
因此非阻塞赋值只是产生了类似滞后的效果,而不是真的滞后(对于时序逻辑是滞后,对组合逻辑不滞后)。除此之外,我们可以理解为i为8等同于“从i=0开始到现在时刻为止,过去了9个时钟循环”,那么接下来需要做的就是判断是否in为高,若是则为stop状态;若in为低,则进入错误状态。