§ Триггеры на логике
В предыдущей статье я разобрал основные понятия, такие как провода, регистры и модули. На самом деле, регистры можно только симулировать при помощи проводов, если создать триггеры. К примеру, простой D-триггер будет выглядеть так:module dtrig(input d, input c, output q); wire w1 = ~(d & c); wire w2 = ~(w1 & c); wire w3 = ~(w2 & q); assign q = ~(w1 & w3); endmoduleЭто D-триггер, который срабатывает по верхнему уровню и он состоит из 4 NAND. Но, во-первых, это слишком простой триггер, во-вторых, уже занимает большое количество логических элементов, в-третьих, не задействует уже встроенные возможности в логических ячейках, в-четверых, не очень то наглядно это все выглядит.
В целом говоря, по всем пунктам неэффективно. А если намного более простой путь. Но сначала про always.
§ Блоки always
Есть такие моменты, когда надо работать с триггерами. Для этого и были созданы блоки always. С их помощью можно определять то, по каким правилам будут записаны данные в триггеры. Общий случай:always @(<блок чувствительности>) begin ... endБлок чувствительности (хоть и странно звучит), это список проводов или выходов регистров, а также правила, по которым будет срабатывать все, что находится в begin .. end. Если же запись в регистр или действие происходит в 1 оператор, то писать begin/end не нужно. Они выполняют ту же самую роль скобок в С++ или, аналогично Паскалю, работает по тому же принципу.
Возьмем пример:
reg a; wire b = 1'b1; always @(posedge clock) a <= b;Тут происходит следующее - на ПОЗИТИВНОМ фронте (то есть, когда провод clock перешел из 0 в 1), происходит защелкивание (flip-flop) значения с провода
b
в регистр a
.Причем, обращу внимание на
<=
эта конструкция значит, что происходит именно синхронное защелкивание, что значит, что все что находится в блоке always, сработает в один и тот же момент.-
posedge clock
— блок срабатывает только на позитивном фронте clock -
negedge clock
— срабатывает на негативном
Есть также такая конструкция
always @*
, которая означает, что блок будет выполнен абсолютно всегда, вне зависимости от значения любых проводов.Еще пример:
reg a; reg b; always @(posedge clock) begin a <= b; // Защелка b -> a b <= a; // Защелка a -> b endТеперь надо кое-то сказать важного. Дело в том, что раз тут
<=
это значит, что обе защелку пройдут именно одновременно. Это важно! Потому что, если в a=5 и в b=3, то следующим значением будет a=5, b=3!Почему так — именно потому, что запись происходит одновременно, а не последовательно. Если бы запись шла последовательно, то тогда сначала в a=3, а потом в b=a=3 и по итогу было бы a=b=3.
§ if в always
Есть еще интересный трюк:always @(posedge c) begin a <= 1'b0; if (c) a <= b; endНесмотря на синхронную логику, в
a
будет записано именно значение b
, но только тогда, когда c=1. Если же c=0, то в a
будет записан 0.Здесь я впервые применил оператор
if
. Это оператор, как и оператор case
, может использоваться исключительно только в блоках always. Вне этих блоков эти операторы работать не будут.Также, стоит сказать что оператор
=
использовать можно:always @* begin a = b; if (c) begin a = b; end endВ данном случае, это значения регистрам присваиваться будут не на позитивном фронте, как раньше, а всегда. Такой способ удобен, потому что позволяет писать код с использованием различных операторов, которые нельзя использовать вне always, таких как if или case.
§ case в always
Существует также весьма полезный и нужный оператор case. Его синтаксис такой:case (<провод>) <значение>: <блок> <значение>: <блок> ... <значение>: <блок> default: <блок> endcaseДело в том, что case выступает в роли сложного мультиплексора, который выполняет тот или иной блок в зависимости от того, какое было значение на проводе. Приведу пример:
case (w) 2'b00: a <= 1'b1; 2'b01: begin a <= 1'b0; end default: a <= w; endcaseТо есть, если
w=2'b00
, то тогда выполняется a <= 1'b1
, при w=2'b01
выполняется a <= 1'b0
, а при любом другом значений, которое не входит в объявленные случаи, выполняется a <= w
.Есть другие варианты case это casex и casez. С их помощью можно проверять не точное значение провода, а по шаблону. casex/casez отличаются только использованием букв "x" (casex) или "?" (casez) в значениях. Приведу пример:
casez (w2) 4'b00_00: a <= 1'b1; // 1-е условие 4'b1?_?0: a <= 2'h2; // 2-е условие default: a <= w2; // 3-е условие endcaseЕсли w2 = 4'b0000, то тогда выполняется условие 1. Если же w2 равно 1000, 1010, 1100, 1110 (один из 4 вариантов), то выполняется второе условие, ну и при любых других значениях — условие 3.