2014-11-23 7 views
1

Я пытался создать приемник RS-232 с использованием подхода FSM. Я признаю, что у меня нет очень хорошо продуманного понимания VHDL, поэтому я работаю над кодом «на лету» и учась, когда я иду. Тем не менее, я считаю, что на данный момент я ударил кирпичную стену.VHDL RS-232 Receiver

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

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 

entity ASyncReceiverV4 is 
Port (DataIn : in STD_LOGIC; 
       Enable : in STD_LOGIC; 
      CLK : in STD_LOGIC; 
       BadData : out STD_LOGIC; 
      DataOut : out STD_LOGIC_VECTOR (7 downto 0)); 
end ASyncReceiverV4; 

architecture Behavioral of ASyncReceiverV4 is 

type states is (StartBitCheck, ReadData, StopBitCheck); 
signal currentState, nextState : states; 

begin 

    process(CLK) 
    begin 
     if rising_edge(CLK) then 
      currentState <= nextState; 
     end if; 
    end process; 

    process(CLK) 
    variable counter : integer := 0; 
    variable dataIndex : integer := 0; 
    begin 
      case currentState is 

       when StartBitCheck => 
        if Enable = '1' then 
         if (DataIn = '0' and counter < 8) then 
          counter := counter + 1; 
         elsif (DataIn = '0' and counter = 8) then 
          BadData <= '0'; 
          nextState <= ReadData; 
          counter := 0; 
         else 
          nextState <= StartBitCheck; 
         end if; 
        end if; 

       when ReadData => 
        if Enable = '1' then 
         if counter < 16 then 
          counter := counter + 1; 
         elsif (counter = 16 and dataIndex < 8) then 
          DataOut(dataIndex) <= DataIn; 
          counter := 0; 
          dataIndex := dataIndex + 1; 
         elsif dataIndex = 8 then 
          dataIndex := 0; 
          nextState <= StopBitCheck; 
         else 
          nextState <= ReadData; 
         end if; 
        end if; 

       when StopBitCheck => 
        if Enable = '1' then 
         if DataIn = '1' then 
          if counter < 16 then 
           counter := counter + 1; 
           nextState <= StopBitCheck;       
          elsif counter = 16 then 
           counter := 0; 
           nextState <= StartBitCheck;       
          end if; 
         else 
          DataOut <= "11111111"; 
          BadData <= '1'; 
          nextState <= StartBitCheck; 
         end if; 
        end if; 

      end case; 

    end process; 


end Behavioral; 

По какой-то причине, на основании моего моделирования, кажется, что мои процессы не синхронизировано. Хотя все должно происходить только на фронте часов, у меня есть переходы, происходящие на заднем фронте. Кроме того, кажется, что вещи не меняются в зависимости от значения счетчика.

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

Я могу предоставить скриншот моей симуляции, если это было бы полезно. Я также не уверен, включаю ли я правильные переменные в свой список чувствительности к процессу. Кроме того, правильно ли я использую переменные счетчика и dataIndex? Что делать, если я сделал их сигналами как часть моей архитектуры перед любым из моих процессов?

Любая помощь по этому вопросу была бы очень оценена!

+0

Кто-то должен посмотреть ваш код, чтобы быть уверенным, что вы пишете о UART. Возможно, вы бы попросили на EE Stack Exchange? Это проблема дизайна, а не проблема программирования. – user1155120

+0

Я сделаю это, спасибо! Я видел здесь другие сообщения, касающиеся UART, и думал, что я в нужном месте. – coolDude

ответ

0

Самый простой способ исправить это, а также производить самый простой для чтения кода, должен был бы перейти к 1 -процесс состояние машины, как это (предупреждение: не соблюдается, может содержать ошибки синтаксиса):

entity ASyncReceiverV4 is 
Port (DataIn : in STD_LOGIC; 
     Enable : in STD_LOGIC; 
     CLK  : in STD_LOGIC; 
     BadData : out STD_LOGIC; 
     DataOut : out STD_LOGIC_VECTOR (7 downto 0)); 
end ASyncReceiverV4; 

architecture Behavioral of ASyncReceiverV4 is 
    type states is (StartBitCheck, ReadData, StopBitCheck); 
    signal state  : states := StartBitCheck; 
    signal counter : integer := 0; 
    signal dataIndex : integer := 0; 
begin 
    process(CLK) 
    begin 
     if rising_edge(CLK) then 
      case state is 
       when StartBitCheck => 
        if Enable = '1' then 
         if (DataIn = '0' and counter < 8) then 
          counter <= counter + 1; 
         elsif (DataIn = '0' and counter = 8) then 
          BadData <= '0'; 
          state <= ReadData; 
          counter <= 0; 
         end if; 
        end if; 
       when ReadData => 
        if Enable = '1' then 
         if counter < 16 then 
          counter <= counter + 1; 
         elsif (counter = 16 and dataIndex < 8) then 
          DataOut(dataIndex) <= DataIn; 
          counter <= 0; 
          dataIndex <= dataIndex + 1; 
         elsif dataIndex = 8 then 
          dataIndex <= 0; 
          state <= StopBitCheck; 
         end if; 
        end if; 
       when StopBitCheck => 
        if Enable = '1' then 
         if DataIn = '1' then 
          if counter < 16 then 
           counter <= counter + 1;       
          elsif counter = 16 then 
           counter <= 0; 
           state <= StartBitCheck;       
          end if; 
         else 
          DataOut <= "11111111"; 
          BadData <= '1'; 
          state <= StartBitCheck; 
         end if; 
        end if; 
      end case; 
     end if; 
    end process; 
end Behavioral; 

Обратите внимание, что в то время как это уже не содержит языковые проблемы, Ther е - все еще странные вещи в логике.

Вы не можете ввести государство StopBitCheck до counter равно 16, потому что переход состояния находится в elsif от counter < 16. Следовательно, if counter < 16 в StopBitCheck недоступен.

отметить также, что переход состояния в StopBitCheck происходит на том же цикле, что вы обычно выборочные данные, поэтому выборки DataIn в StopBitCheck будет цикл поздно. Хуже того, если бы вы когда-либо получали плохие данные (DataIn/='1' в StopBitCheck), counter все равно был бы равен 16, StartBitCheck всегда будет идти в положение else, и автомат остановится.


Несколько больше объяснений о том, что было неправильной раньше:

Вашего моделирования есть вещи, изменяющихся по отрицательному фронту тактового, потому что ваш комбинаторный процесс имеет только часы в списке чувствительности. Правильный список чувствительности для комбинаторного процесса будет включать только DataIn, Enable, currentState и ваши две переменные, counter и dataIndex. Переменные не могут быть частью вашего списка чувствительности, потому что они недоступны для списка чувствительности (также вы не хотите запускать свой процесс самостоятельно, более того, на мгновение).

Список чувствительности, однако, в основном всего лишь костыль для симуляторов. При переходе на реальное оборудование процессы реализуются в LUT и Flip Flops. Ваша текущая реализация никогда не будет синтезирована, потому что вы включаете обратную связь (сигналы или переменные, получающие новое значение как функцию их старого значения) в разблокированной логике, создавая комбинаторный цикл.

counter и dataIndex являются частью данных вашего состояния. Горизонтальный механизм проще понять, потому что они отделились от явного состояния, но они все еще являются частью данных состояния. Когда вы создаете два конечных автомата процесса (опять же, я рекомендую 1 машину состояний процесса, а не 2), вы должны разделить все данные состояния в Flip Flops, используемые для его хранения (например, currentState), и выходные данные LUT, которые генерируют следующее значение (например, nextState). Это означает, что для вашей двух обрабатывающих машин переменные counter и dataIndex должны вместо этого стать currentCounter, nextCounter, currentDataIndex и nextDataIndex и рассматриваться как ваше государственное задание. Обратите внимание, что если вы решите внедрить изменения, чтобы сохранить 2-процессорный конечный автомат, логические ошибки, упомянутые выше линии, будут по-прежнему применяться.


EDIT:

Да, сброс counter обратно в 0, прежде чем перейти в StopBitCheck может быть хорошей идеей, но вы также должны учитывать, что вы ждете всех 16 отсчетов после выборки последних данных бит, прежде чем вы перейдете на StopBitCheck. Это только потому, что counter is не Сбросить, что образец отключен только одним часом вместо 16. Возможно, вы захотите изменить действие ReadData для перехода на StopBitCheck, когда вы пробуете последний бит, когда dataIndex = 7 (а также сброс counter до 0), так как:

    elsif (counter = 16) then 
         DataOut(dataIndex) <= DataIn; 
         counter <= 0; 
         if (dataIndex < 7) then 
          dataIndex <= dataIndex + 1; 
         else 
          dataIndex <= 0; 
          state <= StopBitCheck; 
         end if; 
        end if; 

чистый конечный автомат хранит только состояние. Он генерирует свой вывод исключительно из состояния или из комбинации состояния и входов. Поскольку counter и dataIndex хранятся, они являются частью состояния.Если вы хотите, чтобы перечислить каждое состояние вы бы что-то вроде:

type states is (StartBitCheck_Counter0, StartBitCheck_counter1... 

и вы бы в конечном итоге с 8 * 16 * 3 = 384 состояний (на самом деле несколько меньше, потому что только ReadData использует dataIndex, так что некоторые из 384 являются полностью избыточными состояниями). Как вы, без сомнения, видите, гораздо проще просто объявить 3 отдельных сигнала, так как разные части данных состояния используются по-разному. В двух машинном состоянии процесса люди часто забывают, что сигнал, фактически названный state, не является единственным данным состояния, которое необходимо сохранить в такт.

В однопроцессорной машине, конечно, это не проблема, потому что все, что назначено, по необходимости хранится в флип-флопе (промежуточная комбинационная логика не перечисляется в сигналах). Кроме того, обратите внимание, что 1-процессорные государственные машины и 2-процессорные машины состояний будут синтезироваться с одной и той же вещью (при условии, что они оба были выполнены правильно), хотя я считаю, что это сравнительно просто читать и сложнее испортить 1-технологическая машина.

Я также удалил предложения else, которые поддерживали текущее присвоение состояния в примере 1-процесса; они важны при назначении комбинационного nextState, но синхронизированный сигнал state будет сохранять свое старое значение, когда ему не присваивается новое задание, не вызывая защелки.

+0

Вау, большое вам спасибо за ваш вклад! Это было невероятно. Итак, я считаю, что одним из решений проблем было то, что я не сбрасывал свой счетчик обратно до 0, прежде чем переходить в StopBitCheck, я думаю, что он должен очистить некоторые из ошибок, которые вы объясняете. Это верно? Я по-прежнему немного потерял последнюю часть вашего объяснения: «counter и dataIndex являются частью ваших данных состояния. Головной компьютер проще понять, когда они отделились от явного состояния, но они все еще являются частью данные штата ". Не могли бы вы объяснить это еще? – coolDude

+0

@coolDude см. EDIT в ответе. – QuantumRipple

+1

Обратите внимание, что у всех трех состояний есть тест для 'enable = '1'' и нет теста для' '0'', вы можете свернуть первый оператор if в каждом случае и изменить' if rising_edge (CLK) then' to 'if rising_edge (CLK) и enable = '1' then'. Вам лучше использовать 'DataIn = '1'', чтобы сохранить сброс счетчика в состоянии StartBitCheck, или импульсный фильтр' DataIn' с двумя последовательными включенными флип-флопами. Идея состоит в том, чтобы предотвратить событие метастабильности в 'counter', которое может исказить вашу точку выборки. UART RX может использовать регистр сдвига вместо битового счетчика для еще большей простоты. – user1155120

-1

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

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

Но в вашем случае второй процесс также должен запускаться на каждом пульте CLK (из-за переменной счетчика и индекса), поэтому решение может состоять в том, чтобы запустить первое на восходящем фронте, а второе - на обратном фронте (если ваше оборудование поддерживает это).

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

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 

entity ASyncReceiverV4 is 
    Port (DataIn : in STD_LOGIC; 
      Enable : in STD_LOGIC; 
      CLK :  in STD_LOGIC; 
      BadData : out STD_LOGIC := '0'; 
      DataOut : out STD_LOGIC_VECTOR (7 downto 0) := "00000000"); 
end ASyncReceiverV4; 

architecture Behavioral of ASyncReceiverV4 is 

    type states is (StartBitCheck, ReadData, StopBitCheck); 
    signal currentState : states := StartBitCheck; 
    signal nextState : states := StartBitCheck; 

begin 

    process(CLK) 
    begin 
     if rising_edge(CLK) then 
     currentState <= nextState; 
     end if; 
    end process; 

    process(CLK) 
     variable counter : integer := 0; 
     variable dataIndex : integer := 0; 
    begin 
     if falling_edge(CLK) then 
     case currentState is 
      when StartBitCheck => 
       if Enable = '1' then 
        if (DataIn = '0' and counter < 8) then 
        counter := counter + 1; 
        elsif (DataIn = '0' and counter = 8) then 
        BadData <= '0'; 
        nextState <= ReadData; 
        counter := 0; 
        else 
        nextState <= StartBitCheck; 
        end if; 
       end if; 

      when ReadData => 
       if Enable = '1' then 
        if counter < 15 then 
        counter := counter + 1; 
        elsif (counter = 15 and dataIndex < 8) then 
        DataOut(dataIndex) <= DataIn; 
        counter := 0; 
        dataIndex := dataIndex + 1; 
        elsif dataIndex = 8 then 
        dataIndex := 0; 
        nextState <= StopBitCheck; 
        else 
        nextState <= ReadData; 
        end if; 
       end if; 

      when StopBitCheck => 
       if Enable = '1' then 
        if DataIn = '1' then 
        if counter < 15 then 
         counter := counter + 1; 
         nextState <= StopBitCheck; 
        elsif counter = 15 then 
         counter := 0; 
         nextState <= StartBitCheck; 
        end if; 
        else 
        DataOut <= "11111111"; 
        BadData <= '1'; 
        nextState <= StartBitCheck; 
        end if; 
       end if; 

     end case; 
     end if; 
    end process; 

end Behavioral; 

Это 0x55 с неправильным стоповый бит получить данных моделирования

enter image description here

+0

Наличие второго процесса, выполняющегося на фронте падающих часов, является неэффективным обходным решением проблемы, которая может быть легко исправлена. Правильный подход состоит в том, чтобы либо перейти к 1-процессной машине состояний (мои предпочтения, также легче читать и писать), либо дать все синхронизированные переменные (что было бы лучше в качестве сигналов) той же текущей/следующей тактовой/комбинационной обработкой, как государство. – QuantumRipple

+0

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