Verilog — инструмент разработки цифровых электронных схем. Часть 2

№ 3’2001
PDF версия
Синтаксис операторов в языке Verilog подобен синтаксису языка программирования С. К сожалению, отсутствуют операторы ++, -- и все операторы модификации вида (операция)=, например, *=, ^= и т.д.

Синтаксис операторов в языке Verilog подобен синтаксису языка программирования С. К сожалению, отсутствуют операторы ++, — и все операторы модификации вида (операция)=, например, *=, ^= и т.д.

Все статьи цикла:

Операторы

Синтаксис операторов в языке Verilog подобен синтаксису языка программирования С. К сожалению, отсутствуют операторы ++, — и все операторы модификации вида (операция)=, например, *=, ^= и т. п. Но в то же время существуют логические операторы, полезные для моделирования цифровых схем: при одинаковом синтаксисе (, ~|, ^, ~^, & ~&) данные операторы могут быть битовыми (bitwise) и работать с двумя операндами или операторами свертки (reduction) и с одним операндом. Тип выполняемой операции определяется по положению оператора в выражении. Кроме логических операций языком поддерживаются и арифметические операции, но, чтобы получить информацию о синтезируемости выражений с арифметическими операторами, следует ознакомиться с документацией на средства синтеза. Для эффективной реализации (синтеза) арифметических выражений в «железе» возможно потребуется приобретать специальные средства или библиотеки для datapath элементов.

Рассмотрим применение операторов на примере.

module op_test;

reg [3:0] D0, D1, D2, D3;

reg [3:0] A, B, C, D; //Verilog case-sensitive

reg a, b, c, d; // А и а — различные переменные

initial

begin

D0=4’b0;

D1=4’b1111;

D2=4’b1010;

D3=4’b01xz;

A=D1~^D2; // bitwise операция — два операнда

a=~^D2; // reduction операция — один операнд

B=D0^D3;

b=^D3;

C=D2&D3;

c=&D3;

D=D2|D3;

d=|D3;

$display(«A=%b a=%b B=%b b=%b C=%b c=%b D=%b d=%d», A, a, B, b, C, c, D, d);

end

endmodule

Полученный результат будет выглядеть так:

Highest level modules:

op_test

A=1010 a=1 B=01xx b=x C=00×0 c=0 D=111x d=1

При выполнении логических и битовых операций состояние с высоким импедансом (z) воспринимается как неопределенное (x). При моделировании комбинаторная логика обычно способствует распространению x, но если, например, на один из входов элемента И (And) подан 0, то, независимо от значения на других входах, на выходе будет 0. Это иллюстрируют полученные в примере значения С и с.

В языке присутствует условный оператор ?:, который работает так же, как и в языке С. Таким образом, простейшей записью мультиплексора из 2 в 1 является:

assign Y=(SEL)?A:B;

Список операторов языка Verilog:

{} concatenation

+ — * / arithmetic

% modulus

> >= ! logical negation

&& logical and

|| logical or

== logical equality

!= logical inequality

=== case equality *

!== case inequality *

~ bit-wise negation

& bit-wise and

| bit-wise inclusive or

^ bit-wise exclusive or

^~ or ~^ bit-wise equivalence

& reduction and

~& reduction nand

| reduction or

~| reduction nor

^ reduction xor

~^ or ^~ reduction xnor

>> shift right

>>> arithmetic shift right

?: conditional

Операторы, помеченные *, будут рассмотрены при описании поведенческих конструкций.

Часть операторов повторяется. Например, Применяться операторы могут как к цепям (wire), так и к сигналам (reg) и переменным. При этом используются различные типы присвоения. Для цепей, которые являются моделью физического соединения (провода), требуется подключение непрерывного воздействия, которое моделируется непрерывным (continuous) присвоением. Значения же регистров и переменных могут изменяться в результате процедурных действий и сохраняться между воздействиями (так же, как и переменные процедурного языка программирования). Для моделирования этого используется процедурное (procedural) присвоение.

Непрерывное присвоение употребляется вне процедурных блоков initial или begin и используется либо в описании цепи, либо с ключевым словом assign (существуют также процедурные непрерывные присвоения в блоках initial или always с ключевыми словами assign и deassign). Слева от оператора непрерывного присвоения (=) должен находиться объект типа цепь. При изменении значения какого-либо из объектов, входящих в выражение справа от =, данное выражение будет вычислено, и новое значение будет присвоено.

Ниже приведен пример, иллюстрирующий работу непрерывного присвоения.

module assign_test;

reg [3:0] data;

wire parity, forth;

wire gnd=1’b0; //обьявление присвоение

wire y=(data[0])?data[1]:gnd; //мультиплексор

assign parity=^data; //непрерывное присвоение

assign forth=~|data[1:0]; //еще одно присвоение

initial

$monitor(data,, parity,, forth,, y); //запускаем системную

функцию для индикации изменений

initial

for (data=0; data!=15; data=data+1’d1) //переберем варианты

#1; //задержка нужна, чтобы $monitor работал правильно

endmodule

Результат работы этого примера:

Highest level modules:

assign_test

0 0 1 0

1 1 0 0

2 1 0 0

3 0 0 1

4 1 1 0

5 0 0 0

6 0 0 0

7 1 0 1

8 1 1 0

9 0 0 0

10 0 0 0

11 1 0 1

12 0 1 0

13 1 0 0

14 1 0 0

15 0 0 1

Процедурные присвоения бывают двух типов: blocking (=) и nonblocking (

Допустим, имеется следующее описание:

always @ (posedge CLK) a=b;

always @ (posedge CLK) b=a;

Пусть а и b — регистры единичной длины, и к моменту положительного фронта тактового сигнала CLK а = 0, b = 1. Какое значение будут иметь эти переменные после прохождения фронта? Это не определено в языке и зависит от того, в какой последовательности операции присваивания попадут в список. То есть получается конструкция, поведение которой непредсказуемо. Это значит, что либо обе эти переменные будут равны 0, либо 1. Конкретный результат зависит от симулятора и от последовательности, в которой будут обработаны строки исходного файла. «Блокирующее», или «блочное», присвоение (=) блокирует исполнение других операций в блоке последовательных операций (до тех пор, пока не будет выполнена данная операция). Использование «блокирующего» присвоения в подобной конструкции в конкурентно исполняемых блоках нежелательно, и его следует избегать. Но в то же время, если в блоке требуется провести последовательное исполнение операторов, следует применять данный тип присвоения.

Конструкция

always @ (posedge CLK)

begin

a=0;

b=a;

end

гарантирует, что после фронта CLK обе переменные — a и b — будут обнулены.

Если же предыдущие примеры переписать с использованием «неблокирующего», или «неблочного», присвоения (always @ (posedge CLK) aalways @ (posedge CLK) b

В этом случае в список событий, исполняемых во временном шаге после изменения CLK, эти операции будут помещены параллельно, то есть переменные обменяются своими значениями. После прохождения фронта CLK переменные изменят свои значения: а = 1, b = 0. Данная конструкция будет эквивалентна следующей:

always @ (posedge CLK)

begin

a bend

и описывает два D-триггера, выход каждого подан на вход другого, а на тактовые входы подан сигнал CLK. При этом последовательность записи: aВторая конструкция в этом случае:

always @ (posedge CLK)

begin

a bend

описывает два D-триггера. На вход одного (а) подан 0, а выход подключен к входу другого (b). На тактовые входы подан сигнал CLK.

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

always @ (posedge CLK or posedge SET)

begin

if (SET) a else

begin

a b end

end

Проверим работоспособность данной схемы следующим испытательным стендом (это упрощенный testbench — правильный пример создания испытательных стендов приведен в первой части статьи):

`timescale 1ns/10ps

module test;

reg CLK, SET, a, b;

always

#5 CLK=~CLK; //100 МГц тактовый сигнал

always @ (posedge CLK or posedge SET) //испытываемый блок

begin

if (SET) a else

begin

a b end

end

initial //инициализируем переменные

begin

CLK=0;

SET=0;

a=0; b=0;

$monitor($time,,SET,,a,,b);

end

initial #100 $finish; // через 100 нс завершить моделирование

initial //подача «асинхронного» сигнала SET

begin

#57 SET=1’b1;

#1 SET=1’b0;

end

endmodule

Результат работы этого теста:

Highest level modules:

test

0 0 0 0

57 1 1 0

58 0 1 0

65 0 0 1

75 0 0 0

показывает, что асинхронный сигнал SET, длительностью 1 нс (фронт на 57 нс после пуска), был синхронизирован по фронту CLK в регистре b — длительность 10 нс (фронт 65 нс, срез 75 нс).

Для пользователей VHDL можно провести параллель между variable assignment (:= VHDL) и blocking assignment (= Verilog), signal assignment (Временной и событийный контроль

Так как Verilog используется для моделирования физических систем, то большое внимание уделено привязке события к определенному моменту времени. Для этого используется механизм задержек.

Следует заметить, что средства синтеза (как для Verilog, так и для VHDL) игнорируют временные конструкции в исходном коде. Для правильной генерации цифровой схемы (нет листа) из библиотечных элементов в средства синтеза, наряду с описанием на языке высокого уровня вводятся файлы, содержащие описания «constrain». В этих файлах описываются временные ограничения распространения сигналов. Применяемый для этого синтаксис не стандартизован и определяется используемым средством синтеза. Профессиональная работа с HDL-языками высокого уровня подразумевает не только умение создавать грамотные поведенческие описания, но и умение правильно управлять средством синтеза с помощью «constrain».

Однако для моделирования временной контроль необходим, и в примерах этой статьи неоднократно использовались выражения вида #. Вместо числа может использоваться выражение, содержащее целые и/или вещественные параметры. В синтаксисе языка определено несколько форм временных задержек для описания различных технологических условий, наиболее полной из которых является так называемая mintypmax форма — #(min, typ, max), например #(2, 3, 4). Но данные конструкции используются, как правило, для моделирования на уровне вентилей (нет листа), а более эффективным методом таких описаний является SDF-аннотация с применением специальных SDF-файлов. Поэтому в статье будет использоваться упрощенная форма задания задержки с одним параметром #typ. Для анализа исходных файлов «третьей стороны» можно считать, что всегда используется типовое (среднее) значение.

Рассмотрим применение задержки в непрерывном присвоении.

assign #10 c=a^b;

Данная конструкция описывает элемент «исключающее или» с задержкой распространения 10 нс (вернее, 10 единиц первого параметра директивы `timescale, который по умолчанию равен 1 нс). Все задержки в непрерывных присвоениях являются инерциальными. Это означает, что если сигнал А изменит свое состояние на время, меньшее 10 нс, а затем изменит еще раз, то изменения сигнала С не произойдет. Для того чтобы произошло изменение сигнала С требуется, чтобы сигнал А был зафиксирован в новом состоянии более 10 нс. Такая модель поведения соответствует распространению задержки при прохождении через элементы электронной схемы. Другой тип задержки — транспортная задержка, которая обеспечивает точное соответствие формы задержанного сигнала и сдвиг его по шкале времени. В отличие от VHDL (в котором существуют спецификаторы типа задержки inertial и transport) Verilog не позволяет использовать оба типа задержек в одном моделировании. Для переключения типа задержки служит ключ командной строки запуска Verilog-симулятора.

Рассмотрим задержки в процедурных блоках. Первая форма — простая задержка (она использовалась в примерах статьи). Она имеет вид:

#1 x=y;

Такая задержка вызывает остановку последовательного блока на 1 нс (не влияет на обработку конкурентно исполняемых блоков). Она не обязательно используется с оператором присваивания, может использоваться с пустым оператором #1. То есть #1 x=y может быть записано в такой форме #1; x=y или #0.5; #0.5 x=y. Поведение этих конструкций одинаково.

Также в процедурном блоке задержка может встречаться с другой стороны знака =. Это так называемая intra-assignment delay.

x=#1 y;

В этом случае вначале происходит вычисление выражения, затем задержка, а затем присвоение. То есть рассматриваемый пример эквивалентен

tmp=y;

#1;x=tmp;

Кроме временного контроля существует событийный контроль (который можно считать другой формой временного контроля). Признаком событийного контроля является знак @. В рассмотренных ранее примерах событийный контроль использовался в блоках always. Отличие Verilog от VHDL в данном случае состоит в том, что для описания фронтов и срезов сигналов используются не специализированные атрибуты сигнала, а специальная конструкция языка. Это создает впечатление, что разработчики языка Verilog несколько лучше представляли себе процесс разработки цифровых схем. Событийный контроль используется в процедурных блоках так же, как и временной контроль. При этом задержка исполнения происходит не на фиксированный временной интервал, а до тех пор, пока не произойдет нужное событие.

События бывают следующих типов:

  • @(name) — изменение name, при этом name может являться цепью, регистром, переменной или переменной типа event;
  • @(posedge A) или @(negedge А) — фронт или срез сигнала А, при этом А — однобитовый регистр или цепь;
  • комбинация перечисленных событий с ключевым словом «or», например @(posedge CLK or posedge SET); в этом случае следует различать «or» с одноименной логической операцией; в событийном контроле «or» означает, что ожидается любое из перечисленных событий, а не определенный результат логической операции.

Для генерации синхронного сброса может использоваться такая конструкция:

nReset=0;

repeat (16) @(posedge CLK);

nReset=1’b1;

Конструкция во второй строке примера обеспечивает задержку на 16 тактов, вернее, задержку на прохождение 16 фронтов сигнала CLK.

(Продолжение следует)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *