Учебники

Поведенческое моделирование и сроки в Verilog

Поведенческие модели в Verilog содержат процедурные операторы, которые управляют симуляцией и манипулируют переменными типов данных. Все эти утверждения содержатся в процедурах. С каждой процедурой связан поток операций.

Во время моделирования поведенческой модели все потоки, определенные операторами «Always» и «Initial», начинаются вместе во время моделирования «ноль». Первоначальные операторы выполняются один раз, а операторы Always выполняются многократно. В этой модели регистровые переменные a и b инициализируются в двоичные 1 и 0 соответственно во время моделирования «ноль». Первоначальный оператор затем завершается и не выполняется снова во время этого прогона моделирования. Этот начальный оператор содержит блок начала-конца (также называемый последовательным блоком) операторов. В этом блоке типа begin-end сначала инициализируется a, а затем b.

Пример поведенческого моделирования

module behave; 
reg [1:0]a,b; 

initial 
begin 
   a = b1; 
   b = b0; 
end 

always 
begin 
   #50 a = ~a; 
end 

always 
begin 
   #100 b = ~b; 
end 
End module 

Процедурные Назначения

Процедурные присвоения предназначены для обновления переменных типа reg, integer, time и memory. Существует существенная разница между процедурным назначением и непрерывным назначением, как описано ниже —

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

Процедурные присваивания обновляют значение переменных регистра под контролем окружающих их процедур процедурного потока.

Правая часть процедурного присваивания может быть любым выражением, которое оценивает значение. Тем не менее, выбор части справа должен иметь постоянные индексы. Левая сторона указывает переменную, которая получает назначение с правой стороны. Левая часть процессуального назначения может принимать одну из следующих форм —

  • регистровая, целочисленная, вещественная или временная переменная — присвоение ссылки на имя одного из этих типов данных.

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

  • частичный выбор регистра, целочисленная, вещественная или временная переменная — частичный выбор из двух или более смежных битов, при котором оставшиеся биты остаются нетронутыми. Для формы выбора части допустимы только константные выражения.

  • элемент памяти — одно слово из памяти. Обратите внимание, что битовые выборки и выборочные выборки недопустимы в ссылках на элементы памяти.

  • конкатенация любого из вышеперечисленного — можно указать конкатенацию любой из предыдущих четырех форм, которая эффективно разделяет результат выражения правой части и назначает части разбиения по порядку различным частям конкатенации.

регистровая, целочисленная, вещественная или временная переменная — присвоение ссылки на имя одного из этих типов данных.

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

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

элемент памяти — одно слово из памяти. Обратите внимание, что битовые выборки и выборочные выборки недопустимы в ссылках на элементы памяти.

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

Задержка в назначении (не для синтеза)

В отложенном назначении Δt единицы времени проходят до того, как выполняется оператор, и выполняется левостороннее назначение. С задержкой внутри назначения правая сторона оценивается немедленно, но есть задержка Δt, прежде чем результат будет помещен в левое назначение. Если другая процедура изменяет правый сигнал во время Δt, это не влияет на выход. Задержки не поддерживаются инструментами синтеза.

Синтаксис

  • Процедурная переменная присваивания = выражение

  • Задержка присваивания # Δt переменная = выражение;

  • Переменная задержки внутри присваивания = # Δt выражение;

Процедурная переменная присваивания = выражение

Задержка присваивания # Δt переменная = выражение;

Переменная задержки внутри присваивания = # Δt выражение;

пример

reg [6:0] sum; reg h, ziltch; 
sum[7] = b[7] ^ c[7]; // execute now. 
ziltch = #15 ckz&h; /* ckz&a evaluated now; ziltch changed 
after 15 time units. */ 

#10 hat = b&c; /* 10 units after ziltch changes, b&c is
evaluated and hat changes. */ 

Блокировка Назначений

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

Синтаксис

Синтаксис блокирующего процедурного присвоения следующий:

<lvalue> = <timing_control> <expression>

Где lvalue — это тип данных, который действителен для процедурного оператора присваивания, = — оператор присваивания, а контроль времени — дополнительная задержка внутри присваивания. Задержка управления синхронизацией может быть либо управлением задержкой (например, # 6), либо контролем событий (например, @ (posedge clk)). Выражение — это значение с правой стороны, которое симулятор присваивает левой стороне. Оператор = назначение, используемый для блокировки процедурных назначений, также используется процедурными непрерывными назначениями и непрерывными назначениями.

пример

rega = 0; 
rega[3] = 1;            // a bit-select 
rega[3:5] = 7;          // a part-select 
mema[address] = 8’hff;  // assignment to a memory element 
{carry, acc} = rega + regb;  // a concatenation 

Неблокирующие (RTL) назначения

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

Синтаксис

Синтаксис неблокирующего процедурного присвоения следующий:

<lvalue> <= <timing_control> <expression>

Где lvalue является типом данных, который действителен для процедурного оператора присваивания, <= является неблокирующим оператором присваивания, а управление хронированием является необязательным управлением синхронизацией внутри присваивания. Задержка управления синхронизацией может быть либо задержкой, либо контролем событий (например, @ (posedge clk)). Выражение — это значение с правой стороны, которое симулятор присваивает левой стороне. Оператор неблокирующего присваивания — это тот же оператор, который симулятор использует для менее чем равного реляционного оператора. Симулятор интерпретирует оператор <= как реляционный оператор, когда вы используете его в выражении, и интерпретирует оператор <= как оператор присваивания, когда вы используете его в неблокирующей процедурной конструкции присваивания.

Как симулятор оценивает неблокирующие процедурные назначения Когда симулятор встречает неблокирующее процедурное назначение, симулятор оценивает и выполняет неблокирующее процедурное назначение в два этапа следующим образом:

  • Имитатор оценивает правую часть и планирует назначение нового значения в момент времени, заданный процедурным контролем времени. Имитатор оценивает правую часть и планирует назначение нового значения в момент времени, заданный процедурным контролем времени.

  • В конце временного шага, на котором истекла заданная задержка или произошло соответствующее событие, симулятор выполняет присвоение, присваивая значение левой стороне.

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

В конце временного шага, на котором истекла заданная задержка или произошло соответствующее событие, симулятор выполняет присвоение, присваивая значение левой стороне.

пример

module evaluates2(out); 
output out; 
reg a, b, c; 
initial 

begin 
   a = 0; 
   b = 1; 
   c = 0; 
end 
always c = #5 ~c; 
always @(posedge c) 

begin 
   a <= b; 
   b <= a; 
end 
endmodule 

условия

Условный оператор (или оператор if-else) используется для принятия решения о том, выполняется оператор или нет.

Формально синтаксис выглядит следующим образом —

<statement> 
::= if ( <expression> ) <statement_or_null> 
||= if ( <expression> ) <statement_or_null> 
   else <statement_or_null> 
<statement_or_null> 

::= <statement> 
||= ; 

<Выражение> оценивается; если оно истинно (то есть имеет ненулевое известное значение), выполняется первый оператор. Если оно ложно (имеет нулевое значение или значение x или z), первый оператор не выполняется. Если есть оператор else, а <expression> ложно, выполняется оператор else. Поскольку числовое значение выражения if проверяется на нулевое значение, возможны определенные сочетания клавиш.

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

if (expression) 
if (expression != 0) 

Так как, else-часть if-else является необязательной, может возникнуть путаница, когда else отсутствует во вложенной последовательности if. Это решается путем всегда связывать else с ближайшим предыдущим, если в нем отсутствует else.

пример

if (index > 0) 
if (rega > regb) 
   result = rega; 
   else // else applies to preceding if 
   result = regb; 

If that association is not what you want, use a begin-end block statement 
to force the proper association 

if (index > 0) 
begin 

if (rega > regb) 
result = rega; 
end 
   else 
   result = regb; 

Строительство: если-еще-если

Следующая конструкция встречается так часто, что стоит провести отдельное краткое обсуждение.

пример

if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else  
   <statement>

Эта последовательность if (известная как конструкция if-else-if) является наиболее общим способом написания многофакторного решения. Выражения оцениваются по порядку; если какое-либо выражение является истинным, выполняется связанный с ним оператор, и это завершает всю цепочку. Каждый оператор является либо отдельным оператором, либо блоком операторов.

Последняя часть конструкции if-else-if обрабатывает случай «ничего из вышеперечисленного» или случай по умолчанию, когда ни одно из других условий не было выполнено. Иногда нет явного действия по умолчанию; в этом случае завершающий остальной может быть опущен или может использоваться для проверки ошибок, чтобы поймать невозможное условие.

Заявление о ситуации

Оператор case — это специальный многогранный оператор принятия решения, который проверяет, совпадает ли выражение с одним из ряда других выражений, и соответственно разветвляется. Оператор case полезен для описания, например, декодирования инструкции микропроцессора. Оператор case имеет следующий синтаксис —

пример

<statement> 
::= case ( <expression> ) <case_item>+ endcase 
||= casez ( <expression> ) <case_item>+ endcase 
||= casex ( <expression> ) <case_item>+ endcase 
<case_item> 
::= <expression> <,<expression>>* : <statement_or_null> 
||= default : <statement_or_null> 
||= default <statement_or_null> 

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

Помимо синтаксиса, оператор case отличается от многопоточной конструкции if-else-if двумя важными способами:

  • Условные выражения в конструкции if-else-if являются более общими, чем сравнение одного выражения с несколькими другими, как в операторе case.

  • Оператор case обеспечивает окончательный результат, когда в выражении присутствуют значения x и z.

Условные выражения в конструкции if-else-if являются более общими, чем сравнение одного выражения с несколькими другими, как в операторе case.

Оператор case обеспечивает окончательный результат, когда в выражении присутствуют значения x и z.

Зацикливание заявления

Существует четыре типа циклических операторов. Они предоставляют средства управления выполнением оператора ноль, один или несколько раз.

  • навсегда постоянно выполняет заявление.

  • repeat выполняет оператор фиксированное количество раз.

  • while выполняет оператор, пока выражение не станет ложным. Если выражение начинается с false, оператор вообще не выполняется.

  • для управления выполнением связанных операторов трехступенчатым процессом следующим образом:

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

    • Оценивает выражение — если результат равен нулю, цикл for завершается, а если он не равен нулю, цикл for выполняет соответствующие операторы и затем выполняет шаг 3

    • Выполняет присваивание, обычно используемое для изменения значения переменной loopcontrol, затем повторяет шаг 2

навсегда постоянно выполняет заявление.

repeat выполняет оператор фиксированное количество раз.

while выполняет оператор, пока выражение не станет ложным. Если выражение начинается с false, оператор вообще не выполняется.

для управления выполнением связанных операторов трехступенчатым процессом следующим образом:

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

Оценивает выражение — если результат равен нулю, цикл for завершается, а если он не равен нулю, цикл for выполняет соответствующие операторы и затем выполняет шаг 3

Выполняет присваивание, обычно используемое для изменения значения переменной loopcontrol, затем повторяет шаг 2

Ниже приведены синтаксические правила для операторов зацикливания:

пример

<statement> 
::= forever <statement> 
||=forever 
begin 
   <statement>+ 
end  

<Statement> 
::= repeat ( <expression> ) <statement> 
||=repeat ( <expression> ) 
begin
   <statement>+ 
end  

<statement> 
::= while ( <expression> ) <statement> 
||=while ( <expression> ) 
begin 
   <statement>+ 
end  
<statement> 
::= for ( <assignment> ; <expression> ; <assignment> ) 
<statement> 
||=for ( <assignment> ; <expression> ; <assignment> ) 
begin 
   <statement>+ 
end

Задержка управления

Задержка управления

Выполнение процедурного оператора можно контролировать с помощью следующего синтаксиса:

<statement> 
::= <delay_control> <statement_or_null> 
<delay_control> 
::= # <NUMBER> 
||= # <identifier> 
||= # ( <mintypmax_expression> )

Следующий пример задерживает выполнение назначения на 10 единиц времени —

# 10 rega = regb;

Следующие три примера предоставляют выражение после знака числа (#). Выполнение присваивания задерживается на время симуляции, указанное в значении выражения.

Контроль событий

Выполнение процедурного оператора можно синхронизировать с изменением значения в сети или регистре или возникновением объявленного события, используя следующий синтаксис управления событиями:

пример

<statement> 
::= <event_control> <statement_or_null> 

<event_control> 
::= @ <identifier> 
||= @ ( <event_expression> ) 

<event_expression> 
::= <expression> 
||= posedge <SCALAR_EVENT_EXPRESSION> 
||= negedge <SCALAR_EVENT_EXPRESSION> 
||= <event_expression> <or <event_expression>> 

* <SCALAR_EVENT_EXPRESSION> — это выражение, которое преобразуется в однобитное значение.

Изменения значений в сетях и регистрах могут использоваться как события для запуска выполнения оператора. Это известно как обнаружение неявного события. Синтаксис Verilog также позволяет обнаруживать изменения в зависимости от направления изменения, то есть до значения 1 (позиция) или до значения 0 (необходимость). Поведение posedge и negedge для неизвестных значений выражения выглядит следующим образом:

  • при переходе от 1 к неизвестному и от неизвестного к 0 обнаруживается необходимость
  • обнаружена постановка при переходе от 0 к неизвестному и от неизвестного к 1

Процедуры: всегда и начальные блоки

Все процедуры в Verilog указаны в одном из следующих четырех блоков. 1) Начальные блоки 2) Всегда блоки 3) Задача 4) Функция

Начальные и всегда операторы включаются в начале симуляции. Начальные блоки выполняются только один раз, и их действие прекращается, когда оператор завершается. Напротив, блоки всегда выполняются многократно. Его активность умирает только после завершения симуляции. Нет ограничений на количество начальных и всегда блоков, которые могут быть определены в модуле. Задачи и функции — это процедуры, которые включены из одного или нескольких мест в других процедурах.

Начальные блоки

Синтаксис для начального утверждения следующий:

<initial_statement> 
::= initial <statement>

Следующий пример иллюстрирует использование начального оператора для инициализации переменных в начале моделирования.

Initial 
Begin 
   Areg = 0; // initialize a register 
   For (index = 0; index < size; index = index + 1) 
   Memory [index] = 0; //initialize a memory 
   Word 
End

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

Initial 
Begin 
   Inputs = ’b000000; 
   // initialize at time zero 
   #10 inputs = ’b011001; // first pattern 
   #10 inputs = ’b011011; // second pattern 
   #10 inputs = ’b011000; // third pattern 
   #10 inputs = ’b001000; // last pattern 
End 

Всегда блокирует

Оператор всегда повторяется непрерывно на протяжении всего цикла моделирования. Синтаксис для оператора Always приведен ниже.

<always_statement> 
::= always <statement> 

Оператор всегда из-за своей циклической природы полезен только в сочетании с какой-либо формой контроля времени. Если оператор «всегда» не предоставляет средств для продвижения вперед, оператор «всегда» создает условие тупика моделирования. Следующий код, например, создает бесконечный цикл с нулевой задержкой —

Always areg = ~areg; 

Предоставление контроля времени для вышеуказанного кода создает потенциально полезное описание — как в следующем примере —