Использование блокирующих назначений для вывода триггеров в Verilog

Я прочитал "Неблокирующие присвоения в Verilog Synthesis, убивающие стили кодирования!" пользователя Клиффорд Каммингс. Он говорит, что следующий код (стр. 12, упрощенный) является правильной реализацией триггера, часто используемого в учебниках, даже если он не совсем тот, который следует использовать. Документ получил награду за лучшую бумагу, поэтому я полагаю, что утверждение верно.

module ff (q, d, clk)
  output q;
  input d, clk;
  reg q;

  always @(posedge clk)
    q = d;
endmodule

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

module two_ffs (q, d, clk)
  input d, clk;
  output q;

  wire tmp;

  ff firstff (tmp, d, clk);
  ff secondff (q, tmp, clk);
endmodule

На мой взгляд, возможно, что значение tmp обновляется до того, как оно используется secondff, в результате чего получается один триггер, а не два. Кто-нибудь может сказать мне, в какой части стандарта сказано, что этого не может быть? Большое спасибо.

[не то, чтобы я когда-либо думал о написании такого кода, я просто хочу понять поведение блокировки / неблокирования даже в тех случаях, когда плохой стиль кодирования делает значение неочевидным]

Добавлено позже:

Я теперь думаю, что статья вряд ли будет правильной. Раздел 5 «Семантика планирования» стандарта 1364-2201 Verilog объясняет, что происходит. В частности, в разделе 5.6.6 «Соединения портов» на стр. 68 говорится, что однонаправленные порты аналогичны непрерывным назначениям. В свою очередь, непрерывное назначение - это всегда блок, чувствительный ко всему. Итак, суть в том, что два экземпляра ff в моем примере ниже эквивалентны модулю с несколькими предложениями always, которые, как все согласятся, нарушены.

Добавлено после того, как Клайв Каммингс ответил на вопрос:

Я благодарен CC за указание на то, что приведенные выше утверждения, взятые из раздела 5 стандарта, относятся только к времени событий обновления и не подразумевают буквальную эквивалентность между, например, некоторые сплошные назначения и всегда блоки. Тем не менее, я думаю, они объясняют, почему некоторые симуляторы (например, Icarus Verilog) будут давать разные результаты симуляции с назначением блокировки и неблокирующего назначения в «триггере». [В более крупном примере у меня есть 2 очевидных ffs с блокирующим назначением и правильные пять с неблокирующим назначением.] Другие симуляторы (например, Modelsim с параметрами по умолчанию или Cver), похоже, дают один и тот же результат независимо от того, в какой форме присвоение используется.


person user1002059    schedule 25.06.2012    source источник
comment
Вы уверены, что это пример правильной реализации? Мне это кажется хрестоматийным примером неправильного дизайна. Я думаю, ваше понимание правильное.   -  person Tim    schedule 26.06.2012
comment
На самом деле я нигде не вижу этого первого раздела в этой статье. Я думаю, вы, вероятно, неправильно его читаете, или вы можете указать, откуда именно вы взяли этот образец кода? Я не вижу этого на странице 12 (у меня может быть другая версия, чем у вас).   -  person Tim    schedule 26.06.2012
comment
Тим: извините за то, что не предоставил ссылку (добавлено сейчас). Я имею в виду версию 1.3 документа, тогда как вы нашли версию 1.2. Прости еще раз. Он находится в разделе 9 обоих документов. В примере 13 показана модель триггера, которая встречается в большинстве тестовых сборников Verilog. Тогда его заголовок просто ошибочный ..... но он работает !.   -  person user1002059    schedule 26.06.2012
comment
Хорошо, теперь я понял. Хм, я не могу придумать, почему он так заявил. В любом случае: Читателям рекомендуется отправлять электронное письмо Клиффу Каммингсу (отредактировано) всякий раз, когда они обнаруживают потенциальные ошибки или если они хотят предложить улучшения. Может быть, вы можете спросить автора :)   -  person Tim    schedule 26.06.2012
comment
Если у вас есть доступ к университетской библиотеке, вы можете рассмотреть возможность получения IEEE 1364.1 Standard for Verilog Register Transfer Level Synthesis.   -  person    schedule 26.06.2012


Ответы (2)


Все -

Несколько исправлений и обновлений. Раздел 5.6.6 стандарта Verilog Standard 2001 не говорит, что «однонаправленные порты подобны непрерывным назначениям», он говорит: «Порты соединяют процессы с помощью неявных операторов непрерывного присваивания». Есть разница, которую я отмечу ниже.

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

Что касается моего описания триггера с одним всегда блоком, то мое описание в статье не является на 100% точным (но, как правило, точным). Теоретически у модели триггера с двумя экземплярами действительно есть состояние гонки, хотя это редко встречается. Гонка наблюдается редко, потому что, когда вы выполняете постоянное блочное присвоение переменной, которая объявлена ​​как выход, компиляторы Verilog автоматически добавляют «оператор неявного непрерывного присваивания» (IEEE-1364-2001, раздел 5.6.6, 1-й абзац) для преобразования процедурной переменной в назначение net- Driving (вы никогда не увидите, чтобы это произошло!). Обычно этого преобразования достаточно, чтобы ввести эквивалент неблокирующей задержки назначения для порта , значит, симуляция работает. В прошлом я экспериментировал с переключателями оптимизации компилятора, которые эффективно удаляют порты модулей между триггерами, и наблюдал за нежелательными условиями гонки, поэтому технически мое описание нормально 1-всегда, блокирующего назначения триггера не равно 100 % правильный; следовательно, вы все равно должны использовать неблокирующие присваивания, описанные в документе.

Пример 2-всегда блокирующего назначения в одном и том же модуле имеет определенное состояние гонки. Как написано, это, вероятно, будет работать, потому что большинство компиляторов выполняют код сверху вниз, но если вы измените порядок блоков always, вы, вероятно, увидите гонку.

С уважением - Клифф Каммингс - Verilog и SystemVerilog Guru

person Cliff Cummings    schedule 27.06.2012

Чтение версии 1.3 документа, раздел 9, пример 13. Текст под ним объясняет что это нормально, если модуль содержит только один всегда блок. На данный момент я понимаю, что это не проблема между отдельными модулями. Позвольте вашему примеру работать. Однако, если модуль содержал несколько блоков always, то порядок выполнения не определен и приведет к условиям гонки, о которых говорилось в разделе 2 paper.

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

module ff (q, d, clk)
  output reg q;
  input d, clk;
  reg d_delay ;

  always @(posedge clk)
    d_delay = d;

  always @(posedge clk)
    q = d_delay;

endmodule
person Morgan    schedule 26.06.2012
comment
Я также подумал, что планирование между модулями было чем-то особенным. Но стандарт говорит, что это не так. См. Только что добавленный комментарий вверху. Большое спасибо за ответ. - person user1002059; 27.06.2012