AHDL AHDL (язык описания аппаратуры фирмы Altera) является высокоуровневым, модульным языком, полностью интегрированным в систему MAX+PLUS II. Он особенно хорошо подходит для проектирования сложной комбинационной логики, шин, конечных автоматов, таблиц истинности и параметрической логики. Операторы и элементы AHDL являются мощным, многогранным и легким в использовании средством. Зарезервированные ключевые слова используются для управления операторами AHDL, а также для предопределенных констант GND и VCC. Зарезервированные ключевые слова отличаются от зарезервированных идентификаторов тем, что ключевые слова можно использовать как символьные имена при заключении их в одиночные кавычки ('), в то время как зарезервированные идентификаторы нельзя. Как те так и другие можно свободно использовать в комментариях. Желательно вводить все ключевые слова с заглавных букв для удобства чтения. Символы в языке AHDL имеют предопределенные значения. Этот список включает символы, которые используются в качестве операторов и компараторов в булевых выражениях и как операторы в арифметических выражениях: _ (подчеркивание) - идентификаторы, описанные пользователем и используемые как допустимые символы в символьных именах; - (тире); / (прямой слеш); -- (два тире) - начинает однострочный комментарий в VHDL стиле; % (процент) - ограничивает комментарий в AHDL стиле; ( ) (круглые скобки) - ограничивают и определяют последовательные имена шин. [ ] (скобки) - ограничивают диапазон шины; '...' (кавычки) - ограничивают символьные имена; "..." (двойные кавычки) - ограничивают строки в операторах Title, Parameters, Assert. . (точка) - Оотделяет символьные имена переменных логической функции от имен портов; .. (эллипс) - отделяет старший бит от младшего; ; (точка с запятой) - канчивает операторы и разделы AHDL; , (запятая) - отделяет символьные имена от типов в объявлениях; = (равно) - присваивает входам значения по умолчанию GND и VCC в разделе Subdesign. => (стрелка) - отделяет входы от выходов в операторах Truth Table; + (плюс) - оператор сложения; - (минус) - оператор вычитания; == (два знака равенства) – оператор эквивалентности строк или чисел; ! (восклицательный знак) - оператор НЕ; != (знак восклицание равно) - оператор неравенства; > (больше чем) - компаратор больше чем; >= (больше или равно) - компаратор больше чем или равно; < (меньше чем) - компаратор меньше чем; <= (меньше или равно) - компаратор меньше чем или равно; & (амперсант) - оператор И; !& (восклицание амперсант) - оператор И-НЕ; $ (знак доллара) - оператор исключающее – ИЛИ; !$ (восклицание доллар) - оператор исключающее - ИЛИ - НЕ # (знак фунта) - оператор ИЛИ; !# (восклицание фунт) - оператор ИЛИ-НЕ; ? (вопрос) - тернарный оператор. Он использует следующий формат: <выражение 1> ? < выражение 2> : < выражение 3> Если первое выражение не ноль (истина), то вычисляется второе выражение и результат возвращается тернарному выражению. В противном случае возвращается значение третьего выражения. В AHDL существует три типа имен: символьные имена являются идентификаторами, описываемыми пользователем. имена подпроектов - это имена, которые пользователь определил для файлов проекта более низкого уровня. имена портов - это символьные имена, идентифицирующие входы или выходы логической функции. Компилятор генерирует имена содержащие символ тильда (~). Для трех типов имен доступны два вида представления: с использованием кавычек и без них. Строковые имена заключаются в одиночные кавычки ('), а символьные имена без них. Символьные имена и порты одного и того же типа можно объявить и использовать как шины в булевых выражениях и уравнениях. Шина, которая может содержать до 256 членов (или битов), рассматривается как коллекция узлов и действует как одно целое. В AHDL можно использовать десятичные, двоичные, восьмеричные и шестнадцатеричные числа в любых сочетаниях. Порт - это вход или выход логической функции. Порт может находится в двух местах: • Порт, который является входом или выходом текущего файла, объявляется в разделе Subdesign. • Порт, который является входом или выходом экземпляра примитива или файла разработки более низкого уровня, используется в разделе Logic. Порты текущего файла Порт, который является входом или выходом текущего файла объявляется в следующем формате в разделе Subdesign: <имя порта>: <тип порта> [ = <значение по умолчанию> ] Доступны следующие типы портов: INPUT MACHINE INPUT OUTPUT MACHINE OUTPUT BIDIR Необязательный раздел Variable используется для описания и/или генерации переменных, используемых в разделе Logic. Переменные языка AHDL сходны с переменными, используемыми в языках высокого уровня; они используются для определения внутренней логики. Каждое использование или реализация конкретной логической функции может быть произведено как объявлением переменной в разделе описания переменных, так и процедурой реализации объекта. После указанного объявления входные и выходные порты каждой логической функции можно использовать также как и порты проектируемого текстового файла проекта. При необходимости реализации объекта мега- или макрофункции надо убедиться в существовании соответствующего ей файла с описанием ее логического функционирования. Затем используется оператор Function Prototype для описания портов и параметров функции и производится реализация функции посредством подставляемой ссылки или объявления объекта. Для экземпляра примитива также используется подставляемая ссылка или объявления объекта. Однако, в отличие от мега- и макрофункций, логика функционирования примитива предопределена, поэтому нет необходимости определять логику функционирования примитива в отдельном текстовом файле проекта. В большинстве случаев нет необходимости использовать оператор Function Prototype. При использовании процедуры объявления объекта в разделе описания переменных производится описание переменной типа , или . Для параметрических мега- и макрофункций объявление включает список параметров, используемых объектом и в необязательном порядке, значения этих параметров. После определения переменной порты объекта функции можно использовать с применением следующего формата: <имя экземпляра>.<имя порта> AHDL поддерживает два типа узлов : NODE и TRI_STATE_NODE. Оба типа являются типами переменных общего назначения, используемых для хранения значений сигналов, которые не были описаны ни в разделе Subsection ни в разделе описания переменных. Таким образом, переменные обоих типов могут использоваться как с левой, так и с правой стороны выражения. Объявление регистров используется для определения регистров, включая D, T, JK и SR триггеры (DFF, DFFE, TFF, TFFE, JKFF, JKFFE, SRFF и SRFFE) и защелки (LATCH). Следующий пример демонстрирует описание регистра: VARIABLE ff : TFF; Именем объекта, представляющего собой Т - триггер, является ff. После данного объявления можно использовать входной и выходной порты объекта ff с использованием следующего формата: ff.t ff.clk ff.clrn ff.prn ff.q Поскольку все примитивы имеют только один выход можно использовать имя примитива без указания имени его выходного порта (например, без .q или .out) в правой части выражений. Аналогично, если примитив имеет лишь один вход (т.е. все примитивы за исключением примитивов JKFF, JKFFE, SRFF и SRFFE), то можно использовать имя примитива без указания имени его входного порта в левой части выражений (т.е., без .d, .t или .in). Конечный автомат создается определением его имени, состояний и в необязательном порядке его битами в разделе описания переменных. Раздел Logic определяет логическое функционирование текстового файла проекта (TDF) и является собственно его телом. Раздел Logic заключается в ключевые слова BEGIN и END. За ключевым словом END следует символ (;), заканчивающий раздел. Если используется оператор Defaults, то он должен предшествовать всем другим операторам в этом разделе. AHDL является параллельным языком. Компилятор анализирует поведенческую модель, описанную в разделе Logic, параллельно. Выражения, осуществляющие множественные присваивания объекту, имеющему тип NODE или переменной, объединяются в соответствии с функцией монтажное ИЛИ. Оператор Case определяет список альтернативных вариантов, которые могут быть активизированы в зависимости от значения переменной, группы или выражения, следующего за ключевым словом CASE. Оператор If Then содержит список операторов, выполняемых в том случае, если булевское выражение, расположенное между ключевыми словами IF и THEN, принимает истинное значение . На рисунке 7 приведена функциональная схема устройства для работы с последовательным портом на языке AHDL, после чего для примера приведен листинг программы на данном языке.
 Рис. 7. Функциональная схема устройства для работы с ЖКИ на языке AHDL CONSTANT str_high = X"20312032203320342035203620372038203920313020313120313220313320313420313520313620"; CONSTANT str_low = X"205045A3A54D203C544543543E205045A3A54D203C544543543E205045A3A54D203C544543543E20";
SUBDESIGN lcd_main_ahdl ( CLK : INPUT; -- входной тактовый сигнал я частотой 40 МГц и периодом 25нс SBUSY : INPUT; -- сигнал занятости подчиненного устройства STR[319..0] : OUTPUT; -- строка для вывода значений пересылаемого символа,для записи во внутренний регистр хранения REFRESH : OUTPUT; -- обновление строки в LCD DRAM INSTR[7..0] : OUTPUT; -- инструкция LCD контроллеру DELAY[27..0] : OUTPUT; -- задержка между инструкциями ONE_CYCLE : OUTPUT; -- (1) 4-битная или (0) 8-битная команда (данные) WRITE_COMMAND : OUTPUT; -- запуск процесса загрузки команды в LCD LCD_STATE_OUT : OUTPUT; -- номер состояния переключающего устройства LED[7..0] : OUTPUT; -- номер состояния переключающегося устройства между инициализируемым и рабочим автоматами )
VARIABLE lcd_state : MACHINE OF BITS(lcd_state_reg[4..0]) WITH STATES(s0 = X"00", s1 = X"01", s2 = X"02", s3 = X"03", s4 = X"04", s5 = X"05", s6 = X"06", s7 = X"07", s8 = X"08", s9 = X"09", s10 = X"0A", s11 = X"0B", s12 = X"0C", s13 = X"0D", s14 = X"0E", s15 = X"0F", s16 = X"10", s17 = X"11", s18 = X"12", s19 = X"13", s20 = X"14", s21 = X"15", s22 = X"16", s23 = X"17", s24 = X"18", s25 = X"19", s26 = X"1A", s27 = X"1B", s28 = X"1C", s29 = X"1D", s30 = X"1E", s31 = X"1F"); -- номер состояния переключающего устройства
STR[319..0] : DFF; -- строка для вывода на LCD REFRESH : DFF; -- обновление строки в LCD DRAM INSTR[7..0] : DFF; -- инструкция LCD контроллеру DELAY[27..0] : DFF; -- задержка между инструкциями ONE_CYCLE : DFF; -- (1) 4-битная или (0) 8-битная команда (данные) WRITE_COMMAND : DFF; -- запуск процесса загрузки команды в LCD LED[7..0] : DFF; -- регистр для хранения данных для вывода на светодиоды
position_flag : DFF; -- регистр для временного хранения состояний автомата char_cnt[5..0] : DFF; -- переменная состояния автомата выбора и загрузки исходной строки str_tmp[319..0] : DFF; -- регистр строки значений пересылаемого символа char_tmp[7..0] : DFF; -- переменная состояния автомата выбора и загрузки исходной строки BEGIN
STR[].clk = CLK; REFRESH.clk = CLK; INSTR[].clk = CLK; DELAY[].clk = CLK; ONE_CYCLE.clk = CLK; WRITE_COMMAND.clk = CLK; LED[].clk = CLK;
position_flag.clk = CLK; char_cnt[].clk = CLK; char_tmp[].clk = CLK; str_tmp[].clk = CLK; lcd_state.clk = CLK;
LCD_STATE_OUT = NOT SBUSY; LED[4..0] = NOT lcd_state_reg[]; LED[5] = NOT WRITE_COMMAND; LED[6] = NOT REFRESH; LED[7] = NOT REFRESH;
CASE lcd_state IS -- Инициализация дисплея WHEN s0 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s1; END IF; WHEN s1 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s2; END IF; WHEN s2 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s3; END IF; WHEN s3 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s4; END IF; WHEN s4 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s5; END IF; WHEN s5 => INSTR[] = B"00110000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s6; END IF; -- Переключение в 4-битный режим WHEN s6 => INSTR[] = B"00100000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s7; END IF; WHEN s7 => INSTR[] = B"00100000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"1"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s8; END IF; -- Определение количеств строк(2) и шрифта дисплея(5х8 точек) WHEN s8 => INSTR[] = B"00101000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s9; END IF; WHEN s9 => INSTR[] = B"00101000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s10; END IF; -- Включение дисплея, выключение курсора и его мигание WHEN s10 => INSTR[] = B"00001100"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s11; END IF; WHEN s11 => INSTR[] = B"00001100"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s12; END IF; -- Очищение дисплея WHEN s12 => INSTR[] = B"00000001"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s13; END IF; WHEN s13 => INSTR[] = B"00000001"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s14; END IF; -- выбор режима сдвига курсора вправо при записи в(чтении из) DDRAM WHEN s14 => INSTR[] = B"00000110"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s15; END IF; WHEN s15 => INSTR[] = B"00000110"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN -- инициализация регистров значениями по умолчанию char_cnt[] = X"0"; str_tmp[] = str_low; lcd_state = s16; END IF; -- Выполнение основных функций устройства -- Смещение указателя памяти DDRAM на верхнюю строку WHEN s16 => INSTR[] = B"10000000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s17; END IF; WHEN s17 => INSTR[] = B"10000000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s18; END IF; -- Пересылка верхней строки через блок lcd_DRAM в память LCD контроллера WHEN s18 => INSTR[] = X"00"; STR[] = str_high; REFRESH = B"1"; IF (SBUSY == B"1") THEN lcd_state = s19; END IF; WHEN s19 => INSTR [] = X"00"; STR[] = str_high; REFRESH = B"0"; IF (SBUSY == B"0") THEN lcd_state = s20; END IF; -- Смещение указателя памяти DDRAM на нижнюю строку WHEN s20 => INSTR[] = B"11000000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s21; END IF; WHEN s21 => INSTR[] = B"11000000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s22; END IF; -- Пересылка нижней строки через блок lcd_DRAM в память LCD контроллера WHEN s22 => INSTR[] = X"00"; STR[] = str_low; REFRESH = B"1"; IF (SBUSY == B"1") THEN lcd_state = s23; END IF; WHEN s23 => INSTR[] = X"00"; STR[] = str_low; REFRESH = B"0"; IF (SBUSY == B"0") THEN lcd_state = s24; END IF; -- Постоянное смещение окна вправо WHEN s24 => INSTR[] = B"00011000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"1"; IF (SBUSY == B"1") THEN lcd_state = s25; END IF; WHEN s25 => INSTR[] = B"00011000"; DELAY[] = X"2625A00"; ONE_CYCLE = B"0"; WRITE_COMMAND = B"0"; IF (SBUSY == B"0") THEN lcd_state = s24; END IF; END CASE; END;
--CONSTANT con_str_high = X"20312032203320342035203620372038203920313020313120313220313320313420313520313620"; --CONSTANT con_str_low = X"205045A3A54D203C544543543E205045A3A54D203C544543543E205045A3A54D203C544543543E20";
SUBDESIGN lcd_DRAM_ahdl ( STR[319..0]: INPUT; --строка для вывода на LCD REFRESH: INPUT; --обновление строки в LCD DRAM SBUSY: INPUT; --сигнал занятости подчиненного устройства CLK: INPUT; --входной тактовый сигнал с частотой 40МГц и периодом 25нс START_ACTION: OUTPUT; --сигнал запуска lcd_loader DATA[7..0]: OUTPUT; --инструкция LCD контроллеру BUSY: OUTPUT; --сигнал занятости устройства (0) свободен, (1) занят REG_SELECT: OUTPUT; --флаг выбор регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных) ONE_CYCLE: OUTPUT; --(1) 4-битная или (0) 8-битная команда (данные) )
--Автомат записи строк в память контроллера LCD VARIABLE dram_state : MACHINE OF BITS(lcd_state_reg[4..0]) WITH STATES(s0 = X"00", s1 = X"01", s2 = X"02", s3 = X"03", s4 = X"04", s5 = X"05", s6 = X"06", s7 = X"07", s8 = X"08", s9 = X"09", s10 = X"0A", s11 = X"0B", s12 = X"0C", s13 = X"0D", s14 = X"0E", s15 = X"0F", s16 = X"10", s17 = X"11", s18 = X"12", s19 = X"13", s20 = X"14", s21 = X"15", s22 = X"16", s23 = X"17", s24 = X"18", s25 = X"19", s26 = X"1A", s27 = X"1B", s28 = X"1C", s29 = X"1D", s30 = X"1E", s31 = X"1F");
START_ACTION : DFF; -- сигнал запуска lcd_loader DATA[7..0] : DFF; -- внутренний регистр для приема байт BUSY : DFF; -- сигнал занятости текущего устройста (0-свободен, 1-занят) REG_SELECT : DFF; -- флаг выбора регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных) ONE_CYCLE : DFF; -- (1) 4-битная или (0)битная команда (данные)
str_low[319..0] : DFF; -- регистр для хранения символов нижней строки str_high[319..0] : DFF; -- регистр для хранения символов верхней строки echo_delay[21..0] : DFF; -- счетчик колличества символов char_cnt[5..0] : DFF; -- переменная состояния автомата выбора и загрузки исходной строки data_tmp[7..0] : DFF; -- регистр для временного хранения байт str_tmp[319..0] : DFF; -- регистр строки значений пересылаемого символа
BEGIN START_ACTION.clk = CLK; DATA[].clk = CLK; BUSY.clk = CLK; REG_SELECT.clk = CLK; ONE_CYCLE.clk = CLK;
echo_delay[].clk = CLK; char_cnt[].clk = CLK; dram_state.clk = CLK; data_tmp[].clk = CLK; str_tmp[].clk = CLK;
CASE dram_state IS WHEN s0 => START_ACTION = B"0"; DATA[] = X"00"; REG_SELECT = B"0"; ONE_CYCLE = B"0"; BUSY = B"0"; REG_SELECT = B"0";
char_cnt[] = X"00"; echo_delay[] = X"000000"; IF (REFRESH == B"1") THEN dram_state = s1; END IF;
WHEN s1 => BUSY = B"1"; REG_SELECT = B"1"; ONE_CYCLE = B"0"; START_ACTION = B"0"; DATA[] = X"00";
echo_delay[] = X"000000"; char_cnt[] = X"00"; str_tmp[] = STR[]; dram_state = s4; --начало рабочего цикла WHEN s2 => BUSY = B"1"; REG_SELECT = B"1"; ONE_CYCLE = B"0"; DATA[] = str_tmp[319..312]; START_ACTION = B"1";
data_tmp[] = str_tmp[319..312]; echo_delay[] = X"000000"; char_cnt[] = char_cnt[]; str_tmp[] = str_tmp[];
IF (SBUSY == B"1") THEN dram_state = s3; END IF; WHEN s3 => BUSY = B"1"; REG_SELECT = B"1"; ONE_CYCLE = B"0"; DATA[] = data_tmp[]; START_ACTION = B"0";
data_tmp[] = data_tmp[]; echo_delay[] = X"000000";
IF (SBUSY == B"0") THEN char_cnt[] = char_cnt[] + 1; str_tmp[319..8] = str_tmp[311..0]; str_tmp[7..0] = data_tmp[]; dram_state = s4; ELSE char_cnt[] = char_cnt[]; str_tmp[] = str_tmp[]; END IF; WHEN s4 => BUSY = B"1"; REG_SELECT = B"1"; ONE_CYCLE = B"0"; DATA[] = X"00"; START_ACTION = B"0";
echo_delay[] = echo_delay[] + 1; char_cnt[] = char_cnt[]; str_tmp[] = str_tmp[];
IF (echo_delay[] == X"1FFFFF") THEN IF (char_cnt[] == X"28") THEN dram_state = s0; ELSE dram_state = s2; END IF; END IF; WHEN others => dram_state = s0; END case;
END;
SUBDESIGN lcd_command_ahdl ( CLK: INPUT; -- входной тактовый сигнал с частотой 40МГц и периодом 25 нс SBUSY: INPUT; -- сигнал занятости подчиненного устройства WRITE_COMMAND: INPUT; -- запуск процесса загрузки команды в LCD DELAY[27..0]: INPUT; -- задержка между инструкциями START_ACTION: OUTPUT; -- сигнал запуска lcd_loader BUSY: OUTPUT; -- сигнал занятости текущего устройства (0-свободен, 1-занят) REG_SELECT: OUTPUT; -- флаг выбор регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных) ) --Автомат записи строк в память контроллера LCD VARIABLE command_state : MACHINE OF BITS(cmd_state_reg[1..0]) WITH STATES(s0 = X"00", s1 = X"01", s2 = X"02", s3 = X"03"); --описание состояний цифрового автомата
START_ACTION : DFF; -- сигнал запуска lcd_loader BUSY : DFF; -- сигнал занятости текущего устройста (0-свободен, 1-занят) REG_SELECT : DFF; -- флаг выбора регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных)
delay_command[27..0]: DFF; -- регистр для хранения величины задержки между командами
BEGIN START_ACTION.clk = CLK; BUSY.clk = CLK; REG_SELECT.clk = CLK;
delay_command[].clk = CLK; command_state.clk = CLK;
CASE command_state IS WHEN s0 => BUSY = B"0"; START_ACTION = B"0"; delay_command[] = X"00000"; IF (WRITE_COMMAND == B"1") THEN command_state = s1; END IF; WHEN s1 => BUSY = B"1"; REG_SELECT = B"0"; command_state = s2; WHEN s2 => BUSY = B"1"; IF (SBUSY == B"0") THEN START_ACTION = B"1"; ELSE START_ACTION = B"0"; command_state = s3; END IF; WHEN s3 => BUSY = B"1"; IF (SBUSY == B"0") THEN delay_command[] = delay_command[] + 1; IF (delay_command[] == DELAY[]) THEN command_state = s0; END IF; END IF; WHEN OTHERS => command_state = s0; END CASE; END;
SUBDESIGN lcd_loader_ahdl ( CLK: INPUT; --входной тактовый сигнал с частотой 40МГц и периодос 25 нс REG_SELECT: INPUT; --флаг выбор регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных) START_ACTION: INPUT; --сигнал запуска lcd_loader ONE_CYCLE: INPUT; --(1) 4-битная или (0) 8-битная команда (данные) INSTR_DATA[7..0]: INPUT; --инструкция/команда LCD контроллеру от вышестоящих устройств DATA_OUT[3..0]: BIDIR; --4-х битный двунаправленный канал связи с контроллером LCD BUSY: OUTPUT; --запуск процесса загрузки команды в LCD RW: OUTPUT; --флаг чтения/записи из двунаправленного канала связи с контроллером LCD RS: OUTPUT; --флаг выбор регистра - переключает между записью команд и записью данных в память E: OUTPUT; --старт бит при пересылки команд и данных в контроллер )
--блок lcd_loader VARIABLE proc_state : MACHINE OF BITS(proc_state_reg[2..0]) WITH STATES(s0 = X"00", s1 = X"01", s2 = X"02", s3 = X"03", s4 = X"04", s5 = X"05");--описание состояний цифрового автомата DATA_OUT[3..0] : DFF;--4-х битный двунаправленный канал связи с контроллером LCD BUSY : DFF;--запуск процесса загрузки команды в LCD RW : DFF;--флаг чтения/записи из двунаправленного канала связи с контроллером LCD (0-запись, 1-чтение) RS : DFF;--флаг выбор регистра - переключает между записью команд и записью данных в память (0-команд, 1-данных) E : DFF;--старт бит при пересылки команд и данных в контроллер
epw[4..0] : DFF; --задержка, организующая нужную длительность сигнала Е delay_proc[5..0] : DFF; --задержка, организующая ожидания установки данных на выходных контактах (RS, RW) first_cycle : DFF; --флаг первого цикла - если 1, то идёт загрузка первой части команды (данных)
BEGIN DATA_OUT[3..0].clk = CLK; BUSY.clk = CLK; RW.clk = CLK; RS.clk = CLK; E.clk = CLK;
epw[].clk = CLK; delay_proc[].clk = CLK; first_cycle.clk = CLK; proc_state.clk = CLK;
CASE proc_state IS WHEN s0 => RW = B"0"; BUSY = B"0"; RS = B"0"; E = B"0";
IF (START_ACTION == B"1") THEN proc_state = s1; END IF; WHEN s1 => RW = B"0"; BUSY = B"1"; RS = REG_SELECT; E = B"0";
first_cycle = B"1";
delay_proc[] = X"00"; proc_state = s2; WHEN s2 => RW = B"0"; BUSY = B"1"; RS = REG_SELECT; E = B"0";
first_cycle = B"1"; delay_proc[] = delay_proc[] + 1; IF (delay_proc[] == X"1C") THEN --задержка, организующая ожидания установки данных на выходных контактах (RS, RW) proc_state = s3; END IF; WHEN s3 => RW = B"0"; BUSY = B"1"; RS = REG_SELECT; E = B"0";
first_cycle = first_cycle; epw[] = X"00"; IF (first_cycle == B"1") THEN DATA_OUT[] = INSTR_DATA [7..4]; ELSE DATA_OUT[] = INSTR_DATA [3..0]; END IF; proc_state = s4; WHEN s4 => RW = B"0"; --выдерживаем длительность высокого уровня сигнала Е == min 230 ns BUSY = B"1"; RS = REG_SELECT; E = B"1";
first_cycle = first_cycle; IF (first_cycle == B"1") THEN DATA_OUT[] = INSTR_DATA [7..4]; ELSE DATA_OUT[] = INSTR_DATA [3..0]; END IF;
epw[] = epw[] + 1; IF (epw[] == X"19") THEN epw[] = X"00"; proc_state = s5; END IF; WHEN s5 => RW = B"0"; --выдерживаем длительность низкого уровня сигнала Е == min 230 ns BUSY = B"1"; RS = REG_SELECT; E = B"0";
IF (first_cycle == B"1") THEN DATA_OUT[] = INSTR_DATA [7..4]; ELSE DATA_OUT[] = INSTR_DATA [3..0]; END IF;
epw[] = epw[] + 1; IF (epw[] == X"19") THEN IF ((ONE_CYCLE == B"1") OR (ONE_CYCLE == B"0" AND first_cycle == B"0")) THEN proc_state = s0; ELSIF (ONE_CYCLE == B"0" AND first_cycle == B"1") THEN first_cycle = B"0"; --первая половина команды/данных отправлена proc_state = s3; END IF; ELSE first_cycle = first_cycle; END IF; WHEN OTHERS => proc_state = s0; END CASE; END; |