Pull to refresh

Comments 39

UFO just landed and posted this here
UFO just landed and posted this here
А вас не удивляет, что в комбинационной логике присвоение происходит переменной, объявленной как регистр?
Компилятор не по этим признакам определит что ему использовать: триггер или LUT. Для него важнее список чувствительности always.
UFO just landed and posted this here
Даже если поставить <= синтезатор будет использовать LUT, если список чувствительности включает все сигналы, например так: always @*. Если вы собираетесь делать именно комбинационное устройство, то все входные сигналы должны быть в списке чувствительности, а все мультиплексоры (операторы if, case, ?:) должны иметь явную ветвь default.
Если сигнал в списке чувствительности указан явно и не используется в качестве информационного в потоке, то он будет воспринят как clk или асинхронный сброс/установка. В этом случаи будет использован триггер.
Когда пишешь код для синтеза нужно знать не только особенность стандарта Verilog, но и архитектуру аппаратной платформы, как минимум, устройство логического элемента — что допустимо для Стратикса, пойдет на Циклоне со скрипом.
UFO just landed and posted this here
Я тут мимо проходил, и на верилоге почти не пишу, поэтому не могу сказать, где код ужасен, а где нет, но хотел бы научиться отличаться ужасный код от нормального и толковые комментарии от профессионалов могли бы в этом помочь. Вместо них у вас тут «и так понятно».
Посмотрите для примера на комментарии тов. 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);


Выход комбинационной схемы может давать иголки из-за неодновременного переключения составляющих логических элементов, поэтому их всегда надо пропускать через синхронизатор, прежде чем заводить куда-то дальше в синхронных схемах
Большое спасибо. Плюсанул бы вам в карму с удовольствием, но у вас статей нет. Напишите что-нибудь о верилоге и FPGA, если найдете время.
Не за что) Я думаю насчёт статьи, но у меня нет пока достойного проекта)
При всем уважении, почему в комбинационном только блокирующие?
В моей практике часто бывает, когда комбинационную схему приходится делать регистровой, например, если по времянке не проходит, то добавляю еще одно звено конвейера. В этом случае просто редактируется список чувствительности always, но если там везде блокирующие присваивания, то придется менять и их. А потом удалось оптимизировать где-то еще, и захотелось вернуться к комбинационной схеме… Вероятность допустить ошибку повышается.
Лично я стараюсь использую везде неблокирующие присваивания, и только там, где это действительно нужно использую блокирующие, причем это бывает и в синхронных схемах. Другим так делать не рекомендую, потому как не уверен в своей правоте.
Я сразу указал, что я не профессионал, поэтому не могу ответить на это) читал в книгах и слышал от разбирающихся людей, что в комб. логике не следует использовать неблокирующие присваивания.
Я пишу на SystemVerilog, там можно явно указать, какая логика требуется: флип-флопы, защёлки или комбинационная ( always_ff, always_latch, always_comb) соответственно. У 2 и 3 списка чувствительности нет в принципе.
UFO just landed and posted this here
Я думал это написать, но не смог внятно сформулировать) Насколько я понял, при использовании блокирующих присваиваний важен порядок строк в коде, и тогда если написать
b = c;
a = b;
то для компилятора переменная b уже будет как бы «определена», а её значение равно c. И он подставит c вместо b во всех строках, идущих после.
Пару замечаний.

>1. Необходимо чётко знать, какой тип схемы должен получиться из куска кода: синхронная, защёлка или комбинационная.
Согласен, кроме того, что защелок следует избегать.

>2. В синхронной схеме нужно использовать только неблокирующие присваивания, т.е.
Продублируйте, пожалуйста, текст не до конца сохранился
Я не автор, но спрошу — какие в этом проблемы? Я в HDL языках нуб полнейший, не понял, чему вы так ужасаетесь.
Не за что) Я думаю насчёт статьи, но у меня нет пока достойного проекта)
сорри, не в ту ветку попал, с мобильным приложением какие-то проблемы
отличное упражнение написать игру пинг понг на любом языке программирования. В один тред. С AI на генеративных функциях (иногда ошибается) и с Command pattern'ом.

Хорошая задача, нормальный студент должен суметь кое-как написать.

Вот тут ныли-ныли, что они не хотят быть «мясом» и писать на го. А вот идите ка на своей скале напишите хоть пинг-понг. И покажите и поговорим тогда.
За тематику плюсую. Но к оформлению проекта и кода есть много вопросов. Какая религия не позволила сделать проект полностью на HDL, зачем Top Level в Schematic? Зачем в тексте кода так много Magic number? Но не оставляет ощущение, что вы до сих пор пишете на Си: много begin-end, сложные логические конструкции. Множество приоритетных мультиплексоров на if с неявными else. Вам компилятор про Latch предупреждение не выдавал?

P.S.: Недостаток запятых в тексте делает чтение не комфортным.
Я начинающий, по роду своей деятельности пишу на Си под МК. В FPGA попробовал себя недавно и изучаю эту область сам, ну и решил разродиться статьей.
Привет! Отличная статья, хорошая мотивация для новичков, в т.ч. и меня.
Попробуйте в тестбенчах для генерации клока такую конструкцию:

initial begin
#0 clock_r = 1;
//…
end

always begin
#x clock_r
Не уловил смысл, того что предлагаете сделать.
Почему-то текст комментария не до конца разместился

initial begin
#0 clock_r = 1;
//…
end

always begin
#x clock_r = ~clock_r;
end

х — это половина периода синхронизации

Это нужно чтобы клок сам бегал, без вашего постоянного контроля и портянки кода для этого)
Фронтальной проекции не хватает.
Не требовалось ли в рамках этого конкурса использовать процессор MIPSfpga для решения задачи?

PS: в мир FPGA вошел недавно, очень извиняюсь перед более опытными людьми которым мой код выжег глаза. Прошу понять, простить, помочь советом.

Коли спрашиваете, даю советы:
  • Прочитать «Совершенный код» Макконнелла.
  • Найти в интернете или взять какой-то готовый coding-style по Verilog'у. На правах рекламы показываю тот, которым руководствуюсь я. (Версия не самая полная, я еще кое-чего там буду дописывать).
  • Не путать блокирующие и неблокирующие присваивания.
  • Разобраться, какие файлы надо класть в систему контроля версий, а какие нет. Невооруженным взглядом видно, что много мусорных файлов (например, директория db) лежит в проекте.
  • Для описания констрейнов (например, значений частот у клоков в системе) не использовался sdc-файл. По отчету project_1.sta.rpt видно, что клоки clk:inst4|clock_out[17] и PS2_CLK взяты с частотой 1 ГГц, что на самом деле, конечно не так.


YuriPanchul, вы как-то модерируете (ревьюите) проекты (и исходные коды), которые потом попадают к вам на гитхаб?
Спасибо за coding-style. А на какой-либо площадке предполагается обсуждение и вопросы? Может статью на Хабр заделаете?
Мне кое-что не ясно, есть спорные моменты. Например, чем не угодил вам TAB? Много где встречаю, что вместо него пробелами ровняют, но почему? Переносимость между редакторами?
Кодинг стайл состоит из двух вещей: субъективной (расстановка скобок, пробелов, выравнивание, конвенция о наименовании переменных) и объективной (к примеру, корректное использование присваиваний).

ИМХО, большая часть кодинг стайла — субъективная, и обсуждать её я не вижу особого смысла.
Два примера с 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:
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 Блокирующие и не блокирующие назначение при синтезе дают одит и тот же [правильный] результат в массе своей, так что проблемы только в моделировании можно заметить.
[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х триггеров будет «оптимизирована» и один из триггеров будет выкинут просто-напросто
Ну да, в этом-то случае естественно, когда есть прямая последовательность назначений в одном процессе.
Тогда есть смысла _всегда_ использовать неблокирующие присваивания, чтобы не задумываться, в каком порядке писать строчки кода
Конечно. Я лишь говорил о том, что в синтезе используются заметные упрощения, не позволяющие порой обнаружить формальные ошибки ;)
Причём если поменять местами 2 строчки и написать так:

data_o <= data_between;
data_between <= data_i;

То никакой оптимизации не произойдёт)) У меня нещадно бомбило однажды от такого поведения кода
Бесплатный совет всем, у кого не хватило денег на Spyglass. Используйте Verilator в Lint-режиме.
Sign up to leave a comment.

Articles