Язык HPL (Hardware Programming
Language) разработан специально для описания
низкоуровневых протоколов обмена с различными
устройствами.
Рассмотрим реализацию языка HPL в
трансляторе программатора Orange.
Cимволом (*) обозначены примеры в данном
документе.
Cимволом (W) обозначены операторы и команды,
поддерживаемые только Windows версией.
Символ ';' является признаком
комментария - конец строки начиная с этого
символа не обрабатывается. Все пробелы внутри
текста удаляются, за исключением текстовых
строк, заключенных в кавычки "". Ключевые
слова пишутся только в верхнем регистре
(большими буквами).
Описание
протокола состоит из нескольких секций,
обязательными из них являются три: начальная, [READ]
- чтение слова и [WRITE] - записи слова.
Кроме этого, дополнительно можно использовать
необязательные секции:
[SETUP] - начальная установка,
вызывается однократно при загрузке модуля
[INIT] - инициализация, вызывается однократно
перед всеми операциями.
[WRITEINIT] - инициализации записи, вызывается
один раз перед началом записи EEPROM.
[WRITEEND] - завершение записи, вызывается один
раз после записи всех слов EEPROM.
[READBLOCK] - Чтение блока
[WRITEBLOCK] - Запись блока
[END] - завершение операции, вызывается один раз
для любой операции.
При необходимости могут
быть описаны дополнительные пользовательские
секции, которые добавляются в меню под своим
именем. Их названия также записываются в скобках [].
При использовании пробелов имена следует
дополнительно заключать в кавычки:
(*)["Test 1"]
Можно использовать локальные секции
(функции). Для описания функций перед именем
добавляется символ '_' :
(*) [_START]
Функции должны быть описаны в модуле
до их использования. Вызов функции
осуществляется по имени:
(*) _START.
Передача параметров возможна с
использованием регистров. Разрешено
использовать внутри функций вызовы других
функций. Не рекомендуется использование
рекурсивных вызовов.
Секции чтения и записи вызываются при
выполнении операций Read и Write для каждого слова.
Порядок вызова секций в режиме чтения:
[INIT]
FOR (ADR=0;ADR<SIZE;ADR++) {
[READ]
}
[END]
Порядок вызова секций в режиме записи:
[INIT]
[WRITEINIT]
FOR (ADR=0;ADR<SIZE;ADR++) {
[WRITE]
IF (Проверка записи)
[READ]
}
[WRITEEND]
[END]
Порядок вызова для пользовательских
команд:
[INIT]
[USERSECTION]
[END]
В начальной секции описываются:
Проверка текущего состояния пина (линий ввода)
или бита регистра R0-R7
(*) DAT?1
(*) R2[4]?0
или всего регистра:
(*) R4?0x1234
Если после операции
сравнения в скобках {} описан блок инструкций,
то он выполняется при равенстве бита(регистра) и
константы, иначе выполняется инструкция
следующая за скобками. Если такой блок не описан,
при несовпадении проверяемого значения
выполнение операции прерывается с выводом
сообщения "Error: Chip not respond!" и номером
строки HPL файла, на котором произошла остановка.
(*) R2[4]?0 {P1=0}, P2=1
Если 4 бит регистра R2 равен 0, то выполняется
инструкция P1=0, а потом P2=1, если бит равен 1
- то сразу P2=1
Если перед сравниваемым
числом стоит символ !, то условие меняется на
противоположное. (Блок выполняется при
несовпадении значений).
(*) R2?!1234H {P1=0}
Если R2 не равен 1234H, то выполняется инструкция
P1=0, если R2 = 1234H, выполняется следующая
инструкция.
LOOP - Циклы. Переменная циклов
может быть 3 видов: адрес, данные чтения/записи и
константа. Кроме того может быть описан
универсальный цикл, без указания переменной, в
этом случае доступ осуществляется через счетчик
(индекс) цикла I. Универсальные циклы могут быть
вложенными, во внутреннем цикле непосредственно
доступен только счетчик внутреннего цикла. Для
универсальных циклов границы могут быть также
заданы через регистры. Для совместимости с
будущими версиями рекомендуется использовать
только универсальные циклы!
Константа должна быть задана до начала
цикла с помощью инструкции CONST Границы цикла
должны лежать в пределах:
0...32767 - для универсальных циклов
0...32767 - для циклов CONST
0...1023 - для циклов DATA
0...31 - для циклов ADR
При выполнении кода для универсальных циклов не
контролируется выход индексов за пределы
реального размера DATA, ADR и CONST.
Скобки "{","}" ограничивают
тело цикла.
(*) LOOP=(15,0) {DI=R0[I],...}
линия вывода DI поочередно принимает значения
бит 15..0 значения регистра R0. (*) LOOP=(7,0)
{DI=ADR[I],...} (*) LOOP=ADR(7,0) {DI=I,...} - старый вариант
линия вывода DI поочередно принимает значения бит
7..0 текущего значения адресного слова.
(*) LOOP=(0,15) {DATA[I]=DO,..} (*) LOOP=DATA(0,15) {I=DO,..} - старый
вариант поочередное считывание бит 0..15 слова
данных с линии ввода DO.
(*) R1=10H
LOOP=(5,0) {D=R1[I],..}
(*) CONST=10H
LOOP=CONST(5,0) {D=I,..} - старый вариант линия вывода D
поочередно принимает значения бит 5..0 константы
(10H)
(*) LOOP=(R1,R2) {R3[I]=DATA[I]}
Копирование битов от R1 до R2 адресного слова в
соответствующие биты регистра R3
(*) LOOP=(0,7){ P1=0,LOOP=(6,1){P1=0,P1=1} }
Вложенные циклы.