Язык поведенческого моделирования PML
Своё название PML берёт от английских слов Pc-logs behavioral Modeling Language, что переводится как язык поведенческого моделирования для программы PC-LOGS. Данная программа входит в состав пакета PCAD 4.5 и называется моделятором (PC-LOGS является сокращением от слов LOGic Simulate - логическое моделирование).
Мы начнём знакомство с этого изрядно устаревшего языка по одной простой причине: он является самым простым, в нём всего 11 ключевых слов. Тем не менее, обойти вниманием названный язык было бы неправильным, так как многие заложенные и реализованные в нём идеи с успехом применяются в современных языках моделирования и описания электронной аппаратуры. Поэтому будем считать знакомство с PML своеобразной разминкой.
САПР PCAD 4.5 (Personal Computer Aided Design), в которой используется этот язык, имеет весьма ограниченный набор встроенных моделей. Их всего 30 штук и они разработаны для стандартных (типовых, базовых) компонентов цифровой аппаратуры, таких как логические элементы, триггеры, мультиплексоры и память.
Для прочих изделий цифровой схемотехники (счётчиков, регистров, дешифраторов, сумматоров, компараторов и т. п.) встроенных моделей не существует. Поэтому пользователь должен сам побеспокоиться о возможности моделировать нужные ему компоненты схемы. Пополнить набор встроенных моделей нельзя. Они потому и называются встроенными, что прикомпонованы к моделятору и не существуют в виде отдельного файла. Остаются два пути:
· построить макромодель, то есть иерархический символ, внутренняя структура которого содержит только стандартные элементы, поддерживаемые встроенными моделями;
· описать функцию (поведение) цифрового компонента на специальном языке логического моделирования, который в пакете PCAD носит уже известное нам название PML.
Понятно, что на языке PML можно запрограммировать поведение и тех стандартных компонентов, для которых существуют встроенные модели. Кстати, это хороший способ побыстрее освоить изучаемый язык.
Чтобы создать полное описание компонента, недостаточно запрограммировать его функцию на языке PML. Определённые изменения придётся внести и в графический образ компонента (рис.1).
Прежде всего, надо указать, что данный символ имеет PML-модель. Эта информация задаётся кодом идентификации: Type=100. Кроме того, PCAD должен знать, где найти саму модель.
Ссылка на файл, где хранится откомпилированная PML-модель, указывается с помощью так называемого MDL-атрибута, имеющего формат:
MDL = <имя_файла>.MDL.
Предположим, что мы написали PML-модель для компонента NAND2 и поместили её в файл NAND2.PML (имя файла и модели могут не совпадать). Откомпилировав исходный текст программой PC-MODEL, мы обнаружим объектный код модели в выходном файле NAND2.MDL. На него то и надо сделать ссылку из графического описания компонента NAND2 (рис.1):
MDL = NAND2.MDL
|
Язык моделирования PML напоминает в общих чертах язык программирования Си. Однако в отличие от последнего, язык PML работает с логическими переменными и условными выражениями в трёхзначном алфавите {0, 1, X}. Другими словами, любой сигнал в схеме может принимать одно из трёх значений:
"0" - низкий уровень (Low);
"1" - высокий уровень (High);
"X" - неизвестное значение (Unknown).
Кроме того, каждый логический уровень (то есть "0", "1", "X") характеризуется вполне определённой логической силой (Logic Strength), косвенно показывающей мощность или нагрузочную способность выхода.
В PCAD'e определены четыре значения логической силы:
"S" - питание (Supply);
"D" - управление (Driving);
"R" - резистивный выход (Resistive);
"Z" - высокоомный выход (High Z).
Логическая сила убывает в следующем порядке: S > D > R > Z. Она используется в случае конфликтной ситуации, когда два или более выхода с различными уровнями пытаются управлять общей цепью.
И, пожалуй, самая важная особенность языка PML заключается в том, что он позволяет непосредственно запрограммировать временную задержку, с которой входной сигнал передаётся на выход. Таким образом, язык PML позволяет описать не только функцию (поведение) цифрового компонента, но и представить в модели самые важные его свойства - временную задержку и нагрузочную способность.
На рис.2 показана PML-модель логического элемента NAND2. Создавая эту модель необходимо помнить ещё об одном правиле: имена выводов на условном графическом изображении компонента (рис.1) и соответствующие им имена узлов (сигналов) в PML-модели должны быть одинаковыми. Если, например, выходной контакт символа NAND2 называется OUT1, то таким же должно быть его имя и в PML-модели.
NAND2 ()
INPUT IN1, IN2;
OUTPUT OUT1 (20, 22,"D","D");
{
OUT1 = ~(IN1 & IN2);
}
Рис. 2. PML-модель логического элемента NAND2
Рассмотрим более подробно структуру и названные выше особенности PML-модели. Структура её интуитивно понятна. Она состоит из двух частей - заголовка модели и исполняемого раздела.
В заголовке модели указывается её имя (в примере NAND2), затем в круглых скобках следует список формальных параметров, через которые в модель передаются необходимые данные. Позднее мы рассмотрим эту возможность. Список формальных параметров может быть и пустым, как в нашем примере, однако круглые скобки все равно необходимы.
За именем модели следует описание интерфейса, с помощью которого компонент взаимодействует с внешним окружением. Ключевое слово INPUT определяет входы компонента, а вслед за ключевым словом OUTPUT следует список его выходов. В нашем примере элемент имеет всего один выход OUT1.
Исполняемый раздел выделяется фигурными скобками { ... }. В примере, показанном на рис.2, он содержит всего один оператор присваивания:
OUT1 = ~(IN1 & IN2);
Здесь операция логического умножения закодирована символом "&", а инверсия записывается как "~".
Обратите внимание на список параметров, сопровождающих объявление выходного сигнала (узла) OUT1. Он называется PCL-списком и содержит информацию о задержках, с которыми передаются входные сигналы IN1и IN2 на выход OUT1, и о логической силе этого выхода.
Формат PCL-списка жестко фиксирован. На первой позиции в нём указывается задержка при переключении выхода из 0 в 1 (RISE - подъём, фронт) . В данном примере она составляет 20ns. Затем приводится задержка переключения выхода в обратном направлении - 22ns (FALL - срез, спад). Далее следуют два значения, характеризующие силу выхода при появлении на нём 0 и 1 соответственно. В примере они одинаковы и равны "D".
Заметим, что в языке PML большие и малые буквы не различаются, так что с тем же успехом мы могли записать вместо строки
OUTPUT OUT1 (20, 22,"D","D");
например, такую:
Output Out1 (20,22,"d","D");
PCL-список можно приписать любому выходному узлу, причём это разрешается делать не только в "шапке" модели, но и в исполняемом разделе. Модификация модели, показанная на рис.3, тоже вполне работоспособна. Заметим, что PCL-список допустимо задавать только в одном разрешённом месте.
Обратите внимание ещё на одно изменение, сделанное в PML-модели. Теперь единичное состояние выхода OUT1 имеет силу "Z", что характерно для схем с открытым коллектором.
NAND2 ()
INPUT IN1, IN2;
OUTPUT OUT1;
{
OUT1 = ~(IN1 & IN2) (20, 22,"D","Z");
}
Рис. 3. Модификация PML-модели логического элемента NAND2
Рассмотрим ещё один вариант построения PML-модели элемента NAND2. Он отличается от ранее описанных тем, что содержит список формальных параметров (рис.4).
NAND2 (RISE, FALL, STR0, STR1)
INPUT IN1, IN2;
OUTPUT OUT1 (RISE, FALL, STR0, STR1);
{
OUT1 = ~(IN1 & IN2);
}
Рис. 4. PML-модель логического элемента NAND2 со списком формальных параметров
При вызове модели на место формальных параметров должны быть подставлены фактические, но где PCAD их найдёт? Оказывается, об этом должен позаботиться сам пользователь и сообщить их значения в уже известном нам MDL-атрибуте.
На рис.5 показаны два реальных элемента из различных TTL-серий. Они отличаются не только задержками, но и логической силой выходов. Тем не менее, логическая функция у них одна и та же. Следовательно, можно попробовать промоделировать их с помощью одной и той же модели.

Рис. 5. Элементы 555LA3 и 531LA3 ссылаются на одну и ту же PML-модель NAND2, но в неё передаются различные параметры задержек и логической силы
Как видно из рисунка 5, оба компонента ссылаются на одну и ту же модель NAND2. В этом нет ошибки, так как логическая функция у них одинаковая - 2И-НЕ. Различия между ними заключаются в быстродействии и нагрузочной способности. Но эти уникальные для каждого компонента параметры вынесены за пределы PML-модели. Тем не менее, при моделировании они будут учтены, так как при вызове модели компонента 555LA3 в неё будут переданы значения (20,22,"D","D"), а для компонента 531LA3 - (5,4,"S","S").Таким образом, с помощью одной модели можно имитировать работу многих однотипных компонентов.
В отличие от PCL-списка, имеющего фиксированный формат, список формальных параметров может содержать разное число параметров (от 0 до 32), которые используются как переменные в PCL-списках. Через них можно передать в модель только информацию о задержках и логической силе выходов.
Рассмотрим ещё один пример: PML-модель двоичного суммирующего счётчика COUNT_4 (рис. 6). Он имеет 4 разряда Q0,Q1,Q2,Q3, сбрасывается высоким уровнем на входе R и считает по фронту сигнала C.


Рис. 6. PML-модель двоичного суммирующего счётчика COUNT_4
Здесь мы встречаемся ещё с одним новшеством - описанием так называемых локальных сигналов (узлов) и шин. Объявление локальных сигналов задаётся ключевым словом LOCAL, вслед за которым следует список таких сигналов.
Локальные, то есть внутренние сигналы (узлы или шины), могут быть определены только внутри PML-модели. Они характеризуются пятью особенностями.
Во-первых, они имеют собственную сигнальную память, которая выделяется динамически для каждого экземпляра (копии) схемного элемента с данной PML-моделью. В этой памяти фиксируется текущее состояние каждого экземпляра моделируемого элемента, например триггера, счётчика или регистра.
Заметим, что это состояние сохраняется и после выхода из подпрограммы модели, в отличие от классической локальной переменной, которая за пределами подпрограммы становится неопределённой.
Во-вторых, в списке локальных сигналов можно объявить не только одиночный узел, но и шину. В нашем примере как раз и объявляется такая шина. Имя её MEM, а ширина шины - 4. Она указывается в квадратных скобках и несёт информацию о числе проводников в ней.
В каком то смысле шина - это "вещь в себе", потому что за пределами PML-модели она не видна: пакет PCAD 4.5 не поддерживает описание шинных структур. Именно по этой причине шину нельзя объявить в списках входных, выходных или двунаправленных (ключевое слово INOUT) узлов.
По этой же причине при выдаче результатов моделирования из PML-модели шина должна быть преобразована в соответствующее число одиночных узлов (проводников). В нашем примере эта работа выполняется операторами присваивания:
Q0=MEM [0]; Q1=MEM [1]; Q2=MEM [2]; Q3=MEM [3];
Третья особенность связана с выводом на экран монитора временных диаграмм локальных сигналов. Шинные сигналы вообще нельзя визуализировать ни из программы моделятора, ни из постпроцессора моделирования.
Локальные узлы (и отдельные индексные компоненты шины) в принципе можно наблюдать на экране монитора. Но в отличие от внешних узлов (входных, выходных и двунаправленных), которые ассоциируются с именем цепи, присоединённой к данному выводу, локальный сигнал ассоциируется с именем компонента, для которого он определён.
Например, если в модели JK-триггера определить внутренний (локальный) сигнал Q_INT, а затем использовать эту модель в двухразрядном счётчике с именами триггеров U1 и U2, то увидеть внутренние состояния обоих триггеров можно только через их составные имена: U1/Q_INT и U2/Q_INT.
Видно, что сигнальная память для обоих экземпляров JK-триггера имеет одно и то же название - Q_INT, однако теперь в составном имени сообщается, для какого конкретного триггера выводится нужная нам информация.
Четвёртая особенность заключается в том, что локальные узлы и шины имеют статус сигналов двунаправленного типа. Другими словами, их можно включать как в левую, так и в правую части операторов присваивания (по разные стороны от знака присваивания "="). Входные и выходные сигналы (узлы) такого не допускают. Входы могут находиться только в правой части оператора присваивания, а выходы - только в левой.
Ну и, наконец, последняя особенность заключается в том, локальным узлам и шинам можно задавать PCL-списки, но в отличие от внешних выходных узлов, в них должны быть одинаковые задержки по фронту и срезу. В нашем примере PCL-список (8,8,"d","d"), приписанный шине MEM, удовлетворяет этому ограничению. А вот если бы мы написали (8,12,"d","d"), то PCAD зафиксировал бы некорректность и проигнорировал вторую задержку в 12ns, приписав ей значение первой - 8ns.
Язык PML позволяет одновременно назначать PCL-списки локальным и выходным сигналам. В этом случае их задержки просуммируются. В примере новое состояние счётчика будет вычисляться с задержкой 8ns. В момент передачи нового состояния на выход Q3 к этой задержке добавится ещё 2ns, если выход переключается на 1, и 4ns, если Q3 переключается в 0. Для других выходов Q0, Q1 и Q2 тоже добавится задержка, определённая умолчанием в 1ns.
При построении PML-модели не следует без особой необходимости вводить локальные сигналы (узлы и шины), так как они занимают дополнительную область памяти и замедляют процесс моделирования. Например, PML-модель D-триггера с асинхронным сбросом и динамической записью по фронту может вообще не содержать локальных сигналов, потому что новое состояние триггера определяется только набором входных сигналов R, C, D и не зависит от старого состояния (рис.7).

DFF ()
INPUT R, D, C;
OUTPUT Q;
{
IF (R) Q="0"; /* Асинхронный сброс, если R равно 1 */
ELSE {IF (C=="/") Q=D;} /* Тактируемая запись со входа D, если R равно 0, */
/* а C переключается из 0 в 1 */
OTHERWISE Q="X"; /* Выход не определён, если R равно X */
}
Рис. 7. PML-модель D-триггера, не использующая локальные сигналы
Узлы и шины
Узел (node) - это точка, в которой выполняется электрическое соединение проводников (сегментов) цепи. Разница между проводником и цепью чисто условная: проводник соединяет две точки (два узла) схемы, цепь может иметь более двух узлов (рис. 8).
Узел отражает физическое или логическое состояние цепи. Каждая цепь имеет имя, с которым обычно отождествляется и имя сигнала, действующего в этой цепи. Так как все точки цепи эквипотенциальны, то слово "сигнал" может ассоциироваться и со словом "узел", который является одной из точек цепи.
Итак, присвоив цепи какое-либо имя, например F1, мы можем распространить его и на узел и на сигнал, принадлежащие этой цепи. В языке PML узел - это символическое имя, идентификатор переменной, которая может принимать три значения: "0", "1", "X". Например, запись
Q1 = "1";
означает, что сигналу (узлу, цепи) с именем Q1 присвоен высокий логический уровень. Заметим, что значение узла должно заключаться в двойные кавычки.

Рис. 8. Проводники, цепи, узы, сигналы
Аналогичная запись, дополненная PCL-списком:
Q1 = "1" (4,4,"d","d");
интерпретируется так: узел Q1 получит высокий уровень не в текущий момент модельного времени, а спустя 4 ns, то есть уже в новом цикле работы моделятора.
Отдельные узлы (их называют также одиночными) могут объединяться в узловые выражения с помощью следующих операций:
~ - логическая инверсия;
& - логическое "И";
| - логическое "ИЛИ";
^ - исключающее "ИЛИ".
Примером узлового выражения может служить правая часть оператора присваивания, использованного в нашей первой PML-модели (рис.2):
OUT1=~(IN1 & IN2);
В приведённом примере узловое выражение содержит только одиночные узлы IN1 и IN2. Однако оно может включать и узловые значения, и другие узловые выражения. Например, вполне правомочно написать такое узловое выражение:
(A1&A2)|(B1^D2)|~C1|(A1&"1")
На рис.9 приведена потоковая PML-модель мультиплексора на два входа, где используются только узловые выражения. Внутренние сигналы мультиплексора объявлены как локальные узлы F1, F2 и F3.
|
Рис.9. Структурная схема и PML-модель мультиплексора 2->1
Конечно, проще создать поведенческое описание, которое в нашем случае вообще состоит из одной строки (рис.10):
MUX2_PML ()
INPUT D0,D1,A;
OUTPUT Y;
{
Y=(D0&~A)|(D1&A); /* поведенческое описание, аналитическая модель */
}
Рис. 10. Аналитическая PML-модель мультиплексора 2->1
Из рисунка видно, что операция "~ - логическая инверсия" имеет более высокий приоритет нежели "& - логическое умножение".
Шина - это несколько проводников или узлов, воспринимаемых как единое целое. Шина предназначена для параллельной передачи информации. Внешне описание шины состоит из имени (идентификатора) шины, затем в квадратных скобках указывается её ширина (разрядность) и, наконец, в круглых скобках - PCL-список. Ширина шины несёт информацию о числе узлов (проводников), объединённых в данную шину. PCL-список не является обязательным и может быть опущен.
В приведённом ниже примере объявляются три 16-разрядные шины A, B и ADR в различных допустимых форматах:
LOCAL ADR[16] (0,0,"D","D"), /* десятичный формат */
A[020]; /* восьмеричный формат */
LOCAL B[0X10]; /* шестнадцатеричный формат */
Как видно, ключевое слово LOCAL можно повторять неоднократно, разрешается также переносить список локальных сигналов на новую строку. Максимальное число проводников в шине ограничено числом 31. Поэтому можно написать: BUS_MAX[31]. А вот запись BUS_MAX_BAD[32] уже будет ошибкой.
С математической точки зрения шина представляет собой вектор, то есть одномерный массив узлов (элементов). Доступ к элементу массива осуществляется по индексу.
Объявим четырёхразрядную шину с именем M:
LOCAL M[4];
Эта запись означает, что в шине имеются четыре проводника с именами: M[3], M[2], M[1], M[0]. Старшим считается разряд M[3], младшим - M[0]. Отдельный элемент шины (узел) принято называть индексным компонентом шины (ИКШ). Поскольку ИКШ является узлом (одиночным проводником), то он может входить в любые узловые выражения.
Например, справедливо сделать следующие присваивания:
M[3] = "1";
M[2] = A1&A2;
M[1] = "0";
M[0] = "1";
Здесь A1 и A2 должны быть объявлены как узлы. Если их значения в текущий момент модельного времени равны "1", то и M[2] равно "1". Это означает, что определены значения всех индексных компонентов шины, следовательно, можно говорить и о значении всего шинного сигнала. В примере оно равно
Конечно, проще и быстрее указать значение всей шине сразу, а не перечислять значения каждого его ИКШ. Это легко сделать одним действием:
M = "015"; /* восьмеричный формат */
M = "0XD"; /* шестнадцатеричный формат */
M = "0B1101"; /* двоичный формат */
Во всех случаях шине M присваивается значение 13. Но вот записать его в десятичном формате (M = "13";) нельзя, потому что компилятор языка PML не сможет правильно интерпретировать такую запись. Действительно, имя шины не отличается от имени узла. Значит, компилятор "узнаёт" шину по формату присваиваемого ей значения. Но если написать: M = "1"; (или M = "0";), то отличить шину от узла станет просто невозможно. Та же самая запись, сделанная в других форматах, легко и правильно интерпретируется: M = "01"; M = "0X1"; M = "0B1"; Видно, что M - это шина, а не узел.
Отдельные узлы можно объединять, "складывать" в шину с помощью операции конкатенации, называемой иначе [cat-узел]. Слово Cat происходит от Catenation - сцепление. Таким образом, с помощью cat-узла легко наполнить шину конкретным содержанием.
Например, в следующем фрагменте текста на языке PML шине Q_BUS присваиваются различные значения:
OUTPUT Q1, Q2, Q3, Q4;
INPUT D1, D2, D3, D4;
LOCAL Q_BUS[4];
{
Q_BUS = [D4,D3,D2,D1]; /* значение шины определяется входами D4..D1 */
Q_BUS = ["1","1","0","1"]; /* шине присваивается значение 13 */
Q_BUS = [D4,"1","0",D1]; /* в cat-узел можно подставлять узлы и */
/* узловые значения "0","1","X" */
}
К сожалению, в cat-узле недопустимо использовать узловые выражения, например в следующей строке
Q_BUS = [~D4, D3&D2, "0", D0];
на первых двух позициях в cat-узле стоят узловые выражения, что воспринимается компилятором PML как ошибка.
Мы уже знаем, что шина является внутренним достоянием PML-модели: выдать её значение наружу нельзя. Поэтому приходится выполнять операцию, обратную конкатенации, то есть "раскладывать" шину на отдельные узлы. Такая работа выполняется с привлечением ИКШ:
Q3 = Q_BUS[3];
Q2 = Q_BUS[2];
Q2 = Q_BUS[1];
Q0 = Q_BUS[0];
При конструировании шинных выражений в них могут входить другие шинные выражения, одиночные шины или шинные значения. Шинные значения, как уже известно, должны быть записаны в двоичном, восьмеричном или шестнадцатеричном форматах.
Шины объединяются в шинные выражения с помощью следующих операций:
& - поразрядное "И";
| - поразрядное "ИЛИ";
~ - поразрядная инверсия;
^ - поразрядное исключающее "ИЛИ";
>> - поразрядный сдвиг вправо (в сторону младших разрядов);
<< - поразрядный сдвиг влево (в сторону старших разрядов);
+ - арифметическое сложение.
Обратите внимание, в языке PML нет операции вычитания шинных сигналов. Спрашивается, как же моделировать вычитающие и реверсивные счётчики? На самом деле всё очень просто: вычитание заменяется сложением в дополнительном коде. Это поясняется следующим фрагментом PML-кода:
Q_BUS = Q_BUS + "0X1"; /* добавление 1 в младший разряд шины */
Q_BUS = Q_BUS + "0XF"; /* вычитание 1 из младшего разряда шины */
Несколько слов следует сказать и об операции сдвига. Строка:
Q_BUS = Q_BUS << 1;
предписывает программе сдвинуть значение шины Q_BUS на один разряд влево, то есть в сторону старших разрядов кода. При сдвиге освобождающиеся позиции справа заполняются нулями. Разряды "выталкиваемые" за разрядную сетку левого операнда, теряются. Поэтому двоичное число 1101 при сдвиге влево на один разряд превратится в число 1010. В правой части оператора
Q_BUS = Q_BUS << 1;
стоит константа, указывающая, на сколько разрядов необходимо сдвинуть значение шины. В нашем примере число сдвигается на один разряд. Константа может быть записана в восьмеричном, десятичном или шестнадцатеричном формате.
Заканчивая разговор о шинных сигналах, приведём пример PML-модели двоичного 4-разрядного сумматора 555IM6 (рис.11).
|
Рис.11. Условное графическое обозначение и PML-модель двоичного сумматора 555IM6
Сумматор 555IM6 складывает два четырёхразрядных числа A и B плюс входной перенос CRI и формирует на выходе четырёхразрядную сумму плюс выходной перенос CRO. Младшим разрядом в слове является бит с индексом 0.
В PML-модели объявлены три внутренние шины A, B и S. Шины A и B - четырёхразрядные, шина S - пятиразрядная. Значения сигналов на входных шинах A и B вычисляются с нулевыми задержками, на выход сумма передаётся с задержкой 10 + 1 ns. Задержка в 1ns добавляется к внутренней задержке 10 ns на шине S в момент передачи шинного сигнала на внешние выходы S0, S1, S2, S3 и CRO. Об этой (задаваемой умолчанием) задержке уже говорилось ранее.
Шины A и B формируются известной нам операцией конкатенации (cat-узел). Обратная операция разложения шины S на отдельные узлы выполняется с привлечением индексных компонентов шины ИКШ. Всё это мы уже "проходили".
Новым для нас является формат оператора IF. Он работает так. Если условное выражение (то есть входной перенос CRI) принимает значение "1", то к вычисляемой сумме добавляется "1" в младший разряд, иначе, то есть когда входной перенос CRI определён и равен "0", к полученной сумме ничего не добавляется. В противном случае (когда CRI == "X") определить значение суммы не удаётся. Приходится всем её ИКШ присваивать значение "X".
Можно ли написать PML-модель сумматора более экономным образом? Несомненно. Это подтверждает следующий пример (рис.12).
Здесь с помощью cat-улов объединяются входные узлы обоих слагаемых без предварительного объявления шин A и B. Ещё один новаторский приём состоит в том, что к шинным сигналам добавлен входной перенос CRI. Но посмотрите внимательнее, как он добавлен! Это не узел, а одноразрядная шина. И всё работает. Значит, в языке PML можно суммировать шины разной ширины. Их значения предварительно выравниваются по правому (младшему) разряду.
![]() |
Рис.12. Более компактный код PML-модели двоичного сумматора 555IM6
В связи со сказанным возникает ещё один законный вопрос: а позволит ли PCAD собрать в cat-узел и выходные сигналы без предварительного объявления выходной шины S? Ведь тогда можно было бы написать следующую строку:
[CRO, S3,S2,S1,S0] = [A3,A2,A1,A0] + [B3,B2,B1,B0] +[CRI];
В некоторых языках моделирования, таких как DSL в пакете DesignLab_8 или VHDL в пакете OrCAD 9.1 (понятие "агрегат") такая запись вполне корректна (если не обращать внимания на разную ширину шин). Увы, в языке PML до этого тогда не додумались.
Условные выражения
Они применяются в условных операторах и в операторах выбора и могут принимать три значения:
"1" - истина;
"0" - ложь;
"X" - не определено, то есть условие не известно.
Так как любой одиночный узел или узловое выражение принимают те же самые значения, то из этого следует, что они могут быть использованы в качестве условных выражений. Например, справедливо написать:
IF (~R&S) Q = "0"; /* сброс триггера в 0 низким уровнем на входе R */
IF (R) Q_BUS = "0X0"; /* сброс счётчика в 0 высоким уровнем на входе R */
Узловые выражения, одиночные узлы и их значения могут быть связаны в условные выражения операциями:
== - условное равенство;
!= - условное неравенство.
Например:
IF (R=="1") Q_BUS = "0X0";
IF (A!= B) Y = "0"; /* на выходе Y одноразрядного цифрового */
/* компаратора фиксируется неравенство узлов A и B */
Интересной и исключительно полезной особенностью таких условных выражений является возможность сравнить узел с фронтом или срезом. Благодаря ей легко программируется динамический вход триггеров, регистров или счётчиков. Например, в условном операторе
IF (C == "/") Q =D; /* загрузка в D-триггер данных по фронту C */
переменная Q получит значение переменной D только в том случае, если узел C (тактирующий вход) переключается в текущий момент времени с низкого уровня на высокий. Как видно из данного примера, фронт сигнала обозначается символом "/" - наклонная черта вправо (slash), заключённым в двойные кавычки. Аналогичным образом обозначается срез сигнала: "\" - наклонная черта влево (backslash).
Заметим, что в операторе присваивания использование названных символов недопустимо. Компилятор языка PML зафиксирует ошибку, если вы наберёте следующий текст:
Q = "/"; /* попытка присвоить сигналу Q */
/* несуществующее значение типа фронт */
Условные выражения можно объединять операциями:
&& - условное "И";
|| - условное "ИЛИ".
Например,
IF (R!= "0") && (C == "/") Q=D; /* динамическая запись данных
с входа D, если в текущий момент узел C переключился из 0 в 1
и нет команды сброса на входе R */
IF (R!= "1") && (C == "\") && (Q_BUS!= "0X9") Q_BUS = Q_BUS + "0X1";
В последнем примере приведён фрагмент PML-модели двоично-десятичного счётчика. Здесь проверяется одновременное выполнение трёх условий:
· нет сброса на входе R - (R!= "1");
· пришла команда счёта: срез импульса на входе C - (C == "\");
· в счётчике не достигнуто максимальное значение 9: (Q_BUS!= "0X9").
Только при выполнении всех трёх названных условий содержимое счётчика будет увеличено на 1 (Q_BUS = Q_BUS + "0X1";).
Понятно, что в отличие от узлов, шины и шинные выражения нельзя непосредственно использовать в качестве условных выражений. Однако из них тоже можно образовывать условные выражения, привлекая для этого следующие операции:
== - условное равенство;
!= - условное неравенство;
> - больше;
>= - больше или равно;
< - меньше;
<= - меньше или равно.
Например,
IF (A_BUS > B_BUS) || (A_BUS < B_BUS) Y = "0";
IF (A_BUS == B_BUS) Y = "1";
Здесь сравниваются шинные сигналы A_BUS и B_BUS. Если они не равны, то на выходе Y цифрового компаратора устанавливается низкий уровень, а если равны, то - высокий.
Можно сравнивать шинные сигналы, используя понятие cat-узла. Например:
IF ([A3,A2,A1,A0] == [B3,B2,B1,B0]) Y = "1';
IF (A_BUS == ["1","1","0","1"]) Q_BUS = "0XD";
Условные операторы
В языке PML определены три вида операторов:
· условные операторы;
· операторы присваивания;
· операторы выбора.
Познакомимся с ними более подробно.
Условные операторы реализуют те же функции, что и во всех языках программирования, с одной, правда, оговоркой: условное выражение в операторе может принимать не два, а три значения: "1", "0", "X".
Полный формат условного оператора выглядит следующим образом:
IF (условное_выражение) оператор_1; /* часть 1 */
ELSE оператор_2; /* часть 2 */
OTHERWISE оператор_3; /* часть 3 */
Он предписывает программе выполнить следующие действия. Если значение условного выражения равно "1", то выполняется опреатор_1, если значение условного выражения равно "0", то выполняется оператор_2, в противном случае (OTHERWISE), если условное выражение равно "X", то выполняется оператор_3.
На месте оператора_1, оператора_2 или оператора_3 может стоять список операторов, заключённый в фигурные скобки. Разделяющий символ ";" в этом случае не нужен. Условное выражение во всех случаях должно быть заключено в круглые скобки, чтобы обеспечить требуемый порядок вычислений.
Допустим сокращенный формат условного оператора, в котором отсутствует третья часть, начинающаяся с ключевого слова OTHERWISE, или одновременно вторая и третья части.
В условном операторе можно использовать ключевое слово RETURN, чтобы объявить о досрочном завершении PML-программы. Все последующие операторы в этом случае выполняться не будут.
Рассмотрим применение условного оператора на простых моделях элемента, реализующего логическую операцию 2И. Элемент назовём AND2 (рис.13).
|
![]() |
Рис. 13. Условное графическое обозначение и таблица истинности элемента AND2
в трёхзначной логике
PML-модель элемента AND2, использующая полный формат условного оператора, показана на рис. 14. Заметим, что для того, чтобы "работала" третья часть OTHERWISE условного оператора, необходимо в качестве условного выражения использовать узловое выражение (IN1&IN2).
![]() |
Рис. 14. PML-модель элемента AND2 с полным форматом условного оператора IF
Действительно, стоит заменить строку
(IN1&IN2) /* узловое выражение */
например, такой
(IN1&IN2=="1";) /* условное выражение */
и неизвестное состояние выхода "X" перестанет вычисляться.
По той же причине два узловых выражения, объединённые в условное выражение
(IN1)&&(IN2)
работают правильно, то есть показывают X-состояние. Но стоит заменить их двумя условными выражениями
(IN1=="1")&&(IN2=="1"),
как всё "ломается".
Рассмотрим другой вариант PML-модели того же элемента AND2 (рис. 15). Здесь используется ключевое слово RETURN для досрочного выхода из модели.
|
Рис. 15. PML-модель элемента AND2, в которой используется упрощённый формат
условного оператора с ключевым словом RETURN
Первый оператор IF проверяет, совпадают ли высокие уровни на обоих входах IN1 и IN2. Если это условие выполняется, то на выходе OUT1 устанавливается "1" и модель заканчивает свою работу по команде RETURN.
В противном случае проверяется второе условие (IN1&IN2=="0"). Оно будет выполнено, если любой из сигналов IN1 или IN2 равен "0" (или оба сразу). На другом входе может быть "1" или "X" (см. таблицу истинности).
Если и второе условие не выполняется, то только тогда программа станет исполнять третий оператор присваивания OUT1 = "X";.
Трёхзначная логика нередко является причиной всяких недоразумений. Например, если при построении модели логического элемента "равнозначность", вместо естественного решения
OUT1 = ~(IN1^IN2);
вам пришло в голову написать
IF (IN1==IN2) OUT1="1";
ELSE OUT1="0";
то в этом тексте будет спрятан дефект. Выходу OUT1 будет присвоена "1" не только при равенстве входов IN1 и IN2 логической единице или логическому нулю, но и тогда, когда они оба принимают значение "X". PCAD ошибочно считает, что если значения одинаковы (и то и другое "X"), то они равны!
Операторы присваивания
Вряд ли следует подробно комментировать этот тип операторов. Они интуитивно понятны, поэтому приведём лишь несколько примеров их использования.
OUT1 = "X";
Q_BUS[3] = "1";
Q_BUS = "0XD";
Q_BUS = [A3,A2,A1,A0] + 0B1011;
A_BUS = [A3,A2,A1,A0];
Q_BUS = A_BUS + "013";
Q_BUS = A_BUS & ["1","0","0","0"]; /* маскирование */
Q_BUS = Q_BUS << 2;
В языке моделирования PML определены следующие форматы оператора присваивания:
имя_узла = узловое_выражение (PSL-список);
имя_шины[n] = узловое_выражение (PSL-список);
имя_шины = шинное_выражение (PSL-список);
Здесь имя_шины[n] - индексный компонент шины, а n - восьмеричная, десятичная или шестнадцатеричная константа, указывающая на один из узлов шины.
Операторы выбора
Оператор выбора (SELECT) присваивает значение узлу или шине, как только будет обнаружено первое истинное условное выражение. Рассмотрим один из возможных форматов этого оператора.
Имя_узла = SELECT
FOR (условное_выражение_1) SET узловое_выражение_1 (PCL-список)
FOR (условное_выражение_2) SET узловое_выражение_2 (PCL-список)
...
FOR (условное_выражение_n) SET узловое_выражение_n (PCL-список)
OTHERWISE SET узловое_выражение_x (PCL-список) ;
Ключевое слово FOR в данном операторе означает "если". Ключевое слово SELECT предписывает модели выбирать и проверять условные выражения одно за другим в порядке их следования в операторе выбора, пока не будет обнаружено первое истинное условное выражение. После этого вычисляется значение узлового выражения, идущего за ключевым словом SET в данной строке.
Полученное таким образом значение через связку SELECT - SET присваивается узлу, имя которого указано в начале оператора. Оставшаяся невыполненной часть оператора пропускается, и PML-программа переходит к следующему за ним оператору модели.
Если истинного условного выражения не обнаружено, то вычисляется узловое выражение в строке с ключевым словом OTHERWISE. Заметим, что последняя часть оператора выбора, начинающаяся со слова OTHERWISE, может отсутствовать, и тогда будет происходить переход к следующему оператору в PML-программе.
Другой формат оператора выбора получится, если имя_узла заменить на имя_шины, а все узловые_выражения - на шинные_выражения.
Оператор выбора является наивысшим достижением в языке PML и широко используется на практике. Приведём несколько простых примеров.
На рис.16 показано условное графическое обозначение JK-триггера и его PML-модель. Фактически оператор выбора копирует логическую таблицу его работы. Последняя строка не производит никаких действий (это режим хранения) и приведена в модели только для полноты картины. Её можно исключить без всяких печальных последствий. Чтобы не перегружать модель деталями, в ней реализована только выполняемая функция, описание задержек опущено.
На рис. 17 приводится ещё один пример. Это универсальный регистр сдвига 555IR11. Конкретный режим работы задаётся комбинацией сигналов S1 и S0. Если оба сигнала равны "1", то в регистр производится параллельная загрузка данных со входов D3, D2, D1, D0. В режиме хранения оба сигнала S1 и S0 равны "0".


Рис.16. Условное графическое обозначение и PML-модель JK-триггера
При S1=="1" и S0=="0" содержимое регистра сдвигается в сторону младших разрядов. При этом в освобождающийся старший разряд Q3 записываются данные с входа DL. В справочниках этот режим называется сдвигом влево.
При S1=="0" и S0=="1" всё наоборот. Содержимое регистра сдвигается в сторону старших разрядов, а в младший разряд Q0 записываются данные с входа DR (правый сдвиг).
|

Рис. 17. Графическое и функциональное описания универсального регистра сдвига 555IR11
Приведём ещё один вариант модели регистра сдвига 555IR11 (рис. 18). Здесь оператор выбора заменён оператором IF, с использованием трёх уровней вложенности. Кроме того, демонстрируется применение операции сдвига.
При внимательном рассмотрении текста модели может показаться, что в ней есть ошибка. Действительно, в строке:
IF (~S0&S1) {Q=Q>>1; Q[3]=DL;} /* сдвиг влево */
показан правый сдвиг, а комментарии написано, что это сдвиг влево. На самом деле здесь (как и в следующей строке) всё правильно. В справочниках младший разряд Q0 рисуется слева, а старший - справа, то есть вот так: Q0, Q1, Q2, Q3. В PML-модели, наоборот: старший разряд Q3 всегда левый крайний. Отсюда и происходит путаница. Поэтому точнее говорить не "сдвиг влево", а "сдвиг в сторону младших разрядов".
IR11 ()
INPUT D0,D1,D2,D3,DR, DL, S0,S1, C, R;
OUTPUT Q3,Q2,Q1,Q0;
LOCAL Q_INT[4](10,10,"D","D");
{
IF (R=="0") Q_INT="0X0"; /* асинхронный сброс */
ELSE
IF (C=="/")
{
IF (S0&S1) Q_INT=[D3,D2,D1,D0]; /* параллельная загрузка */
IF (~S0&S1) {Q_INT=Q_INT>>1; Q_INT[3]=DL;} /* сдвиг влево */
IF (S0&~S1) {Q_INT=Q_INT<<1; Q_INT[0]=DR;} /* сдвиг вправо */
}
Q0=Q_INT[0]; Q1=Q_INT[1]; Q2=Q_INT[2]; Q3=Q_INT[3];
}
Рис. 18. Реализация PML-модели регистра сдвига 555IR11
с использованием операции сдвига
Приведём ещё одну модель дешифратора 531ID14, использующую оператор выбора (рис.19). По-видимому, она не нуждается в особых комментариях, здесь всё просто и понятно.
531ID14()
INPUT E, A1,A0;
OUTPUT Q0,Q1,Q2,Q3;
LOCAL Q_INT [4] (15,15,"d","d");
{
Q_INT=SELECT
FOR (E=="1") SET "0XF"
FOR (A1=="0" && A0=="0") SET "0XE" /* E=="1110" */
FOR (A1=="0" && A0=="1") SET "0XD" /* D=="1101" */
FOR (A1=="1" && A0=="0") SET "0XB" /* B=="1011" */
FOR (A1=="1" && A0=="1") SET "0X7"; /* 7=="0111" */
Q0=Q_INT[0]; Q1=Q_INT[1];
Q2=Q_INT[2]; Q3=Q_INT[3];
}
Рис. 19. PML-модель дешифратора 531ID14 с использованием оператора выбора
Для схем с шинной организацией обмена данных широко используются элементы с открытым коллектором, трёхстабильные элементы и шинные драйверы.
На рис.20 показан элемент с тремя состояниями выхода. При E=="0" он транслирует данные с входа D на выход OUT1. При E=="1" выход должен перейти в Z-состояние, но в пакете PCAD эта операция имитируется весьма своеобразно: выход по прежнему повторяет вход, но теперь его сила уменьшается до значения "Z"(тогда как в активном режиме она равнялась "D").
PML_TBUF0 ()
INPUT D, E;
OUTPUT OUT1;
{
IF (E=="0") {OUT1=D (2,2,"D","D"); RETURN;};
IF (E=="1") {OUT1=D (2,2,"Z","Z"); RETURN;};
OUT1="X" (2,2,"d","d");
}
Рис.20. PML-модель элемента с трёхстабильным выходом
В САПР PCAD имеется встроенная модель описанного элемента. Она называется TBUF0 и имеет код идентификации TYPE=12. Реакции, получаемые на встроенной модели можно считать эталонными в процессе отладки и верификации собственной PML-модели трёхстабильного вентиля. Ранее этот способ создания PML-моделей уже удостоился нашей похвалы.
Разработка моделей элементов с открытым коллектором мало чем отличается от только что рассмотренного примера. В этом легко убедиться, сравнивая рис. 20 и 21.
OC_INV ()
INPUT IN1;
OUTPUT OUT1;
{
IF (IN1=="1") OUT1="0"(4,4,"D","D");
IF (IN1=="0") OUT1="1"(4,4,"Z","Z");
IF (IN1=="X") OUT1="X"(4,4,"d","d");
}
Рис. 21. PML-модель инвертора с открытым коллектором
Заканчивая разговор о языке моделирования PML, приведём модель двунаправленного шинного драйвера BD (рис.22).
|

Рис.22. PML-модель двунаправленного шинного драйвера


![Подпись: 555IM6 ()
INPUT A0,A1,A2,A3, B0,B1,B2,B3, CRI;
OUTPUT S0,S1,S2,S3, CRO;
LOCAL S[5] (10,10,"D","D");
{
S = [A3,A2,A1,A0] + [B3,B2,B1,B0] +[CRI];
S0=S[0]; S1=S[1]; S2=S[2]; S3=S[3]; /* "раскладываем" */
CRO=S[4]; /* шину S на отдельные узлы */
}](/text/78/380/images/image009_77.gif)


