Описание комбинационных схем в Verilog
6 января 2019 г., доктор Стив Арар
Эта статья знакомит с методами описания комбинационных схем в Verilog, исследуя, как использовать условный оператор для описания комбинационных таблиц истинности.
В этой статье представлены методы описания комбинационных схем в Verilog путем изучения того, как использовать условный оператор для описания комбинационных таблиц истинности. Здесь также показано, как использовать блок Verilog «всегда» для описания комбинационных схем - блок «всегда» может предоставить нам еще более простое решение для описания цифровой схемы.
В предыдущей статье мы обсуждали использование ключевого слова «assign» Verilog для выполнения непрерывного назначения. Такие назначения всегда активны и могут использоваться для получения описания цифровых схем на уровне логических элементов. Например, в следующем коде, который описывает логический элемент И, правая часть непрерывно оценивается, и результат помещается в цепь out1:
назначить out1 =a &b;
В Verilog есть условный оператор (? :), который позволяет нам проверять условие перед выполнением таких присваиваний. Синтаксис приведен ниже:
assign [имя_сигнала] =[условное_выражение]? [value_if_true]:[value_if_false];
«Условное_выражение» оценивается. Если это правда, «value_if_true» присваивается «signal_name». Если это не так, «signal_name» получает «value_if_false». В качестве примера рассмотрим следующий код:
назначить out1 =(sel)? (a и b):(a | b);
Если «sel» истинно, a и b будут присвоены «out1». Если это неправда, out1 получит a | b. Следовательно, приведенный выше код реализует функциональность мультиплексора 2:1. Концептуальная реализация этого кода может быть такой, как показано на рисунке 1 ниже.
Условное присвоение позволяет нам иметь более абстрактное описание определенных схем, поскольку оно имеет функциональные возможности оператора «если», присущего традиционным языкам компьютерного программирования. Условный оператор может использоваться во вложенной форме для реализации более сложных схем. Эти детали обсуждаются в примере 1.
Пример 1. Вложенные условные операторы
Используйте условный оператор (? :), чтобы описать кодировщик приоритета 4 к 2 со следующей таблицей истинности:
Код Verilog для этого кодировщика приоритета приведен ниже:
модуль Prio_4_to_2 (входной провод [3:0] x, выходной провод [1:0] y, выходной провод v); присвоить y =x [3]? 2'b11:x [2]? 2'b10:x [1]? 2'b01:2'b00; присвоить v =(x [3] | x [2] | x [1] | x [0])? 1'b1:1'b0; конечный модуль
Помимо строк с 7 по 10, код содержит основные языковые элементы, которые обсуждались в нашей предыдущей статье. Итак, давайте посмотрим на эти строки.
Термины 2’b11, 2’b10, 2’b01 относятся к обозначениям Verilog, которые представляют собой двухбитовые двоичные числа. Как правило, первое число (перед ‘b) указывает количество битов. Буква b указывает, что числа являются двоичными. Цифры после ‘b указывают значение числа. Следовательно, 2’b01 - это нотация Verilog для представления двухбитового двоичного числа со значением 01, а 3’b100 обозначает трехбитовое двоичное число со значением 100.
Строка 7 проверяет старший бит входа x [3] в условном операторе. Если x [3] =1, условие оценивается как истинное и 2’b11 присваивается y (присвоенное значение берется из таблицы истинности). Если x [3] =0, условие оценивается как ложное, и выражение после двоеточия (:) будет присвоено y. Выражение после двоеточия - это код в строке 8, который сам по себе является еще одним условным оператором.
Второй условный оператор в строке 8 проверяет второй наиболее значимый бит ввода, x [2], чтобы определить, следует ли присвоить 2'b10 переменной y или выражению после двоеточия, которое снова является другим условным оператором (строка 9). быть оцененным. Вы можете проверить, что значения, присвоенные y, соответствуют данной таблице истинности.
Допустимый выход (v) таблицы истинности будет иметь высокий логический уровень, если хотя бы один бит входа имеет высокий логический уровень. Строка 11 показывает это описание с применением побитового оператора ИЛИ (|) к битам ввода. Имитация приведенного выше кода в Xilinx ISE показана на рисунке 2.
Рисунок 2. Имитация Xilinx ISE из приведенного выше кода.
Важно отметить, что условные выражения вычисляются последовательно, пока не будет найдено истинное выражение. Присваивание, соответствующее этому истинному выражению, будет выполнено. В результате выражения, вычисленные ранее, имеют более высокий приоритет по сравнению со следующими. Это означает, что теоретически условный оператор больше подходит для реализации приоритетной сети (рисунок 3), чем сбалансированная структура, такая как мультиплексор (рисунок 4).
Рисунок 3. Приоритетная сеть.
Рисунок 4. Мультиплексор n-to-1, в котором нет приоритета между входами.
Предыдущая статья раскрывает аналогичное обсуждение одновременных назначений VHDL.
Процедурные инструкции Verilog
Мы можем разделить любую комбинационную схему на несколько основных логических элементов (И, ИЛИ, НЕ и т. Д.) И использовать оператор «assign» для описания этих элементов (описание на уровне элементов). Мы также можем использовать условный оператор, обсужденный в предыдущем разделе, чтобы получить более абстрактный способ описания некоторых комбинационных схем (аналогично оператору «if» в языках программирования). Однако есть еще более действенное решение:использовать блок Verilog «всегда».
Внутри блока «всегда» мы можем иметь процедурные операторы, которые выполняются последовательно. Кроме того, блок «всегда» поддерживает конструкции абстрактного языка, такие как операторы «if» и «case».
Функция последовательного выполнения вместе с абстрактными языковыми конструкциями, доступными в блоке «всегда», позволяет нам более легко описывать функциональность схемы из-за того, что человеческое мышление имеет последовательный характер и основывается на абстрактных описаниях. Обычно мы думаем алгоритмически на высоком уровне, а не на низкоуровневых логических элементах. Блок «всегда» может предоставить нам более простое решение для описания цифровой схемы. Дополнительные сведения о том, почему HDL поддерживают описания на основе последовательных операторов, см. В моей статье «Введение в последовательные операторы VHDL».
Пример 2:"Всегда" блокирующие инструкции
Упрощенный синтаксис блока «всегда» приведен ниже:
всегда @ (список_чувствительности) begin sequence_statements; конец
Список чувствительности определяет, когда должны выполняться последовательные операторы внутри блока «всегда». Например, рассмотрите возможность использования блока «всегда» для описания схемы на рисунке 5.
Рисунок 5. Схема_1
Когда изменяется либо a, либо b, выход может измениться, что означает, что и a, и b должны быть в списке чувствительности блока «always». В общем, для комбинационной схемы все входные сигналы должны быть включены в список чувствительности.
Теперь мы можем использовать побитовый оператор AND, чтобы описать функциональность схемы (a и b) и присвоить результат выходу. Внутри блока «всегда» существует два разных типа назначений:блокирующее назначение (=) и неблокирующее назначение (<=). Используя присвоение блокировки, мы получаем следующий код:
always @ (a, b) begin out1 =a &b; end
В чем разница между блокирующим назначением и неблокирующим назначением?
При блокирующем назначении правая часть оценивается и сразу же присваивается out1. Следовательно, когда выполняется строка 3, out1 немедленно обновляется, прежде чем мы перейдем к следующей строке кода. Название «блокирующее назначение» подчеркивает, что следующие строки блокируются до тех пор, пока не обновится левая часть.
При неблокирующем присваивании вычисляется правое выражение, но оно не применяется к левой переменной, пока мы не дойдем до конца блока «всегда». Выбор блокирующего или неблокирующего назначения может сбивать с толку новичка, а неправильное их использование может привести к нежелательной функциональности. Например, использование назначений блокировки для вывода триггеров может привести к возникновению состояния гонки.
В этой вводной статье мы не будем вдаваться в подробности и будем придерживаться одного простого правила, чтобы избежать потенциальных ловушек:используйте назначения блокировки при написании кода для комбинационной схемы. Следовательно, блок «всегда» в листинге 1 будет использоваться для описания логического элемента И.
В предыдущей статье мы познакомились с «проводным» типом данных Verilog. Этот тип данных представляет собой физический провод в нашем проекте FPGA. В блоке «всегда» стандарт Verilog не позволяет нам присваивать значение «проводу». Вместо этого мы используем тип данных «reg». Название «reg» несколько сбивает с толку, но учтите, что «reg» может или не может вести к элементу физического хранилища в вашем дизайне. Следующий код представляет собой Verilog-описание рисунка 5 с использованием блока «всегда». Обратите внимание, что тип выходных данных должен быть «reg», потому что он получает свое значение в результате процедурного присвоения.
модуль Circuit_1 (входной провод a, входной провод b, выходной регистр out1); всегда @ (a, b) begin out1 =a &b; конец конечного модуля
В этой статье мы познакомились с условным оператором Verilog. Мы использовали вложенную форму этого оператора для описания кодировщика приоритета. Затем мы коснулись более мощной языковой конструкции, блока «всегда», для описания комбинационных схем. В следующих статьях мы рассмотрим использование блока «всегда» для реализации последовательных схем.
Встроенный