Пример:
subdesign example6
(
clock, oe, data :input;
outa :output;
)
begin
outa=dffea(data, clock, ,oe, ,);
. . .
В приведенном примере использован один триггер типа dffea. Выход триггера подключен к внешнему выходному порту outa. Вход d триггера подключен к внешнему входному порту data (первый элемент в списке), а вход clk – к внешнему порту clock (второй элемент). Входы clrn и prn, следующие по списку, оставлены неподключенными, так же как и последние по списку входы adata и aload. Вход ena, пятый по счету, подключен к внешнему входному порту oe.
Модули
Модулями в описании языка AHDL называются предварительно спроектированные цифровые блоки, используемые в текущем проекте как компоненты. В оригинальном описании языка модули носят название функций. Применение модулей похоже на применение встроенных примитивов. Отличие в использовании обусловлено тем, что модули не являются предопределенными объектами языка.
Для применения модуля (функции) в текущем проекте необходимо следующее.
1. Наличие файла *.tdf с описанием модуля на языке AHDL. Этот файл должен быть помещен в папку подключаемой библиотеки пакета САПР или находиться в папке текущего проекта.
2. В предварительном разделе проекта должен быть помещен прототип модуля (см. раздел 2.12, оператор prototype) или с помощью оператора include подключен файл *.inc используемого модуля (собственно тоже содержащий прототип модуля). Прототип описывает имена и последовательности перечисления внутренних входных и выходных портов модуля.
3. В разделе переменных необходимо объявить именованные экземпляры модуля аналогично объявлению экземпляров примитивов. Если модуль параметризированный, то при его объявлении параметрам, например разрядности регистров, присваиваются необходимые значения.
4. В логическом разделе следует произвести подключение выходных внутренних портов модуля и обеспечить присваивание сигналов входным внутренним портам в соответствии с решаемой задачей.
Пример:
include "4count";
include "16dmux";
include "lpm_ff";
subdesign example7
(
clock :input;
outena :input;
out[15..0] :output;
)
variable
counter :4count;
decoder :16dmux;
outreg :lpm_ff with (lpm_width=16);
begin
counter. clk=clock;
counter. dnup=gnd;
outreg. clock=clock;
outreg. enable=outena;
decoder.(d, c,b, a)=counter.(qd, qc, qb, qa);
outreg. data[15..0]=decoder. q[15..0];
out[15..0]=outreg[15..0];
end;
В приведенном примере в предварительном разделе операторами include в проект включены файлы 4count. inc и 16dmux. inc из библиотеки фирмы Altera "..\quartus\libraries\others\maxplus2\" и lpm_ff. inc из библиотеки "..\ quartus\libraries\megafunctions\storage\". Далее в разделе переменных объявлен один экземпляр модуля 4count (4-разрядный двоичный счетчик) с именем counter; один экземпляр модуля 16dmux (дешифратор 4-разрядного адреса) с именем decoder и один экземпляр параметризированного модуля lpm_ff (регистр с масштабируемой разрядностью) с именем outreg. Экземпляры модулей и примитивов называются также переменными, поскольку объявляются в разделе переменных. При объявлении регистра outreg с помощью ключевого слова with и списка в круглых скобках его параметру lpm_width (разрядность регистра) присвоено конкретное значение 16. В логическом разделе первые четыре строки подключают к внутренним входным портам экземпляров модулей counter и outreg сигнальные линии clk и outena и логическую константу gnd. Пятая строка подключает выходы переменной counter ко входам переменной decoder, шестая – выходы переменной decoder ко входам переменной outreg, и седьмая – выходы переменной outreg к внешним портам устройства out.
Модули, так же как и примитивы, можно использовать с помощью способа "вызов по ссылке", т. е. без объявления переменных. В этом случае при обращении к внутренним портам модуля необходимо строго придерживаться последовательности перечисления входных портов, указанной в прототипе, или использовать явное указание имен портов для присваивания им значений. В этом случае предыдущий пример может быть представлен следующим образом:
include "4count";
include "16dmux";
include "lpm_ff";
subdesign example7_1
(
clock :input;
outena :input;
out[15..0] :output;
)
variable
temp_a[3..0] :node;
temp_b[15..0] :node;
begin
(temp_a[3..0],)=4count(clock,,,,,gnd,,,,);
temp_b[15..0]=16dmux(temp_a[]);
out[]=lpm_ff(temp_b[],clock, outena,,,,,,)
with
(lpm_width=16, lpm_fftype="dff");
end;
В приведенном примере в первой строке логического раздела четырем линиям группы temp_a[3..0] присваиваются сигналы четырех первых (qd, qc, qb, qa) из пяти выходных портов счетчика 4count (см. ниже прототип). Пятый выходной порт счетчика cout остается неподключенным, для него во временной группе слева от знака присваивания оставлено пустое место после запятой. На первый вход счетчика (см. список, следующий за именем счетчика) подается сигнал с внешнего порта clock – это первый элемент в списке. Следующие четыре входных порта остаются неподключенными – это пустые места между запятыми. На шестой входной порт подана константа gnd. Последние четыре входных порта также не подключены.
Во второй строке логического раздела 16 линий группы temp_b[15..0] подключаются к 16 же выходам дешифратора 16dmux, а на четыре входа дешифратора поданы сигналы с линий группы temp_a[](список после имени дешифратора) и т. д.
Приведенный пример требует для своего прочтения наличия информации об именах внутренних входных и выходных портов модулей и последовательностях их перечисления. Эта информация содержится в прототипах модулей (функций). Ниже представлены прототипы модулей, используемых в примере.
function 4count(clk, clrn, setn, ldn, cin, dnup, d,c, b,a)
returns(qd, qc, qb, qa, cout);
function 16dmux(d, c,b, a)
returns(q[15..0]);
function lpm_ff(data[lpm_width-1..0],clock, enable, sclr, sset, sload, aclr, aset, aload)
with(lpm_width, lpm_avalue, lpm_svalue, lpm_fftype)
returns(q[lpm_width-1..0]);
В прототипе после ключевого слова function следуют имя модуля и список его внутренних входных портов в установленной разработчиком модуля последовательности. После ключевого слова returns следует список выходных портов модуля, также в установленной последовательности. Если модуль подготовлен как параметризированный (настраиваемый), то внутри прототипа после ключевого слова with должен быть приведен список всех его параметров. Следует отметить, что не все параметры требуют обязательного присваивания им конкретного значения при использовании модуля в проекте. В приведенном примере для модуля lpm_ff обязательным параметром является только lpm_width.
Как видно из приведенного примера, не все входные или выходные порты модуля могут быть подключены в проекте. В списке перечисления для неподключенных портов оставляются пустые места между запятыми. Однако существуют порты, подключение которых обязательно. Их имена должны быть указаны в техническом описании модуля, сопровождающем библиотеку.
Конечные автоматы
Конечный автомат (state machine) – это цифровое устройство с памятью, выходные сигналы которого зависят от предыстории поступления входных сигналов. Примером простого конечного автомата может служить устройство, обеспечивающее деление частоты следования входных импульсов, например на 10. У такого автомата должны быть 10 состояний, один вход и один выход. Переход из одного состояния в другое должен происходить, например, с приходом каждого очередного возрастающего фронта входного сигнала. При переходе из девятого состояния в десятое на выходе должна появляться логическая единица, которая вновь превращается в логический нуль при переходе из десятого состояния в первое (начальное). При всех остальных переходах значение выходного сигнала не изменяется.
В AHDL конечный автомат характеризуется конечным набором состояний, каждому из которых дается свое имя. Любое состояние автомата представляется уникальным набором значений на выходах внутренних триггеров (кодом состояния), хранящих состояния автомата. Необязательно, чтобы все триггеры были подключены к выходам автомата. Переход из состояния в состояние происходит при требуемой комбинации входных сигналов с приходом сигнала синхронизации (clk). В AHDL конечный автомат кроме информационных управляющих входов, определяющих его функционирование в соответствии с замыслом разработчика, содержит три предопределенных управляющих входа:
· clk – вход сигнала синхронизации триггеров (активным, т. е. изменяющим состояние триггеров, является возрастающий фронт);
· reset – вход асинхронного сброса автомата – принудительного перевода его в исходное состояние (активный уровень – vcc);
· ena – вход разрешения работы автомата (активный уровень – vcc).
Вход clk должен быть обязательно подключен. Входы reset и ena не являются обязательными. Если они не будут явно подключены в проекте, то на них компилятором будут поданы логические значения, обеспечивающие нормальное функционирование автомата.
Для создания конечного автомата на языке AHDL необходимо выполнить следующие шаги:
· в разделе переменных объявить конечный автомат и его состояния;
· в логическом разделе поместить выражения, обеспечивающие поступление предопределенных сигналов управления в автомат;
· в логическом разделе с помощью операторов case или table описать логику переходов между состояниями автомата, а также поставить в соответствие тем состояниям, для которых это необходимо, логические значения выходов автомата.
Простейшее объявление конечного автомата должно содержать имя автомата, сопровождаемое двоеточием и ключевым словом machine. За ключевым словом machine должен следовать список состояний, заключенный в круглые скобки и предваряемый ключевыми словами with states. Первое состояние в списке является исходным для автомата, оно устанавливается по сигналу reset.
Пример:
subdesign example8
(
clock :input; --сигнал синхронизации
f_on :input; --внешний сигнал включения
outf :output; --выход автомата
)
variable
fcont :machine with states(f0,f1,f2,f3);
begin
fcont. clk=clock; --подключение сигнала синхр.
fcont. ena=f_on; --подача сигнала включения
table
fcont => fcont, outf;
f0 => f1, gnd;
f1 => f2, gnd;
f2 => f3, vcc;
f3 => f0, gnd;
end table;
end;
В приведенном примере реализован простейший автомат, обеспечивающий деление на четыре частоты следования импульсов синхронизации, поступающих на внешний входной порт clock. Выходные импульсы автомата, появляющиеся в четыре раза реже синхронизирующих, подаются на внешний порт outf.
Автомат объявлен в разделе переменных с именем fcont и четырьмя состояниями: f0,f1,f2 и f3. Первые две строки логического раздела подключают внешние входные порты clock и f_on к предопределенным управляющим входам автомата clk и ena. Следующий далее оператор table описывает переходы между состояниями, а также соответствующие новым состояниям значения выходного порта outf (см. описание оператора table, п. 2.11.3).
Согласно приведенному описанию автомата, для перехода из любого текущего состояния в следующее не требуется каких-либо управляющих сигналов. Переходы осуществляются с приходом возрастающего фронта сигнала clk. Поскольку все конечные автоматы, синтезируемые компилятором САПР Quartus II, являются синхронными, явное указание сигнала clk в качестве логической переменной внутри оператора table (или иного оператора, описывающего переходы между состояниями автомата) не требуется.
При синтезе такого автомата компилятор выделит минимальное количество триггеров (запоминаемых битов), требуемое для выбранного семейства микросхем. В частности, при реализации примера в микросхеме семейства MAX3000 это будут два триггера.
Обязательными элементами объявления автомата являются его имя и список состояний. Однако можно дополнительно явно указать необходимые для его реализации именованные биты состояний, т. е. триггеры. Их число может быть больше автоматически определяемого компилятором, например когда требуется чтобы биты состояния одновременно являлись и выходными сигналами автомата. Оно также может быть меньше требуемого минимального числа битов хранения состояний. В этом случае компилятор автоматически добавит недостающие для функционирования автомата триггеры к явно объявленным.
Пример:
subdesign example9
(
clock :input;
f_on :input;
outb[3..0] :output; --выходы автомата
)
variable
fcont :machine of bits(ba, bb, bc, bd)
with states(
f0=b"0001",
f1=b"0010",
f2=b"0100",
f3=b"1000"
);
begin
fcont. clk=clock; --подключение сигнала синхр.
fcont. ena=f_on; --подача сигнала включения
outb[]=(ba, bb, bc, bd);
table
fcont => fcont;
f0 => f1;
f1 => f2;
f2 => f3;
f3 => f0;
end table;
end;
В приведенном примере реализован автомат, который, в отличие от предыдущего, в каждом состоянии подает логическую единицу на соответствующий этому состоянию выход (так работает игрушка "бегущие огни"). Поскольку в таком устройстве необходимы 4 выхода, то автомат с помощью ключевых слов of bits и списка в круглых скобках объявлен с четырьмя именованными битами хранения состояний ba, bb, bc и bd. Эти биты (триггеры) в логическом разделе подключены к 4 выходным портам outb[3..0]. Кроме этого, при объявлении состояний автомата им поставлены в соответствие требуемые значения кодов состояний (b"0001",b"0010" и т. д.), т. е. наборов бит для хранения состояний. Эти коды автоматически присваиваются объявленной группе битов (ba, bb, bc, bd), что позволяет не включать значения выходов автомата в каждую строку оператора table.
2.8. Логические уравнения
Основными функциональными операторами текстового описания на языке AHDL являются логические уравнения (logic equations). Они представляют собой конструкции языка, которые наряду с логическими операторами реализуют структурную и функциональную сущность проекта. Логические уравнения размещаются в логическом разделе проекта и устанавливают соединения узлов, внешних входных и выходных портов, портов примитивов, модулей и конечных автоматов.
Логические уравнения состоят из левого операнда, знака операции присваивания "=" и логического выражения, стоящего справа от знака присваивания. Логическое уравнение устанавливает то, что к левому операнду подключается сигнал, являющийся результатом выполнения правого выражения. Левый операнд может быть внешним выходным или двунаправленным портом, входным внутренним портом примитива, модуля или конечного автомата, а также объявленным узлом или группой перечисленных элементов. К этому операнду допустимо применение логической операции отрицания. Число элементов группы левого операнда должно быть равно или кратно числу элементов группы, представляющей результат вычисления правого логического выражения. Если отдельные элементы группы правого выражения не присоединяются к каким-либо элементам временной группы левого операнда, то для них должны быть оставлены пустые места.
Примеры:
a=b; -- к узлу a присоединен узел b
c=gnd; -- узел c присоединен к земле
out[3..1]=(clk, a,vcc);
(out[0],,out[2..1])=in[3..0];
В последнем из приведенных примеров элемент группы из правого логического выражения in[2]не подключается ни к одному из элементов группы левого операнда и для него оставлено пустое место между запятыми. Это уравнение эквивалентно следующим трем:
out[0]=in[3];
out[2]=in[1];
out[1]=in[0].
2.9. Логические выражения
Логические выражения являются основой логических уравнений.
Они также могут быть использованы в логических операторах if .. then и case.
Логические выражения формируются из переменных (портов, узлов и их групп, включая порты примитивов, модулей и конечных автоматов), логических констант и числовых констант (последние рассматриваются как группы двоичных разрядов). При составлении логических выражений используются:
· логические операции;
· арифметические операции умножения, сложения, вычитания и изменения знака;
· операции сравнения.
Логическое выражение может представлять собой следующее.
1. Одиночный операнд (порт, узел или их группа): b, c[3..0], 9, gnd.
2. Модуль, используемый по способу "вызов по ссылке": out_b[15..0]=16dmux(addr[3..0]); %модуль расположен в выражении справа от знака "="%.
3. Унарный оператор ("!" или "-"), примененный к логическому выражению: !w, -3, not c[3..0];.
4. Бинарную операцию над двумя операндами: a $ b, ina[3]#inb[2], q[3..0]&5;.
5. Логическое выражение, заключенное в скобки: (!a & c $ i).
Последовательность выполнения операций в логическом выражении определяется приоритетами операций и может управляться применением круглых скобок. В первую очередь выполняются операции, заключенные в скобки, затем операции наивысшего приоритета (приоритета с минимальным номером), затем следующего приоритета и т. д. Несколько операций одного приоритета выполняются в порядке слева – направо.
Пример:
a[]=((c[]and –b"00101")+e[5..1])xor(p, q,r, t,v);
В приведенном примере стоящее справа от знака присваивания выражение вычисляется в следующем порядке:
· в первую очередь выполняется часть выражения во внутренних круглых скобках, при этом первой выполняется операция высокого уровня приоритета – унарная операция изменения знака числа b"00101" (отрицательное число в логических выражениях представляется в дополнительном коде, то есть число - b"00101" - это b"11011");
· второй выполняется логическая операция and над группой c[]и числом b"11011", находящаяся в этих же скобках, но характеризующаяся более низким приоритетом;
· третьей по порядку выполняется арифметическая операция сложения группы e[5..1] c результатом второй операции, поскольку она расположена внутри вторых (внешних) круглых скобок;
· последней выполняется логическая операция xor над результатом третьей операции и временной группой (p, q,r, t,v).
Полученное в результате выражение присваивается группе a[].
В табл. 2.3. представлены операции языка AHDL, используемые в логических выражениях, с их приоритетами.
Таблица 2.3
Операции, используемые в логических выражениях
ПРИОРИТЕТ | ОПЕРАЦИЯ | ОБОЗНАЧЕНИЕ | РАЗМЕРНОСТЬ РЕЗУЛЬТАТА |
1 | СМЕНА ЗНАКА (УНАРНАЯ) | - | ОПЕРАНД |
1 | ОТРИЦАНИЕ | ! или not | ОПЕРАНД |
2 | УМНОЖЕНИЕ | * | ОПЕРАНД |
3 | СЛОЖЕНИЕ | + | ОПЕРАНД |
3 | ВЫЧИТАНИЕ | - | ОПЕРАНД |
4 | РАВНО | = = | ОДИН РАЗРЯД |
4 | НЕ РАВНО | != | ОДИН РАЗРЯД |
4 | МЕНЬШЕ | < | ОДИН РАЗРЯД |
4 | МЕНЬШЕ ИЛИ РАВНО | <= | ОДИН РАЗРЯД |
4 | БОЛЬШЕ | > | ОДИН РАЗРЯД |
4 | БОЛЬШЕ ИЛИ РАВНО | >= | ОДИН РАЗРЯД |
5 | И | & или and | ОПЕРАНД |
5 | И – НЕ | !& или nand | ОПЕРАНД |
6 | ИСКЛЮЧАЮЩЕЕ ИЛИ | $ или xor | ОПЕРАНД |
6 | ИСКЛЮЧАЮЩЕЕ ИЛИ – НЕ | !$ или xnor | ОПЕРАНД |
7 | ИЛИ | # или or | ОПЕРАНД |
7 | ИЛИ – НЕ | !# или nor | ОПЕРАНД |
2.9.1. Логические операции
В табл. 2.3 логические операции представлены приоритетами с 5-го по 7-й и операцией отрицания. Унарная операция отрицания not применяется к одному операнду. Остальные логические операции бинарные и при схемной реализации представляют собой двухвходовые логические вентили.
Результаты логических операций зависят от типа операндов.
Операция отрицания
1. Если операндом является одиночный узел или логическая константа, то выполняется инверсия этого операнда (переменная пропускается через одиночный инвертор, а константа vcc заменяется на gnd и наоборот).
2. Если операндом является группа узлов, то каждый элемент группы пропускается через инвертор т. е. !a[2..0]; эквивалентно! a2; !a1; !a0;.
3. Если операндом является число, то число рассматривается как двоичное и каждый его разряд инвертируется, например: !9 интерпретируется компилятором как! b"1001" и дает в результате b"0110".
Бинарные логические операции (and,nand,or,nor,xor,xnor)
1. Если оба операнда являются одиночными узлами или логическими константами (vcc или gnd), то операция выполняется над двумя этими элементами данных.
2. Если оба операнда являются группами узлов, то операция выполняется поразрядно над отдельными элементами групп. Группы должны быть одинакового размера. Например: a[2..0]#(s, e,d); эквивалентно a2#s; a1#e; a0#d;.
3. Если один операнд группа, а второй – одиночный узел или логическая константа, то второй операнд дублируется для создания группы такого же размера, как и первый. Далее выполняется операция над двумя группами. Например: ina[2..0]& a; эквивалентно ina2 & a; ina1 & a; ina0 & a;.
4. Если один операнд группа или узел, а второй – числовая константа, то двоичное представление константы изменяется для согласования с размером группы. Оно либо усекается, либо расширяется с учетом знака. Расширение с учетом знака означает, что у положительных чисел недостающие биты заполняются нулями, а у отрицательных – единицами. Далее операция выполняется над группой и числом, как над группами одинакового размера.
5. Если оба операнда являются числами, то более короткое число расширяется с учетом знака до размеров другого. Далее операция выполняется над числами как над одинаковыми группами двоичных разрядов. Например: выражение 8 # -2; будет преобразовано компилятором в b"1000" # b"1110", которое в результате даст b"1110".
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 |


