Недавно решил освоить ПЛИС. Кой-что по мелочи успешно сделал (вроде отображения кода, набранного на переключателях, на семисегментном индикаторе).
Теперь вот хочу сделать контроллер PS/2, чтобы можно было подключить клаву или мышу, но плохо представляю, с какой стороны подойти. С собственно "электронной" точки зрения проблем нет, на рассыпной логике такие вещи делаются без проблем. Но я хочу реализовать контроллер на VHDL, причём контроллер полнофункциональный: с возможностью не только ввода, но и вывода данных, буферизации принятых данных, пока они не будут считаны извне, обнаружения ошибок...
Может кто-нибудь что-нибудь по этому поводу посоветовать толкового? Или, может, дать работающий пример?
Зарегистрирован: Пн сен 01, 2008 12:16:12 Сообщений: 1
Рейтинг сообщения:0
Ну и что мешает описать логику простой рассыпухи на VHDL, собрать в символы и сделать на основе символов?
Можно даже не описывать символы а использовать стандартную библиотеку.
Схемку сделать, пройти верификацию и посмотреть какой VHDL код получился.
Ну и что мешает описать логику простой рассыпухи на VHDL, собрать в символы и сделать на основе символов? Можно даже не описывать символы а использовать стандартную библиотеку. Схемку сделать, пройти верификацию и посмотреть какой VHDL код получился.
То, что я хочу описать поведение схемы, а не её устройство, и оставить конструирование схемы компилятору VHDL. Понятно, что можно "собрать" схему из компонентов, прописав на VHDL связи между ними, но это же самое можно сделать и в виде традиционной схемы, причём последняя будет удобнее и нагляднее. Преимущество VHDL -- именно возможность описывать поведение, а не устройство, но вот как это сделать правильно, я пока не пойму. Мозги старые, привыкли к обычным схемам
Ну-с, прошёл примерно год (чуть-чуть меньше), и я сделал-таки свой контроллер интерфейса PS/2. Проверен на семи разных клавиатурах. Две штуки с интерфейсом USB, подключаемые к PS/2 через переходник, работать не хотят, пять остальных (с "родным" интерфейсом PS/2) работают вполне себе успешно. Мышки тоже фурычат, причём и PS/2, и USB. Судя по всему, клавы USB "закладываются" на поведение контроллера PS/2, стоящего в ПК, почему и не хотят признавать мой контроллер.
ADD: Пытался выложить здесь свой код -- так ведь нормально не выкладывается, зараза. Пожирает часть операторов и т.п. Похоже, тэг code ведёт себя неадекватно...
---------- -- -- Copyright (c) 2009, 2010 SII -- -- Version 1.0 -- -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. -- ---------- -- -- КОНТРОЛЛЕР ИНТЕРФЕЙСА PS/2 -- -- Этот контроллер позволяет подключить к ПЛИС любое устройство с интерфейсом -- PS/2 (клавиатуру или мышь). Универсальные устройства, допускающие подключе- -- ние к компьютеру как к порту USB, так и к PS/2 (через переходник), могут -- оказаться неработоспособными, поскольку встроенный в них контроллер часто -- ориентирован на особенности работы контроллера PS/2, применяемого в совре- -- менных ПК. Иногда отказываются работать и "чистые" устройства PS/2; причина -- этого явления пока не ясна. -- -- -- Сигналы контроллера: -- -- CLK синхронизация (частота 50 МГц); -- RST сброс; -- PS2_CLK линия синхронизации интерфейса PS/2; -- PS2_DATA линия данных интерфейса PS/2; -- BUSY признак занятости контроллера; -- PE признак ошибки чётности в принятых данных; -- TE признак таймаута в операции приёма или передачи данных; -- SE признак ошибки в последовательности сигналов интерфейса; -- DIN входной байт данных для передачи устройству; -- WR команда вывода байта данных на устройство; -- DOUT выходной байт данных, полученный от устройства; -- RDY признак поступления байта данных от устройства; -- ACK подтверждение приёма данных/состояния. -- -- -- После сброса (сразу после включения питания или после поступления сигнала -- RST) контроллер переходит в состояние бездействия, при этом на линиях BUSY, -- PE, TE, SE, RDY устанавливается низкий (неактивный уровень). В этом состоя- -- нии он остаётся до начала приёма или передачи данных, причём на BUSY будет -- удерживаться низкий уровень (остальные указанные сигналы характеризуют по- -- следнюю выполненную операцию и поэтому могут иметь различные состояния). -- Как только контроллер выйдет из состояния бездействия в связи с началом -- приёма или передачи данных, на линии BUSY появится высокий уровень, а сигна- -- лы PE, TE, SE и RDY будут сброшены. -- -- В состояние приёма контроллер переходит, обнаружив, что подключенное к нему -- устройство желает передать ему данные (при переходе линии PS2_CLK в низкий -- уровень). После успешного окончания приёма выдаётся сигнал RDY, который ос- -- танется активным до начала следующей операции приёма или передачи либо до -- поступления сигнала сброса. Считать полученный байт можно в любой момент, -- когда активен RDY, по линиям DOUT. RDY сбрасывается в начале новой операции -- приёма/передачи данных, при сбросе контроллера (сигнал RST) или при получении -- сигнала подтверждения приёма данных/состояния ACK. -- -- В процессе приёма могут возникнуть ошибки всех трёх типов. Ошибка последова- -- тельности (SE) возникает, если устройство выдало единичный стартовый бит, а -- не нулевой. Таймаут (TE) будет обнаружен, если очередного ожидаемого фронта -- или спада сигнала PS2_CLK не будет в течение достаточно длительного времени -- (более 100 мкс для частоты 50 МГц). Ошибка чётности (PE) обнаруживается в -- конце приёма при неверном бите чётности. Обнаружение любой ошибки вызывает -- установку соответствующего сигнала и переход контроллера в состояние бездей- -- ствия. Сигналы ошибок останутся активными до тех пор, пока не будет начата -- новая операция приёма или передачи данных, не поступит сигнал сброса контрол- -- лера либо не поступит сигнал подтверждения приёма данных/состояния ACK. -- -- В состояние передачи контроллер переходит, если он находится в состоянии -- бездействия, устройство не начинает выдачу данных (линия PS2_CLK имеет высо- -- кий уровень) и на линии WR появляется высокий уровень. В этом случае кон- -- троллер запоминает данные, находящиеся на линиях DIN, и переходит к передаче -- сохранённого байта данных. Когда передача данных оканчивается, контроллер -- возвращается в состояние бездействия. -- -- В процессе передачи могут возникнуть таймаут (TE) и ошибка в последователь- -- ности сигналов (SE); последняя возникает, если после окончания передачи -- устройство выдаёт неправильный бит подтверждения. При обнаружении любой из -- ошибок устанавливается сигнал на соответствующей линии, и контроллер перехо- -- дит в состояние бездействия. Сигналы ошибок останутся активными до начала но- -- вой операции приёма или передачи данных, поступления сигналап сброса контрол- -- лера либо сигнала подтверждения приёма данных/состояния ACK. -- -- Для сигналов интерфейса PS/2 предусмотрена "очистка" от дребезга: переход -- сигнала из одного состояние в другое фиксируется только в том случае, если -- сигнал находится в новом состоянии не менее 16 тактов. -- -- Контроллер реагирует на сигнал сброса RST всегда (немедленно прекращает теку- -- щую операцию и выполняет сброс), а на сигнал ACK - только в состоянии бездей- -- ствия. -- ---------- -- -- Известные проблемы: -- -- 1. Универсальные клавиатуры (USB, подключаемые к PS/2 через переходник) могут -- не работать. Вероятно, они "закладываются" на особенности поведения кон- -- троллера PS/2, используемого в ПК, и не могут правильно опознать подключен- -- ие к этому контроллеру. -- ----------
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity PS2 is port ( CLK : in std_logic; RST : in std_logic; PS2_CLK : inout std_logic; PS2_DATA : inout std_logic; BUSY : out std_logic; PE : out std_logic; TE : out std_logic; SE : out std_logic; DIN : in std_logic_vector (7 downto 0); WR : in std_logic; DOUT : out std_logic_vector (7 downto 0); RDY : out std_logic; ACK : in std_logic); end PS2;
architecture Behavioral of PS2 is
-- Состояния контроллера. type TState is ( Reset, -- Сброс контроллера WaitAfterReset, -- Ожидание успокоения сигналов интерфейса после сброса Idle, -- Бездействие RX_Start, -- Начало приёма RX_WaitClkUp, -- Ожидание фронта PS2C при приёме RX_WaitClkDown, -- Ожидание среза PS2C и приём очередного бита RX_Done, -- Приём окончен, проверка корректности TX_Start, -- Начало передачи, формирование передаваемых битов TX_InitWait, -- Удержание PS2_CLK на низком уровне в течение 100 мкс TX_WaitInitClkUp, -- Ожидание установки PS2C после освобождения PS2_CLK TX_WaitInitClkDown, -- Ожидание первого спада PS2C
TX_WaitClkUp, -- Ожидание фронта PS2C после выдачи очередного бита TX_WaitClkDown, -- Ожидание спада PS2C и выдача очередного бита TX_WaitAck, -- Ожидание спада PS2C и поступления бита подтверждения TX_Done -- Окончание передачи, ожидание 1 на PS2C и PS2D );
signal State : TState;
-- Сдвиговый регистр для приёма и передачи данных. Сдвиг всегда происходит -- вправо. В конечном состоянии при приёме и начальном при передаче разряд -- 0 содержит стартовый бит (всегда 0), биты 1-8 - байт данных (биты 0-7), -- бит 9 является контрольным, 10-й разряд - стоп-бит (всегда 1). signal ShiftReg : std_logic_vector (10 downto 0);
-- Счётчик количества обработанных битов. signal BitCnt : std_logic_vector (3 downto 0);
-- Счётчик для реализации таймаутов и константы задержек (для частоты 50 МГц). signal TimeoutCnt : std_logic_vector (19 downto 0);
-- Внутренние сигналы для "очистки" сигналов интерфейса PS/2 от дребезга. signal PS2CL, PS2DL : std_logic; signal PS2CC, PS2DC : std_logic_vector (3 downto 0);
-- "Очищенные" значения сигналов интерфейса PS/2. signal PS2C, PS2D : std_logic;
-- Постоянно вычисляемые значения принимаемого и передаваемого байтов, -- используются для контроля принятого байта и формирования передаваемой -- последовательности битов. signal ParityIn, ParityOut : std_logic;
begin
-- "Очистка" сигнала PS2_CLK. process (CLK) begin if rising_edge(CLK) then if PS2_CLK /= PS2CL then PS2CL <= PS2_CLK; PS2CC <= "0000"; elsif PS2CC = "1111" then PS2C <= PS2CL; else PS2CC <= PS2CC + 1; end if; end if; end process;
-- "Очистка" сигнала PS2_DATA. process (CLK) begin if rising_edge(CLK) then if PS2_DATA /= PS2DL then PS2DL <= PS2_DATA; PS2DC <= "0000"; elsif PS2DC = "1111" then PS2D <= PS2DL; else PS2DC <= PS2DC + 1; end if; end if; end process;
-- Формирование признака занятости контроллера. BUSY <= '0' when State = Idle else '1';
-- Машина состояний. process (CLK, RST) begin if RST = '1' then RDY <= '0'; TE <= '0'; SE <= '0'; PE <= '0'; ShiftReg <= "00000000000"; TimeoutCnt <= (others => '0'); PS2_CLK <= '0'; PS2_DATA <= 'Z'; State <= Reset; elsif rising_edge(CLK) then
case State is
-- Состояние сброса. Исходные значения сигналов уже установлены, здесь -- лишь ждём не менее 100 мкс, чтобы устройство, подключенное к интер- -- фейсу, успело опознать запрет передачи. when Reset => if TimeoutCnt = Wait100us then TimeoutCnt <= (others => '0'); PS2_CLK <= 'Z'; State <= WaitAfterReset; else TimeoutCnt <= TimeoutCnt + 1; State <= Reset; end if;
-- Ожидание не меньше 100 мкс после сброса для успокоения значений сиг- -- налов интерфейса. when WaitAfterReset => if TimeoutCnt = Wait100us then State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= WaitAfterReset; end if;
-- Состояние бездействия. Анализируется начало передачи от устройства -- (переход PS2C в низкий уровень) и появление запроса хоста на вывод -- данных (появление высокого уровня на WR). when Idle => if ACK = '1' then TE <= '0'; SE <= '0'; PE <= '0'; RDY <= '0'; State <= Idle; elsif PS2C = '0' then if PS2D = '0' then State <= RX_Start; else SE <= '1'; State <= Idle; end if; elsif WR = '1' then State <= TX_Start; else State <= Idle; end if;
-- Начало приёма. Переход сюда происходит из состояния бездействия -- (Idle) при обнаружении на PS2C и PS2D низкого уровня (начало переда- -- чи от устройства). when RX_Start => RDY <= '0'; TE <= '0'; PE <= '0'; SE <= '0'; BitCnt <= "0000"; TimeoutCnt <= (others => '0'); ShiftReg <= "00000000000"; State <= RX_WaitClkUp;
-- Ожидание фронта PS2C при приёме. Переход в это состояние осуществля- -- ется из состояний RX_Start и RX_WaitClkDown после приёма очередного -- бита. when RX_WaitClkUp => if PS2C = '1' then TimeoutCnt <= (others => '0'); if BitCnt = "1010" then State <= RX_Done; else State <= RX_WaitClkDown; end if; elsif TimeoutCnt = Wait100us then TE <= '1'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= RX_WaitClkUp; end if;
-- Ожидание спада PS2C и приём по нему очередного бита. В это состояние -- контроллер переходит из RX_WaitClkUp после появления на PS2C высоко- -- го уровня. when RX_WaitClkDown => if PS2C = '0' then ShiftReg(9 downto 0) <= ShiftReg(10 downto 1); ShiftReg(10) <= PS2D; TimeoutCnt <= (others => '0'); BitCnt <= BitCnt + 1; State <= RX_WaitClkUp; elsif TimeoutCnt = Wait100us then TE <= '1'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= RX_WaitClkDown; end if;
-- Проверка паритета принятого байта и значе- -- ния стоп-бита. Контроллер переходит в это состояние из состояния -- RX_WaitClkUp после приёма последнего бита. when RX_Done => if ShiftReg(10) = '0' then SE <= '1'; elsif (ParityIn xor ShiftReg(9)) = '0' then PE <= '1'; else RDY <= '1'; end if; State <= Idle;
-- Начало передачи. Переход сюда из состояния бездействия при обнаруже- -- нии активного сигнала WR при PS2C='1' (устройство не начинает пере- -- дачу). Формируется битовая последовательность, которая должна быть -- передана, и устройство оповещается о начале передачи. when TX_Start => RDY <= '0'; TE <= '0'; PE <= '0'; SE <= '0'; ShiftReg(0) <= '0'; ShiftReg(8 downto 1) <= DIN(7 downto 0); ShiftReg(9) <= not ParityOut; ShiftReg(10) <= '1'; PS2_CLK <= '0'; PS2_DATA <= '0'; TimeoutCnt <= (others => '0'); BitCnt <= "0000"; State <= TX_InitWait;
-- Оповещение устройства о начале передачи: в течение не менее 100 мкс -- на линии PS2_CLK контроллер удерживает низкий уровень, по окончании -- чего линия PS2_CLK освобождается (на PS2_DATA присутствует 0 - стар- -- товый бит из ShiftReg). when TX_InitWait => if TimeoutCnt /= Wait100us then TimeoutCnt <= TimeoutCnt + 1; State <= TX_InitWait; else PS2_CLK <= 'Z'; PS2_DATA <= ShiftReg(0); ShiftReg(9 downto 0) <= ShiftReg(10 downto 1); ShiftReg(10) <= PS2D; BitCnt <= BitCnt + 1; TimeoutCnt <= (others => '0'); State <= TX_WaitInitClkUp; end if;
-- Ожидание установления на PS2C высокого уровня после освобождения -- PS2_CLK (до 100 мкс, потом таймаут). when TX_WaitInitClkUp => if PS2C = '1' then TimeoutCnt <= (others => '0'); State <= TX_WaitInitClkDown; elsif TimeoutCnt = Wait100us then TE <= '1'; PS2_DATA <= 'Z'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_WaitInitClkUp; end if;
-- Ожидание первого спада на PS2C после освобождения PS2_CLK. Это приз- -- нак того, что устройство приняло стартовый бит, и надо выдавать сле- -- дующий (первый из битов данных). Ожидание продолжается до 15 мс. when TX_WaitInitClkDown => if PS2C = '0' then TimeoutCnt <= (others => '0'); PS2_DATA <= ShiftReg(0); ShiftReg(9 downto 0) <= ShiftReg(10 downto 1); ShiftReg(10) <= PS2D; BitCnt <= BitCnt + 1; State <= TX_WaitClkUp; elsif TimeoutCnt = Wait15ms then TE <= '1'; PS2_DATA <= 'Z'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_WaitInitClkDown; end if;
-- Ожидание фронта PS2C после выдачи очередного бита. when TX_WaitClkUp => if PS2C = '1' then TimeoutCnt <= (others => '0'); if BitCnt = "1011" then State <= TX_WaitAck; else State <= TX_WaitClkDown; end if; elsif TimeoutCnt = Wait100us then TE <= '1'; PS2_DATA <= 'Z'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_WaitClkUp; end if;
-- Ожидание спада PS2C и выдача очередного бита. when TX_WaitClkDown => if PS2C = '0' then PS2_DATA <= ShiftReg(0); ShiftReg(9 downto 0) <= ShiftReg(10 downto 1); ShiftReg(10) <= PS2D; TimeoutCnt <= (others => '0'); BitCnt <= BitCnt + 1; State <= TX_WaitClkUp; elsif TimeoutCnt = Wait100us then TE <= '1'; PS2_DATA <= 'Z'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_WaitClkDown; end if;
-- Ожидание спада PS2C и прихода бита подтверждения. when TX_WaitAck => PS2_DATA <= 'Z'; if PS2C = '0' then TimeoutCnt <= (others => '0'); SE <= PS2D; State <= TX_Done; elsif TimeoutCnt = Wait100us then TE <= '1'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_WaitAck; end if;
-- Ожидание перехода PS2C и PS2D в высокий уровень после окончания пе- -- редачи данных и приёма бита подтверждения. when TX_Done => if (PS2C = '1') and (PS2D = '1') then State <= Idle; elsif TimeoutCnt = Wait100us then TE <= '1'; State <= Idle; else TimeoutCnt <= TimeoutCnt + 1; State <= TX_Done; end if;
А фиг её знает. Она ж у меня не в голом виде испытывалась, а вместе с индикацией и прочим -- что тоже ресурсы потребляет. Но в общем-то немного -- несколько больше, чем контроллер, идущий в составе тестово-демонстрационного примера, зашитого во флэшку на плате, но функционал у того контроллера меньше (не ловятся некоторые ошибки, не допускает горячего подключения/отключения устройств, может зависнуть при возникновении глюков в процессе передачи). Если интересно, дома могу слепить типа проверочную схемку, где выходы этого компонента просто идут на выводы ПЛИС (чтобы оптимизатор не решил, что эти сигналы вообще не нужны, и не упразднил их).
Хм, на Coolrunner почему-то всегда оказывется нужно больше макроячеек. Но зато облом наступил с Pterm - вот преимущество PLA перед PAL.
Еще, не в плане критики, а в плане деления опытом, такие присвоения "std_logic_vector (19 downto 0) := "00000001001110001000";"я научился делать так: std_logic_vector (19 downto 0) := conv_std_logic_vector(5000,20);(ломает на калькуляторе каждый раз высчитывать).
Еще, не в плане критики, а в плане деления опытом, такие присвоения "std_logic_vector (19 downto 0) := "00000001001110001000";"я научился делать так: std_logic_vector (19 downto 0) := conv_std_logic_vector(5000,20);(ломает на калькуляторе каждый раз высчитывать).
Ну, константы используются в коде несколько раз, поэтому их определения вынесены в раздел объявлений, так что считал их только один раз Кроме того, их вынос наверх помогает быстро их менять, если изменится исходная частота. Хотя, если уж ориентировать компонент на разные частоты, надо предусмотреть и автоматическое изменение ширины счётчика (например, для 50 МГц надо 20 разрядов, для 100 потребуется уже 21).
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения