Возможно немного странная тема, но думаю кому нибудь будет интересно. Недавно набрел в интернетах на статьи авторов – это Stuart Sutherland, Don Mills и Chris Spear. Сами статьи на английском можно взять вот тут:
- Verilog Gotchas Part 1 ( 405843 bytes )
- Verilog Gotchas Part 2 ( 290724 bytes )
Gotcha – это ошибка в программе, но не такая, которая выявляется компилятором. И не такая, которая появляется из-за неудачного дизайна проекта. Это ошибка в программе возникающая из-за несовершенства или особенностей языка программирования или даже компилятора, либо из-за неполного понимания программистом синтаксиса и смысла языковых конструкций. Такие ошибки очень трудно искать. Кажется вроде бы все правильно написано, компилятор успешно откомпилировал и синтезировал, но проект не работает, работает не правильно. Или еще хуже – результат симуляции проекта Verilog один, а в железе проект работает совсем не так.
Некоторые самые простые примеры я приведу прямо из упомянутых выше статей (хотя конечно, лучше почитать в оригинале).
Пример 1.
Вложенные if-else.
Вот такой "неправильный" код на Verilog HDL. Кстати в языке C/C++ возможна абсолютно похожая ошибка.
if (a >= 5)
if (a <= 10)
$display (“ 'a' между 5 и 10”);
else
$display (“ 'a' меньше 5”); // GOTCHA!
Очень часто в текстах программ применяются отступы или табуляции для более наглядного представления кода, для «красоты и стиля». Однако сами отступы могут сбить с толку, как в этом случае. Из-за отступов кажется, что else составляет пару для первого if, но на самом деле это не так. Второй if и else образуют пару.
Как избежать такой ошибки? Например, используйте begin-end:
if (a >= 5) begin
if (a <= 10)
$display (“ 'a' между 5 и 10”);
end
else
$display (“ 'a' меньше 5”); // Правильно!
Пример 2.
Использование необъявленных идентификаторов не всегда приводит к ошибке компиляции.
Например, посмотрим на вот этот код Verilog HDL:
module bad_adder (
input wire a, b, ci,
output wire sum, co );
wire n1, n2, n3;
xor g1 (n1, a, b);
xor g2 (sum, nl, ci); // GOTCHA!
and g3 (n2, a, b, c); // GOTCHA!
and g4 (n3, n1, ci);
or g5 (co, n2, n3);
endmodule
Известно, что в Verilog HDL переменные типов сигналов reg или wire нужно объявлять. Однако оказывается, если переменная не объявлена, то компилятор выдает ошибку далеко не всегда. Например, ошибки не будет, если необъявленный сигнал подключен к портам ввода вывода какого нибудь модуля.
В примере выше обозначены просто «типографские» ошибки набора текста кода. Это просто «описки». Имя "n1" похоже на "nl", только в первом случае используется цифра «один», а во втором случае написана маленькая буква «эль». Похоже правда?
Вторая «типографская» ошибка в этом примере – вместо сигнала "co" написан сигнал "c", который не объявлен.
Компилятор создает неподключенные сигналы (implicit net declarations) и естественно, поведение такого модуля оказывается не таким как задумывалось.
В принципе, есть способ выключить такое поведение компилятора с помощью директивы ‘default_nettype none. Ее можно писать в начале Verilog файла. В конце можно добавить ‘default_nettype wire, чтобы вернуть компилятор в исходное состояние.
Оказывается иногда implicit declaration даже удобно, когда код большой и нужно соединить много модулей, то зачем лишняя писанина с объявлением сигналов?
Пример 3.
Ошибки copy-paste редактирования текста.
Программист часто может написать кусок кода, а затем, чтобы не переписывать заново похожий код просто копирует его и вставляет в другое место. При этом, код уже редактируется по месту, но иногда не совсем неудачно.
always @*
begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b01: y = c; // OOPS! GOTCHA
2'b11: y = d;
endcase
end
Вот такой пример: получилось два обработчика для sel==2’b01 и нет обработчика для sel==2’b10. При копировании кода забыли исправить. Компилятор не выдает ошибки и будет работать только один, первый обработчик. Потом попробуй найди из-за чего проект не работает. Хотя.. я думаю, что синтезатор должен в этом случае выдать предупреждение, что в проекте появилась защелка (latch).
Все же нужно читать предупреждения компиляторов.
Пример 4
Операции в списке чувствительности, неполный список чувствительности.
Блоки always @ служат для описания событий происходящих при определенных обстоятельствах, когда произойдет одно из событий, описанных в скобках.
always @(a or b)
sum = a + b;
В этом примере сумма "sum" будет вычисляться симулятором, когда изменится либо сигнал "a" либо сигнал "b".
always @(a | b) // GOTCHA! “|” это оператор
sum = a + b;
А в этом случае сумма "sum" будет вычисляться симулятором, когда изменится вычисленное значение a | b. То есть, если, например, a==1, то изменение "b" никак не повлияет на результат симуляции!
Или вот еще пример:
always @(a)
sum = a + b;
Представьте себе, что написан большой сложный always блок и к нему описали список чувствительности. Потом, позже код исправили и усложнили. Вычисления теперь зависят от большего числа сигналов, но их забыли добавить в список чувствительности always блока.
Симулятор будет пересчитывать этот блок только когда изменятся сигналы в списке чуствительности, а значит, возможно, не всегда верно.
В примере сигнал "b" участвует в вычислениях, но его нет в списке чуствительности. Когда он будет меняться пересчета суммы симулятором не произойдет. Однако, синтезатор создаст схему для ПЛИС, которая будет вычислять сумму всегда при изменении любого сигнала, потому, что это комбинационная функция. Получается, что симулятор и реальное железо поведут себя по разному.
Чтобы не попадать в такие ловушки лучше использовать конструкции always @*, они пересчитываются симулятором при изменениях любых сигналов участвующих в always блоке.
Правда нет.. бывают случаи, если always блок вызывает функцию, которая использует сигналы не участвующие в данном always блоке, то пересчета так же не будет. Это уже другая gotcha :-)
Интересно, что авторы вышеупомянутых статей нашли более сотни gotchas при использовании Verilog и System Verilog! Кстати развитие языка Verilog в System Verilog позволило избежать некоторых Verilog gotcha, но, возможно, добавились новые..
Ну что тут поделать – нужно учиться их избегать.
Подробнее...