Exams/ece241 2014 q5a和Exams/ece241 2014 q5b两道题要求我们设计一个单输入单输出Moore/Mealy状态机,输入是一个不定长的序列x(最低有效位即LSB先输入),输出z是输入x的补码。状态机还包括一个异步高电平复位,复位时停止工作,取消复位后开始工作。
求解问题之前我们先要找到问题所涉及的两个知识点:
首先是,关于补码的概念,大家可以参考阮一峰老师在2009年发布的关于2的补码这篇文章,从中我们可以知道一个求关于2的补码的简便方法(请注意,这不是定义方式):
- 第一步,每一个二进制位都取反,0变成1,1变成0。比如,00001000的相反值就是11110111。
- 第二步,将上一步得到的值+1。11110111就变成11111000。
其次是,在明白如何通过输入求其关于2的补码的输出之后,我们还需要留意一点——无论是
Moore型状态机(这里指采用现态确定输出的Moore型状态机),
还是Mealy型状态机(只能由现态确定输出,否则会出错),
在时序图上的输出都延迟对应位输入一个时钟周期,因为次态在当前时钟周期内确定后,在下一个时钟周期上升沿才同步到现态,而输出是跟随现态的。这样有助于我们对波形正确与否做出判断。
关于状态机中输出相对于输入的延迟,请参见博主“孤独的单刀”的这篇文章:【FPGA/IC】状态机FSM的各种写法(一段式、二段式、三段式、摩尔型Moore、米勒型Mealy)
了解了以上两个知识点后,再来分析问题本身。要求一个数关于2的补码,无非就是先将这个数整体取反再加一。但是对于一个LSB先输入(这一点很重要,后文分析)的可变长数来说,在不知道其具体长度的情况下,无法进行整体取反再加一的操作。
所以在这里应当转换思路,对于一个串行输入,要求其关于2补码,我们应当关心的是:对于输入的每一个位,与其对应的输出最终应当与输入保持相同还是相反?问题的关键就是输入数据第一次出现1的这个节点。
我用题中一个输入序列进行解释,LSB根据阅读习惯放在了右边,示意图如下:
在示意图中我们可以发现,在从右向左读取输入的过程中:
- 对于遇到输入1之前的所有输入0,对应输出均与输入保持一致,即输出为0;
- 第一次遇到1时,输出也与输入保持一致,即输出为1;
- 第一次遇到1之后的各个输入,无论是1还是0,输出均与对应输出相反。
也就是说,可以将第一次遇到输入1看作是一个节点。由此我们可以抽象出3个状态(这里先分析Moore状态机的情况),并由这三个状态判断对应位的输出。
- 首先是表示在第一次遇到输入1之前的状态,此时对于输入的每一位,其对应输出与输入保持一致,均为0,将其命名为
IDLE
状态; - 其次是第一次遇到1和其之后遇到0的状态,虽然第一次遇到1时输出与输入保持一致,但由于二者最终都输出1,所以这两个情况对于输出来说其实是一回事,将其命名为
S0
状态; - 最后是第n次(n >= 2)遇到1的状态,这时对应位输出与输入相反,为0,将其命名为
S1
状态。
要注意的是,上面所说的“对应位输出”在实际的波形图中相对于输入是延迟一个时钟周期的,不要看错(这也是第二个知识点所强调的)。这里还有一个疑问:这种方法确实能计算一个串行LSB先输入序列的关于2的补码,**如果输入序列中包含多个要求补码的序列,如何进行区分?**答案是——这个状态机区分不了,一旦进入IDLE
状态,后续所有输入都被认为是一个数中的具体位,除非进行复位,这也就是题中下面这句话隐含的意思了。
The circuit requires an asynchronous reset. The conversion begins when Reset is released and stops when Reset is asserted.
最终,我们给出Moore型状态机的状态转换图和与其对应的Verilog HDL:
module top_module (
input clk,
input areset,
input x,
output z
);
localparam IDLE = 3'b001, S0 = 3'b010, S1 = 3'b100;
reg [3:1] state;
reg [3:1] next_state;
always @(posedge clk or posedge areset)
if(areset)
state <= IDLE;
else
state <= next_state;
always @(*)
case(state)
IDLE : next_state = x ? S0 : IDLE;
S0 : next_state = x ? S1 : S0;
S1 : next_state = x ? S1 : S0;
endcase
assign z = state == S0;
endmodule
对于Mealy型状态机,则将第一次遇到输入1之前和第n次(n >= 2)遇到1看作是一种情况,因为此时他们的输出都为0,但是要注意这里的输出应当和输入有关,因此Mealy型状态机的状态转移图(实际上原题已经给出)和Verilog HDL如下:
module top_module (
input clk,
input areset,
input x,
output z
);
localparam A = 2'b01, B = 2'b10;
reg [2:1] state;
reg [2:1] next_state;
always @(posedge clk or posedge areset)
if(areset)
state <= A;
else
state <= next_state;
always @(*)
case(state)
A : next_state = x ? B : A;
B : next_state = B;
endcase
assign z = state[1] & x | state[2] & !x;
endmodule
至此,两道题(实际上是一个问题的两种描述方式)都已完成。
总结:这类问题就是逻辑上比较难以理解但是具体实现上非常简单的典型代表,遇到时一定要耐心思理顺问题的逻辑,不要害怕花时间思考。搞清问题的本质再着手进行状态机的设计,否则很容易把自己绕进去。