我正在尝试制造一个基于串行输入来切换状态的有限状态机。 我需要一些有关如何执行代码的解释。 我从一本教科书中看到,我在过程中标记为"默认值"的部分是放置默认值的地方。 但是,无论何时切换状态,我的信号似乎都采用这些值。 例如,我将state_next设置为idle为默认值。 这样做导致FSM无缘无故地继续从其他状态跳入空闲状态。
我的另一个问题是澄清如何执行FSM的整个过程。 当我从一种状态转移到另一种状态时,是否应该执行case语句之前的部分(标记为DEFAULT VALUES的部分)? 还是仅当我从稍后的状态返回到空闲状态时才执行? 应何时执行"默认值"部分?
我的代码如下所示。 请参考"下一状态逻辑"部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity delay_incrementor is
generic ( delay_ports : natural := 3;
width_ports : natural := 3
);
Port ( clk,reset: in STD_LOGIC;
update : in STD_LOGIC;
in_data : in STD_LOGIC_VECTOR (7 downto 0);
led : out STD_LOGIC_VECTOR (2 downto 0);
out_data : out STD_LOGIC_VECTOR (7 downto 0);
d_big,d_mini,d_opo : inout STD_LOGIC_VECTOR (25 downto 0);
w_big,w_mini,w_opo : inout STD_LOGIC_VECTOR (25 downto 0));
end delay_incrementor;
architecture fsm_arch of delay_incrementor is
type state_type is (idle,channel,d_or_w,delay_channel,delay_channel_inc,width_channel,width_channel_inc);
type delay_file_type is array (delay_ports-1 downto 0) of std_logic_vector (25 downto 0);
type width_file_type is array(width_ports-1 downto 0) of std_logic_vector (25 downto 0);
signal d_reg,d_next,d_succ: delay_file_type;
signal w_reg,w_next,w_succ: width_file_type;
signal state_reg,state_next: state_type;
signal which_channel,which_channel_next: natural;
begin
--------------------------------------
--State Register
--------------------------------------
process(clk,reset)
begin
if reset='1' then
state_reg <= idle;
d_reg <= (others => (others => '0'));
w_reg <= (others => (others => '0'));
which_channel <= 0;
elsif (clk='1' and clk'event) then
state_reg <= state_next;
d_reg <= d_next;
w_reg <= w_next;
which_channel <= which_channel_next;
end if;
end process;
--------------------------------------
--Next-State Logic/Output Logic
--------------------------------------
process(state_reg,in_data,d_reg,w_reg,which_channel)
begin
state_next <= idle; --DEFAULT VALUES
d_succ <= d_reg;
w_succ <= w_reg;
which_channel_next <= 0;
case state_reg is
when idle =>
if in_data ="01100011" then --"c"
state_next <= channel;
which_channel_next <= 0;
end if;
when channel =>
if (48 <= unsigned(in_data)) and (unsigned(in_data)<= 57) then
which_channel_next <= (to_integer(unsigned(in_data))-48);
state_next <= d_or_w;
elsif in_data ="00100011" then --"#"
state_next <= idle;
which_channel_next <= which_channel;
end if;
when d_or_w =>
if in_data ="01100100" then --"d"
state_next <= delay_channel;
elsif in_data ="01110111" then --"w"
state_next <= width_channel;
elsif in_data ="00100011" then --"#"
state_next <= idle;
end if;
when delay_channel =>
if in_data ="01101001" then --"i"
state_next <= delay_channel_inc;
elsif in_data ="00100011" then --"#"
state_next <= idle;
end if;
when delay_channel_inc =>
if in_data ="01110101" then --"u"
d_succ(which_channel) <= std_logic_vector(unsigned(d_reg(which_channel))+250);
elsif in_data ="01100100" then --"d"
d_succ(which_channel) <= std_logic_vector(unsigned(d_reg(which_channel))-250);
else
d_succ(which_channel) <= d_reg(which_channel);
end if;
if in_data ="00100011" then --"#"
state_next <= idle;
end if;
when width_channel =>
if in_data ="01101001" then --"i"
state_next <= width_channel_inc;
elsif in_data ="00100011" then --"#"
state_next <= idle;
end if;
when width_channel_inc =>
if in_data ="01110101" then --"u"
w_succ(which_channel) <= std_logic_vector(unsigned(w_reg(which_channel))+250);
elsif in_data ="01100100" then --"d"
w_succ(which_channel) <= std_logic_vector(unsigned(w_reg(which_channel))-250);
else
w_succ(which_channel) <= w_reg(which_channel);
end if;
if in_data ="00100011" then --"#"
state_next <= idle;
end if;
end case;
end process;
process(update,d_reg,w_reg,reset)
begin
if reset='1' then
d_next <= (others => (others =>'0'));
w_next <= (others => (others =>'0'));
elsif (update'event and update='1') then
d_next <= d_succ;
w_next <= w_succ;
else
d_next <= d_reg;
w_next <= w_reg;
end if;
end process;
--------------------------------------
--Output Logic
--------------------------------------
d_big <= d_reg(0);
d_mini <= d_reg(1);
d_opo <= d_reg(2);
w_big <= w_reg(0);
w_mini <= w_reg(1);
w_opo <= w_reg(2);
end fsm_arch; |
这是单处理样式的替代版本。
如您所料,只要您没有显式设置值,"默认值"就会重置包括State在内的所有内容:这可能是不希望的,而我已经进行了一些到显式空闲状态的转换(在* _channel_inc中)。我对这里的语义有些猜测:如果一个字符在InData上存在一个以上的周期,或者输入了另一个字符,会发生什么?但逻辑应该易于更改。
一些注意事项:
任何时候编写x <= std_logic_vector(unsigned(y)+250);都可能是错误的类型。这意味着您正在使用类型系统而不是使用它。我制作了Unsigned的d_reg etc数组,并从程序流中排除了将类型转换转换为输出的情况。 X <= Y + 250;更清晰,更简单。
这些端口应该是Out,而不是InOut-我会协商将它们设置为Unsigned,从而进一步简化和阐明设计。
空间不消耗门。
单一过程样式的优点之一是,用于互连过程的信号更少,并且在难以确定的时间进行更新。此处,"更新"与其他所有时钟都在同一时钟沿使用。
幻数... if in_data ="01101001" then --"i"与if in_data = to_slv('i') then后者更易于阅读,更容易编写(并正确)。如果您不喜欢to_slv辅助函数(可综合),请至少使用命名常量(其名称反映字符)。这也可以很容易地看出该代码正在处理ASCII(对不起,Latin-1)。
编辑:为了使SM不太混乱,我在本地重载了=以允许在slv和字符之间进行直接比较:将这种技巧本地化到需要的地方是一个好主意。这些功能甚至可以在流程本身中声明。
首选rising_edge(clk)而不是过时的(clk='1' and clk'event)样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity delay_increment is
generic ( delay_ports : natural := 3;
width_ports : natural := 3
);
Port ( clk,reset: in STD_LOGIC;
update : in STD_LOGIC;
in_data : in STD_LOGIC_VECTOR (7 downto 0);
led : out STD_LOGIC_VECTOR (2 downto 0);
out_data : out STD_LOGIC_VECTOR (7 downto 0);
d_big,d_mini,d_opo : out STD_LOGIC_VECTOR (25 downto 0);
w_big,w_mini,w_opo : out STD_LOGIC_VECTOR (25 downto 0));
end delay_increment;
architecture fsm_arch of delay_increment is
type state_type is (idle,channel,d_or_w,delay_channel,delay_channel_inc,width_channel,width_channel_inc);
type delay_file_type is array (delay_ports-1 downto 0) of unsigned (25 downto 0);
type width_file_type is array(width_ports-1 downto 0) of unsigned (25 downto 0);
signal d_reg, d_succ: delay_file_type;
signal w_reg, w_succ: width_file_type;
signal state_reg : state_type;
signal which_channel : natural;
function to_slv(C : Character) return STD_LOGIC_VECTOR is
begin
return STD_LOGIC_VECTOR(to_unsigned(Character'pos(c),8));
end to_slv;
function"=" (A : STD_LOGIC_VECTOR(7 downto 0); B : Character)
return boolean is
begin
return (A = to_slv(B));
end function"+";
begin
--------------------------------------
--State Machine
--------------------------------------
process(clk,reset)
begin
if reset='1' then
state_reg <= idle;
d_reg <= (others => (others => '0'));
w_reg <= (others => (others => '0'));
which_channel <= 0;
elsif rising_edge(clk) then
-- default actions ... update if asked
if update ='1' then
d_reg <= d_succ;
w_reg <= w_succ;
end if;
case state_reg is
when idle =>
if in_data = 'c' then
state_reg <= channel;
which_channel <= 0;
end if;
when channel =>
if (Character'pos('0') <= unsigned(in_data)) and (unsigned(in_data)<= Character'pos('9')) then
which_channel <= (to_integer(unsigned(in_data)) - Character'pos('0'));
state_reg <= d_or_w;
elsif in_data = '#' then
state_reg <= idle;
which_channel <= which_channel;
end if;
when d_or_w =>
if in_data = 'd' then
state_reg <= delay_channel;
elsif in_data = 'w' then
state_reg <= width_channel;
elsif in_data = '#' then
state_reg <= idle;
end if;
when delay_channel =>
if in_data = 'i' then
state_reg <= delay_channel_inc;
elsif in_data = '#' then
state_reg <= idle;
end if;
when delay_channel_inc =>
if in_data = 'u' then
d_succ(which_channel) <= d_reg(which_channel) + 250;
state_reg <= idle;
elsif in_data = 'd' then
d_succ(which_channel) <= d_reg(which_channel) - 250;
state_reg <= idle;
else
d_succ(which_channel) <= d_reg(which_channel); -- wait for any of 'u', 'd', '#'
end if;
if in_data = '#' then
state_reg <= idle;
end if;
when width_channel =>
if in_data = 'i' then
state_reg <= width_channel_inc;
elsif in_data = '#' then
state_reg <= idle;
end if;
when width_channel_inc =>
if in_data = 'u' then
w_succ(which_channel) <= w_reg(which_channel) + 250;
state_reg <= idle;
elsif in_data = 'd' then
w_succ(which_channel) <= w_reg(which_channel) - 250;
state_reg <= idle;
else
w_succ(which_channel) <= w_reg(which_channel); -- wait for any of 'u', 'd', '#'
end if;
if in_data = '#' then
state_reg <= idle;
end if;
end case;
end if;
end process;
--------------------------------------
--Output Logic
--------------------------------------
d_big <= std_logic_vector(d_reg(0));
d_mini <= std_logic_vector(d_reg(1));
d_opo <= std_logic_vector(d_reg(2));
w_big <= std_logic_vector(w_reg(0));
w_mini <= std_logic_vector(w_reg(1));
w_opo <= std_logic_vector(w_reg(2));
end fsm_arch; |
-
在一个时钟上升沿尝试执行过多操作是否有问题?如果我要在该过程中再添加20个状态怎么办?
-
(1)视情况而定。在某些时候,做太多会消耗比您更多的FPGA资源(门等)。但是请注意,状态机一次只能处于一种状态,因此它在任何时钟沿只能做的事情受到限制。 (2)该过程将起作用;但是,可能很难理解,维护和调试。我建议不要让状态机发展到无法理解的程度。我已经看到一些具有许多状态的SM,它们具有简单的结构(几个简单状态链),并且易于维护和理解-因此不会建议任何严格的规则。
每当列出的信号之一发生更改时,都会对过程进行评估。因此,此列表称为"敏感性列表"。
有两种类型的过程:
-顺序的(带有时钟信号)和
-组合式(只是普通逻辑)。
第一种只需要灵敏度列表中的时钟信号,后一种则需要右手边的信号,否则仿真将显示除实际硬件以外的其他结果。
因此,每次输入信号发生变化('event = true)时,都会根据begin ... end process评估过程。
因此,对于DEFAULT部分,每个信号都会获得默认值,并且不可能以这种编码方式生成锁存器。通常,state_next不会设置为空闲。设置为state_reg
免费翻译:保持当前状态,除非另行通知
您的代码:
1 2 3 4 5 6 7 8
| ...
elsif (update'event and update='1') then
d_next <= d_succ;
w_next <= w_succ;
else
d_next <= d_reg;
w_next <= w_reg;
end if; |
无法合成。我认为更新不是真正的时钟信号,因此不应在上升沿表达式中使用它。其次,其他条件何时成立?
解决方案:您的寄存器需要一个使能信号。
附录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| process(clk,reset)
begin
if reset='1' then
state_reg <= idle;
d_reg <= (others => (others => '0'));
w_reg <= (others => (others => '0'));
which_channel <= 0;
elsif (clk='1' and clk'event) then
state_reg <= state_next;
which_channel <= which_channel_next;
if update = '1' then
d_reg <= d_next;
w_reg <= w_next;
end if;
end if;
end process; |
-
谢谢,这样可以解决问题。关于我的代码段,我试图使用update作为启用信号。从到目前为止的测试来看,似乎有些奏效,并且只要更新信号不发生变化,else条件就成立。没有这样的更新信号,如何更新d_next和w_next?
-
@eugenewu将使能信号嵌入到edge_edge条件的then分支中。您可以删除最后一个过程。
|