Регистры созданы для выходных портов в FSM, зачем?

Я реализую простой мастер SPI в VHDL. Проблема, с которой я столкнулся, заключается в том, что во время синтеза создаются два триггера для ss и data_rdy. Я думал, что вывод на эти порты всегда указан, так зачем же создаются эти регистры и как от них избавиться?

Мой код приведен ниже, а состояния, в которых нет ss или data_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 разделен на два регистра?


person BrtH    schedule 28.11.2014    source источник


Ответы (2)


Регистрация ваших выходов, как правило, хорошая вещь: она позволяет гораздо лучше определять тайминги, что может привести к более надежной работе, особенно с внешними устройствами, такими как периферийные устройства SPI.

Проблема (я думаю), с которой вы сталкиваетесь, заключается в том, что когда вы хотите, чтобы вывод был истинным, когда вы находитесь в определенном состоянии, назначение этого вывода, когда вы находитесь в этом состоянии (в рамках тактового процесса), приведет к задержке тактового цикла.

Дэвид Кунц дает один ответ: переместите все назначения на этот выход из тактовой части - или вообще из этого процесса, в зависимости только от сигнала "состояния".

Но другой способ сохранит регистр для чистых таймингов и при этом устранит эту задержку цикла. То есть продвигать выходное назначение к любому условию в любом состоянии, которое переходит в это состояние или (находясь в нем) не переходит из него. Так, например, изменить

            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_rdy и rx12 всегда устанавливаются одновременно, и использовать для них общий регистр.

РЕДАКТИРОВАТЬ: я вернулся, чтобы еще раз взглянуть, и заметил, что data <= datasig; также зарегистрирован, и, следовательно, также с опозданием на цикл: хотя я рассматривал только data_rdy как один из сигналов, которые вы упомянули, вам нужно рассмотреть, следует ли также продвигать другие назначения, такие как data <= datasig; . (Я бы так догадался: нелогично сигнализировать data_rdy в цикле перед новыми данными!)

person user_1818839    schedule 29.11.2014

Проблема, с которой я столкнулся, заключается в том, что во время синтеза создаются два триггера для ss и data_rdy. Я думал, что вывод на эти порты всегда указан, так зачем же создаются эти регистры и как от них избавиться?

Регистры для data_rdy и ss создаются прямо здесь:

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

Способ избавиться от них — назначить только data_rdy и ss вне оператора if, оценивающего clk.

Кроме того, почему состояние разделено на два регистра?

У вас есть 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;

потому что вы синхронно глушите состояние на холостой ход, на запуск или с помощью входного мультиплексора, управляемого состоянием.

Если у вас есть возможность увидеть схематическое изображение нанесенного на карту дизайна, это будет поучительно.

person Community    schedule 28.11.2014