本系列内容来自于知乎专栏,链接如下:https://zhuanlan.zhihu.com/c_1131528588117385216 本系列文章将和读者一起巡礼数字逻辑在线学习网站 HDLBits 的教程与习题,并附上解答和一些作者个人的理解,相信无论是想 7 分钟精通 Verilog,还是对 Verilog 和数电知识查漏补缺的同学,都能从中有所收获。
Problem 133 One-hot FSM / Fsm onehot
前面Problem 125 Simple one-hot state transtion 3应该已经介绍过独热one-hot编码方式。这是一种生成逻辑最为简单的编码方式。在这里不详细解介绍了。
牛刀小试
下图是具有1个输入和2个输出的状态转移图:
假设该状态机使用独热码,state[0]到state[9]分别对应于状态S0到S9。图中没有标注的输出均为0。
下面请实现状态机的状态转移逻辑和输出逻辑(不需要实现状态触发器)。模块的输入是当前状态state[9:0],输出下个状态next_state[9:0]和电路的两个输出out1和out2。请通过假设输入独热码入去推导状态转移的逻辑方程。(提供的测试平台将使用非独热码的输入进行测试,以确保你没有尝试做更复杂的事情)。
小提示:可以通过查看状态转换图的转移的路径来导出独热码状态转换逻辑的逻辑方程式。
解答与分析
拿到题目很多人估计看了一眼觉得简单,就是不就是个简单的状态机么?只不过采用了独热码,然就写成了这种:
module top_module(
input in,
input [9:0] state,
output [9:0] next_state,
output out1,
output out2);
reg [9:0] nextstate;
parameter s0 = 10'b0000000001;
parameter s1 = 10'b0000000010;
parameter s2 = 10'b0000000100;
parameter s3 = 10'b0000001000;
parameter s4 = 10'b0000010000;
parameter s5 = 10'b0000100000;
parameter s6 = 10'b0001000000;
parameter s7 = 10'b0010000000;
parameter s8 = 10'b0100000000;
parameter s9 = 10'b1000000000;
always@(*)
case(state)
s0: nextstate = in? s1: s0;
s1: nextstate = in? s2: s0;
s2: nextstate = in? s3: s0;
s3: nextstate = in? s4: s0;
s4: nextstate = in? s5: s0;
s5: nextstate = in? s6: s8;
s6: nextstate = in? s7: s9;
s7: nextstate = in? s7: s0;
s8: nextstate = in? s1: s0;
s9: nextstate = in? s1: s0;
default:nextstate = 10'b0;
endcase
assign out1= state == s8 | state == s9;
assign out2= state == s7 | state == s9;
assign next_state = nextstate;
endmodule
然后就得到了下面的错误波形。
错误波形(网页显示原因,无法截到信号名)
仔细观察发现在当前状态state为正常独热码(0x100, 0x1, 0x80)的时候,输出的波形是正确的,但是输入不是独热码(0x900, 0x180)的时候,输出就不正常了,这就是这道题所考察的地方,写错的同学自行翻看前一个独热码状态机的题目(Problem 125 fsm3onehot)再看正确答案。
module top_module(
input in,
input [9:0] state,
output [9:0] next_state,
output out1,
output out2);
parameter s0 = 0, s1 = 1, s2 = 2, s3 = 3, s4 = 4, s5 = 5, s6 = 6, s7 = 7, s8 = 8, s9 = 9;
assign next_state [s0] = state [s0] & (~in) | state [s1] & (~in) | state [s2] & (~in) |
state [s3] & (~in) | state [s4] & (~in) | state [s7] & (~in) |
state [s8] & (~in) | state [s9] & (~in);
assign next_state [s1] = state [s0] & ( in) | state [s8] & ( in) | state [s9] & ( in);
assign next_state [s2] = state [s1] & ( in);
assign next_state [s3] = state [s2] & ( in);
assign next_state [s4] = state [s3] & ( in);
assign next_state [s5] = state [s4] & ( in);
assign next_state [s6] = state [s5] & ( in);
assign next_state [s7] = state [s6] & ( in) | state [s7] & ( in);
assign next_state [s8] = state [s5] & (~in);
assign next_state [s9] = state [s6] & (~in);
assign out1 = state [s8] | state [s9];
assign out2 = state [s7] | state [s9];
endmodule
独热码状态机的正确写法。独热码状态机仅使用单bit表示状态,是否处在某一状态仅跟一个bit有关,而下个状态的转移也仅与一个bit有关。与其他bit无关。独热码的好处就是生成状态机的逻辑是最简单的,但缺点就是,当偶然进入了一个不是该电路的状态的时候,除了复位以外是跳转不回去了。再回头看一下题目中的小提示:代码中状态转移部分的或逻辑连接起来的每一个部分就是状态转移的一条路径。
Problem 134 PS/2 packet parser / Fsm ps2
这道题和下一道题让我们来了解一下比较古老的外设鼠标的接口协议PS2。
鼠标使用的PS/2协议每次需要发送长度为三个字节的消息。但是,每条消息的开始和结束在连续的字节流中并不明显。只知道,每个条消息(3字节)中的第一个字节始的bit[3] = 1 (但其他两个字节的bit[3]根据数据可能为1也可能为0)。
现在想要一个有限状态机,当给定输入字节流时,可以搜索到每条消息的边界。所使用的算法是丢弃无用的字节,直到看到bit[3] = 1的字节。然后,假定这是消息的第一个字节,并在接收完所有3个字节后,发出消息接收完成的信号。
注意:在成功接收到消息的第三个字节之后,状态机应立即在当周期中发出完成信号。
用来说明状态机行为的时序图
在没有错误的条件下,每三个字节组成一条消息
当发生错误的时候,寻找in[3]为1的数据
注意,这与检测1XX的序列识别器不同,此处不允许重叠序列(即不要把byte2和byte3中的in[3]=1视为数据开始)
小提示:
状态图
解答与分析
小提示中已经讲得很清楚了,而且还给出了状态图,基本没有难度,直接给出答案。
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output done); //
reg [1:0] state;
reg [1:0] next_state;
parameter A = 2'd0;
parameter B = 2'd1;
parameter C = 2'd2;
parameter D = 2'd3;
// State transition logic (combinational)
always@(*)
case(state)
A:next_state=in[3]?B:A;
B:next_state=C;
C:next_state=D;
D:next_state=in[3]?B:A;
endcase
// State flip-flops (sequential)
always@(posedge clk)
if(reset)
state<=A;
else
state<=next_state;
// Output logic
assign done = state == D;
endmodule