Лекция 14. Языки моделирования и описания цифровой аппаратуры

Нам придётся сделать небольшой обзор, чтобы определить место названным в заголовке языкам.

Язык (в широком понимании этого слова) – любое средство общения между людьми, средство выражения их мыслей. Данное нестрогое определение относится к естественным (неформальным) языкам, например, английскому, русскому и т. д. Такие языки способны развиваться, а существующая их теперешняя форма является результатом длительной эволюции.

В отличие от них искусственные (формальные) языки создаются сразу целиком, преследуют более узкие цели и не способны эволюционировать. Например, в этом смысле Эсперанто – искусственный язык. К искусственным языкам относятся и языки программирования, которые являются средством общения человека с ЭВМ.

По словам Б. Хигмана понятие языка и его составляющих не поддаётся точному определению. Соглашаясь с ним, мы получаем известную свободу в выборе основных «размытых» определений, которые приводятся ниже с небольшими комментариями.

Любой язык состоит из множества элементов (словарь языка), из которых можно составлять строки (предложения) по определённым в нём правилам (правилам грамматики языка).

Для всех языков (формальных и неформальных) определено понятие алфавит, под которым подразумевают множество различных символов (букв, цифр и других условных знаков), разрешённых для применения в данном языке. Все символы алфавита расположены в определённом порядке, который не подлежит модификации.

В русском языке – это буквы: {А, Б, В, Г, … , Я}, называемые кириллицей. В английском языке алфавит составляют другие буквы: {A, B, C, D, … , Z}, называемые латиницей. Алфавит естественного русского языка именуется азбукой.

НЕ нашли? Не то? Что вы ищете?

Символы алфавита формального языка тоже упорядочены: каждому символу ставится в соответствие его порядковый номер. Другими словами, каждый символ алфавита кодируется целым положительным числом в диапазоне 0…127 (например, ASCII – код, КОИ-7) или в диапазоне 0…255 (расширенный ASCII – код, КОИ-8).

Заметим, что алфавит языка программирования могут составлять не только отдельные знаки (литеры), но и некоторая их комбинация (слово), воспринимаемая как единое целое, как символ. Такие слова называются служебными, например: if, then, begin, end, function и т. д.

Из символов языка можно конструировать слова, то есть некоторые единицы словарного состава языка. Совокупность всех слов, определённых в каком-либо языке, называется словарём.

Каждое слово может существовать в нескольких формах, а порой и в нескольких смыслах, определяемых контекстом, например, стол, стола, столом, столе или коса, лук, посол, ключ, нотация. Такие формы существования слова объединяются понятием лексема.

Отсюда происходит и слово лексика, означающее совокупность слов, допустимых в данном языке. Уточним, что это определение касается естественного языка, в котором общее количество слов ограничено обычно несколькими десятками тысяч, так что имеется возможность перечислить их все и назвать эту совокупность слов словарём.

В формальных языках словарный состав может быть несоизмеримо большим. Например, в языках программирования любой идентификатор (имя), порождённый буйной фантазией программиста и правильный с точки зрения грамматики этого языка, является словом и, следовательно, должен войти в словарь языка.

Если считать, что правильный идентификатор должен начинаться с буквы и состоять только из букв латинского алфавита и арабских цифр (типичные требования для языков программирования), то при ограничении на длину идентификатора в восемь символов (более чем жёсткое требование), возможное их число составит весьма внушительную величину – ХХХХХХ.

Поэтому для формальных языков вместо того, чтобы составлять словарь невероятных размеров, проще сформулировать совокупность правил, определяющих способы построения правильных слов из символов. Именно в этом смысле интерпретируется понятие «лексика» для формальных языков.

Слова имеют определённую структуру, то есть состоят из отдельных частей (морфем), которые в русском языке называются корнем, приставкой, суффиксом и окончанием. Раздел грамматики, изучающий структуру слова, словообразование и словоизменение, называется морфологией. Изменения слов по родам, лицам, числам, временам и падежам, несомненно, обогащают язык, делают его более гибким и мощным, но одновременно лишают его однозначности истолкования.

Для формальных языков, то есть математически строгих и однозначно интерпретируемых языков, приходится вынужденно отказываться от всех этих «наворотов». Иначе говоря, слово, определённое в данном тексте (в программе), не меняет своей формы, где бы оно ни находилось и в каком контексте не встречалось.

Поэтому слово в языке программирования фактически воспринимается как единое целое, и если оно правильное, то его структура уже никого не интересует. Отсюда следует, что понятие «символ» можно распространить и на все правильные слова такого языка.

Мы уже говорили о служебных словах языка программирования, которые воспринимаются как символы, теперь это понятие можно распространить и на все идентификаторы. Вероятно, по этой причине все слова формального языка принято называть терминальными символами. Заметьте, символами, а не словами. А само понятие «терминальный» (то есть конечный) означает простейший, элементарный, неразложимый.

Основной синтаксической единицей языка является предложение. В естественных языках разделителем предложений служит точка «.», а в языках программирования эту роль чаще всего исполняет символ «;» - точка с запятой.

Раздел грамматики, изучающий структуру предложения (то есть его состав и связи), а также определяющий правила построения предложений из отдельных слов, называется синтаксисом.

Кроме грамматических правил язык характеризуется семантическими правилами истолкования (интерпретации) отдельных конструкций языка. Семантика – это смысловая сторона единиц языка.

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

Для искусственных языков, например языков программирования, в данном контексте больше подходит слово «интерпретация». Одну и ту же конструкцию, допустим цепочку из единиц и нулей длиною в один байт, можно интерпретировать как команду, число или букву. Понятно, что компилятор обязан «понять», с каким типом информации он в действительности имеет дело.

Практически семантика языка программирования есть описание того, как предложение следует выполнять на машине.

Рассмотрим кратко основные разновидности языков программирования (рис.1).

Каждая машина имеет свой собственный язык, называемый языком команд, языком нулей и единиц или машинным языком (уровень 0). Программирование на таком языке вызывает значительные трудности из-за чрезмерной детализации программы и необходимости запоминать команды и адреса в двоичной системе счисления.

Чтобы хоть как-то облегчить работу, некоторые программисты машин первого поколения начали использовать сокращённый метод записи команд с помощью восьмеричной системы счисления. Как вспоминает звезда программирования того времени Грейс Хоппер, - все были убеждены, что это единственный способ писать эффективные программы.

Немного позже (в 1949 году) Морис Уилкс заменил двоичные коды команд символическими. Так как при этом использовалась ещё и библиотека подпрограмм, а также настройка адресов по месту размещения программы в памяти, то такую программу он назвал «собирающей» (от англ. assembly system). Эта разработка ввела в обиход машинно-зависимые языки, называемые теперь ассемблером (уровень 1).

Мнемокоды недалеко ушли от машинного языка. Символические аналоги двоичных кодов фактически повторяли структуру машинной команды, поэтому перевод их в двоичный эквивалент выполняется по правилу «один в один».

Макроязыки, наряду с символическими аналогами машинных команд, из которых состоит мнемокод, допускают также использование макрокоманд, не имеющих прямых аналогов в машинном языке (уровень 2). При трансляции каждая макрокоманда заменяется группой команд машинного языка по правилу «один в несколько». Макроязыки имеют и другие названия – автокоды или


макроассемблеры.

Рис.1. Классификация языков программирования

Первый машинно-независимый язык появился в 1957 году. Он был разработан Джоном Бэкусом из фирмы IBM в 1957 году. С него берут начало языки, называемые теперь процедурно-ориентированными (уровень 3). Название объясняется тем, что такие языки предназначены для описания алгоритмов (процедур) решения задач.

Процедурно-ориентированные языки относятся к языкам высокого уровня, пословный перевод здесь невозможен, поэтому трансляция в машинный код выполняется по правилу «несколько в несколько».

До сих пор мы говорили о так называемых процедурных языках, которые представляют алгоритм решения задачи большим количеством мелких шагов, выполняемых последовательно команда за командой. Другими словами, чтобы решить задачу, надо не только сформулировать (поставить) её, но и указать в деталях, как получить решение.

Если пользователь не является программистом и математиком, то в такой ситуации он не может надеяться на успех. Вот здесь-то самое время вспомнить о другой группе языков программирования, называемых непроцедурными, описательными или дескрипторными (от англ. description - описание).

Они позволяют описать задачу, ситуацию или объект исследования, указать условия, в которых он работает, и сформулировать, каким экспериментам он должен быть подвергнут. При этом у пользователя «не болит голова» как всё это будет достигнуто. Такие языки, ориентированные на задачи, проблемы (от англ. problem - задача), принято называть проблемно-ориентированными (уровень 4).

Для нас особый интерес представляет подмножество этих языков, называемое языками проектирования. Обычно оно включает и две другие важные для нас разновидности языков программирования – языки моделирования и языки описания аппаратуры (рис.1).

Сосредоточив внимание на только что названных языках, представим классификацию языков программирования в несколько другой форме (рис.2). Как видно из рисунка, все языки можно (весьма условно) разделить на универсальные (многофункциональные, многоцелевые) и специализированные. Первые из них предназначены для решения широкого круга задач, назначение вторых – узкая область специальных применений.

Первую группу составляют все языки ассемблера, макроассемблера и процедурно-ориентированные языки общего применения, такие как Фортран, Алгол, Бейсик, Паскаль и Си. В разные времена предпринимались попытки устранить «лингвистический хаос» и решить проблему «вавилонского столпотворения». Результатом таких действий явились языки ПЛ/1 и Ада.

В качестве примеров специальных языков можно назвать Лисп и Пролог (языки искусственного интеллекта), Форт (язык управления), Симула-67, GPSS, Симскрипт, Форос, VHDL (языки моделирования).

Лингвистическое обеспечение САПР и СМ включает обе группы языков. Универсальные языки программирования являются инструментом разработчика САПР. На них создаются прикладные программы для решения отдельных проектных задач, которые затем объединяются в пакет (программный комплекс), называемый системой автоматизированного проектирования. Пользователя эти языки ни коим образом не касаются, он даже может не знать о языке написания системы.

Пользователь (обычно это инженер-разработчик) работает с другими языками, называемыми языками проектирования (рис.2). Как видно, это специализированные языки – процедурно - или проблемно-ориентированные. В первом случае речь идёт о языках моделирования, которые описывают функцию, поведение объекта.

Во втором случае подразумевается структурное описание объекта в виде списков элементов и цепей (в текстовой или табличной форме) или непосредственно в графической форме (например, в виде структурной, функциональной или
принципиальной схем).

Рис.2. Языки проектирования – разновидность специализированных
языков программирования

Языки проектирования делят на входные, выходные и языки сопровождения. Первые служат для задания исходной информации об объектах и целях проектирования, вторые – для представления результатов исполнения программ САПР или СМ. Языки сопровождения предназначены для непосредственного общения пользователя с системой в интерактивном режиме.

Языки проектирования, так же как и прикладные программы, составляют неотъемлемую часть САПР или СМ и, как правило, являют собой весьма ответственный этап в разработке подобных систем. Правда, в последнее время и в этом «лингвистическом хаосе» тоже наводится определённый порядок, например для моделирования и описания электронной аппаратуры в качестве стандарта принят единый язык VHDL.

Данные, передаваемые человеком в автоматизированную систему проектирования, можно разделить на две группы:

§  сведения о проектируемом объекте;

§  сведения о характере и последовательности проектных процедур, подлежащих выполнению.

Для представления этих сведений служат соответствующие разновидности входных языков, называемые соответственно языком описания объекта (ЯОО) и языком описания заданий (ЯОЗ).

Используя ЯОО, разработчик цифровой аппаратуры описывает структуру проектируемого устройства и, возможно, значения его параметров. Средства ЯОЗ служат для указания системе, какие задачи должны быть решены и в каких условиях. Запись на ЯОЗ представляет собой последовательность директив – управляющих операторов.

В некоторых системах проектирования, таких как Pspice, обе функции реализуются единым входным языком, а директива отличается от описания компонента схемы тем, что она начинается с символа «.» - точка.

Языки моделирования

Мы уже знаем, что эти языки относятся к классу специализированных процедурно-ориентированных языков программирования. Они представляют собой функциональное (поведенческое) описание объекта проектирования. Другими словами, с помощью языков моделирования сведения об объекте выражаются в виде протекающих в нём процессов.

Таким образом, язык моделирования (ЯМ) – это обычный процедурный язык программирования, преобразованный в «нечто большее и лучшее». В чём же заключаются эти преобразования? Что отличает их от обычных языков? Пожалуй, самым важным отличительным признаком ЯМ является наличие в нём изобразительных средств, обслуживающих время.

Процессы, протекающие в динамических объектах, развиваются в реальном времени, которое при имитации заменяется некоторой искусственной переменной, называемой модельным временем. Эта системная переменная может присутствовать в языке моделирования в явном виде и обозначаться соответствующим идентификатором, например T, TIME, CLOCK или как-то ещё.

Однако чаще модельное время задаётся неявно и реализуется специальной управляющей программой моделирования, называемой коротко моделятором. Эта программа, как и все другие прикладные программы САПР или СМ, является готовым программным продуктом. Именно на моделятор возлагается задача синхронизации моделируемых процессов со стандартными средствами ЯМ.

Кроме хранения и продвижения модельного времени, к которому остальные программы имеют доступ только «по чтению», моделятор ведёт дневник или календарь событий, доступный всем программам и в режиме записи.

Из сказанного становится ясным, что пользователь не может произвольно управлять модельным временем. Например, моделирование всегда начинается с нулевой отметки модельного времени, и пользователь бессилен здесь что-либо изменить.

Конечно, он может косвенным образом влиять на модельное время, изменяя его шаг или финальное время модельного эксперимента. Более того, он в состоянии остановить модельное время и запустить его заново. Но произвольные манипуляции с системным временем также невозможны, как и в реальной жизни.

В языках моделирования имеются средства для описания временных параметров, например задержек распространения сигналов в элементах, из которых состоит объект. Так, в языке VHDL можно определить указанную задержку с невероятной лёгкостью:

OUT <= not INP after 20ns;

Фраза «after 20ns» означает, что выход OUT получит новое значение не в текущий момент модельного времени, а спустя 20ns, то есть сигнал OUT будет сохранять старое значение ещё некоторое время.

Таким образом, ЯМ позволяют не только моделировать поведение (функцию) объекта, но и воспроизводить его инерционные свойства. Кроме того ЯМ позволяют контролировать соблюдение временных соотношений в объекте, таких как минимальная длительность импульсов, правильность синхронизации, соблюдение требований к временам предустановки, удержания и т. д.

Языки моделирования обычно содержат и операторы, обслуживающие время. Например, в том же языке VHDL имеется оператор ожидания WAIT, позволяющий имитировать взаимодействие асинхронно протекающих процессов. Кстати, с его помощью легко описать временную диаграмму изменения любого сигнала. Например, фрагмент временной диаграммы для сигнала сброса RESET может выглядеть так:

RESET: process

begin

RESET <= ‘0’;

wait for 100ns;

RESET <= ‘1’;

wait for 50ns;

RESET <= ‘0’;

wait;

end process;

Ещё одна особенность языков моделирования заключается в том, что они позволяют описывать параллельно протекающие процессы. Например, в ситуации:

Y <= A and B;

Z <= Y or B;

на языке VHDL определены два параллельных процесса (прохождение сигналов через логические элементы and и or). При изменении сигнала B они запустятся одновременно.

Если бы в приведённых выражениях использовались операторы присваивания (такие как в процедурных языках программирования), то значение Z было бы вычислено для нового, только что полученного в предыдущем выражении значения Y. Типичный последовательный алгоритм, как если бы элементы срабатывали один за другим. Понятно, что результат изменится, если мы поменяем местами оба выражения.

Использование в ЯМ операторов назначения «<=» коренным образом меняет ситуацию. Теперь новое значение Y (хотя оно уже вычислено) не доступно моделятору в текущем цикле его работы. Поэтому при вычислении Z будет использовано старое значение Y, что создаёт иллюзию одновременно протекающих процессов. Теперь порядок следования операторов не влияет на результат, что и характерно для параллельных процессов.

Многие ЯМ, предназначенные для имитации вероятностных процессов, имеют, как правило, встроенные датчики случайных чисел и генераторы типовых воздействий и средства для статистической обработки результатов экспериментов.

Справедливости ради заметим, что в таких языках программирования как ПЛ/1 и Алгол-68 реализованы некоторые из перечисленных выше особенностей, в частности, возможность описания параллельных процессов.

Процедурная ориентация языков моделирования наводит на мысль о том, что их можно строить на основе какого-либо многоцелевого языка программирования, используя последний в качестве базового. При этом ЯМ может быть построен как подмножество базового языка или как его расширение (что более очевидно).

Преимущество названного подхода (в отличие от третьей возможности создания автономного ЯМ с собственным синтаксисом) заключается в том, что такие языки мобильны и легко переносятся вместе базовой системой программирования на любую другую вычислительную систему.

Если ЯМ вложен в базовый язык, то в простейшем случае он включает обращения к подпрограммам, входящим в систему моделирования и написанным на базовом языке. Хорошим примером сказанному является ЯМ ГАСП-4, который полностью вложен в универсальный язык программирования Фортран-4.

Здесь можно также назвать язык процессов SIMULATION, определённый как класс универсального языка программирования СИМУЛА-67. Достоинство рассмотренного подхода в том, что он не требует разрабатывать собственный транслятор с языка моделирования.

Если к базовому языку добавляется некоторое число новых конструкций, необходимых для моделирования, то такой приём называют расширением языка. Теперь уже без препроцессора моделирования не обойтись. Исходное описание на ЯМ придётся сначала транслировать на уровень базового языка, а уже затем с помощью стандартных средств базовой системы программирования – в объектный код. Иногда этот метод называют «раскруткой».

Примером такого подхода является язык СИМПЛ/1, являющийся расширением многоцелевого языка ПЛ/1. Он относится к языкам процессов и используется для цифрового моделирования.

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

Третий путь – создание автономного ЯМ самый трудоёмкий, так как требует разработки собственного компилятора. Однако он и самый перспективный, потому что не ограничивает разработчика рамками уже существующего базового языка. Именно этот путь был выбран при создании наиболее мощного, эффективного и универсального языка моделирования и описания аппаратуры VHDL, принятого ныне в качестве неофициального мирового стандарта.

Рассмотрим несколько примеров наиболее популярных языков моделирования. Более подробно они будут обсуждаться в третьей теме, а здесь мы собираемся получить только «общее впечатление».

В качестве объекта описания возьмём двоичный реверсивный счётчик 555ИЕ7,графическое изображение которого показано справа. Напомним, что он имеет четыре режима работы: асинхронный сброс (наиболее приоритетная команда), асинхронная параллельная загрузка, счёт на увеличение (счёт вверх) и счёт на уменьшение (счёт вниз).

На рис.3 приведена его модель, построенная с помощью языка моделирования PML пакета автоматизированного проектирования PCAD.

555IE7()

INPUT D0,D1,D2,D3,L, CU, CD, R;

OUTPUT Q0,Q1,Q2,Q3,CR, BR;

LOCAL Q[4];

{

IF (R=="1") Q="0x0" (2,2,"D","D"); /* сброс */

ELSE

{ IF (L=="0") Q=[D3,D2,D1,D0] (4,4,"D","D"); /* загрузка */

IF (L=="1")&&(CU=="/") Q=Q+"0X1" (8,8,"D","D"); /* счёт вверх */

IF (L=="1")&&(CD=="/") Q=Q+"0XF" (8,8,"D","D"); /* счёт вниз */

}

CR=~(Q[0]&Q[1]&Q[2]&Q[3]&~CU) (8,10,"D","D");

BR=~(~Q[0]&~Q[1]&~Q[2]&~Q[3]&~CD) (8,10,"D","D");

Q0=Q[0] (0,2,"D","D");

Q1=Q[1];

Q2=Q[2];

Q3=Q[3];

}

Рис.3. PML-модель двоичного реверсивного счётчика 555IE7

Структура PML-модели состоит из двух частей. Первая часть (раздел описания) содержит заголовок модели и описание внешних входных и выходных сигналов. Здесь же вслед за ключевым словом LOCAL можно объявить локальную переменную или внутреннюю шину, как в нашем примере: Q[4]. Они используются для хранения текущего состояния моделируемого компонента.

Вторая часть модели (исполняемый раздел) заключена в фигурные скобки и содержит набор исполняемых операторов. В конце каждого оператора может присутствовать необязательный PCL-список, заключённый в круглые скобки. В нём указываются задержки переключения выхода из 0 в 1 и в обратном направлении.

Например, в режиме параллельной загрузки (L=="0") задержка срабатывания выходов Q0…Q3 составит 4 условные единицы времени. В PCAD’е по умолчанию условная единица равна 1нс. Если PCL-список отсутствует, то задержка принимается равной 1нс.

Два последних параметра в PCL-списке задают логическую силу выхода, косвенно отражающую его нагрузочную способность. В пакете PCAD существуют четыре уровня логической силы: S, D, R, Z. Они перечислены в порядке убывания. Выход с силой S – самый мощный. Этот простой механизм оказывается достаточным для разрешения конфликтов на шинах.

Язык PML не имеет внешней шины, поэтому при выдаче результатов приходится внутреннюю шину преобразовывать в соответствующее число одиночных узлов (проводников). В нашем примере шина Q[4] «раскладывается» на четыре одиночных сигнала Q0…Q3. Это делается с помощью последних четырёх операторов присваивания.

Для демонстрации возможностей языка PML первому из них приписан PCL-список:

Q0=Q[0] (0,2,"D","D");

Указанные в нём задержки будут просуммированы с теми, что указаны при вычислении шинного сигнала. Например, при параллельной загрузке данных суммарная задержка при переключении выхода Q0 вверх составит 4нс (4нс+0нс), а переключение вниз будет осуществляться с задержкой 6нс (4нс+2нс).