状态机的设计 一串口通信系统数据包头为 0111110,然后为数据内容,然后以 01111100 结尾,为了 防止数据中出现 0111110,在发送数据是每 5 个连续的 1 插入一个 0,接受数据时每 5 个连 续的 1 删除一个 0。所以状态机的功能是检测二进制序列 11111,考虑到检测的 5 个 1 后要 在后面插入 5,所以在检测到 5 个 1 后再重新计数 1,而不考虑序列重叠的情况。 定义 6 个状态分别表示检测到 0,1,2,3,4,5 个连续的 1,分别用 A,B,C,D,E,F 表示 五个状态,下面是状态转移图:
输入是x,输出是z,当遇到到连续5个1后z=1,否则为0,可以根据z的取值来判断是否进行插0。
程序代码
module seqDet(x,z,clk,rst,state);
input x,clk,rst;
output z;
output [2:0] state;
reg [2:0] state;//6个状态需要3位状态变量保存
wire z;
//------定义状态-------
parameter A=3'd0, B=3'd1, C=3'd2,
D=3'd3, E=3'd4, F=3'd5;//分别表示检测到0-5个1的状态
//状态机输出组合逻辑
assign z=(state==F&&x==1)?1:0;//检测到5个1后输出z应该为1
//每个时钟上沿进行状态转换
always @(posedge clk)
//同步置位
if(!rst)
state=A;
else
case(state)
A:if(x==1)
state=B;//检测到1个1
else
state=A;
B:if(x==1)
state=C;//检测到2个1
else
state=A;
C:if(x==1)
state=D;//检测到3个1
else
state=A;
D:if(x==1)
state=E;//检测到4个1
else
state=A;
E:if(x==1)
state=F;//检测到5个1
else
state=A;
F:if(x==1)
state=B;//重新计数1,所以回到B状态
else
state=A;
default:state=A;//默认状态位0个1
endcase
endmodule
程序将控制输出组合逻辑和控制状态转换的时序逻辑分开,当状态转时,输出组合逻辑assign语句使得输出也能相应改变。这样做比较容易发现问题和改正模块编写中的问题,更适合编写复杂的状态机。
Case语句根据状态状态转移图编写,增加default分支项,以确保回到初始状态。
查看原理图
测试代码
module seqDet_test;
// Inputs
wire x;
reg clk;
reg rst;
// Outputs
wire z;
wire [2:0] state;
// Instantiate the Unit Under Test (UUT)
seqDet uut (
.x(x),
.z(z),
.clk(clk),
.rst(rst),
.state(state)
);
reg [11:0] data;//输入的数据流
always #10 clk=~clk;
always @(negedge clk)
data={data[10:0],data[11]};//数据左移
assign x=data[11];//最高位与输入x连接
initial begin
// Initialize Inputs
clk = 0;
rst = 1;
#2 rst=0;
#30 rst=1;//清零操作
data='b1111_1000_1111_1000;
#500 $stop;
end
endmodule
测试代码先进行清0操作,然后定义一个数据流data[11:0],在每个时钟下沿将向左循环移位,取最高位作为状态机数据输入,当检测到数据流中5个连续的1后,状态机输出z=1,其他状态时z=0,方便和另外实现插0操作的程序相结合,完成整个数据组包工作。
时序仿真结果
这一部分是清0操作,确保状态state=A,即检测到0个1。
这部分是检测阶段,可以看到在每个x=1时钟上沿,状态依次从A,B,C,D,E,F转换,并在stae=F是输出z=1。另外数据流的循环左移发生在时钟下沿,避免和状态变化发生在同一时刻。
所以状态机的功能得到了实现。用verilog HDL来描述有限状态机,可以充分发挥硬件描述语言的抽象建模能力,使用always块语句和case,if等条件语句及赋值语句即可方便实现。具体的逻辑化简、逻辑电路到触发器映射均可由计算机完成,使电路设计工作得到简化,效率也有很大提高。