Справочник от Автор24
Поделись лекцией за скидку на Автор24

Программирование ПЛИС

  • 👀 327 просмотров
  • 📌 271 загрузка
Выбери формат для чтения
Загружаем конспект в формате docx
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Программирование ПЛИС» docx
Программирование ПЛИС ЛЕКЦИЯ 1 Литература: 1. Грушвицкнй Р. И., Мурсаев А. X., Угрюмов Е. П. Проектирование систем на микросхемах программируемой структурой. — 2-е издание. СПб.: БХВ -Петербург, 2006. — 736 с. (3.3. Элементы языка Verilog HDL 499-549 с.с.) Или: Грушвицкнй Р. И., Мурсаев А. X., Угрюмов Е. П. Проектирование систем на микросхемах программируемой логики. — СПб.: БХВ -Петербург, 2002. — 608 с. (3.3. Элементы языка Verilog HDL 419-467 с.с.) 2. http://marsohod.org (Здесь лекции, пособия, примеры) Имена и комментарии и строки Verilog различает строчные и прописные буквы. 1. В именах используются буквы, цифры, символ _ , и символ $. С них могут имена начинаться. Но в именах нельзя использовать пробел. 2. Комментарии. Можно использовать 2 варианта комментария : a. // комментарий  строчный комментарий /* …. */  на несколько строк. 3. Строки. Строка – последовательность символов, ограниченная слева и справа “кавычками” . Обычно в строке пишется комментарий, который используется для вывода на экран. Вывод на экран производится с помощью дополнительного оператора. Процедуры, которые удобно применять к строкам при выводе на экран. \n // печатать текст с новой строки; \t // табуляция текста. Описание устройства. Понятие модуля Основным элементом программного описания устройства является модуль. Различают модули верхнего и нижнего уровня. Модуль нижнего уровня содержит описание конкретного устройства в виде программы, записанной на одном из языков HDL, например Verilog или VHDL. Модуль верхнего уровня так же содержит программные конструкции, но может дополнительно содержать еще наименования модулей нижнего уровня с описанием параметров вхождения в них. Модули могут располагаться в произвольном порядке. Структура (синтаксис) модуля. module <имя> (<список портов>); <определение портов>; <определение переменных>; <параметры>; <непрерывное присваивание>; < последовательное присваивание >; <подключение модулей>; <системные задачи и функции>; endmodule Пример: module summa (a, b, sum); // в скобках список всех портов input [3:0] a, b; // определение входных портов с указанием их разрядности: от 0 до 3 output [4:0] sum; // определение выходных портов с указанием их разрядности от 0 до 4 assign sum = a + b; - оператор вычисления суммы входных величин endmodule assign – указание на непрерывное присваивание. При этом величина sum изменяется каждый раз, когда изменяется величина a или b. Определение портов <направление> <диапазон разрядности> <имя>; Пример: input [7:0] a; Указание разрядности может отсутствовать, тогда подразумевается, что величина может иметь значения 0 или 1, например output sum; В качестве портов как впрочем и описываемых переменных вообще могут использоваться отдельные разряды in2[3] группы разрядов out[2:0] и объединение в одну группу отдельных разрядов нескольких переменных с помощью операции конкатенации{ in2[3], out[2:0] }. Определение переменных Если величина не входная и не выходная, а промежуточная, то мы её должны специально определить, используя следующий синтаксис <тип переменной><знаковая><сила сигнала><векторная/скалярная><диапазон разрядности><имя>; Все параметры указывать не обязательно, только те которые необходимы. Пример: wire signed ( weak1, supply0) vectored [3:0 ] adr; Пример: reg (string1, weak0) scalared adr; Тип переменной Основная форма представления информации или данных, это сигналы, кроме которых могут быть ещё и служебные данные. Сигналы представляются в алфавите : {0, 1, x, z}. Возможны 2 типа сигналов (переменных). 1. Цепи (nets). Признак цепи при определении типа переменной: wire, tri – эти операторы указывают на цепь. wire - когда цепи подключены к единственному источнику сигнала. tri – когда имеется несколько источников сигнала. Основное свойство цепи - она не сохраняет состояния и управляется драйвером, который соответствует оператору параллельного присваивания. Имя драйвера совпадает с именем цепи, и это отражается в названии операторов этого типа – непрерывное присваивание. Continuous. 2. Регистры, или регистровые переменные – элементы, сохраняющие своё состояние. Признаком их задания (описания) является указание reg. При описании тех и других можно указывать диапазон их разрядности: [<значение старшего разряда>:<значение младшего разряда>] Пример: reg[10:0]continue ; reg a; // разрядность не указана, то есть 1 разряд. reg [2:0]b; a = counter[1]; b = counter[4:2]; Величина может быть определена как вектор vectored или как скаляр scalar . [ …] - диапазон разрядности]. Указывается, если это нужно. Право доступа к отдельным разрядам определяется следующим образом: 1. Если перед указанием диапазона записано ключевое vectored , то доступ к отдельным разрядам запрещён. 2. Если присутствует ключевое слово scalared а так же по умолчанию (когда ни первого, ни второго слова нету), к каждому разряду или группе разрядов можно обращаться индивидуально. Как регистр, так и цепь, могут быть объявлены как знаковые. reg signed [15:0] My16BitReg – регистровая переменная имеет 16 разрядов, и одновременно знаковая. В примере 1 указывается, что это знаковая переменная (signed), а дальше стоит указатель силы. Сила сигнала Сила сигнала (переменной) указывается в случае, если работаем с несколькими сигналами, которые готовы к работе, и можно назначить приоритет, с кем хочешь работать. Задается парой значений уровня сигнала (т.е. его приоритетом), например (weak1, supply0). Значения, заканчивающиеся символом 1, определяют уровень драйвера в состоянии логической единицы, а заканчивающиеся символом 0, – в состоянии логического нуля. Часто в описании verilog используется название «сила драйвера», которая выбирается из следующей таблицы : supply1 supply0 strong1 strong0 pull 1 pull 0 weak 1 weak 0 highz 1 highz 0 Значения выбираются из разных столбцов, так как символ 1 определяет силу драйвера в состоянии логической 1, а символ 0 – в состоянии логического 0. wire a; reg b, c; assign (strong0, weak 1) a = b;// мы хотим присвоить величине a либо b либо c, при этом b и c непрерывно меняются. assign (strong1, weak0) a = c; initial begin c = 1`bz, b = 1; #10 c = 0, b=0; #10 c=0, b = 1; #10 c = 1, b=0; end Здесь сила сигнала (драйвера) увеличивается по мере вверх и если силы драйверов совпадают, то устройство принимает значение Z - высокоимпедансное. #10 – добавление 10 тактов. Каждый раз в этой программе происходит добавление по 10 тактов. Для интервала в тактах – (0 – 10) : a = c = 1, так как для с strong 1 более сильный чем b. Для интервала в тактах – (10 – 20) : a = b, так как теперь для 0 имеем более сильный сигнал для b нежели для c. Для интервала в тактах – (20 –30) : a = z (неопределённое состояние), поскольку для обоих сигналов c=0 и b=1 силы сигналов совпадают. Для интервала в тактах – (30 –40) : a = z (неопределённое состояние), поскольку для обоих сигналов c=1 и b=0 силы сигналов совпадают. Параметры Параметрами являются константы, которые имеют следующий вид : parameter <имя> = <значение>; Вместо значения можно написать выражение. parameter b = 7; parameter b = 7, a = 5; parameter w = a + b; Достоинством использования параметров является возможность внесения изменений в значения переменных, являющихся параметрами, сразу в программном модуле моделирования, не изменяя исходную программу, и не выполняя промежуточных действий по компиляции и созданию новых модулей, необходимых для моделирования. После того, как мы убедились, что параметры выбраны правильно, можно убрать слово parameter и прошить ПЛИС. ЛЕКЦИЯ 2 Памяти Память описывается как массив регистров. reg [<разрядность>]<имя памяти>[<объем памяти или число ячеек памяти>] При определении разрядности сначала указывается большее значение, затем меньшее: [ большее значение : меньшее значение], например [31:0]. Запись reg [10:0] memory1 [31:0] определяет массив из 32 слов памяти memory1 , где каждое слово имеет разрядность 11 бит. Двумерная память : reg[7:0] twoDim [15:0][b3:0]; reg[9:0]treeDim [31;0][31:0][43:0] // трехмерная память. Как можно обращаться к памяти? memory[5] = 75; A_reg = twoDim [3][2]; - если у нас объявлен a_reg Многомерный массив: A = threeDim[21][4][40][7:0]; выбор 8-ми битов, начиная с элемента, указанного слева. Числа Числа могут представляться - в двоичном формате (b или B) - в восьмиричном формате (O или o) - в десятичном формате (D или d) - в шестнадцатиричном формате (H или h) Формат записи : 1. <разрядность>`<основание><значение> Разрядность всегда указывается в количестве двоичных разрядов. Кроме того, в числах можно использовать символы x и z. X – неопределенное состояние, z – высокоимпедансное значение. Если операция выполняется над двумя числами различной разрядности, то для числа с меньшей разрядностью позиция старших разрядов заполняется нулями, чтобы оба числа имели одинаковую разрядность. Если в большем числе присутствует символы x или z, то в другом числе так же заполнение производится этими символами. В состав числа можно включать позицию символ подчёркивания, чтобы улучшить читаемость поля, однако, этот символ не может быть первым. Пример правильной записи. 12`b 0x0x_1101_0zx1 - 12 позиций разделили подчёркиванием, для лучшей читабельности кода. Пример неправильной записи. 8`b_001 Так же возможны ещё варианты представления: 1. <основание><значение числа> Форма записи является машинозависимой. Обычно понимается разрядность 32. 2. <значение> 193 – десятичная форма записи. По умолчанию разрядность равна 32. `h 7d2 – десятичное, по умолчанию 32. Можно указывать знак числа : 1. Положительным 2. Отрицательным При этом указывается число в дополнительном коде. S – для чисел со знаком, и число представляется в дополнительном коде. 3` sd 7 – это число, имеющее разрядность 3 бита, то есть децимальное число 7 представлено 3мя битами, как 111 в дополнительном коде. Следовательно, значение такого числа равно -1. Как это так вышло? 111 =>(инвертируем) 100 (и к младшему разряду добавляем 1) => 101 и того, это равно -1. Старший разряд -это знаковый разряд. Вместо символа z можно использовать символ ?. Например : 12`d? = 12`dz Вещественные числа могут записываться в десятичном или в научном формате. Число в десятичном формате должно иметь, по крайней мере, хотя бы одну цифру после десятичной точки. Пример : 1.8 3. // неверно! Пример научного формата : 8e10 // 8^10 Язык verilog преобразует вещественные числа в целые, округляя их до ближайшего целого. Целые числа и время integer a, b; integer c [100 : 1]; time q, r; time s [31:0]; -целочисленная величина , указывается дополнительно. Операторы Арифметические : 1. * умножение 2. / деление 3. + сложение 4. – вычитание Конкатенации и репликации (объединение и повторение) {a,b} – объединение (конкатенация) {{4а}} – повторение (репликация) ? – условный оператор Синтаксис условного оператора <оператор условия> ? <истина> : <ложь>; Например, если записано wire [15:0] busa = drive_busa ? data : 16’bz; то результат будет следующий: шина busa примет значение (состояние) data, если drive_busa - истина, либо равен единице. В приведенном примере будет drive_busa = 1, а в противном случае шина переключится в состояние z. == сравнение на равенство != … на неравенство. Операторы идентичности: === -идентично !== - неидентично = //описание процедуры присваивания с= a + b; == // используется в ситуации проверки условия равенства переменных a == b; за исключением позиции x. === // если мы хотим, чтобы все позиции совпали, то нужно использовать это выражение. a = 0110x1 b = 0110x1 a === b возвращает true. Битовые (поразрядные) операторы 1. ~ // побитовая инверсия 2. & // поразрядная двоичная И 3. | // поразрядная двоичная ИЛИ 4. ^ // поразрядная двоичная исключающая ИЛИ 5. ^~ // поразрядная двоичная исключающая ИЛИ – НЕ. 6. ~^ // - || - Логические операторы 7. && // логическая И 8. || // логическая ИЛИ 9. ! // логическое НЕ (инверсия) Задержки Задержка определяет задержку между изменением значения находящегося справа от операнда и назначением, сделанным на левую сторону операнда. Пример: module D (out, a, b, c); output out; input a, b, c; wire e; // определение внутренней цепи assign #(5) e = a + b;// фиксация результата e и out относительно входных сигналов. assign #(4) out = e & c; endmodule; Можно указывать параметр задержки в виде одного значения, а можно указать 2 или 3 значения. # 5  #(5) # (5, 7)  задержка по переднему фронту и по заднему фронту. #(5, 7, 10)  указывается 3 типа задержки. Первое число – задержка на переключение сигнала из 0 в 1 , иначе – срабатывание по переднему фронту сигнала. Второе число – задержка при переключении или на переключение из 1 в 0, и третье – значение задержки при переключении в Z состояние. Дополнительно по свойствам задержек можно указать: различают инерционную и транспортную задержки. Инерционная – когда сигнальный импульс имеет длительность меньше задержки и в результате задержка отработана не будет. Транспортная – все изменения входного сигнала , какими бы кратковременными они не были, будут отработаны. Пример: В дальнейшем будут использоваться только транспортные задержки. Кроме того различают еще три типа задержек: 1. Минимальная. 2. Типовая. 3. Максимальная. assign #10 out = in1& in2; out # (5:7:9, 8:10:12, 15:18:20)(i1,i2,i3) (минимальное – min) (типовое typ) (максимальное max) Передний фронт: задний фронт: переход в высокоимпедансное значение. ЛЕКЦИЯ 3 Присваивания (назначения) Различают 2 типа назначения или присваивания: непрерывное и последовательное ( иначе процедурное). “Непрерывное” означает, что назначение осуществляется каждый раз, когда значение в правой части выражения, содержащего знак равенства или какой-то оператор, изменяется. Выражение в правой части не ограничено, то есть это могут быть операторы выбора case, условный оператор if, и т.п. Более того не обязательно, чтобы это было какое то значение, т.е. может быть ещё и ветвление. Существенными свойствами непрерывного назначения являются : а) это назначение распространяется на цепи; б) на определённый бит цепи; в) на группу бит цепи; г) на комбинацию любых предыдущих вариантов. Поскольку мы говорим, что назначение распространяется на цепи и не распространяется на регистры, то определение цепи указывается оператором wire или tri. wire (strong1, weak0) x=a+b;// обозначение силы сигнала по выводу 0 и 1 для цепи. Варианты осуществления назначения 1. В Verilog возможно выполнить сетевое назначение при объявлении цепи. Пример записи: wire (strong1, weak0) x=a+b tri 2. Назначение с помощью оператора непрерывного присваивания assign. assign (strong 1, weak 0) x = a+b; В ряде случаев переменная цепи, например x, может отсутствовать при объявлении цепи, являясь внутренней, или не может быть объявлена в начале. Например, если это конкатенация. Рассмотрим примеры использования оператора непрерывного присваивания и других операторов рассмотренных ранее. Пример 1. Синтезировать сумматор 2-х 4-х битных чисел с входом и выходом сигнала переноса: Текст программы: module adder (sum_out, carry_cut, carry_in, ina, inb); // объявление имени модуля и всех его портов output [3:0] sum_out; // классификация портов с указанием, входные или выходные intput [3:0] ina, inb; // порты и разрядности, если это необходимо. output carry_out; // - |…| - input carry_in; // - |…| - wire carry_out, carry in; // указание, это цепь или регистр? Тут цепь. wire [3:0] sum_out, ina, inb; // характеристика типов используемых переменных – в данном случае это цепи так же с указанием разрядности, если это нужно. assign { carry_out, sum_out} = ina + inb + carry_in; // определение непрерывного присваивания на оператор конкатенации. Сложение входов a+ b и ещё одноразрядная величина, которая указывает код знакопереноса. endmodule; Пример 2. Создать модуль, который осуществляет сдвиг 16 разрядных входных на 5 разрядов влево. Текст программы: module zd1_2(din, dout); // объявление входов и выходов input [15:0] din; output [15:0] dout; // выполним сдвиг assign dout = (din << 5); // assign dout[10:0] = din [15:5]; assign dout[15:11] = din [4:0]; endmodule. Примечание: вместо «желтого» оператора можно использовать «зеленый». Однако, в этом случае может возникнуть неопределённость в значениях высвободившихся правых разрядов. Поэтому часто используют кольцевой Сдвиг путем добавления розового оператора. Пример 3. Разработать модуль, реализующий 8ми разрядный сумматор, имеющий 2 входа A и B и 2 выхода – сума- sum и перенос - cout. Текст программы: module zd1_7 (a, b, sum, cout); input [7:0] a,b; output [7:0] sum; output cout; // 9-ти разрядная переменная, хранящая сумму и перенос. wire [8:0] sum_with_cout; // 9-ти разрядная переменная, хранящая сумму и перенос. assign sum_with_cout = a + b; // после этого в старшем разряде окажется признак переноса, а у младших 8ми результат //сложения. assign sum = sum_with_cout [7:0]; // перенесём сумму на выход. assign cout = sum_with_cout [8]; // перенесём перенос на выход. 8-ой разряд полученной величины. endmodule. Пример 4. Создать модуль, представляющий собой одноразрядный элемент И с программируемой задержкой. Задержка должна задаваться параметром T. Текст программы: module zdz_1 (x1, x2, y); input x1, x2; output y; parameter T = 10; assign #(T) y = x1 & x2; endmodule. Пример 5. Реализовать 8-ми разрядный буфер с 3-им состоянием. Буфер управляется сигналом oe. При oe = 1 входные данные передаются на выход. При oe = 0, выход устанавливается в высокоимпедансное значение. Текст программы: module ad2_7 (din, dout, oe); intput [7:0] din; input oe; output [7:0] dout; // в зависимости от oe зададим выходные сигналы. assign dout = (oe) ? din : 8`bzzzz_zzzz;// 8ми разрядная переменная, формат бинарный. При реализации, _ не проявляется как разряд. endmodule Процедурное (последовательное) назначение (присвоение) Процедурное назначение применяется для переменных типа reg - регистр, integer - целое, real - вещественное, time – время. Будем в основном использовать reg. Эти присвоения обладают памятью. То есть, присвоенное значение переменной будет оставаться неизменным, пока не произойдёт новое присвоение. В качестве процедурных назначений используется 2-а оператора. аlways и initial. Они определяют процедуру назначения. Оператор initial: В этом случае выполнение операции начинается в нулевой момент времени. И этот блок выполняется единожды. Блоков initial может быть несколько. И они могут располагаться в различных местах программы, но при этом они выполняются параллельно (одновременно) и независимо. Действие оператора initial распространяется только на один последующий оператор. Если его действие нужно распространить на несколько операторов, то нужно их заключить в так называемые скобки begin …. end. Пример: module primer; // портов нет поскольку все действия реализуются внутри программы. reg x, y, a, b, m; // вместо wire указали reg initial m = 1`b0; initial begin #5 a = 1`b1; #25 b = 1`b0; end initial begin // выполнение началось вместе со всеми initial, но в блоке begin .. end выполняется последовательно. #10 x = 1`b0; #25 y = 1`b1; end initial #50 $finish; // оператор finish означает остановку инструкций, чтобы избежать неопреденённости. endmodule Все 4-е оператора initial выполняются одновременно, начинаясь в нулевой момент времени. Время задержки исполнения Выполняется Оператор m = 1`b0 5 a= 1`b1 10 x = 1`b0 30 b = 1`b0 35 y = 1`b1 50 $finish Оператор always: Его действие так же распространяется на 1 последующий оператор или на группу, заключённую в скобки begin … end. Блок always так же начинается в нулевой момент времени, но его выполнение циклически повторяется. Поэтому его удобно использовать для генератора тактовых импульсов. module clock_gen (output reg clock); initial clock = 1`b0; // устанавливает в 0 момент времени, чтобы переменная clock была определена. always #10 clock = ~clock; // происходит через 10 тактов задержки. clock инвертируется и эта операция многократно повторяется. initial #1000 $finish; // остановка через 1000 тактов. endmodule Цикл выполнения блока always начинается сразу по окончанию предыдущего цикла. Возможно другое использование оператора always по событийному выражению. always @ (<событийное выражение 1>or<соб. выр. 2>… <>). В этом случае блок always срабатывает каждый раз, когда изменяется событийное выражение. Событийное выражение - любая переменная величина. Если она изменяется, выполняется всё выражение: always @ (clock) q = d;// присвоение q= d изменяется при изменении сигнала clock. Могут быть разновидности: always @ (pasedge clock) q = d; срабатывание по положительному фронту сигнала always @ (pasedge clock) q = d; срабатывание по отрицательному фронту сигнала always @ (a or b) X = a + b; // при изменении любой переменной a или b происходит изменение суммы. always @(*) X = a + b; //* - означает срабатывание блока операторов, при изменении любой переменной блока. ЛЕКЦИЯ 4 Блокирующие и не блокирующие присваивания Блокирующее присваивание (обозначается обычным знаком равенства =) запрещает исполнение других присваиваний до своего завершения. То есть гарантируется последовательное исполнение операторов в блоке. Если присвоение содержит задержку, то изменение будет выполнено через число тактов, соответствующих значению задержки. (В дальнейшем мы будем использовать этот тип). Неблокирующее присвоение (обозначается двойным знаком <=) разрешает исполнение последующих операторов до собственного завершения. Пример. module block_nonblock; reg a, b, c , d, e, f; initial // фрагмент блокирующего присвоения begin a = #10 1; //a – на 10 такте b = #2 0; // b – на 12 такте c = #4 1; // c – на 16 такте end initial // фрагмент неблокирующего присвоения begin d <= #10 1; // d – на 10 такте e <= #2 0; // e – на 2ом такте f <= #4 1; //f – на 4ом такте end endmodule Операторы ветвления (условные) if … else … Варианты: - if (выражение/условие) <оператор, выполняемый в случае, когда условие истинно> else <оператор, выполняемый в случае, когда условие ложно>; - if (выражение) <оператор> // если выражение истинно, выполняется оператор. В противном случае оператор пропускается. - if (…) <… > else if (…) else if (…)…//вложенные условия Пример. if (instruction == ad) begin carryin = 0; complement_arg = 0; end else if (instrucrion == sub) begin carry_in = 1; complement_arg = 1; end else illegal = 1; Условный оператор case Состоит из ключевого слова case, после которого записывается выражение в круглых скобках. Кроме того, в конструкцию входит одно или более значений для case, после которого ставится 2 точки и приводится оператор для выполнения. Конструкция заканчивается endcase. Оператор работает следующим образом. Происходит сравнение выражения в круглых скобках с приводимыми далее значениями case. При совпадении с каким либо из них, выполняется оператор или присвоение, указанное для этого значения после двоеточия. Проверка осуществляется последовательно, и при первом же совпадении процедура останавливается. Возможно, что никакое значение не совпадет, тогда можно определить выбор и назначение некоторого результата, указав в качестве значения оператор default. Пример: default : next_state = IDLE; case (state) IDLE : begin if (state) next_state = STEP; else next_state = IDLE;// IDLE - неопределённое состояние end STEP1 next_sate = STEP2; STEP2 : next_state = IDLE; default : next_state = IDLE; endmodule; Пример 6. Описать мультиплексор 4 x 1 с использованием последовательного присваивания и условных операторов. module zds_1 (din1, din2, din3, din4, dout, select); // перечисление всех портов input din1, din2, din3, din4; intput [1:0] select; output reg dout; always @(*) // срабатывает каждый раз при изменении переменной блока begin if(select == 2`b00) dout = din1; else if(select == 2`b01) dout = din2; else if(select == 2`b10) dout = din3; else if(select == 2`b11) dout = din4; end endmodule; Пример 7. Описать 3х разрядный дешифратор с использованием последовательного присваивания и оператора case. module zd3_2 (din, dout); intput [2:0] din; output reg [7:0] dout always @(*) begin case(din) 3`d0: dout = 8`b 0000_0001; 3`d1: dout = 8`b 0000_0010; 3`d2: dout = 8`b 0000_0100; 3`d3: dout = 8`b 0000_1000; 3`d4: dout = 8`b 0001_0000; 3`d5: dout = 8`b 0010_0000; 3`d6: dout = 8`b 0100_0000; 3`d7: dout = 8`b 1000_0000; endcase end endmodule Возможны еще две разновидности оператора case. - casex –При сравнении не учитываются значения разрядов замещенных символом x. Например 101x01 , в позиции x может быть 1 или 0; - casez – не учитывает значения разрядов замещенных символом z. Примитивы Примитив - это модуль, имеющий один одноразрядный выход. Бывают примитивы: - предопределённые; - определяемые пользователем. Предопределённые содержатся в библиотеке САПРа. Например or, and, … Как правило, пользуются операторами | или &. Структура примитива, определяемого пользователем: primitive <имя> (<список портов>) <определение портов> table . . . endtable endprimitive В списке портов первым указывается выходной. Пример primitive test (z, a, b, c) input a, b, c; output z; table // a, b, c, z - указываем возможные величинв a,b,c 0 0 1 :1 1 0 1 :0 ? 1 1: 1 ……………. endtable endprimitive Символ ? – означает нечувствительность результата к значению сигнала в этой позиции. Приведённый пример соответствует комбинационному типу примитива. Возможен последовательный тип примитива, когда выходной сигнал зависит ещё от предыдущего значения. В этом случае дополнительно указывается, что Z это регистр: output reg z; Пример output reg z; table // a b c zn zn+1 0 0 1 : 1 : 0 1 0 1 : 1 : 1 ? 0 1 : 0 : 1 endtable ЛЕКЦИЯ 5 Операторы цикла Эти операторы могут использоваться только в последовательных блоках. Возможны следующие 4-е варианта их реализации. 1. for (index = min; index (неравенство < (или >)) max (или min); index = index+ (или -) step) <оператор>; // пошагово значение index достигает max или min значения c шагом step. Например, for (I = 0; I < 4; i= I +1) <оператор> ; // начальное значение, как и размер шага переменной может быть любым. Эти процедуры применимы к целочисленным значениям. i принимает только целые значения, поэтому длжно быть указание: integer i или reg i. 2. while (count < 128 ) begin count = count +1; end Если пишешь выражение, состоящее из нескольких операторов, то нужно его заключать в скобки begin … end; Для оператора while допускается использование их в блоках always, но в этом случае в операторе always в качестве событийного выражения следует указывать фронт сигнала. Неправильная запись: always while (x < a) x = x + z; Правильная запись: always begin @ (posege clk) while (x < y) begin x = x + z; end end 3. forever < оператор> // оператор, который будет выполняться бесконечное количество раз. Такой оператор применяется для создания тактовых генераторов (тактовых сигналов). Пример. module clk_sign; reg clk; initial begin clk = 1`b0; // регистр или регистровая переменная. forever #10 clk = ~clk; end initial // последовательный оператор, для начала процедуры в нулевой момент времени программы. #100 $finish; // параллельно будет выполняться подсчёт 100 тактов задержки, после чего будет выполнен оператор finish. Команда finish просто останавливает действие программы и выходит из неё. 4. repeade (n) <>; повторение; // n – число повторений оператора в скобках. Пример 8. Создать 8-ми разрядный двоичный сумматор, имеющий вход и выход переноса. При этом не использовать операцию сложения, а использовать оператор цикла. module cikl_1 (a, b, dout, cin, cout) input [7: 0] a,b; // a, b име.т разрядность 8 input cin; output reg [7:0] dout; // выходная переменная reg [3:0] I; output reg cout; always @(*) //процедура begin cout = cin; for (I = 0; I < = 7; I = I + 1) begin dout [i] = a[i]^b[i]^cout; // операция сложения выполняется с помощью логическх операторов ^ исключающее или. count – добавляется знак переноса из предыдущего блока. cout = (a[i]&b[i])|(a[i]&cout)|(b[i]&cout); // сравниваем (может возникнуть переполнение, если сложили 2 единицы). Значит, нужно делать двоичный сдвиг. end end endmodule dout [i] = a[i]^b[i]^cout; - поэтапное логическое сложение значений одноименных разрядов (И) входных переменных a и b, и учёт для I = 0 входного сигнала переноса. Для 0 < I < 7 сигналы переноса – переполнения от выполнения вычислений по предыдущему разряду. Пример 9. Создать модуль, вычисляющий бит четности для 8-разрядного операнда. Использовать операторы цикла, операцию свертки не использовать. module cikl_2 (din, dout); input [7:0] din; output reg dout; reg [3:0] i; // переменная цикла always @(*) begin dout=0; for (i=0;i<=7;i=i+1) dout=dout+din[i]; end endmodule
«Программирование ПЛИС» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Найди решение своей задачи среди 1 000 000 ответов
Найти

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot