CUPL, part V, немного практики

Есть ПЛИС и желание сделать сравнение адресов. Для определённости, есть ATmega8515, которая имеет 512 байт ОЗУ внутри, плюс регистры, плюс ввод-вывод… Итого, первые 608 байт заняты. Надо сделать так, чтобы для адресов 608-65535 декодер адреса выдавал логическую 1 для выбора нужной микросхемы ОЗУ. Язык CUPL сравнение не поддерживает совсем. Спрашивается – как решить задачу?

Ответ очевиден – врукопашную. Для начала нужно почитать чего-нибудь теоретического. Например вот. Из этой статьи достаточно вытащить два момента:

ci = ai & !bi;
di = ai # !bi;

Первое условие сравнивает старшие разряды и даёт истину, если A больше B.
Второе говорит, что со старшими разрядами ничего не получилось, можно сравнивать младшие.
С этими условиями есть две проблемы:

  1. Последний внутренний адрес у ATmega – 0x25F, это 10 0101 1111. Единицы в младших разрядах это плохо, ибо ножек у ПЛИС мало. Поэтому хочется проверять адрес 0x260.
  2. Функция сравнения умеет проверять “больше”. А хочется получать результат вида “адрес >= 0x260”.

Решений может быть два: переписать сравнение так, чтобы оно давало результат функции “больше или равно”, либо перевернуть операнды местами.
Я выбрал второй вариант – сравнение “0x260 > адрес”. Фактически, оно даёт истину, при обращении к внутренним адресам, значит достаточно проинвертировать результат.
Сравнение нужно делать поразрядно. Сначала бит 15. Если не получилось, бит 14. Если не получилось, бит 13…
Бит 15 сравниваем так:

INT_RAM = K15 & !A15; // проверяем условие c15

K15 – это константа, A15 – это адрес.
INT_RAM – истина, если обращаемся к внутренним адресам (INTernal RAM).
Теперь сравниваем бит 14:

INT_RAM = K14 & !A14 & (K15 # !A15); // проверяем условие c14, если d15 истина (по старшим разрядам сравнить не удалось)

Результат сравнения всех разрядов объединяем по ИЛИ. В языке CUPL операцию ИЛИ можно писать в несколько строк с использованием ключевого слова APPEND.
Конечный результат будет выглядеть так:

APPEND INT_RAM = K15 & !A15;
APPEND INT_RAM = K14 & !A14 & (K15 # !A15);
APPEND INT_RAM = K13 & !A13 & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K12 & !A12 & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K11 & !A11 & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K10 & !A10 & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K9 & !A9 & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K8 & !A8 & (K9 # !A9) & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K7 & !A7 & (K8 # !A8) & (K9 # !A9) & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K6 & !A6 & (K7 # !A7) & (K8 # !A8) & (K9 # !A9) & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K5 & !A5 & (K6 # !A6) & (K7 # !A7) & (K8 # !A8) & (K9 # !A9) & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);
APPEND INT_RAM = K4 & !A4 & (K5 # !A5) & (K6 # !A6) & (K7 # !A7) & (K8 # !A8) & (K9 # !A9) & (K10 # !A10) & (K11 # !A11) & (K12 # !A12) & (K13 # !A13) & (K14 # !A14) & (K15 # !A15);

Младшие 4 бита (3..0) не участвуют в сравнении, они равны нулю (сравниваем с адресом 0x260). (Доказать не могу, но это работает :))
Результирующая функция получается достаточной сложной. Что делать? Очень просто. В качестве K15…K4 используется константа. И её надо подставить в выражение. Делать это ручной заменой всех K15..K4 совершенно не хочется (а вдруг адрес изменится!!), поэтому надо придумать красивое решение. Попробуем воспользоваться препроцессором, который есть в языке CUPL. Например, директивой define:

$define K15 'b'0
$define K14 'b'0
$define K13 'b'0
$define K12 'b'0
....

Осталось запустить компилятор и посмотреть, что же получится:

INT_RAM =>
!A5 & !A7 & !A8 & !A10 & !A11 & !A12 & !A13 & !A14 & !A15
# !A9 & !A10 & !A11 & !A12 & !A13 & !A14 & !A15
# !A6 & !A7 & !A8 & !A10 & !A11 & !A12 & !A13 & !A14 & !A15

Кажется, стало гораздо лучше 🙂

Полностью код проекта можно посмотреть вот здесь.
Помимо декодера адреса там есть реализация вот этой моей идеи.