状态机
如果状态机的输出信号不仅与电路当前状态有关,还与当前输入有关,则这种状态机称为米勒(Mealy)型状态机。
如果状态机的输出信号仅仅与电路当前状态有关,则称为摩尔(Moore)型状态机。
下图是一种相对通用的状态机原理图,主要由组合逻辑电路和时序逻辑电路两部分组成,
状态机的设计比较灵活多样,下面介绍两种相对常见的结构清晰,易于理解的状态机设计风格
设计风格1
该风格设计状态机时,将时序逻辑电路和组合逻辑电路进行分开设计,需要先定义一个枚举类型,包含状态机需要的所有状态。
首先时时序逻辑部分的设计
process (reset, clock)
begin
if(reset = '1') then
pr_state <= state0;
elsif (clock'event and clock = '1') then
pr_state <= nx_state;
end if;
end process;
这种设计风格的优点是时序电路的设计方法基本上是标准的。此外,这种风格占用的寄存器数量最少。这段代码综合得到的寄存器的数量等于对状态机所有状态进行编码所需要的位数。
状态机中时序逻辑电路的设计
process(input, pr_state)
begin
case pr_state is
when state0 =>
if(input=...)then
output <= <value>;
nx_state <= state1;
else...
end if;
when state1 =>
if(input=...)then
output <= <value>;
nx_state <= state2;
else...
end if;
when state2 =>
if(input=...)then
output <= <value>;
nx_state <= state3;
else...
end if;
...
end case;
end process;
整个代码中,由于没有任何信号的赋值是通过其他某个信号的跳变来触发的,所以不会生成寄存器
下面给出完整的模板
library ieee;
use ieee.std_logic_1164.all;
entity <entity_name> is
port(
input: in <data_type>;
reset, clock: in std_logic;
output: out <data_type>;
);
end <entity_name>;
architecutre <arc_name> of <entity_name> is
type state is (state0,state1,state2,state3...);
signal pr_state,nx_state: state;
begin
process(reset,clock)
begin
if(reset = '1') then
pr_state <= state0;
elsif (clock'event and clock = '1') then
pr_state <= nx_state;
end if;
end process;
process(input, pr_state)
begin
case pr_state is
when state0 =>
if(input=...)then
output <= <value>;
nx_state <= state1;
else...
end if;
when state1 =>
if(input=...)then
output <= <value>;
nx_state <= state2;
else...
end if;
when state2 =>
if(input=...)then
output <= <value>;
nx_state <= state3;
else...
end if;
...
end case;
end process;
end <arc_name>;
设计风格2
前面我们介绍的设计风格,在进行综合后,会产生如下的电路结构
此时,如果是米勒型状态机,那么输出随输入的变化而改变,但是在很多应用中,需要同步的寄存器输出,输出信号只有在时钟边沿出现时才能更新,此时输出必须先用寄存器存储起来。使用风格2设计的状态机会产生如下电路结构
风格2与1相比,只有一个差别,就是引入了内部信号temp。这个信号将输出结果存储起来,只有当所需的时钟边沿到来时才将它赋给输出端口
library ieee;
use ieee.std_logic_1164.all;
entity <entity_name> is
port(
input: in <data_type>;
reset, clock: in std_logic;
output: out <data_type>;
);
end <entity_name>;
architecutre <arc_name> of <entity_name> is
type state is (state0,state1,state2,state3...);
signal pr_state,nx_state: state;
signal temp: <data_type>;
begin
process(reset,clock)
begin
if(reset = '1') then
pr_state <= state0;
elsif (clock'event and clock = '1') then
output <= temp;
pr_state <= nx_state;
end if;
end process;
process(pr_state)
begin
case pr_state is
when state0 =>
temp <=<value>;
if(condition)then
nx_state <= state1;
else...
end if;
when state1 =>
temp <=<value>;
if(condition)then
nx_state <= state2;
else...
end if;
when state2 =>
temp <=<value>;
if(condition)then
nx_state <= state3;
else...
end if;
...
end case;
end process;
end <arc_name>;
二进制编码与独热码
对状态机的状态进行编码时有多种选择,默认的方式是以二进制编码。它的优点是需要的寄存器数量最少,有n个寄存器就可以对2^n个状态进行编码。但是这种编码方式需要更多的外部辅助逻辑,并且速度较慢。
另一种极端的编码方式是独热(OneHot)编码。此时,每个状态都需要一个寄存器。因此,它需要的寄存器最多,n个状态就需要n个寄存器。但是这种方法需要最少的辅助逻辑并且最有最快的速度。
一种介于两者之间的编码方式就是双热编码方式。在这种编码方式下,每一次状态变化会带来两个位的跳变,因此n个寄存器可以实现对n(n-1)/2个状态进行编码
状态 | 编码风格 | ||
二进制 | 双热 | 独热 | |
state0 | 000 | 00011 | 00000001 |
state1 | 001 | 00101 | 00000010 |
state2 | 010 | 01001 | 00000100 |
state3 | 011 | 10001 | 00001000 |
state4 | 100 | 00110 | 00010000 |
state5 | 101 | 01010 | 00100000 |
state6 | 110 | 10010 | 01000000 |
state7 | 111 | 01100 | 10000000 |
实例
风格1
一个使用摩尔状态机实现的简易的0-9环形计数器的状态转移图,流程图如下
library ieee;
use ieee.std_logic_1164.all;
entity counter is
port(clk,rst: in std_logic;
cout: out std_logic_vector(3 downto 0));
end counter;
architecture state_machine of counter is
type state is(zero,one,two,three,four,five,six,seven,eight,nine);
signal pr_state,nx_state: state;
begin
process(rst,clk)
begin
if(rst = '1')then
pr_state <= zero;
elsif (clk'event and clk = '1') then
pr_state <= nx_state;
end if;
end process;
process (pr_state)
begin
case pr_state is
when zero =>
count <= "0000";
nx_state <= one;
when one =>
count <= "0001";
nx_state <= two;
when two =>
count <= "0010";
nx_state <= three;
when three =>
count <= "0011";
nx_state <= four;
when four =>
count <= "0100";
nx_state <= five;
when five =>
count <= "0101";
nx_state <= six;
when six =>
count <= "0110";
nx_state <= seven;
when seven =>
count <= "0111";
nx_state <= eight;
when eight =>
count <= "1000";
nx_state <= nine;
when nine =>
count <= "1001";
nx_state <= zero;
end process;
end state_machine;
风格2
下面的代码实现了一个序列检测器电路,状态机输入是一个串行位流,当检测到序列“111”时,输出1。此外,如果出现连续的‘1’,如“···011111···”,此时需要连续输出3个1。流程图如下:
library ieee;
use ieee.std_logic_1164.all;
entity string_detector is
port(d,clk,rst: in bit;
q: out bit);
end string_detector;
architecture my_arch of string_detector is
type state is (zero,one,two,three);
signal pr_state,nx_state: state;
begin
process(rst,clk)
begin
if (rst = '1') then
pr_state <= zero;
elsif (clk'event and clk = '1') then
pr_state <= nx_state;
end if;
end process;
process (d,pr_state)
begin
case pr_state is
when zero =>
q <= '0';
if (d = '1') then
nx_state <= one;
else
nx_state <= state;
end if;
when one =>
q <= '0';
if (d = '1') then
nx_state <= two;
else
nx_state <= zero;
end if;
when two =>
q <= '0';
if (d = '1') then
nx_state <= three;
else
nx_state <= zero;
end if;
when three =>
q <= '1';
if(d = '1') then
nx_state <= three;
else
nx_state <= zero;
end if;
end case;
end process;
end my_arch;