Перейти к содержанию
    

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

 

Процентов 10% - 20% всех передач байта не отрабатывает, как положено - сбрасывается после еще до передачи первого бита... Буду очень благодарен, если кто-то очень умный :) сможет подсказать, что в стиле написания FSM у меня неправильно. Мне кажется, что проблема именно в стиле (а не в конкретной логике - тогда бы, наверное, все работало бы пускай неправильно, но стабильно) - очевидно, я попросил компилятор сделать что-то такое, от чего у него крыша поехала.

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

 

Quartus 16.1 и Cyclone IV

 

process (clk, rst) begin

if rst = '1' then
    
    state    <= Init;
    
elsif rising_edge(clk) then 

    case state is
    
        when Init =>
        
            state    <= WaitStart;
            modeRst    <= '1';
            SPIready    <= '0';
        
        when WaitStart =>
        
            modeRst    <= '0';
        
            if StartCmd = '1' then
        
                state    <= ClkUp;
                MainCnt        <= "111";
                SPIready        <= '0';
                ShiftRegTX     <= BYTEo;
            
                else
                    SPIready    <= '1';
            
            end if;
        
        when ClkUp =>
    
            state        <= ClkDwn;
            SD_CLKi    <= '1';
    
        when ClkDwn =>
        
            SD_CLKi    <= '0';
    
            if MainCnt > 0 then
            
                state                            <= ClkUp;
                MainCnt                        <= MainCnt - 1;
                ShiftRegTX(7 downto 1)    <= ShiftRegTX(6 downto 0);
                ShiftRegRX                     <= ShiftRegRX(5 downto 0) & MISO;
                
                else
                    state        <= WaitStart;
                    DO         <= ShiftRegRX & MISO;
                    SPIready    <= '1';
                    modeRst    <= '1';
            end if;
            
    end case;

end if;

end process;

 

Еще смущает картинка в FSM viewer - почему-то не показывает переход от ClkDwn до WaitStart - возможно, именно здесь собака порылась...

 

post-84003-1497112566_thumb.jpg

 

Заранее всем огромное спасибо !

 

P.S. Небольшое замечание - понятно, что базовых вещей не знаю, но для меня это всего лишь хобби, одно из многих... Еще раз спасибо !

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Лично я считаю что автомат надо описывать в 3 или 4 процессах.

Так меньше шансов допустить ошибку.

1 Процесс - условие смены состояния автомата.

2 Процесс - Логика выбора следующего состояния автомата

3 Процесс - Комбинаторная часть работы автомата в текущем состоянии

4 Процесс - Синхронная часть работы автомата в текущем состоянии

Ну и не забывайте в CASE описывать все состояния.

У вас ещё осталось огромное количество неописанных состояний автомата.

P.S А перехода в состояние WaitStart может не быть, если счетчик не считает, и оптимизатор выбросил этот переход, как невозможный.

Не претендую на правильность, но вот как я обычно описываю:

   ----------------- СБРОС АВТОМАТА, ПЕРЕКЛЮЧЕНИЕ В СЛЕДУЮЩЕЕ СОСТОЯНИЕ -----------------
   -- Здесь описывается условие смены состояний автомата
   -- И что будет происходить при сигнале сброса
   RESET_AND_NEXT_STATE : PROCESS
   (
       CLKx16
   )
   BEGIN
       IF (RISING_EDGE(CLKx16)) THEN           -- По каждому переднему фронту 
           PRESS_STATE <= NEXT_STATE;          -- Меняем состояние автомата на NEXT_STATE
           IF (SYNC_RESET = '1') THEN          -- Когда сброс в активном уровне
               PRESS_STATE <= IDLE;            -- Сбрасываем автомат в состояние ожидания
           END IF;
       END IF;
   END PROCESS RESET_AND_NEXT_STATE;

   ----------------- ЛОГИКА ПЕРЕКЛЮЧЕНИЯ АВТОМАТА -----------------
   UART_NEXT_STATE_LOGIC:PROCESS
   (
       PRESS_STATE,
       RX_SYNC,
       MAJ_DATA,
       BIT_PRD_CNT,
       RX_BIT_CNT
   )
   BEGIN
       CASE PRESS_STATE IS                                                     -- Анализируем текущее состояние автомата
           WHEN IDLE         =>                                                -- Когда состояние ожидания
                               NEXT_STATE <= IDLE;                             -- Зацикливаемся в этом состояниии
                               IF (RX_SYNC = '0') THEN                         -- Если на линии найнен лог. 0
                                   NEXT_STATE <= START_BIT;                    -- Перейдем в состояние проверки старт бита
                               END IF;

           WHEN START_BIT    =>                                                -- Когда мы в состоянии проверки старт-бита 
                               NEXT_STATE <= START_BIT;                        -- Зацикливаемся в этом состояниии
                               CASE BIT_PRD_CNT IS                             -- Анализиурем счетчик периода бита
                                   WHEN 9     =>                               -- Когда у нас середина битового интервала
                                                   IF (MAJ_DATA = '1') THEN    -- Если мажоритарная выборка показала что пришла лог. 1               
                                                       NEXT_STATE <= IDLE;     -- Значит скоре всего пришла помеха и вернёмся в состояние ожидания
                                                   END IF;  
                                   WHEN 14     =>                              -- Если счётчик успел досчитать 15 тактов
                                                   NEXT_STATE <= RECEIVE_BIT;  -- Значит действительно был старт бит и можно принимать данные от передатчика

                                   WHEN OTHERS =>  NULL;                       -- В остальных случаях ничего не делаем
                               END CASE;   

           WHEN RECEIVE_BIT  =>                                                -- Когда автомат в состояние приёма битов
                               NEXT_STATE  <= RECEIVE_BIT;                     -- Зацикливаемся в этом состояниии 
                               IF (RX_BIT_CNT >= SDB-1) THEN                   -- Если счётчик приемя битов равен 7 (приинимаем 8 биит)      
                                   IF (BIT_PRD_CNT >= 15) THEN                 -- Если счётчик битового интервала досчитал до конца
                                       NEXT_STATE  <= STOP_BIT;                -- Значит дальше должен быть стоп бит -> перейдем в состоение приёма стоп бита
                                   END IF;                   
                               END IF;

           WHEN STOP_BIT     =>                                                -- Когда мы в состоянии проверки стоп-бита
                               NEXT_STATE  <= STOP_BIT;                        -- Зацикливаемся в этом состояниии в
                               IF (BIT_PRD_CNT >= 9) THEN                      -- Можно сразу после мажорирования 
                                   NEXT_STATE <= IDLE;                         -- Перейти в состояние ожидания
                               END IF;

           WHEN OTHERS       =>    
                               NEXT_STATE <= IDLE;                             -- В остальных случаях ждем старт бита
       END CASE;
   END PROCESS UART_NEXT_STATE_LOGIC;

   ------------------- Работа атомата -----------------------------------------------------
   RECEIVE_BIT_FROM_UART : PROCESS
   (
       PRESS_STATE,
       BIT_PRD_CNT,
       MAJ_DATA
   )
   BEGIN
       ------------------- Значения флагов по умолчанию -------------------------------------
       DATA_VALID         <= '0';                                      -- Постоянно сбрасываем флаг валидности данных
       DATA_ERROR         <= '0';                                      -- Постоянно сбрасываем флаг ошибки приёма данных   
       RX_BIT_CNT_ENA     <= '0';
       SHIFT_DATA_ENA     <= '0';
       BIT_PRD_CNT_SCLR   <= '0';
       RX_BIT_CNT_SCLR    <= '0';
       -------------------- Работа автомата --------------------------------------
       CASE PRESS_STATE IS                                                -- Анализируем текущее состояние автомата
           WHEN IDLE        =>                                            -- Если у нас состояние ожидания        
                          BIT_PRD_CNT_SCLR   <= '1';                      -- Счётчики периода бита сбрасывается
                          RX_BIT_CNT_SCLR    <= '1';                      -- Счётчики количества принятых бит сбрасывается

           WHEN START_BIT   =>                                            -- Когда проверяем старт бит
                           IF (BIT_PRD_CNT >= 14) THEN                     -- Поскольку на 14 такте сбрасыаем счётчик периода бита
                               BIT_PRD_CNT_SCLR    <= '1';
                           END IF;

           WHEN RECEIVE_BIT =>                                            -- Когда автомат в состоянии приёма битов
                           CASE BIT_PRD_CNT IS                            -- Анализируем состояние счетчика 16 битных интервалов
                               WHEN 10     =>                             -- Когда счетчик сделал все 3 выборки
                                               SHIFT_DATA_ENA <= '1';     -- Разрешим сдвинем данные в регистре на 1 разряд
                               WHEN 15     =>                             -- Когда счётчик досчитал до конца битового интервала 
                                               RX_BIT_CNT_ENA <= '1';     -- Разрешим счётчику считать 
                               WHEN OTHERS =>  NULL;                      -- В остальных случаях ничего не делаем
                           END CASE;

           WHEN STOP_BIT   =>                                             -- Когда автомат в состоянии проверки стоп бита
                           CASE BIT_PRD_CNT IS                            -- Анализируем состояние счетчика 16 битных интервалова
                               WHEN 10     =>                             -- В середине  битового интервала                        
                                               IF (MAJ_DATA = '0') THEN   
                                                   DATA_ERROR <= '1';
                                               ELSE
                                                   DATA_VALID <= '1';
                                               END IF;
                               WHEN OTHERS =>  NULL;                       -- В остальных случая ничего не делаем
                           END CASE;
           WHEN OTHERS => NULL;
       END CASE;
   END PROCESS RECEIVE_BIT_FROM_UART;   

Изменено пользователем Flip-fl0p

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Лично я считаю что автомат надо описывать в 3 или 4 процессах.

Я читал умные книжки и пробовал по разному. В процессе получился вариант "Single process, Single decoder" - от остальных вариантов ум за разум заехал...

 

У вас ещё осталось огромное количество неописанных состояний автомата.

Не могли бы указать хотя бы на одно неописанное состояние ? А то я как раз читал об этом, но запутался, что нужно обязательно описывать, а что - нет...

 

P.S А перехода в состояние WaitStart может не быть, если счетчик не считает, и оптимизатор выбросил этот переход, как невозможный.

Так вот с толку сбивает, что в большинстве случаев все работает правильно - не просто счетчик считает, а еще у в нужные места переходит, в т.ч. и в WaitStart. Именно это и поставило меня в полный тупик...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Применение перечисляемого типа позволяет более удобно описывать состояния автомата. То как он будет кодироваться ONE-HOT, GRAY или пр зависит от синтезатора.

Но в любом варианте существует вероятность свалиться в то, состояние которое у Вас не описано.

Например у вас есть 3 состояния S1, S2, S3. Кодирование ONE-HOT. Физически каждое состояние - это отдельный триггер.

Существует вероятность того, что 2 триггера будут включены в результате ошибки или сбоя. И как следствие автомат зависнет.

Используйте директивы синтезатору, для создания автомата, который сам выходит из невозможных состояний.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Блин, ну ничего не понимаю... Попробовал переделать на "Two processes, single decoder" - вообще получил миллион предупреждений (Latches, Unsafe behavior и т.д.).

Такой вопрос - а вообще можно счетчик в FSM вставлять так, как я сделал (в самом первом сообщении) ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Блин, ну ничего не понимаю... Попробовал переделать на "Two processes, single decoder" - вообще получил миллион предупреждений (Latches, Unsafe behavior и т.д.).

Такой вопрос - а вообще можно счетчик в FSM вставлять так, как я сделал (в самом первом сообщении) ?

При описании FSM LATCH - это частая проблема когда неправильно указана логика переключения состояний. Где-то ошибка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Буду очень благодарен, если кто-то очень умный :) сможет подсказать, что в стиле написания FSM у меня неправильно.

P.S. Небольшое замечание - понятно, что базовых вещей не знаю, но для меня это всего лишь хобби, одно из многих... Еще раз спасибо !

 

Могу рассказать как надо...

голосом по скайпу...

И еще... Почитайте в "Кратком Курсе" дополнительный раздел про автоматы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И еще... Почитайте в "Кратком Курсе" дополнительный раздел про автоматы.

Я столько всего перечитал... И вроде бы, исходя из прочитанного, должно работать. Но при этом также понимаю, что у меня там есть какая-то грубая системная ошибка...

Просто очень тяжело в самом начале, когда нормальной точки опоры еще нет...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я столько всего перечитал... И вроде бы, исходя из прочитанного, должно работать. Но при этом также понимаю, что у меня там есть какая-то грубая системная ошибка...

Просто очень тяжело в самом начале, когда нормальной точки опоры еще нет...

Понятно, что много... Вот только из множества возможных вариантов описания Вы выбрали самый, самый тяжелый. Предложение было сделано, дальше дело Ваше...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

justontime

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Думаю (скорее, даже уверен), что в данном случае проблема точно не в не-синхронности входных сигналов...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сигнал MISO, например, также должен быть синхронизированным.

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

 

Если что-то работает стабильно, а временами ломается - проблема не в стиле описания, а в мета-стабильнтости или гонках сигналов.

 

Как я вижу из описания у вас вроде как SPI мастер на половинной частоте основного клока. Вопросы вызывает сигнал StartCmd - откуда он берется? Если он внешний или с кнопочки то его надо синхронизовать, а то и защитить от дребезга. Потому что огромный шанс что у вас пол автомата переходит в передачу-прием, а половина остается в старом состоянии. Так же имеет смысл проверить разрядности счетчика. Ну и попробовать снизить частоту, может просто реально слишком быстро для вышей платы.

 

В целом описание популярное, не очень хорошее, но для маленьких автоматов вполне годное.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот Вам в помощь очень неплохое руководство по написанию FSM : https://electronix.ru/forum/index.php?act=a...st&id=92974

А вот пример совсем кратенький, но от этого не менее информативный https://habrahabr.ru/post/254885/

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В Харрис-Харрис по моему более чем исчерпывающее описание (и словами, и на VHDL - Verilog) как делать КА. Книжка есть даже на русском.

 

На английском есть хорошая книжка (даже несколько) от некого Pong Chu - там вообще объяснена тема чуть ли для самых маленьких.

 

http://academic.csuohio.edu/chu_p/rtl/index.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Во-первых, как всегда - большое спасибо всем откликнувшимся !

 

К сожалению, все еще мучаюсь с проблемой...

 

Насчет чтения книг - проблема в том, что я действительно много прочитал (например, одна из книг упомянутого выше Pong Chu вообще является одной из моих "любимых" по теме). Дело не в том, что я что чувствую общее непонимание темы.

Ровно наоборот - мне кажется, что я все понял (в рамках необходимого), и все сделал правильно, но устройство почему-то не работает... Поэтому мне важно понять, в чем я допустил ошибку именно в этой моей конструкции, а не вообще углублять освоение FSMов/FSMDэмов и т.д.

 

Исходя из того, что я увидел в SignalTap - такое впечатление, что иногда не загружается счетчик (почему-то с самого начала у меня было подозрение в ту сторону - просто даже в голове сложно представить, как это все синтезируется в "железе").

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

 

Насчет сигнала StartCmd - верное предположение, что от него могут быть проблемы. Как минимум, я знаю, что он асинхронный, и приходит без синхронизатора, что уже неправильно. Только, честно говоря, не думаю, что именно в нем проблема:

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

2. Крайне сомневаюсь, что при моих частотах метастабильность может проявляться приблизительно в 10% случаев...

 

Насчет частоты - ну, для моей платы (DE2-115) 14 МГц для такого узла вряд ли вообще можно назвать частотой... К сожалению, я на некоторое время остался без платы, поэтому сейчас проверить более правильную обработку StartCmd не могу :(

 

Все-таки мне продолжает казаться, что есть именно системная ошибка - данная FSM в принципе где-то описана в корне неверно. Как уже с самого начала показывал - FSM viewer неправильно показывает диаграмму, что не может не настораживать.

Мало того, когда я вроде бы непринципиально поменял местами два кусочка в районе счетчика (естественно, с изменением проверки его состояния):

 

if MainCnt = 0 then
                
        state        <= WaitStart;
               DO        <= ShiftRegRX & MISO;
               SPIready    <= '1';
               modeRst    <= '1';
                
                else
                     
                        state            <= ClkUp;
                        MainCnt            <= MainCnt - 1;
                        ShiftRegTX(7 downto 1)    <= ShiftRegTX(6 downto 0);
                        ShiftRegRX            <= ShiftRegRX(5 downto 0) & MISO;
                   
            end if;

 

Quartus стал понимать мой замысл вроде бы правильно:

 

post-84003-1497366437_thumb.jpg

 

Тем не менее, работа узла от этого не изменилась вообще...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...