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

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

  • 👀 235 просмотров
  • 📌 179 загрузок
Выбери формат для чтения
Загружаем конспект в формате 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 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ

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

Автор(ы) Беклемишев Д. Н., Орлов А. Н., Переверзев А. Л., Попов М. Г., Горячев А. В., Кононова А. И.
Смотреть все 588 лекций
Все самое важное и интересное в Telegram

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

Перейти в Telegram Bot