Верилог всегда, начало и конец оценки - PullRequest
5 голосов
/ 15 января 2012

Я пытаюсь выучить Верилог, используя книгу Понга П. Чу. У меня есть вопрос о том, как всегда оценивать и реализовывать блок всегда. Стиль в коде авторов сбивает меня с толку.

В этом примере он кодирует FSM с двумя выходными регистрами 'y1' и 'y2'. Часть, в которой я запутался, находится в блоке NEXT STATE LOGIC AND OUTPUT LOGIC всегда, где после оператора begin и always@* y1 и y0 установлены на 0. Мне кажется, что независимо от состояния, y1 и y0 переключатся на 0 на каждом такте и изменении сигнала. Согласно диаграмме состояний в книге reg y1 должен быть равен 1, а в состоянии 0 или 1.

Так y1 переключается на 0 каждый тактовый цикл, а затем обратно к какому-либо значению в текущем состоянии ?? Я предполагаю, что это не так, и что я просто запутался в оценке блока. Может кто-нибудь объяснить, что делает эта часть кода. Я потерялся. Спасибо

module fsm_eg_2_seg
    (
     input wire clk, reset, a, b,
     output reg y0, y1
    );

    //STATE DECLARATION
    localparam [1:0]    s0 =2'b00, 
                    s1=2'b01, 
                    s2=2'b10;

    // SIGNAL DECLARATION
    reg [1:0] state_reg, state_next ;

    //STATE REGISTER
    always @(posedge clk, posedge reset)
        if (reset)
            state_reg <= s0;
        else
            state_reg <= state_next;

    //NEXT STATE LOGIC AND OUTPUT LOGIC
    always @*
    begin
        state_next = state_reg; // default next state: the same
        y1 = 1'b0;              // default output:  0
        y0 = 1'b0;              // default output:  0
        case (state_reg)
            s0:  begin
                y1 = 1'b1;
                if (a)
                    if(b)
                        begin
                            state_next = s2;
                            y0 = 1'b1;
                        end
                    else
                        state_next = s1;
                end
            s1:  begin
                    y1 = 1'b1;
                    if (a) 
                        state_next = s0;
                    end
            s2: state_next = s0;
            default: state_next = s0;
        endcase
    end
endmodule

Ответы [ 4 ]

4 голосов
/ 15 января 2012

Должен сказать, что я не согласен с аква.То, что он (и wisemonkey) говорит о @*, правильно, но все остальное неправильно.

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

  • Представьте, что этих двух утверждений нет.
  • Далее предположим, что state_reg = S0 и a = b = 0
  • Какмы вычисляем блок Always, вводим инструкцию case, половину s0 и присваиваем 1 для y1
  • a, равным нулю, поэтому мы не вводим оператор if и выпадаем из регистраи завершить блок

В конце блока y1 == 1 и y0 == ... erm, зависнуть на том, что получает y0?Я предполагаю, что это должно сохранить это старое значение.Он не получил новый.

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

... но мы этого не хотим.y1 и y0 должны были быть простыми проводами.Поэтому мы должны удостовериться, что каждому из них всегда назначается, независимо от состояния или входов.Мы могли бы сделать это, имея назначения во всех ветвях логики, но это становится большой работой.В качестве альтернативы у нас может быть задание по умолчанию, которое мы позже переопределим при необходимости.

Причина, по которой эти операторы не вводят y1 при переходе к 0 в s0 или s1, заключается в том, что все, что происходит внутри блока Always, происходит без прохождения времени.Время не проходит между 0, назначаемым сверху, и 1 в s0 или s1.Все, что видно, это конечное состояние.

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

Хороший чистый конечный автомат.В этом нет ничего плохого.

4 голосов
/ 15 января 2012

Выражение

always @* begin : name_of_my_combinational_logic_block
    // code
end

описывает комбинационную логику. Как правило, сигналы clk и rst не считываются из этого типа блока всегда, поэтому они не отображаются в списке чувствительности, как говорит wisemonkey. Лучше всего использовать @ * для списков чувствительности комбинационной логики, чтобы вы не забыли включить сигнал, который определил бы некоторую память и больше не был бы комбинационной логикой.

Внутри комбинационного логического блока вы должны использовать так называемые блокирующие назначения . Они выглядят как обычные присвоения переменных в большинстве языков программирования и используют одинаковые равенства. Значение, которое вы присваиваете переменной (reg) внутри блока комбинационной логики, происходит немедленно относительно других операторов и выражений в том же блоке комбинационной логики, но не распространяется за пределы этого блока комбинационной логики пока не дойдёшь до конца. Блок всегда должен достигать конца, прежде чем какие-либо изменения будут видны за пределами блока. Пол С прав, что вы хотите всегда присваивать что-то вашим переменным всякий раз, когда выполняется блок Always, в противном случае вы получите память.

1 голос
/ 01 марта 2012

Я не думаю, что другие ответы прямо и правильно решают вопрос о том, переключаются ли y0 и y1 в 0 и обратно при каждом такте.

Допустим, конечный автомат меняется с s0 на s1. В обоих состояниях конечное значение y1 равно 1, но при повторной оценке всегда назначается блоку y1 0. Это переключение может происходить несколько раз за такт, или не происходить вообще за такт, в зависимости от того, сколько раз a, b и изменение состояния_рег. Распространяется ли это переключение на провод, подключенный к выходу y1, зависит от симулятора. Назначения портов обрабатываются как постоянные назначения в Verilog, которые являются отдельно выполняемыми потоками выполнения. Для симулятора вполне законно приостановить выполнение блока всегда после назначения y1 = 0, присвоить 0 проводу, подключенному к выходу y1, и возобновить выполнение блока всегда после этого. С практической точки зрения, это не имеет значения, ЕСЛИ практикуются хорошие стили кодирования, потому что значение y1 не будет зафиксировано ни в одном регистре до следующего тактового цикла, долго после того, как все переключение будет выполнено и будет доступно окончательное значение y1.

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

1 голос
/ 15 января 2012

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

Таким образом, для первого блока always планируется запускать каждый тактовый переход с 0 на 1, а сброс выполняется асинхронно.

Второй блок всегда имеет нотацию @*, которая в основном создает список чувствительности для вас на основе логики в блоке. Напомним, что только входы имеют значение в списке чувствительности. Поэтому этот блок всегда будет запланирован, если a, b или state_reg изменятся.

В этом примере

    y1 = 1'b0;              // default output:  0
    y0 = 1'b0;              // default output:  0

пытается смоделировать состояние IDLE , состояние, при котором FSM выводит 0. Если вы быстро изучите работу FSM, вы увидите, что, как только он начнет переходить через состояния , (заявления по делу) не выйдет.

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

...