Входной порт чтения VHDL искажает выходной сигнал

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

Всякий раз, когда линия

elsif state = sensor_answer_0 AND trigger_sensor = '1' then 

для параметра ouput_signal установлено значение "X".

Но когда я закомментирую trigger_sensor:

elsif state = sensor_answer_0 then 

output_signal фактически принимает присвоенное ему значение.

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

В приведенном ниже коде я использую output_signal также для отладки и устанавливаю его в каждом состоянии, чтобы увидеть, какое состояние выполняется на самом деле. Я проверил свой тестовый стенд и не обнаружил конфликта записи-записи с output_signal. Однако я устанавливаю trigger_sensor. Но только после того, как для trigger_sensor уже установлено значение «Z» в моем основном коде.

Почему я вижу это странное поведение? Разве я не могу просто прочитать сигнал inout?

Я использую Quartus II 15.0 и ModelSim для моделирования.

ibrary ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity sensor_read is port(
    clk_50: in std_logic;
    enable : in std_logic;              -- enable sensor read
    trigger_sensor: inout std_logic;    -- 1. trigger sensor, 2. read sensor value
    output_sensor : out std_logic_vector(7 downto 0) 
);
end sensor_read;


architecture behavior of sensor_read is
--  
    type state_type is (init, trigger_0, trigger_1, sensor_answer_0 );

    signal state : state_type := init;
    signal icnt : std_logic_vector( 18 downto 0 ) := ( others => '0' );  -- counter

----------------------------------------------------------------------  
--- wait for enable
--- trigger sensor
--- wait for sensor to pull trigger line HIGH and assign output
----------------------------------------------------------------------  
begin
    process (clk_50 )
    begin
        if rising_edge( clk_50 ) then
            -- start here
            if state = init AND unsigned( icnt ) = 0 then
                icnt <= ( others => '0' );
                if enable = '1' then                            -- start trigger process
                    state <= trigger_0;
                    trigger_sensor <= '0';
                else
                    state <= init;
                    trigger_sensor <= 'Z';  
                end if; 

            -- TRIGGER SENSOR
            elsif state = trigger_0 AND unsigned( icnt ) = 50 then      
                state <= trigger_1;
                trigger_sensor <= '1';
                output_sensor <= "00000001";    -- debug                                

            elsif state = trigger_1 AND unsigned( icnt ) = 550 then     -- 500 clk cycle
                state <= sensor_answer_0;                   
                icnt <= ( others => '0' );
                trigger_sensor <= 'Z';        -- set trigger to high impedance to let sensor drive line
                output_sensor <= "00001111";  -- debug

            -- WAIT FOR SENSOR TO PULL TRIGGER=HIGH
            -- ERROR HERE -> when trigger_sensor is used in if statement output_sensor becomes xxx
            --               when trigger_sensor = not('1') is commented out, output_sensor is set correctly to "00011111"
            elsif state = sensor_answer_0 AND trigger_sensor = '1' then 
                state <= init;                              
                icnt <= ( others => '0' );                          
                output_sensor <= "00011111";        -- assign final output
            else    
                icnt <= std_logic_vector ( unsigned( icnt ) + 1 );
            end if;
        end if; 
    end process;
end behavior;

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

-- clock generation
process
begin
  clk_50 <= '1';
  wait for 10 ns;
  clk_50 <= '0';
  wait for 10 ns;
end process;

-- input data
process
begin
    enable <= '0'; 
    trigger_sensor <= 'Z';
    wait for 1 ms;

    -- start the meassurement
    enable <= '1';

    -- wait for the trigger signal to be over
    while trigger_sensor /= '1' loop wait for 1 us; end loop;
    while trigger_sensor = '1' loop wait for 1 us; end loop;

    -- now send response
    wait for 100 us;
    trigger_sensor <= '1';
    wait for 1 ms;
    trigger_sensor <= '0';
    wait for 10 us;
    trigger_sensor <= 'Z';

    wait;
end process;

person TriceratopsMagician    schedule 12.03.2017    source источник
comment
Похоже, вы неправильно смоделировали двунаправленный сигнал. Покажите свой тестовый стенд и значения trigger_sensor.   -  person    schedule 12.03.2017
comment
почему вы используете inout в первую очередь? Логика открытого коллектора? VHDL и FPGA это не очень нравится. Лучше использовать отдельные входы и выходы: это обычно предотвращает подобные ошибки. X часто означает, что сигнал имеет несколько драйверов.   -  person JHBonarius    schedule 13.03.2017
comment
@user1155120 user1155120 Я добавил испытательный стенд @J.H.Bonarius Я не знал об этом. Я использую Altera FPGA и думаю, что inout очень хорошо поддерживается в инструменте синтеза Altera.   -  person TriceratopsMagician    schedule 13.03.2017
comment
это поддерживается. Но, как и все, вы должны знать, как правильно им пользоваться.   -  person JHBonarius    schedule 14.03.2017
comment
Я согласен. Пока не вижу, что не так. Согласно этому не так много, чтобы рассмотреть.   -  person TriceratopsMagician    schedule 14.03.2017


Ответы (1)


После завершения минимально полного и проверяемого примера для тестовой среды:

library ieee;
use ieee.std_logic_1164.all;

entity sensor_read_tb is
end entity;

architecture foo of sensor_read_tb is
    signal clk_50:          std_logic;
    signal enable:          std_logic;
    signal trigger_sensor:  std_logic;
    signal output_sensor:   std_logic_vector (7 downto 0);

    --added for visual markers:
    signal waitfor100us:    bit;
    signal waitfor1ms:      bit;
    signal waitfor10us:     bit;
begin
    DUT:
    entity work.sensor_read
        port map (
            clk_50 => clk_50,
            enable => enable,
            trigger_sensor => trigger_sensor,
            output_sensor => output_sensor

        );
    -- clock generation
    process
    begin
      clk_50 <= '1';
      wait for 10 ns;
      clk_50 <= '0';
      wait for 10 ns;
      if now > 2.2 ms then
          wait;
      end if;
    end process;

    -- input data
    process
    begin
        enable <= '0';
        trigger_sensor <= 'Z';
        wait for 1 ms;

        -- start the meassurement
        enable <= '1';

        -- wait for the trigger signal to be over
        while trigger_sensor /= '1' loop wait for 1 us; end loop;
        while trigger_sensor = '1' loop wait for 1 us; end loop;

        -- now send response
        waitfor100us <= '1';
        wait for 100 us;
        waitfor100us <= '0';
        trigger_sensor <= '1';
        waitfor1ms <= '1';
        wait for 1 ms;
        waitfor1ms <= '0';
        trigger_sensor <= '0';
        waitfor10us <= '1';
        wait for 10 us;
        waitfor10us <= '0';
        trigger_sensor <= 'Z';

        wait;
    end process;
end architecture;

Мы видим проблему:

введите датчик_read_tb.png

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

Так как же он должен работать?

В первой дюжине или около того результатов поиска Google оригинальная патентная однолинейная мультиплексная система для датчиков и исполнительных механизмов появляется, выданный в 1982 году как патент США 4,311,986.

Пока срок действия патента истек, он «учит» изобретение.

Также есть техническое описание MAX6575L/H, в котором говорится, что вы хотите работать счетчик с момента, когда ПЛИС сбрасывает trigger_sensor, до момента, когда датчик снова сбрасывает датчик триггера.

Это работает следующим образом: ПЛИС сбрасывает trigger_sensor на фиксированный интервал, сигнализируя о запуске любых датчиков или приводов на однопроводной шине. До 8 устройств могут совместно использовать шину, используя множители для масштабирования времени в соответствии с двумя входными контактами с двумя семействами задержек (суффиксы H и L).

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

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

Счетчик может быть достаточно большим, чтобы его можно было перезапускать, переворачивая всякий раз, когда ни один датчик не отвечает, перезапуская интервал выборки. Модель VHDL, которую мы здесь имеем, считывает только датчики.

Итак, вернемся к тому, что не так с двунаправленным сигналом.

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

Хитрость здесь в том, что ни один из концов не передает на провод «1», только «0» или «Z» (высокое сопротивление). Подтягивание будет от «Z» до «H» и представляет собой резистор (в техническом описании показан резистор на 10 кОм).

Чтобы прочитать, нужно отфильтровать значения «0» или «1», а в пакете std_logic_1164 есть функция TO_01 для этого с добавленным вводом со значением по умолчанию, которое сопоставляет мета-значения с «0». Для наших целей, используя модель открытого коллектора, мы хотим, чтобы функция сопоставляла мета-значения с «1».

Для случаев, когда вы не используете TO-01, который недоступен в более ранних версиях пакета std_logic_1164, он был включен. Все изменения, внесенные в исходный код VHDL, были аннотированы:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity sensor_read is
    port (
        clk_50:         in  std_logic;
        enable:         in  std_logic;      -- enable sensor read
        trigger_sensor: inout std_logic;    -- 1. trigger sensor, 2. read sensor value
        output_sensor:  out std_logic_vector(7 downto 0)
    );
end entity sensor_read;


architecture behavior of sensor_read is

    type state_type is (init, trigger_0, trigger_1, sensor_answer_0);
    signal state:  state_type := init;
    signal icnt:  std_logic_vector( 18 downto 0) := (others => '0');
    -- for VHDL revisions prior to -2008:
    function TO_01 (s: STD_ULOGIC; xmap: std_ulogic := '0') return std_ulogic is
    begin
        case s is
            when '0' | 'L' => 
                return '0';
            when '1' | 'H' => 
                return '1';
            when others    => 
                return xmap;
        end case;
    end function TO_01;
begin
    process (clk_50)
    begin
        if rising_edge(clk_50) then
            if state = init AND unsigned(icnt) = 0 then
                icnt <= ( others => '0' );
                if enable = '1' then
                    state <= trigger_0;
                    trigger_sensor <= '0';
                else
                    state <= init;
                    trigger_sensor <= 'Z';
                end if;
            elsif state = trigger_0 AND unsigned(icnt) = 50 then
                state <= trigger_1;
                trigger_sensor <= 'Z'; -- WAS '1';
                output_sensor <= "00000001";

            elsif state = trigger_1 AND unsigned(icnt) = 550 then
                state <= sensor_answer_0;
                icnt <= (others => '0');
                trigger_sensor <= 'Z';
                output_sensor <= "00001111";
            elsif state = sensor_answer_0 AND 
                   TO_01(trigger_sensor, xmap => '1') = '1' then -- CHANGED
                state <= init;
                icnt <= ( others => '0' );
                output_sensor <= "00011111";
            else
                icnt <= std_logic_vector (unsigned(icnt) + 1);
            end if;
        end if;
    end process;
end architecture behavior;

library ieee;
use ieee.std_logic_1164.all;

entity sensor_read_tb is
end entity;

architecture foo of sensor_read_tb is
    signal clk_50:          std_logic;
    signal enable:          std_logic;
    signal trigger_sensor:  std_logic;
    signal output_sensor:   std_logic_vector (7 downto 0);

    --added for visual markers:
    signal waitfor100us:    bit;
    signal waitfor1ms:      bit;
    signal waitfor10us:     bit;
    -- for VHDL revisions prior to -2008:
    function TO_01 (s: STD_ULOGIC; xmap: std_ulogic := '0') return std_ulogic is
    begin
        case s is
            when '0' | 'L' => 
                return '0';
            when '1' | 'H' => 
                return '1';
            when others    => 
                return xmap;
        end case;
    end function TO_01;
begin
    DUT:
    entity work.sensor_read
        port map (
            clk_50 => clk_50,
            enable => enable,
            trigger_sensor => trigger_sensor,
            output_sensor => output_sensor

        );
    -- clock generation
    process
    begin
      clk_50 <= '1';
      wait for 10 ns;
      clk_50 <= '0';
      wait for 10 ns;
      if now > 2.2 ms then
          wait;
      end if;
    end process;

    -- input data
    process
    begin
        enable <= '0';
        trigger_sensor <= 'Z';
        wait for 1 ms;

        -- start the meassurement
        enable <= '1';

        -- wait for the trigger signal to be over
        while TO_01(trigger_sensor, '1') /= '1' loop wait for 1 us; end loop;
        while TO_01(trigger_sensor, '1')  = '1' loop wait for 1 us; end loop;

        -- now send response
        waitfor100us <= '1';
        wait for 100 us;
        waitfor100us <= '0';
        -- trigger_sensor <= '1';
        trigger_sensor <= 'Z';  -- WAS '1';
        waitfor1ms <= '1';
        wait for 1 ms;
        waitfor1ms <= '0';
        trigger_sensor <= '0';
        waitfor10us <= '1';
        wait for 10 us;
        waitfor10us <= '0';
        trigger_sensor <= 'Z';

        wait;
    end process;
PULLUP:                       -- ADDED
    trigger_sensor <= 'H';    -- PULLUP
end architecture;

И это дает нам сигнал, который работает:

sensor_read1.png

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

Если вы изучите форму волны, отображаемую после времени, показывающего импульс waitfor10us, вы увидите, что тестовый стенд не соответствует интервалу счета для блока дизайна sensor_read. Возможно, потребуется некоторая доработка. Также обратите внимание, что счетчик запускается после того, как тестовый стенд сигнализирует значение. Здесь нужно немного повозиться.

person Community    schedule 13.03.2017