为FSM中的输出端口创建的寄存器,为什么?

时间:2014-11-28 21:36:15

标签: vhdl spi fsm

我在VHDL中实现了一个简单的SPI主控。我面临的问题是,在合成期间,为ssdata_rdy创建了两个触发器。我认为总是指定那些端口的输出,为什么这些寄存器被创建,我怎么能摆脱它们呢?

我的代码如下,其中省略了ssdata_rdy的状态。

实体:

library IEEE;
use IEEE.std_logic_1164.ALL;

entity mcu is
    port(clk      : in  std_logic;
         res      : in  std_logic;
         pc       : in  std_logic_vector(7 downto 0);
         pc_new   : in  std_logic;
         data_ack : in  std_logic;
         miso     : in  std_logic;
         data     : out std_logic_vector(12 downto 0);
         data_rdy : out std_logic;
         mosi     : out std_logic;
         sclk     : out std_logic;
         ss       : out std_logic);
end mcu;

架构:

library IEEE;
use IEEE.std_logic_1164.ALL;

architecture behaviour of mcu is
    -- r: send read command
    -- a: send address
    -- rx: receive data
    type state_t is (r0, r1, r2, r3, r4, r5, r6, r7,
                     a0, a1, a2, a3, a4, a5, a6, a7,
                     rx0, rx1, rx2, rx3, rx4, rx5, rx6, rx7, rx8, rx9, rx10, rx11, rx12,
                     idle, starting, done);
    signal state    : state_t                      := idle;
    signal datasig  : std_logic_vector(12 downto 0);
begin
    sclk <= clk;
    mosi <= datasig(12);

    sync : process(clk) is
    begin
        if rising_edge(clk) then
            data_rdy <= '0';
            ss       <= '0';

            if res = '1' then
                state <= idle;
            else
                datasig <= datasig(11 downto 0) & miso;

                if pc_new = '1' then
                    state <= starting;
                else
                    case state is
                    when idle =>
                        ss      <= '1';
                        datasig <= (others => '0');
                        state   <= idle;

                    ...

                    when rx12 =>
                        data     <= datasig;
                        data_rdy <= '1';
                        state    <= done;
                    when done =>
                        if data_ack = '1' then
                            state <= idle;
                        else
                            state <= done;
                        end if;
                end case;
                end if;
            end if;
        end if;
    end process sync;
end behaviour;

相关的合成器输出:

===============================================================================
|    Register Name    |   Type    | Width | Bus | MB | AR | AS | SR | SS | ST |
===============================================================================
|    data_rdy_reg     | Flip-flop |   1   |  N  | N  | N  | N  | Y  | N  | N  |
|       ss_reg        | Flip-flop |   1   |  N  | N  | N  | N  | Y  | N  | N  |
|      data_reg       | Flip-flop |  13   |  Y  | N  | N  | N  | N  | N  | N  |
|      state_reg      | Flip-flop |   3   |  Y  | N  | N  | N  | N  | Y  | N  |
|      state_reg      | Flip-flop |   2   |  N  | N  | N  | N  | Y  | Y  | N  |
|     datasig_reg     | Flip-flop |  13   |  Y  | N  | N  | N  | N  | N  | N  |
===============================================================================

另外,为什么state分成两个寄存器?

2 个答案:

答案 0 :(得分:1)

注册输出通常是一件好事:它允许更好的定义时序,这可以转化为更可靠的操作,尤其是对于SPI外设等外部设备。

您遇到的问题(我认为)当您希望每当您处于某种状态时输出为真时,在您处于该状态时(在时钟进程内)分配该输出将引入一个时钟周期延迟。

David Koontz给出了一个答案:将所有分配移动到时钟部分的输出 out - 或者实际上完全取决于该过程,仅取决于“状态”信号。

但另一种方法是保持寄存器的清洁时序,并且仍然消除循环延迟。 这是为了将输出分配提前到任何状态,将转换为该状态,或者(一旦进入)状态不会转换出状态。例如,修改

            when rx12 =>
                data     <= datasig;
                data_rdy <= '1';
                state    <= done;

并转换到状态rx12

            when rx11 =>
                if ready_to_go_to_rx12 then
                   data_rdy <= '1';  -- keep data_rdy synched with rx12
                   state    <= rx12;
                end if;

            when rx12 =>
                data     <= datasig;
                -- data_rdy <= '1'; -- no, because we leave state rx12 immediately
                state    <= done;

同样适用于具有相同问题的其他输出。

很可能这不会产生额外的逻辑,因为合成工具通常可以识别出data_rdyrx12总是同时被断言,并为它们共享一个寄存器。

编辑:我回来看了一眼,注意到data <= datasig;也被注册了,因此也是一个周期晚了:虽然我只考虑data_rdy作为你提到的信号之一,但你需要考虑是否还应推进其他任务,如data <= datasig;。 (我猜是这样的:在新数据出现之前在循环中发出信号data_rdy是违反直觉的!)

答案 1 :(得分:0)

我面临的问题是在合成期间,为ss和data_rdy创建了两个触发器。我认为总是指定那些端口的输出,为什么这些寄存器被创建,我怎么能摆脱它们呢?

此处创建了data_rdyss的注册表:

    if rising_edge(clk) then
        data_rdy <= '0';
        ss       <= '0';

摆脱它们的方法只分配给评估data_rdy的if语句之外的ssclk

另外,为什么州分为两个寄存器?

你有32个状态,所以5个触发器听起来正确。如果您查看报告中最后两列用于这两组触发器,您会发现其中三个是同步设置的,并且两组触发器都是同步重置的。

这是出现:

    if rising_edge(clk) then
        data_rdy <= '0';
        ss       <= '0';

        if res = '1' then
            state <= idle;
        else
            datasig <= datasig(11 downto 0) & miso;

            if pc_new = '1' then
                state <= starting;
            else
                case state is
                when idle =>
                    ss      <= '1';
                    datasig <= (others => '0');
                    state   <= idle;

因为您的同步干扰状态为空闲,启动或使用由状态驱动的输入多路复用器。

如果你有能力看到映射设计的示意图,那将是有启发性的。