Комментарии 39
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А вас не удивляет, что в комбинационной логике присвоение происходит переменной, объявленной как регистр?
Компилятор не по этим признакам определит что ему использовать: триггер или LUT. Для него важнее список чувствительности always.
Компилятор не по этим признакам определит что ему использовать: триггер или LUT. Для него важнее список чувствительности always.
НЛО прилетело и опубликовало эту надпись здесь
Даже если поставить <= синтезатор будет использовать LUT, если список чувствительности включает все сигналы, например так:
Если сигнал в списке чувствительности указан явно и не используется в качестве информационного в потоке, то он будет воспринят как clk или асинхронный сброс/установка. В этом случаи будет использован триггер.
Когда пишешь код для синтеза нужно знать не только особенность стандарта Verilog, но и архитектуру аппаратной платформы, как минимум, устройство логического элемента — что допустимо для Стратикса, пойдет на Циклоне со скрипом.
always @*
. Если вы собираетесь делать именно комбинационное устройство, то все входные сигналы должны быть в списке чувствительности, а все мультиплексоры (операторы if, case, ?:) должны иметь явную ветвь default. Если сигнал в списке чувствительности указан явно и не используется в качестве информационного в потоке, то он будет воспринят как clk или асинхронный сброс/установка. В этом случаи будет использован триггер.
Когда пишешь код для синтеза нужно знать не только особенность стандарта Verilog, но и архитектуру аппаратной платформы, как минимум, устройство логического элемента — что допустимо для Стратикса, пойдет на Циклоне со скрипом.
НЛО прилетело и опубликовало эту надпись здесь
Я тут мимо проходил, и на верилоге почти не пишу, поэтому не могу сказать, где код ужасен, а где нет, но хотел бы научиться отличаться ужасный код от нормального и толковые комментарии от профессионалов могли бы в этом помочь. Вместо них у вас тут «и так понятно».
Посмотрите для примера на комментарии тов. khim, он тоже не терпит говнокода, но при этом не ленится пояснять почему конкретный код — говно и что с этим можно сделать. В итоге в выигрыше все, и автор, и комментатор, и читатели.
TL;DR: Критикуешь — предлагай.
Посмотрите для примера на комментарии тов. khim, он тоже не терпит говнокода, но при этом не ленится пояснять почему конкретный код — говно и что с этим можно сделать. В итоге в выигрыше все, и автор, и комментатор, и читатели.
TL;DR: Критикуешь — предлагай.
Как говорится, я, конечно, не гинеколог, но посмотреть могу… Я не профессионал, но могу пару общих правил привести, по возможности, с примером ошибки из кода:
1. Необходимо чётко знать, какой тип схемы должен получиться из куска кода: синхронная, защёлка или комбинационная. Для каждого типа есть свои правила написания кода, их я ниже изложу
2. В синхронной схеме нужно использовать только неблокирующие присваивания, т.е. <=
В приведённом коде этот кусок с ошибкой
…
always @(negedge clock)
begin
if(PS2_CLK_in == 1)
count_clk = count_clk + 1;
else
…
3. В комбинационной только блокирующие =
В приведённом коде этот кусок с ошибкой
…
always @*
begin
visible <= hvisible & vvisible;
end
…
4. Все возможные ветви комбинационного кода должны быть описаны, иначе получится защёлка. Например, если у вас кейс по 4х-битной переменной, нужно либо описать все 16 ветвей либо добавить ветку default для всех неописанных.
Если получилась где-то защёлка, где вы не собирались её создавать (вы это увидите в варнингах), то скорее всего, код косячный.
Ещё по коду увидел такую вещь: синхронизация дисплея берётся с выхода комбинационной схемы
…
wire w_hsync = (pixel_count < h_sync);
…
Выход комбинационной схемы может давать иголки из-за неодновременного переключения составляющих логических элементов, поэтому их всегда надо пропускать через синхронизатор, прежде чем заводить куда-то дальше в синхронных схемах
1. Необходимо чётко знать, какой тип схемы должен получиться из куска кода: синхронная, защёлка или комбинационная. Для каждого типа есть свои правила написания кода, их я ниже изложу
2. В синхронной схеме нужно использовать только неблокирующие присваивания, т.е. <=
В приведённом коде этот кусок с ошибкой
…
always @(negedge clock)
begin
if(PS2_CLK_in == 1)
count_clk = count_clk + 1;
else
…
3. В комбинационной только блокирующие =
В приведённом коде этот кусок с ошибкой
…
always @*
begin
visible <= hvisible & vvisible;
end
…
4. Все возможные ветви комбинационного кода должны быть описаны, иначе получится защёлка. Например, если у вас кейс по 4х-битной переменной, нужно либо описать все 16 ветвей либо добавить ветку default для всех неописанных.
Если получилась где-то защёлка, где вы не собирались её создавать (вы это увидите в варнингах), то скорее всего, код косячный.
Ещё по коду увидел такую вещь: синхронизация дисплея берётся с выхода комбинационной схемы
…
wire w_hsync = (pixel_count < h_sync);
…
Выход комбинационной схемы может давать иголки из-за неодновременного переключения составляющих логических элементов, поэтому их всегда надо пропускать через синхронизатор, прежде чем заводить куда-то дальше в синхронных схемах
Большое спасибо. Плюсанул бы вам в карму с удовольствием, но у вас статей нет. Напишите что-нибудь о верилоге и FPGA, если найдете время.
При всем уважении, почему в комбинационном только блокирующие?
В моей практике часто бывает, когда комбинационную схему приходится делать регистровой, например, если по времянке не проходит, то добавляю еще одно звено конвейера. В этом случае просто редактируется список чувствительности
Лично я стараюсь использую везде неблокирующие присваивания, и только там, где это действительно нужно использую блокирующие, причем это бывает и в синхронных схемах. Другим так делать не рекомендую, потому как не уверен в своей правоте.
В моей практике часто бывает, когда комбинационную схему приходится делать регистровой, например, если по времянке не проходит, то добавляю еще одно звено конвейера. В этом случае просто редактируется список чувствительности
always
, но если там везде блокирующие присваивания, то придется менять и их. А потом удалось оптимизировать где-то еще, и захотелось вернуться к комбинационной схеме… Вероятность допустить ошибку повышается. Лично я стараюсь использую везде неблокирующие присваивания, и только там, где это действительно нужно использую блокирующие, причем это бывает и в синхронных схемах. Другим так делать не рекомендую, потому как не уверен в своей правоте.
Я сразу указал, что я не профессионал, поэтому не могу ответить на это) читал в книгах и слышал от разбирающихся людей, что в комб. логике не следует использовать неблокирующие присваивания.
Я пишу на SystemVerilog, там можно явно указать, какая логика требуется: флип-флопы, защёлки или комбинационная ( always_ff, always_latch, always_comb) соответственно. У 2 и 3 списка чувствительности нет в принципе.
Я пишу на SystemVerilog, там можно явно указать, какая логика требуется: флип-флопы, защёлки или комбинационная ( always_ff, always_latch, always_comb) соответственно. У 2 и 3 списка чувствительности нет в принципе.
НЛО прилетело и опубликовало эту надпись здесь
Я думал это написать, но не смог внятно сформулировать) Насколько я понял, при использовании блокирующих присваиваний важен порядок строк в коде, и тогда если написать
b = c;
a = b;
то для компилятора переменная b уже будет как бы «определена», а её значение равно c. И он подставит c вместо b во всех строках, идущих после.
b = c;
a = b;
то для компилятора переменная b уже будет как бы «определена», а её значение равно c. И он подставит c вместо b во всех строках, идущих после.
Пару замечаний.
>1. Необходимо чётко знать, какой тип схемы должен получиться из куска кода: синхронная, защёлка или комбинационная.
Согласен, кроме того, что защелок следует избегать.
>2. В синхронной схеме нужно использовать только неблокирующие присваивания, т.е.
>1. Необходимо чётко знать, какой тип схемы должен получиться из куска кода: синхронная, защёлка или комбинационная.
Согласен, кроме того, что защелок следует избегать.
>2. В синхронной схеме нужно использовать только неблокирующие присваивания, т.е.
Я не автор, но спрошу — какие в этом проблемы? Я в HDL языках нуб полнейший, не понял, чему вы так ужасаетесь.
отличное упражнение написать игру пинг понг на любом языке программирования. В один тред. С AI на генеративных функциях (иногда ошибается) и с Command pattern'ом.
Хорошая задача, нормальный студент должен суметь кое-как написать.
Вот тут ныли-ныли, что они не хотят быть «мясом» и писать на го. А вот идите ка на своей скале напишите хоть пинг-понг. И покажите и поговорим тогда.
Хорошая задача, нормальный студент должен суметь кое-как написать.
Вот тут ныли-ныли, что они не хотят быть «мясом» и писать на го. А вот идите ка на своей скале напишите хоть пинг-понг. И покажите и поговорим тогда.
За тематику плюсую. Но к оформлению проекта и кода есть много вопросов. Какая религия не позволила сделать проект полностью на HDL, зачем Top Level в Schematic? Зачем в тексте кода так много Magic number? Но не оставляет ощущение, что вы до сих пор пишете на Си: много begin-end, сложные логические конструкции. Множество приоритетных мультиплексоров на if с неявными else. Вам компилятор про Latch предупреждение не выдавал?
P.S.: Недостаток запятых в тексте делает чтение не комфортным.
P.S.: Недостаток запятых в тексте делает чтение не комфортным.
Привет! Отличная статья, хорошая мотивация для новичков, в т.ч. и меня.
Попробуйте в тестбенчах для генерации клока такую конструкцию:
initial begin
#0 clock_r = 1;
//…
end
always begin
#x clock_r
Попробуйте в тестбенчах для генерации клока такую конструкцию:
initial begin
#0 clock_r = 1;
//…
end
always begin
#x clock_r
Не уловил смысл, того что предлагаете сделать.
Красивая девушка.
Не требовалось ли в рамках этого конкурса использовать процессор MIPSfpga для решения задачи?
Коли спрашиваете, даю советы:
YuriPanchul, вы как-то модерируете (ревьюите) проекты (и исходные коды), которые потом попадают к вам на гитхаб?
PS: в мир FPGA вошел недавно, очень извиняюсь перед более опытными людьми которым мой код выжег глаза. Прошу понять, простить, помочь советом.
Коли спрашиваете, даю советы:
- Прочитать «Совершенный код» Макконнелла.
- Найти в интернете или взять какой-то готовый coding-style по Verilog'у. На правах рекламы показываю тот, которым руководствуюсь я. (Версия не самая полная, я еще кое-чего там буду дописывать).
- Не путать блокирующие и неблокирующие присваивания.
- Разобраться, какие файлы надо класть в систему контроля версий, а какие нет. Невооруженным взглядом видно, что много мусорных файлов (например, директория db) лежит в проекте.
- Для описания констрейнов (например, значений частот у клоков в системе) не использовался sdc-файл. По отчету project_1.sta.rpt видно, что клоки clk:inst4|clock_out[17] и PS2_CLK взяты с частотой 1 ГГц, что на самом деле, конечно не так.
YuriPanchul, вы как-то модерируете (ревьюите) проекты (и исходные коды), которые потом попадают к вам на гитхаб?
Спасибо за coding-style. А на какой-либо площадке предполагается обсуждение и вопросы? Может статью на Хабр заделаете?
Мне кое-что не ясно, есть спорные моменты. Например, чем не угодил вам TAB? Много где встречаю, что вместо него пробелами ровняют, но почему? Переносимость между редакторами?
Мне кое-что не ясно, есть спорные моменты. Например, чем не угодил вам TAB? Много где встречаю, что вместо него пробелами ровняют, но почему? Переносимость между редакторами?
Кодинг стайл состоит из двух вещей: субъективной (расстановка скобок, пробелов, выравнивание, конвенция о наименовании переменных) и объективной (к примеру, корректное использование присваиваний).
ИМХО, большая часть кодинг стайла — субъективная, и обсуждать её я не вижу особого смысла.
Два примера с if:
Кому-то больше нравится первый вариант, вариант кому-то второй. Это абсолютно нормально: и я не вижу смысла тратить время на холивары.
Пробелы гарантируют одинаковое отображение исходного кода везде (при использовании monospace шрифта, разумеется), а табы — в зависимости от настроек, но это не значит, что табы должны быть запрещены: в linux kernel их используют, и я не имею права за это в них кинуть камень. У них есть свой CodingStyle, где это явно прописано.
Тот кодинг стайл, что я скинул, это компиляция опыта (~5-7 лет коммерческой разработки) и предпочтений небольшой команды FPGA-разработчиков. Этот стиль позволял быстро (за предсказуемое время), качественно разрабатывать новые модули (сам себя не похвалишь — никто не похвалит) и осуществлять дальнейшую поддержку и багфикс кода. Версия не самая полная, потому что оригинал находится в формате wiki/html, и я постепенно переношу это в markdown, причёсывая его структуру.
Я всегда рад услышать вопросы, замечания, предложения, но скатываться в холивар я не буду. Это является оффтопом к теме с созданием игры, поэтому можно писать мне в личку.
ИМХО, большая часть кодинг стайла — субъективная, и обсуждать её я не вижу особого смысла.
Два примера с if:
1. if( a > b )
2. if (a > b)
Кому-то больше нравится первый вариант, вариант кому-то второй. Это абсолютно нормально: и я не вижу смысла тратить время на холивары.
Пробелы гарантируют одинаковое отображение исходного кода везде (при использовании monospace шрифта, разумеется), а табы — в зависимости от настроек, но это не значит, что табы должны быть запрещены: в linux kernel их используют, и я не имею права за это в них кинуть камень. У них есть свой CodingStyle, где это явно прописано.
Тот кодинг стайл, что я скинул, это компиляция опыта (~5-7 лет коммерческой разработки) и предпочтений небольшой команды FPGA-разработчиков. Этот стиль позволял быстро (за предсказуемое время), качественно разрабатывать новые модули (сам себя не похвалишь — никто не похвалит) и осуществлять дальнейшую поддержку и багфикс кода. Версия не самая полная, потому что оригинал находится в формате wiki/html, и я постепенно переношу это в markdown, причёсывая его структуру.
Я всегда рад услышать вопросы, замечания, предложения, но скатываться в холивар я не буду. Это является оффтопом к теме с созданием игры, поэтому можно писать мне в личку.
ishevchuk в данном случае я не модерировал, рассматривая это как упражнение для Бориса, которого все равно раскритикуют. Когда он собрался публиковать, я ему сказал про проблемы с стилем (см. описание ниже) и предложил либо их исправить, либо опубликовать как есть, с идеей, что на Хабре его все равно раскритикуют.
Раз уж тут пошла критика, прибавляю своей:
Борис, я рекомендую перед публикацией привести стиль вашего кода к некоторым принятым в индустрии традициям, которые были сформулированы в районе 2000-го года (т.е. вы можете найти много старого верилога, который этому не следует, но лучше чтобы следовало). В принципе, можно публиковать и так, но вас тогда раскритикуют на Хабре (это может быть полезно, так как там много людей, и они сделают критику быстрее чем я, потому что я связан другими делами).
Но как минимум стоит избавиться от блокирующих присваиваний внутри последовательностных always-блоков. С ними можно нарваться на гонки, race-condition, когда результат симуляции может не совпасть с результатом работы платы после синтеза. Например см. мой коммент в обсуждении на https://habrahabr.ru/post/278005/
Простейший пример race condition:
А вот если делать так, то все будет однозначно:
Также использование генерируемого сигнала в качестве clock-а – это не очень хорошо:
Если вам нужно такое сгенерить, то лучше делать чего-нибудь типа (хоть это и выглядит громоздко):
Кроме этого, код типа
лучше заменить на
Наконец, есть более компактные способы записи кода в testbench:
Вместо:
Можно написать что-нибудь типа:
Но опять же: вы можете выложить as-is и посмотреть, как народ на Хабре будет реагировать (так как вы начинающий, то вашу репутацию это не испортит).
Кроме этого я рекомендую выложить ваш код на GitHub (например поместить проект в https://github.com/MIPSfpga/pre-mipsfpga ) чтобы ссылаться на ваш код из поста.
Это мои первые мысли – завтра я посмотрю ваш код более внимательно.
Спасибо,
Юрий Панчул
Раз уж тут пошла критика, прибавляю своей:
Борис, я рекомендую перед публикацией привести стиль вашего кода к некоторым принятым в индустрии традициям, которые были сформулированы в районе 2000-го года (т.е. вы можете найти много старого верилога, который этому не следует, но лучше чтобы следовало). В принципе, можно публиковать и так, но вас тогда раскритикуют на Хабре (это может быть полезно, так как там много людей, и они сделают критику быстрее чем я, потому что я связан другими делами).
Но как минимум стоит избавиться от блокирующих присваиваний внутри последовательностных always-блоков. С ними можно нарваться на гонки, race-condition, когда результат симуляции может не совпасть с результатом работы платы после синтеза. Например см. мой коммент в обсуждении на https://habrahabr.ru/post/278005/
Простейший пример race condition:
module dut ( input clk, input [7:0] d, output logic [7:0] q ); logic [7:0] r; always @(posedge clk) // ПЛОХО! Результат симуляции зависит от того, какой блок симулируется раньше в event queue – первый или второй r = d; always @(posedge clk) q = r; endmodule
А вот если делать так, то все будет однозначно:
module dut ( input clk, input [7:0] d, output logic [7:0] q ); logic [7:0] r; always @(posedge clk) r <= d; always @(posedge clk) q <= r; endmodule
Также использование генерируемого сигнала в качестве clock-а – это не очень хорошо:
always @ (posedge pixel_clock) begin hsync <= (pixel_count < h_sync); …. always @ (posedge hsync) …
Если вам нужно такое сгенерить, то лучше делать чего-нибудь типа (хоть это и выглядит громоздко):
wire hsync <= (pixel_count < h_sync); reg prev_hsync; always @ (posedge clk) prev_hsync <= hsync; … wire pos_hsync = hsync & ~ prev_hsync; …. always @ (posedge clk) if(reset) … else if (pos_hsync)
Кроме этого, код типа
if(goal == 0) begin HEX_1 = 7'b1000000; end if(goal == 1) begin HEX_1 = 7'b1111001; end if(goal == 2) begin HEX_1 = 7'b0100100; end
лучше заменить на
case (goal) 0: HEX_1 = 7'b1000000; 1: HEX_1 = 7'b1111001; 2: HEX_1 = 7'b0100100; . . . . default: HEX_1 = 7’b1111111; endcase
Наконец, есть более компактные способы записи кода в testbench:
Вместо:
#50 PS2_CLK_r = 0; //start #50 PS2_CLK_r = 0; //0 #50 PS2_CLK_r = 1; //1 #50 PS2_CLK_r = 1; //2 #50 PS2_CLK_r = 0; //3 #50 PS2_CLK_r = 1; //4 #50 PS2_CLK_r = 0; //5 #50 PS2_CLK_r = 1; //6 #50 PS2_CLK_r = 1; //7 #50 PS2_CLK_r = 1; //parity bit #50 PS2_CLK_r = 0; //stop #50 PS2_CLK_r = 1; //s #50 PS2_CLK_r = 1; //s
Можно написать что-нибудь типа:
reg [511:0] test_bits; integer i; initial begin … test_bits = 512’b01010111000110010101; for (i = 0; i < 512; i = i + 1) #50 PS2_CLK_r = test_bits [i];
Но опять же: вы можете выложить as-is и посмотреть, как народ на Хабре будет реагировать (так как вы начинающий, то вашу репутацию это не испортит).
Кроме этого я рекомендую выложить ваш код на GitHub (например поместить проект в https://github.com/MIPSfpga/pre-mipsfpga ) чтобы ссылаться на ваш код из поста.
Это мои первые мысли – завтра я посмотрю ваш код более внимательно.
Спасибо,
Юрий Панчул
Спасибо за подробный ответ.
Жаль, что изначальная предпосылка такая:
Жаль, что изначальная предпосылка такая:
рассматривая это как упражнение для Бориса, которого все равно раскритикуют. Когда он собрался публиковать, я ему сказал про проблемы с стилем (см. описание ниже) и предложил либо их исправить, либо опубликовать как есть, с идеей, что на Хабре его все равно раскритикуют.
Коллеги, будьте объективны. Человек написал первый в жизни простенький проектик. Несмотря на объективные проблемы с кодом, довел его до работоспособного состояния (по крайней мере в данных конкретных условиях, хе-хе). Зачем же сразу зло клевать, ласковее надо… Другое дело, для чего его выкладывать сразу на общее обозрение… Я вот прошлую неделю впервые на жабаскрипте писал, но показывать код, пока, пожалуй никому не буду. Пусть лучше потом следственный комитет разбирается, отчего ракета упала.
PS Блокирующие и не блокирующие назначение при синтезе дают одит и тот же [правильный] результат в массе своей, так что проблемы только в моделировании можно заметить.
PS Блокирующие и не блокирующие назначение при синтезе дают одит и тот же [правильный] результат в массе своей, так что проблемы только в моделировании можно заметить.
[quote]PS Блокирующие и не блокирующие назначение при синтезе дают один и тот же [правильный] результат в массе своей, так что проблемы только в моделировании можно заметить.[/quote]
Не совсем так.
Если под рукой есть синтезатор, тот же квартус, попробуйте написать такой код ( SystemVerilog ):
module top (
input logic rst_i,
input logic clk_i,
input logic data_i,
output logic data_o
);
logic data_between;
always_ff @( posedge clk_i or posedge rst_i ) begin
if ( rst_i ) begin
data_o <= 0;
data_between <= 0;
end else begin
data_between <= data_i;
data_o <= data_between;
end
end
endmodule
Просинтезируйте и посмотрите RTL модель, затем поменяйте неблокирующие присваивания на блокирующие, просинтезируйте и посмотрите, что будет. А будет следующее: цепочка из 2х триггеров будет «оптимизирована» и один из триггеров будет выкинут просто-напросто
Не совсем так.
Если под рукой есть синтезатор, тот же квартус, попробуйте написать такой код ( SystemVerilog ):
module top (
input logic rst_i,
input logic clk_i,
input logic data_i,
output logic data_o
);
logic data_between;
always_ff @( posedge clk_i or posedge rst_i ) begin
if ( rst_i ) begin
data_o <= 0;
data_between <= 0;
end else begin
data_between <= data_i;
data_o <= data_between;
end
end
endmodule
Просинтезируйте и посмотрите RTL модель, затем поменяйте неблокирующие присваивания на блокирующие, просинтезируйте и посмотрите, что будет. А будет следующее: цепочка из 2х триггеров будет «оптимизирована» и один из триггеров будет выкинут просто-напросто
Ну да, в этом-то случае естественно, когда есть прямая последовательность назначений в одном процессе.
Причём если поменять местами 2 строчки и написать так:
data_o <= data_between;
data_between <= data_i;
То никакой оптимизации не произойдёт)) У меня нещадно бомбило однажды от такого поведения кода
data_o <= data_between;
data_between <= data_i;
То никакой оптимизации не произойдёт)) У меня нещадно бомбило однажды от такого поведения кода
Бесплатный совет всем, у кого не хватило денег на Spyglass. Используйте Verilator в Lint-режиме.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Создание простой игры на базе FPGA