Описание языка HPL


rev 2.8 (10.10.2011)


    Язык HPL (Hardware Programming Language) разработан специально для описания низкоуровневых протоколов обмена с различными устройствами.
Рассмотрим реализацию языка HPL в трансляторе программатора Orange.
Cимволом (*) обозначены примеры в данном документе.

Символ ';' является признаком комментария - конец строки начиная с этого символа не обрабатывается. Все пробелы внутри текста удаляются, за исключением текстовых строк. Текстовые строки следует заключать в кавычки "_".

Ключевые слова пишутся только в верхнем регистре (большими буквами).
Зарезервированные ключевые слова: "ADR", "BREAK", "BUSI", "BUSO", "CDELAY", "CONST" (устаревшее), "DATA", "EXIT", "GET", "IENABLE", "IDISABLE", "INFO", "INFOFILE", "LOOP", "MARK", "PING", "PINI", "PINO", "POWER", "Rxx", "RETURN", "SOCKET", "VCC", "VPP".

  • Числа. Числа могут задаваться в двоичном, десятичном и шестнадцатеричном формате.
    Если шестнадцатеричное число записано в виде nnnnH и начинается с буквы (A-F), то перед ним следует писать дополнительный 0.
    Двоичное число должно содержать только символы '0', '1' и завершаться 'b'.
    (*) 1234 - десятичная константа
    (*) 010101B - двоичная
    (*) 0x1234 или 0A234H - шестнадцатеричная.

  • Строки. Текстовой строкой является последовательность символов, заключенных в двойные кавычки (*) "Test"
    Пробелы внутри строк не удаляются. При необходимости использования символа " внутри самой строки, он заменяется на две простые кавычки: ' '.
  • Адрес. Ключевое слово ADR содержит текущее значение адресного регистра (32 разряда), по которому выполняется чтение/запись данных. Для адресного регистра могут использоваться операции установки (только в пользовательских секциях) и инкремента (в секциях блочного чтения и записи).
  • Данные. Для доступа к данным в буфере по текущему адресу используется ключевое слово DATA. Данные доступны пословно (8 или 16 бит в зависимости от установленного типа памяти) или поразрядно.
  • Регистры. Для выполнения математических и логических операций, описания констант и хранения данных можно использовать тридцать два 32-разрядных регистра R0...R9, RA...RF, R10...R1F. Регистры R0...R9 универсальные, регистры RA...RF специального назначения. Через них передаются параметры:
    RA - передача параметров
    RB - размер блока чтения/записи (из cfg) =$BLOCKSIZE
    RC - адрес текущей области
    RD - номер области для микросхем, имеющих несколько областей памяти (например, для микроконтроллеров) =$AREA.
    RE - глобальный код операции (1-READ,2-VERIFY,3-WRITE,4-USER)
    RF - полный размер памяти (из cfg) = $SIZE.

    Параметры устанавливаются автоматически перед запуском секции. Если значение, передаваемое специальным регистром не используется, он может использоваться как универсальный внутри секций. Формат объявления регистров дан ниже.

    Доступ к регистрам специального назначения возможен также через символьные имена:
    $SIZE - полный размер памяти (из cfg) = RF,
    $BLOCKSIZE - размер блока чтения/записи (из cfg) =RB,
    $VERIFY - режим сравнения (0 - отключен),
    $CDELAY - задержка (эквивалентна CDELAY),
    $WDELAY - задержка записи,
    $AREA - номер области памяти =RD,
    $VERSION - версия hardware и software.
    $MODE - параметр MODE из конфигурационного файла.
    Описание протокола состоит из нескольких секций, основными являются три: начальная, [READ] - чтение слова и [WRITE] - записи слова.

Кроме этого, дополнительно можно использовать следующие секции:
[SETUP] - начальная установка, вызывается однократно при загрузке модуля
[INIT] - инициализация, вызывается однократно перед всеми операциями.
[WRITEINIT] - инициализации записи, вызывается один раз перед началом записи EEPROM.
[WRITEEND] - завершение записи, вызывается один раз после записи всех слов EEPROM.
[READBLOCK] - Чтение блока
[WRITEBLOCK] - Запись блока
[END] - завершение операции, вызывается один раз для любой операции
[BOOT] - зарезервировано.
    При необходимости могут быть описаны дополнительные пользовательские секции, которые добавляются в меню под своим именем. Их названия также записываются в скобках [ ], они не должны совпадать с названиями основных секций. При использовании пробелов имена следует дополнительно заключать в кавычки:
(*) ["Test 1"]
Секции с именами [---] служат для использования в качестве разделителей меню и не вызываются.
Перед названием секции можно указывать дополнительные символы, определяющие порядок работы секции:
'!' - функция будет выполнятся без подачи питания на микросхему.
'#' - функция будет исполняться на процессоре ПК (реализация зависит от аппаратной платформы).
'~' - функция будет исполняться без секций [INIT] [END].
Для функций, предназначенных для математической обработки буфера рекомендуется использовать оба символа !#:
(*) [!#Calculator]
Можно использовать локальные секции (функции). Для описания функций перед именем добавляется символ '_' :
(*) [_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]
Измение ADR внутри секции не меняет глобального значения. В начальной секции описываются:

  • Номер панельки для установки микросхемы

    (*) SOCKET=1

    Если параметр SOCKET не указан, он считается равным 0. Нумерация панелек условная:
    1 I2C
    2 MicroWire
    3 SDA2506
    4 SPI
    5 NVM3060
    6 S2100R
    7 AT17XXX
    8 M35080
    В программаторах Orange4,5,SE установлена одна общая панелька, трансляция номеров выводов для заданного Socket производится автоматически.
  • Входные и выходные сигналы для подключения к программируемой микросхеме (pins):
    (*) PINO=SCL,0 - Выход, SCL - имя сигнала, 0 - виртуальный номер бита
    (*) PINI=SDA,1 - Вход, SDA - имя сигнала, 1 - виртуальный номер бита
    (*) PING=DAT,2 - Двунаправленный вывод типа "Открытый коллектор".

    Входные и выходные сигналы могут иметь одинаковые имена. Имена должны быть не длиннее восьми символов. Имена не могут совпадать с зарезервированными ключевыми словами. Трансляция виртуальных номеров в физические номера выводов производится в зависмости от значения SOCKET.
    Символ # перед именем является признаком инверсии сигнала:
    (*) PINO=#DTA,0

    Дополнительно можно указать реальный вывод микросхемы, к которому подключен сигнал:
    (*) PINO=SDA,1,5
    Это необходимо для создания графического изображения микросхем, имеющих неописанное либо нестандартное расположение выводов.
  • Входные и выходные шины (порты):
    (*) BUSO=ADDRBUS,1 - Выход, ADDRBUS - имя шины, 1 - виртуальный номер.
    (*) BUSI=DATABUS,0 - Вход, DATABUS - имя шины, 0 - виртуальный номер.

    Входные и выходные шины могут иметь одинаковые имена. Имена должны быть не длиннее восьми символов и не могут совпадать с зарезервированными ключевыми словами. Номера шин являются предопределенными, их набор зависит от установленного значения SOCKET и модели программатора.
  • Задержка после каждой операции установки пина (в микросекундах):
    (*) CDELAY=10
    Позволяет регулировать скорость обмена. Реальные задержки могут получаются насколько больше заданных.
  • Дополнительная информация о модуле, которая отображается в нижнем информационном окне. Не более одной строки.
    (*) INFO="Новый модуль"
  • Дополнительный справочный файл (вызывается из меню). Рекомендуемый формат файла - chm. Файл может находится в каталоге HPL или IMAGE
    (*) INFOFILE="megahelp.chm"
  • Общее число выводов в корпусе EEPROM (по умолчанию 8 для EEPROM, не более 48). Должен указываться после параметра SOCKET.
    (*) ALLPINS=14
  • Объявления регистров, доступных для использовании в функции GET, а также редактирования через меню (Buffer->Registers). Имена регистров не должны совпадать. Если регистры используются локально внутри секций, объявлять их не нужно.

    (*) R0=Protect - где "Protect" - имя регистра

    После имени регистра через запятую может быть указан тип (режим отображения значения) регистра:
    H - hex, D - Dec, B - Bin, L - List, C - CheckBox, S - String, P - PushButton.
    Если тип набран в нижнем регистре, он не отображается в окне редактирования.

    (*) R1=CODE,h

    Для типов H,D,B,C дополнительно может быть указано число отображаемых знаков:
    (*) R2=FLAGS,B8
    (*) R3=Box,C4

    Для типа L добавляется список текстовых строк.
    Значение регистра соответствует порядковому номеру выбранной строки начиная с 0.
    (*) R0=Mode,L,On,Off

    Строковые типы S при использовании в функции GET обязательно должны быть связаны с соответствующим массивом. Для этого в регистр следует занести порядковый номер массива (начиная с 0).
  • HPL - Ссылка на имя базового модуля. Только для Orange5. При необходимости функции могут быть разделены на 2 части: первая содержит базовые функции для работы с конкретной микросхемой, вторая - специфические вычисления. В конфигурационном файле должно указываться имя второго модуля, а во втором модуле - имя первого:
    (*) HPL=25c040.hpl
  • Массивы - предназначены для работы с блоками памяти заданной длины.
    Имена массивов должны начинаться с символа '@'.
    (*) @AR1=25 - незаполненый массив (25 элементов)
    (*) @AR2={1,2,3,4,5,6,7,8,9,10,11,12,13} - заполненый массив
    Работа с массивами описана ниже.
  • POWER - режим управления питанием:
    1 - автоматическое включение/выключение
    0 - управление из модуля.

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

Описание инструкций (команд):

  • Установка пина константой, битом данных, адреса или регистра:
    (*) SDA=1 - установка на выводе логической 1
    (*) SDA=DATA[2]
    (*) SCL=ADR[3]
    (*) PIN=R2[3]
    (*) PIN=Z - переключение в Z-состояние
    (*) PIN=P - вывод положительного импульса (аналогично PIN=1,PIN=0)
    (*) PIN=N2 - вывод двух отрицательных импульсов
  • Проверка текущего состояния пина (линий ввода) или бита регистра R0-R1F
    (*) 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, иначе выполняется сразу следующая инструкция.
  • Установка шины (порта) константой, cловом данных, адреса или регистра:
    (*) DBUS=1
    (*) DBUS=R5
  • Установка константы (устаревшее). Значения констант должны лежать в пределах 0...65535.
    (*) CONST=159

  • Операции с регистрами.

    1. Операции присвоения (копирования):
      Регистр-регистр (*) R2=R5
      Регистр-данные (*) R2=DATA
      Данные-регистр (*) DATA=R3
      Данные-число (*) DATA=0x45
      Разрядность данных в зависимости от установленного типа микросхемы составляет 8 либо 16 бит.

    2. Арифметические и логические операции
      (В операциях может участвовать константа или другой регистр):
      установка "=" (*) R0=356H
      сложение "+" (*) R0=+123 ; R0=R0+123
      вычитание "-" (*) R0=-R1 ; R0=R0-R1
      умножение "*" (*) R0=*R1 ; R0=R0*R1
      деление "/" (*) R0=/5 ; R0=R0/5
      взятие остатка "%" (*) R0=%10 ; R0=R0%10
      циклический сдвиг ">>", "<<" (*) R0=>>1 ; сдвиг R0 вправо на 1 разряд (бит 0 переносится в бит 31, бит 1 - в 0, бит 2 - в 1 и т.д....)
      Логическое И - AND "&" (*) R0=&R2 ; R0=R0 and R2
      Логическое ИЛИ - OR "|" (*) R0=|R2 ; R0=R0 or R2
      Исключающее ИЛИ - XOR "^" (*) R0=^R2 ; R0=R0 xor R2
      В операциях деления и взятия остатка запрещено использовать константу 0 либо регистр, содержащий 0!
      3) Битовые операции с регистрами (*) R0[R1]=1 - бит регистра R0, равный R1 установить в 1
  • LOOP - Циклы. Переменная циклов может быть 3 видов: адрес, данные чтения/записи и константа. Кроме того может быть описан универсальный цикл, без указания переменной, в этом случае доступ осуществляется через счетчик (индекс) цикла I. Универсальные циклы могут быть вложенными, во внутреннем цикле непосредственно доступен только счетчик внутреннего цикла. Для универсальных циклов границы могут быть также заданы через регистры. В Orange5 можно использовать только универсальные циклы! В в других программаторах для совместимости с будущими версиями также рекомендуется использовать только универсальные циклы.
    Константа должна быть задана до начала цикла с помощью инструкции 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,..} - поочередное считывание бит 0..15 слова данных с линии ввода DO
    (*) LOOP=DATA(0,15) {I=DO,..} - старый вариант .

    (*) R1=10H
    LOOP=(5,0) {D=R1[I],..}
    (*) LOOP=(R1,R2) {R3[I]=DATA[I]}
    Копирование битов данных от R1 до R2 в соответствующие биты регистра R3

    (*) LOOP=(0,7){ P1=0,LOOP=(6,1){P1=0,P1=1} }
    Вложенные циклы.
  • BREAK - выход из текущего цикла.
    (*) LOOP=(7,0) {PI?0{BREAK}}
  • Массивы. Для массивов доступны операции чтения и записи заданного элемента, а также получение размера массива. Хранится в массивах могут только байтовые (символьные) элементы.
    Массивы описываются в начальной секции.
    (*) R0=@AR - получить размер массива.
    (*) R0=@AR[1] - получить элемент массива N1.
    (*) LOOP(@M2){R0=@M1[I],@M2[I]=R0} - копировать данные из массива M1 в M2.
  • Задержка (в микросекундах). Значение задержки может быть в пределах 1мкс...65с.
    (*) P=200
  • PRINT Вывод сообщения. Вывод на экран окна с сообщением, строка должна быть заключена в кавычки. Внутри строки можно использовать стандартные модификаторы аналогичные используемым в языке C для printf(). (В DOS версии для всех числовых аргументов необходим дополнительный параметр l (long int)).
    %X - шестнадцатеричное значение
    %u - десятичное значение
    %c - символ (char)

    Для самостоятельного использования символа % (процент) внутри строки, он должен быть повторен дважды %%. Транслятор не проверяет правильность и количество передаваемых аргументов!

    (*) PRINT=("Hello, World")
    (*) PRINT=("Code=%04lX",R1) - Вывод значения регистра R1 в шестнадцатеричном виде - минимум 4 цифры.

    Можно также использовать дополнительные модификаторы, определяющие тип окна:
    E - Error - сообщение об ошибке
    (*) PRINT=E("Hello, Mr. Bug!")

    S - Status - запись в статусное окно
    (*) PRINT=S("Progress %03lu",R0)

    L - Log - запись в окно протокола
    (*) PRINT=L("Operation %02lu",R0)

    P - ProgressBar - Установка положения индикатора выполнения. Первое число - текущее значение, второе - максимальное значение (может не указываться).
    (*) PRINT=P(R0,32)

    A - Ask - запрос подтверждения пользователя
    Значение возвращается в регистре RA (Ok=1,Cancel=0)
    PRINT=A("Are you ready ?")
  • GET Ввод значений. На экран выводится окно с запросом данных. Строка в скобках задает заголовок окна. Одновременно могут быть введены сразу несколько значений в заданные регистры. Имена регистров и формат данных соответствуют описанным в начальной секции.
    (*) GET=("Enter value",R7,R8)
  • MARK Установка и получение отметки (выделения) текущего слова. Адрес соответствует значению ADR.
    (*) MARK=R1
    (*) R2=MARK
  • EXIT - Завершение работы секции.
  • RETURN - Выход из функции. Не может использоваться для прерывания цикла.
  • VCC - Управление напряжением питания (включение, выключение, установка):
    (*) VCC=1 - включить
    (*) VCC=0 - выключить
    (*) VCC=3000 - установить напряжение 3.0 V (значение в милливольтах)
  • VPP - Управление напряжением программирования (включение, выключение, установка). Функционирует при наличии аппаратной реализации в программаторе.
    (*) VPP=10000 - установить напряжение 10.0 V
    (*) VPP=0 - выключить
  • IDISABLE/IENABLE - Запрет/Разрешение прерываний. Используются для выполнения критичных по времени операций.
    Для каждой команды IDISABLE обязательна последующая IENABLE.

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