【问题标题】:Registers created for output ports in FSM, why?为 FSM 中的输出端口创建的寄存器,为什么?
【发布时间】:2014-11-28 21:36:14
【问题描述】:

我正在用 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会被分成两个寄存器?

【问题讨论】:

    标签: vhdl spi fsm


    【解决方案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 &lt;= datasig; 也已注册,因此也延迟了一个周期:虽然我只将data_rdy 视为您提到的信号之一,但您需要考虑是否有其他像data &lt;= datasig; 这样的作业也应该提前。 (我猜是这样:在新数据之前的循环中发出data_rdy 的信号是违反直觉的!)

    【讨论】:

      【解决方案2】:

      我面临的问题是在合成过程中为 ss 和 data_rdy 创建了两个触发器。我以为这些端口的输出总是指定的,那么为什么要创建这些寄存器,我该如何摆脱它们?

      data_rdyss 的寄存器在此处创建:

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

      摆脱它们的方法是只在评估clk的if语句之外分配data_rdyss

      另外,为什么状态被分成两个寄存器?

      你有 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;
      

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-09
        • 1970-01-01
        • 2011-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-14
        相关资源
        最近更新 更多