文章目录
- 1011状态机的实现
- 四状态下的移位操作
处理相对复杂的逻辑时就会用到有限状态机,设定好不同的状态,根据触发条件跳转到相应的状态,然后在不同的状态下做相应的处理。有限状态机主要用到 always 语句和 case 语句。
1011状态机的实现
文章 Quartus II下设计一个用于识别2进制序列“1011”的状态机 是在 Quartus II 软件中设计的一个识别2进制序列“1011”的状态机,这里在 Vivado 中对该例子再进行实现。
设计源代码如下。
module timing(
input clk,
input rst,
input en,
input in,
output reg out
);
parameter ST0 = 0;
parameter ST1 = 1;
parameter ST2 = 2;
parameter ST3 = 3;
parameter ST4 = 4;
reg [2:0] state;
reg [2:0] next_state;
initial
begin
next_state = ST0;
end
always @ (in or en or state)
begin
case (state)
ST0:
begin
if(en == 1 && in == 1)
next_state = ST1;
else
next_state = ST0;
end
ST1:
begin
if(en == 1 && in == 0)
next_state = ST2;
else if(en == 1 && in == 1)
next_state = ST1;
else
next_state = ST0;
end
ST2:
begin
if(en == 1 && in == 1)
next_state = ST3;
else
next_state = ST0;
end
ST3:
begin
if(en == 1 && in == 1)
next_state = ST4;
else if(en == 1 && in == 0)
next_state = ST2;
else
next_state = ST0;
end
//ST4:
//next_state = ST0;
endcase
end
always @ (posedge clk or negedge rst)
begin
if(!rst)
state <= ST0;
else
state <= next_state;
end
always @ (state or posedge clk)
begin
if(state == ST4)
begin
state = ST1;
out = 1;
end
else
out = 0;
end
endmodule
仿真测试源代码如下。
module sim_timing();
reg clk;
reg rst;
reg en;
reg in;
wire out;
initial
begin
en = 0;
clk = 0;
rst = 1;
#50
en = 1;
#400
rst = 0;
#50
rst = 1;
end
initial
begin
in = 0;
forever
begin
#20
in = {$random}%2;
end
end
always #10 clk = ~clk;
timing uut_timing(
.clk(clk),
.rst(rst),
.en(en),
.in(in),
.out(out)
);
endmodule
仿真输出结果如下图所示。
通过上图可知,每输入一个“1011”序列,输出 out 就置为1,持续一个时钟周期后清零,输出结果与预想的一致。
四状态下的移位操作
下图中给出了四个状态,初始状态是 Idle,在此状态下判断移位开始信号 shift_start 是否为高,如果为高就进入Start 状态,在Start 状态下延迟若干个周期,进入 Run 状态,然后进行移位处理,如果移位停止信号shift_stop有效,进入Stop 状态,把存储器中的值清零然后返回到 Idle 状态。
Mealy 有限状态机的输出不仅与当前状态有关,也与输入信号有关。
Mealy 有限状态机的设计源代码如下。
module timing(
input clk,
input rst,
input in,
input shift_start,
input shift_stop,
output reg[7:0] out
);
parameter Idle = 0;
parameter Start = 1;
parameter Run = 2;
parameter Stop = 3;
reg [1:0] state;
reg [3:0] count;
always @ (posedge clk or negedge rst)
begin
if(!rst)
begin
state <= Idle;
count <= 0;
out <= 0;
end
else
case(state)
Idle:
begin
if(shift_start)
state <= Start;
end
Start:
begin
if(count == 4'd10)
begin
count <= 0;
state <= Run;
end
else
count <= count + 1'b1;
end
Run:
begin
if(shift_stop)
state <= Stop;
else
out <= {out[6:0],in};
end
Stop:
begin
out <= 0;
state <= Idle;
end
default : state <= Idle;
endcase
end
endmodule
Moore 有限状态机的输出只与当前状态有关,与输入信号无关,输入信号只影响状态的改变,不影响输出。比如本例中的计数信号和输出信号。
Moore 有限状态机的设计源代码如下。
module timing(
input clk,
input rst,
input in,
input shift_start,
input shift_stop,
output reg[7:0] out
);
parameter Idle = 0;
parameter Start = 1;
parameter Run = 2;
parameter Stop = 3;
reg [1:0] cur_state;
reg [1:0] next_state;
reg [3:0] count;
always @ (posedge clk or negedge rst)
begin
if(!rst)
cur_state <= Idle;
else
cur_state <= next_state;
end
always@(*)
begin
case(cur_state)
Idle:
begin
if(shift_start)
next_state <= Start;
else
next_state <= Idle;
end
Start:
begin
if(count == 4'd10)
next_state <= Run;
else
next_state <= Start;
end
Run:
begin
if(shift_stop)
next_state <= Stop;
else
next_state <= Run;
end
Stop:
next_state <= Idle;
default : next_state <= Idle;
endcase
end
always@(posedge clk or negedge rst)
begin
if(!rst)
count <= 0;
else if (cur_state == Start)
count <= count + 1'b1;
else
count <= 0;
end
always@(posedge clk or negedge rst)
begin
if(!rst)
out <= 0;
else if (cur_state == Run)
out <= {out[6:0],in};
else
out <= 0;
end
endmodule
在上面两个程序中用到了两种方式的写法,第一种的 Mealy 状态机,采用了一段式的写法,代码中只用了一个 always 语句,所有的状态转移、判断状态转移条件、数据输出都在一个 always 语句里。这种写法的缺点是如果状态太多,会使整段程序显的冗长。第二种是 Moore 状态机,采用了三段式的写法,状态转移用了一个 always 语句,判断状态转移条件用了一个 always 语句,数据输出也用了一个 always 语句,这样写下来的代码比较直观清晰,状态很多时也不会显得繁琐。
两者的仿真测试代码是一样的,如下所示。
module sim_timing();
reg clk;
reg rst;
reg in;
reg shift_start;
reg shift_stop;
wire [7:0] out;
initial
begin
clk = 0;
rst = 0;
in = 0;
#100
rst = 1;
forever
begin
#({$random}%100)
in = ~in;
end
end
initial
begin
shift_start = 0;
shift_stop = 0;
#200
shift_start = 1;
#500
shift_start = 0;
shift_stop = 1;
#50
shift_stop = 0;
shift_start = 1;
end
always #10 clk = ~clk;
timing uut_timing(
.clk(clk),
.rst(rst),
.in(in),
.shift_start(shift_start),
.shift_stop(shift_stop),
.out(out)
);
endmodule
仿真输出结果如下图所示。
由上图可以看到,在 Start 状态下,计数到 10 后就跳转到 Run 状态开始移位操作。上面的图是整体的仿真过程,移位操作看得不清楚,所以将输出 out 以二进制显示,放大图如下,可以看到在 shift_start 有效时,向左移位操作正确的进行。
以上就是 Vivado下有限状态机实现的所有内容了!
参考资料:
ZYNQ 开发平台 FPGA 教程 AX7020