本题在状态机的实现上并不算复杂,只要能够通过题目描述画出状态转移图就能够得出标准的三段式状态机,只是其中有两个关键点需要特别注意,因而作此纪录,希望大家批评指正!
本题涉及两个关键点:
- 监视输入x的时机,即如何为输入x设置相关状态
- 理解“在两个周期内输入y为1则……”这句话的实现
第一个关键点题中有多次指明:
- then after the next clock edge the FSM has to set the output f to 1 for one clock cycle. Then, the FSM has to monitor the x input.
- hint : The FSM does not begin to monitor the x input until the cycle after f is 1.
题目中称若复位则保持在A状态,取消复位后,将输出一个时钟周期的f = 1
,在这之后,才进入x的监视状态,这就说明:
- 需要一个状态来表示“输出了f = 1”,之后才进入x相关的状态,我们将这个状态称为“F”
- 对于x的序列判断,需要一个IDLE状态表示x的默认状态(或称首先接收到0的状态)而非直接从“F”状态判断输入
x = 1
进而判断x = 0
,这样当x的输入序列不符合101
需要跳回到某个状态重新开始时,就有了一个“立足点”。不使用“F”状态的原因是:F状态负责f的输出,如果跳回这个状态会导致f输出1,而f只在状态机脱离复位状态的下一个时钟周期上升沿输出1个时钟周期的1
第二个关键点在于:对于涉及次数的状态跳转,使用计数器还是直接将其化为状态本身?
我的观点是,如果转换后的状态不是很多,则优先采用转换为状态的方法。题目中的描述“在两个周期内若输入y = 1
”很自然地联想到可以创建一个计数器对y的输入进行计数,但是在某些情况下这会带来计数器溢出的问题,如果再结合标志位,则会使原本描述简洁的状态机在形式上变得复杂。因此,将x接收到101
序列后对输入y两周期内是否出现1的判断转化为相应的状态,得到状态转移图如下:
如图所示,当x输入序列101
之后,g持续输出1,之后进入到输入y的判断中,如果y在第一个状态或者第二个状态跳转到“已经输入1”状态(Y_1),则g继续保持1输出,直到复位发生;若y时钟没有输入1,则先跳转到“第一次没有输入1”状态(Y_0)后跳转到“第二次没有输入1”状态(Y_00),此时g持续输出0,直到复位发生。
While maintaining g = 1 the FSM has to monitor the y input.…
But if y does not become 1 within two clock cycles, then the FSM should set g = 0 permanently (until reset).
因此,只要明白了以上两个关键点,此题的思路就不难分析。最后,给出本题的Verilog HDL:
module top_module (
input clk,
input resetn, // active-low synchronous reset
input x,
input y,
output f,
output g
);
localparam A = 0, F = 1, X_IDLE = 2, X_1 = 3, X_10 = 4, X_101 = 5, Y_0 = 6, Y_1 = 7, Y_00 = 8;
reg [3:0] state;
reg [3:0] next_state;
always @(posedge clk)
if(~resetn)
state <= A;
else
state <= next_state;
always @(*)
case(state)
A : next_state = F;
F : next_state = X_IDLE;
X_IDLE : next_state = x ? X_1 : X_IDLE;
X_1 : next_state = x ? X_1 : X_10;
X_10 : next_state = x ? X_101 : X_IDLE;
X_101 : next_state = y ? Y_1 : Y_0;
Y_1 : next_state = Y_1;
Y_0 : next_state = y ? Y_1 : Y_00;
Y_00 : next_state = Y_00;
endcase
assign f = state == F;
assign g = state == X_101 || state == Y_0 || state == Y_1;
endmodule