цифровая электроника
вычислительная техника
встраиваемые системы

 

Verilog. Базовый курс. Часть V

Автор: Mike(admin) от 9-02-2014, 08:47

Поведенческое описание схем на Verilog


До сих пор мы рассматривали только структурную логику на Verilog, когда поведение схемы определяется только один раз, и эта схема не изменяется в зависимости от входных состояний (меняется только выходное значение в соответствии со спроектированной цепью). Поведенческая логика позволяет вам изменить поведение схемы на основе информации о сигналах на входах. Идея этого подхода напоминает циклы с условиями и конструкции типа if/else/case в C/C++.


Always-блоки


Сочетание слов «Verilog» и «поведенческий» у знающих людей вызывает ассоциацию с always-блоками. Always-блок представляет собой кодовую структуру, которая переопределяется всякий раз, когда изменяется состояние триггера. Что это значит? Рассмотрим простой always-блок с двумя входами sw0 и sw1.



always @ (sw0, sw1)
begin
end

В этом блоке нет тела, но мы пока обойдемся без него. Обратите внимание на @, что стоит перед ключевым словом always. Также обратите внимание на выражение внутри скобок: sw0,sw1.


Используя простую таблицу истинности, мы определим, что для двух входных значений можно определить 4 уникальных состояния: оба в лог «1», оба в лог «0», один из них в лог «1», другой в лог «0» и наоборот. Строка «always @ (sw0,sw1)» говорит нам: «Если в любой вход изменит свое состояние, то следующий код изменит свое поведение». На C/C++ такое написать не просто, а Verilog позволяет сделать это без проблем. Такой прием напоминает мультиплексор. Когда любой из выбранных управляющих входов меняет свое значение, мультиплексор повторно решает, какой вход будет подключен к выходу.


Блокирующие и неблокирующие присвоения


Прежде чем продолжать тему always-блоков, разберемся с новой для нас концепцией блокирующих и неблокирующих присвоений. Когда мы пишем код на C/C++, он выполняется построчно в последовательном порядке, пока программа не укажет на отход от такой последовательности, например, при прыжке на какую-либо другую строку. При таком подходе сразу после выполнения кода одной строки выполняется код другой строки. В большинстве случаев мы не замечаем никаких задержек в простых консольных приложениях. Но в проектировании цифровых устройств задержка при переходе от выполнения одной строки к другой может иметь весьма негативные и заметные последствия. Даже задержки меньше одной наносекунды могут вызвать проблемы и сбои.


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


Представьте себе, что у вас есть два переключателя (sw0 и sw1), которые находились бы в лог «0», и один выходной светодиод, который бы зажигался при «0» на обоих входах, то есть (LED = ~(sw0 & sw1)). Но, предположим, вы переключили sw1 в лог «1». Какое-то время (очень малую долю секунды) светодиод продолжит гореть, пока сигнал от sw1 идет по проводникам через все вентили к выходу. Тут-то и рушится теория, ведь ваш светодиод должен уже быть выключен, а он все еще в состоянии лог «1». Ваша схема нарушает правила собственных логических уравнений!


Чтобы избежать таких проблем, нужно правильно пользоваться блокирующими и неблокирующими присвоениями. Но что это такое, и как ими правильно пользоваться? Блокирующее присвоение представляет собой просто «=» и позволяет выполнять код как в C/C++: последовательно и по порядку. Неблокирующее присвоение выражается оператором «<=». Строки кода с неблокирующими присвоениями будут выполняться параллельно.


Можно подумать, что поскольку Verilog является языком описания аппаратуры, то не должно быть никакого значения, какое присвоение мы используем, поскольку все равно будет синтезирована та же самая схема, и будет она себя вести тем же самым образом. Это не совсем верно, так как Verilog-компилятор создаст различные транзисторные структуры для прохождения различных сигналов по различным путям. И вы должны указать ему, как проводить эти сигналы – в одно и то же время или в различные промежутки времени. Таким образом, неправильное использование присвоений может оказать неблагоприятное воздействие на поведение цепи.


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


Ниже приведем три простых примера с использованием always-блоков и блокирующих и неблокирующих присвоений.



//Пример 1
always @ (sw0, sw1)
begin
if(sw0 == 1'b1)
begin
//все эти выходы изменят свое
//состояние в одно и то же время
output1 <= 1'b0;
output2 <= 1'b1;
output3 <= 1'b0;
end
end


// Пример 2
always @ (sw0, sw1)
begin
if(sw0 == 1'b1)
begin
//Здесь приоритет за первым выходом,
//затем одновременно поменяются второй и третий
output1 = 1'b0;
output2 <= 1'b1;
output3 <= 1'b0;
end
end


// Пример 3
always @ (sw0, sw1)
begin
if(sw0 == 1'b1)
begin
//Сначала изменится первый,
//потом второй, за ним третий
output1 = 1'b0;
output2 = 1'b1;
output3 = 1'b0;
end
end

Стоит помнить, что в always-блоках лучше использовать «<=», а не «=». Также с ключевым словом assign следует использовать «=».


В примерах мы задействовали выражение «if». Подробнее о нем мы поговорим в следующей, заключительной части базового курса.




Часть I


Часть II


Часть III


Часть IV


Часть VI




Перевод © digitrode.ru


<Источник>


Теги: Verilog, ПЛИС



Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий