Некоторое время назад при обсуждении в компании профессиональных разработчиков FPGA возникла дискуссия о прохождении собеседования. Какие вопросы там задают, и что можно было бы задать. Я предложил два вопроса:

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

После этого вопроса завязалась оживлённая дискуссия, в результате которой я решил более подробно рассмотреть этот вопрос.

Я уже немного касался этого вопроса в предыдущей статье. Сейчас более подробно. Вот текст примера:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity delta_delay is
end delta_delay;

architecture delta_delay of delta_delay is

signal	clk1		: std_logic:='0';
signal	clk2		: std_logic;
alias	clk3		: std_logic is clk1;	-- назначение другого имени clk1

signal	a			: std_logic;
signal	b			: std_logic;
signal	c			: std_logic;
signal	d			: std_logic;
begin							
--- Формирование тестовых сигналов ---
clk1 <= not clk1 after 5 ns;

pr_a: process begin
	a <= '0' after 1 ns;
	wait until rising_edge( clk1 );
	wait until rising_edge( clk1 );
	a <= '1' after 1 ns;
	wait until rising_edge( clk1 );
	wait until rising_edge( clk1 );
	wait until rising_edge( clk1 );
	wait until rising_edge( clk1 );
end process;	
	
--- Синтезируемая часть - переназначение тактового сигнала  ---
clk2 <= clk1; -- вот в этом проблема, не надо так делать без крайней необходимости

--- Вариант 1 - Синтезируемая часть без задержек  ---

b <= a when rising_edge( clk1 );
c <= b when rising_edge( clk1 );
d <= b when rising_edge( clk2 );

--- Вариант 2 - Синтезируемая часть с задержками  ---
--
--clk2 <= clk1;
--b <= a after 1 ns when rising_edge( clk1 );
--c <= b after 1 ns when rising_edge( clk1 );
--d <= b after 1 ns when rising_edge( clk2 );

--- Вариант 3 - Синтезируемая часть без задержек но с переназначением сигнала через alias  ---
--b <= a when rising_edge( clk1 );
--c <= b when rising_edge( clk1 );
--d <= b when rising_edge( clk3 );

end delta_delay;

Для упрощения весь код размещён в одном компоненте.

Сигналы clk1 и a это сигналы тестового воздействия. clk1 это тактовая частота 100 MHz, Сигнал а держится два такта в 0 и четыре такта в 1. Сигнал a формируется с задержкой 1 nc относительно нарастающего фронта clk1. Этих двух сигналов достаточно для описания проблемы.

Различные варианты синтезируемого кода можно раскомментировать и промоделировать.
Рассмотрим первый вариант, это синтезируемый код без задержек и с использованием переназначения тактовой частоты.

Вот результаты моделирования для варианта 1:



На диаграмме визуально видно, что сигналы тактовой частоты clk1 и clk2 совпадают, но на самом деле clk2 задержан относительно clk1 на величину дельта задержки. Сигнал c отстаёт от сигнала b на один такт. Это правильно. Но вот сигнал d должен совпадать с сигналом c, а этого не происходит. Он срабатывает раньше.

Давайте вспомним что такое дельта задержка. Это принципиальное понятие, на нём основана работа событийных симуляторов, которыми мы пользуемся при моделировании логических схем.

Симулятор имеет понятие модельного времени. К этому модельному времени привязаны все события в системе. Давайте посмотрим на формирование тактовой частоты:

clk1 <= not clk1 after 5 ns;

Предположим что сейчас мы моделируем только clk1, других сигналов нет.
В начальный момент времени clk1 равен 0, это задано при объявлении сигнала. Симулятор видит требование инвертировать сигнал. Ключевое слово after даёт инструкцию провести назначение нового значение через 5 ns относительно текущего модельного времени. Симулятор это видит и делает отметку, что в момент времени 5 ns значение clk1 будет равно 1. Пока это модельное будущее, оно кстати ещё может измениться. Далее симулятор просматривает остальные сигналы. Симулятор увидит что для данного момента модельного времени всё выполнено и он может рассчитывать следующий момент. Возникает вопрос – а какой момент следующий? В принципе возможны разные варианты. Например Simulink имеет режим с фиксированным шагом. В этом случае произойдёт приращении модельного времени на какую то величину и вычисления продолжаться.

Системы моделирования цифровых схем делают по другому. Они переходят к ближайшему событию, которые они уже разместили в будущем на своей оси модельного времени. В данном случае это будет момент 5 нс. Симулятор увидит что clk1 изменился и просчитает для него новое значение, это будет 0 который также будет размещён с задержкой в 5 нс на временной оси. Т.е. это будет момент 10 нс. И так процесс будет продолжаться пока не закончиться заданное время моделирования.

Теперь давайте добавим сигналы a и b.

Сигнал a назначается в процессе. Для сигнала b используется условная конструкция when; Функция rising_edge(clk1) анализирует clk1 и возвращает true когда зафиксирован фронт, т.е. предыдущее значение равно 0 а текущее равно 1.

В момент модельного времени 5 ns произойдёт изменение clk1. Он станет равным 1 и для момента 10 ns будет создано событие установки его в 0. Но это потом. Пока мы ещё в моменте 5 ns и продолжаем вычисления. Симулятор переходит к строчке
b<=a when rising_edge(clk1);
Поскольку есть функция которая зависит от clk1 то симулятор вычислит значение функции, увидит что она вернула true и произведёт присваивание
b<=a;


Вот здесь начинается самое интересное — когда надо изменить значение b. Казалось бы надо изменить его сейчас, в этот момент времени. Но у нас параллельные процессы. Может быть, нам ещё понадобиться значение b для расчёта других сигналов. И вот здесь появляется понятие дельта задержки. Это минимальная величина, на которую смещается модельное время. Эта величина даже не имеет размерности времени. Это просто дельта. Но их может быть много. Причём настолько много что симулятор просто останавливается по ошибке или зависает.
Итак, новое значение b будет установлено для момента 5 ns + 1 (1 – это первая дельта задержка). Симулятор увидит, что рассчитывать для момента 5 ns уже нечего и перейдёт к следующему моменту, а это будет 5 ns + 1; В этот момент rising_edge(ckl1) не срабатывает. А значение b будет установлено в 1. После этого симулятор перейдёт к моменту 10 nc.

А вот теперь давайте добавим сигналы c, d и разберёмся почему они разные.
Лучше всего это рассмотреть момент модельного времени 25 ns с учётом дельта задержек

delta clk1 clk2 re(clk1) re(clk2) b c d
0 1 0 true false 0 0 0
1 1 1 false true 1 0 0
2 1 0 false false 1 0 1

Примечание: re — rising_edge

Из таблицы видно что в момент срабатывания функции rising_edge(clk2) значение b уже равно 1. И поэтому оно будет присвоено сигналу d.

Исходя из здравого смысла это не то поведение, которое мы ожидали от кода. Ведь мы просто переназначили сигнал clk1 на clk2 и ожидали, что сигналы c и d будут одинаковыми. Но следуя логике работы симулятора это не так. Это ПРИНЦИПИАЛЬНАЯ особенность. Эту особенность конечно надо знать разработчикам FPGA проектов и поэтому это хороший и нужный вопрос для собеседования.

Что же произойдет при синтезе? А вот синтезатор проследует здравому смыслу, он сделает сигналы clk2 и clk1 одним сигналом и поэтому c и d тоже будут одинаковыми. А при определённых настройках синтезатора они тоже будут объединены в один сигнал.

Это как раз случай, когда моделирование и работа в реальной аппаратуре приведут к разным результатам. Хочу обратить внимание, что причина разных результатов – это разная логика симулятора и синтезатора. Это ПРИНЦИПИАЛЬНАЯ разница. Это не имеет ничего общего с временными ограничениями. И если ваш проект в модели и в железе показывает разные результаты то проверьте, может быть там закралась конструкция подобная

clk2 <= clk1 

Теперь второй вопрос – исправьте этот код при помощи задержек.
Это вариант 2. Его можно раскомментировать и промоделировать.
Вот результат.



Результат правильный. Что же произошло? Давайте ещё раз составим таблицу для интервала 25 – 36 нс
time delta clk1 clk2 re(clk1) re(clk2) b c d
25 0 1 0 true false 0 0 0
25 1 1 1 false true 0 0 0
26 0 1 1 false false 1 0 0
35 0 1 0 true false 1 0 0
35 1 1 1 false true 1 0 0
36 0 1 1 false false 1 1 1

Видно, что значение b не меняется в моменты фронтов clk1, clk2. Задержка в 1 нс уводит момент изменения сигналов за зону срабатывания фронтов. Этот код становиться ближе к реальности. В реальной схеме существует какое то время на срабатывание триггера и на распространение сигнала. Это время должно быть меньше периода тактовой частоты, собственного говоря, именно этого добивается трассировщик и именно это проверяет временной анализ.

Причина возникновения ошибки это переназначение тактового сигнала обычным присваиванием при котором появляется дельта задержка. Однако язык VHDL имеет конструкцию alias. Это позволяет получить другое имя для сигнала. Вот объявление:

alias clk3 	: std_logic is clk1;

В тексте примера можно раскомментировать вариант 3 – он будет работать правильно.

Данный пример написан на языке VHDL. Может быть это проблемы только этого языка? Но вот те же варианты на языке Verilog.

Скрытый текст
`timescale 1 ns / 1 ps

module delta_delay_2 ();
	
reg  clk1 = 1'b0;	 
reg  clk2;
wire clk3;		

reg a = 1'b0;
reg b;
reg c;
reg d;

initial begin
forever clk1 = #5 ~clk1;
end 

initial begin
repeat(10)
begin

#20 a = 1'b1;
#60 a = 1'b0;
end
end

// Синтезируемая часть - переназначение тактового сигнала  ---
always @(clk1) clk2 <= clk1;	
	
// Вариант 1 - Синтезируемая часть без задержек  
	
always 	@(posedge clk2)	d <= b;			
	
always 	@(posedge clk1)
begin	
	c <= b;		
	b <= a;
end				  

// Вариант 2 - Синтезируемая часть с задержеками  
    
//always 	@(posedge clk1)	b = #1 a;	
//	
//always 	@(posedge clk1)	c = #1 b;		
//	
//always 	@(posedge clk2)	d = #1 b;			
	
// Вариант 3 - Синтезируемая часть без задержек 
// но с переназначением сигнала через assign  

//assign clk3 = clk1;		
//
//always 	@(posedge clk3)	d <= b;			
//	
//always 	@(posedge clk1)
//begin	
//	c <= b;		
//	b <= a;
//end	
endmodule



  • Вариант 1 – без задержек. Работает неправильно.
  • Вариант 2 – с задержками. Работает правильно.
  • Вариант 3 – переназначение через wire. Работает правильно.

В языке Verilog есть понятие reg и wire. В данном случае переназначение тактового сигнала через wire выглядит более естественным. Это является аналогом присвоения через alias в языке VHDL. Это несколько снимает напряжённость проблемы, но всё равно это надо знать.
Также в языке Verilog есть понятие блокирующего и неблокирующего присваивания. Назначение сигналов b и c можно написать и по другому:

always 	@(posedge clk1)
begin	
	c = b;		
	b = a;
end	

А можно так:

always 	@(posedge clk1)
begin	
	b = a;
	c = b;		
end

В зависимости от порядка строк результат будет разный.

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

Файлы примеров доступны здесь

Комментарии (34)


  1. Mox
    11.08.2018 02:30

    А какие сейчас симуляторы используют? Они платные?


    1. dsmv2014 Автор
      11.08.2018 03:02

      Я использую Active-HDL и Vivado. Active-HDL платный. Vivado имеет бесплатную версию.


    1. eugenk
      11.08.2018 03:27

      Icarus verilog + gtkwave. Правда с тем проектом который делаю сейчас, хочу перейти на verilator. Уж больно там тесты громадные. Всё это бесплатно и опенсорсно.


  1. eugenk
    11.08.2018 03:32
    -1

    Не понял, почему тему заминусовали? Конечно неблокирующее присваивание в операциях по клоку это говнокод, тут спору нет. Но знать такие вещи нормальный разработчик по-моему просто обязан. Проглядел пока весьма бегло (отвлёкся немного от работы), но статья по-моему вполне достойная.


  1. eugenk
    11.08.2018 03:47

    Прошу прощения, не могли бы Вы всё-таки пояснить, из каких соображений используете именно задержки? И кстати интересно, как на них реагируют синтезаторы? Тут пока проголосовали трое (и я в том числе). Результат — 100% тех кто задержки не использует.


    1. dsmv2014 Автор
      11.08.2018 09:55

      В соответствии со стандартом языка синтезатор задержки игнорирует.
      Когда то много лет назад я потратил очень много времени на поиск несоответствия между результатами моделирования и реальной работой. Причина — вот такое присваивание клока. Обнаружить это в чужом коде практически нереально. А в наших проектах чужого или старого кода очень много. Установка задержек проблему решает. Ну и кроме того на временной диаграмме сигналы с задержками выглядят гораздо понятней.


      1. eugenk
        11.08.2018 11:49

        Честно говоря присваивания клока вообще никогда не видал. Правда практически никогда не работал с унаследованным кодом на верилоге. Почти всегда писал своё и в своём стиле. Надо будет подробнее разобраться в Вашей статье и примерах. Во всяком случае большое спасибо за публикацию. Не каждый день удаётся взглянуть на знакомый предмет под несколько иным углом. И это всегда интересно.


  1. nerudo
    11.08.2018 10:54
    +4

    «Придумайте грабли которые попадут вам точно по тестикулам. Придумайте как их исправить.»
    Крайне надуманная проблема, в т.ч. как вопрос для собеседования. Ну т.е. если вы только что искали такую проблему в коде и живете этим — то вам очевидно. Человек же с улицы не факт что вспомнит об этом по наводящим вопросам. Когда же вы ткнете его носом в этот код, недоуменно посмотрит на вас: «Ну да, есть такой нюанс. Но зачем кто-то так делает?»
    PS Послезавтра кто-нибудь напишет «clk2 <= clk1 after 5 ns;» тоже не знаю зачем и всю эту эпопею можно начинать сначала.


    1. eugenk
      11.08.2018 11:51

      Придумайте грабли которые попадут вам точно по тестикулам. Придумайте как их исправить.

      Иногда такая постановка вопроса вполне себе имеет смысл. Хотя бы для разминки мозгов.


  1. AlexanderS
    11.08.2018 11:13
    +3

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


    1. eugenk
      11.08.2018 11:46
      +2

      Аналогично.


  1. old_bear
    11.08.2018 15:21
    +2

    За свою практику я использовал задержки только в верхнем уровне тестовой сборки из fpga и памяти (имитировал clock to output и input setup у fpga). Исключительно ради нормального функционирования rtl-кода fpga и модели памяти, которую поставляет производитель и которая напичкана контролем таймингов. Ну и в тестах. Хотя и в них предпочитаю тестовое воздействие с регистра подавать, который по тактовому сигналу изменяется.
    По поводу использования задержек внутри боевого кода активно согласен с предыдущими ораторами — это отличный способ выстрелить себе в ногу. Логично проверить на собеседовании, что кандидат в курсе, что это плохо-плохо (и что за переназначение тактового сигнала путём присваивания отрывают руки). А вот придумывать тестовые задачи на это плохо-плохо попахивает глупостью.


    1. dsmv2014 Автор
      12.08.2018 23:45

      По поводу использования задержек внутри боевого кода активно согласен с предыдущими ораторами — это отличный способ выстрелить себе в ногу.

      А собственно говоря почему? Наличие задержек делает временную диаграмму более наглядной. Гарантирует, что не будет проблемы при подобном переприсваивании клока.
      Синтезатор задержки всегда игнорирует.


      1. old_bear
        13.08.2018 02:48

        Переприсваивание клоков не может быть корректно синтезировано. Внесение такой конструкции в код — грубая ошибка. Маскирование грубой ошибки на этапе моделирования путём использования задержек никаким образом не поспособствует корректному синтезу, а только затруднит поиск этой самой грубой ошибки.
        Неужели это так трудно понять? Все эти «примеры» эквивалентны элегантному способу выстрелить себе в печень левой рукой стоя на голове. И обсуждения формы костыля, который позволяет при выстреле точно попасть в нужную долю этой печени.
        В реальной жизни, где нужно написать rtl-код, который исполняется в железе одинаково с симулятором, всё это не нужно.


        1. dsmv2014 Автор
          13.08.2018 11:02

          Переприсваивание клоков не может быть корректно синтезировано.

          Это не так. Синтезатор прекрасно понимает эту конструкцию и делает эту цепь общей.


          1. old_bear
            13.08.2018 12:48
            +1

            Вы сейчас ведёте речь именно про переприсвоение, а не про assign (в терминологии verilog-а)? Если да, то вы глубоко заблуждаетесь. В общем то это ваше личное дело, но мне банально жаль тех, кого вы научите тому, что «синтезатор понимает эту конструкцию».
            P.S. Не думаю, что имеет смысл продолжать это бесплодное обсуждение с вами.


  1. Inanity
    12.08.2018 11:34

    Если честно, немного удивлён результатами голосования. Лично я всегда использую задержки при неблокирующем присваивании.

    Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно, хотя в реальности это триггер и новое значение на его выходе формально образуется через время tCQ (clock to q delay). Без этой задержки события в симуляторе будут происходить на такт раньше, что может привести к неправильному пониманию работы схемы при симуляции. В то время как с задержкой мы видим то, что происходит в реальности. Собственно исходя из этих соображений можно привести массу примеров того, когда результаты симуляции отличаются от железа.

    Так уж получилось, что на широких просторах интернета мне на глаза попадались исходники фирменных ядер Xilinx, Altera, Mictrotronix, SLS и т.д. Очень часто вижу задержки при неблокирующем присваивании. У Xilinx вообще все ядра симулируются с задержкой на триггерах в 100ps.


    1. nerudo
      12.08.2018 12:12

      1. Никак не могут «события в симуляторе происходить на такт раньше» если тест написан правильно. Если неправильно — задержки не спасут.
      2. Мы не видим что происходит в реальности, т.к. в реальности задержки куда как многообразнее и с ними справляется трассировщик на пару с временным анализатором
      3. Задержки ухудшают читаемость кода, если вы пишете не триггер, а что-то более сложное.
      4. У меня сейчас нет доступа к рабочей машине, чтобы посмотреть исходники, но не замечал особого обилия задержек кроме как в коде предназначенном для timing simulation. Тот же github можно взять для примера.


    1. old_bear
      12.08.2018 17:18
      +1

      Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно

      Погуглите по словам «delta delay».
      Без этой задержки события в симуляторе будут происходить на такт раньше, что может привести к неправильному пониманию работы схемы при симуляции.

      Если вам не сложно, приведите пример этого чудесного явления. Я ещё не встречался с таким явлением в коде, который написан с соблюдением стандарта.
      У Xilinx вообще все ядра симулируются с задержкой на триггерах в 100ps.

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

      В синхронных схемах (т.е. в 99% случаев для проектов для FPGA), результаты железа будут отличаться от реальности только при нарушении таимингов и/или при некорректно заданных таймингах для специфичных случаев межклокового взаимодействия и сигналов с многотактовым таймингом.


      1. dsmv2014 Автор
        12.08.2018 23:41

        Если вам не сложно, приведите пример этого чудесного явления. Я ещё не встречался с таким явлением в коде, который написан с соблюдением стандарта.

        Пример в этой статье. Вот фрагмент кода:
        always 	@(posedge clk1)
        begin	
        	c = b;		
        	b = a;
        end
        

        Если в нём поменять местами строки с присваиванием, то результат будет разный. Это опять же связано с отсутствием дельта задержки.
        В синхронных схемах (т.е. в 99% случаев для проектов для FPGA), результаты железа будут отличаться от реальности только при нарушении таимингов и/или при некорректно заданных таймингах для специфичных случаев межклокового взаимодействия и сигналов с многотактовым таймингом.

        Не так. Один из примеров — в данной статье.


        1. old_bear
          13.08.2018 02:53

          Если в нём поменять местами строки с присваиванием, то результат будет разный.

          Разумеется он будет разный, в полном соответствии со стандартом языка.
          Не так.

          Высосанный из пальца пример, как не надо делать ни в коем случае, как то опровергает моё утверждение, что в 99% случаях rtl-кода для FPGA так делать не надо?


    1. eugenk
      12.08.2018 18:03

      Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно

      Да нет, симулятор как раз их вносит. Почитайте у Полякова (названия сейчас увы не помню, книжка про верилог и vhdl). Там это хорошо объясняется на примере RS-триггера из двух элементов И-НЕ.

      Лично я всегда использую задержки при неблокирующем присваивании

      Если Вас не затруднит, не могли бы Вы выложить куда-нибудь образцы кода? Тема мне кажется вполне достойная.


    1. Inanity
      12.08.2018 22:54
      +1

      Чтобы не повторяться с ответами, nerudo, old_bear, eugenk, предлагаю вам прочитать эту небольшую статью, она даёт исчерпывающий ответ на вопрос о целесообразности использования задержек при неблокирующем присваивании.


      1. old_bear
        12.08.2018 23:17

        В этой статье речь про то, как её автор пытается смешать ежа RTL и ужа gate level-а из-за своих нетрадиционных взглядов на дизайн ASIC-ов. Какое отношение она имеет к обсуждаемому вопросу написания корректно синтезируемого кода для FPGA?


        1. Inanity
          13.08.2018 10:17
          +1

          Хорошо, давайте уберём в сторону gate level симуляцию, хотя это вполне себе аргумент, мы же не коня в вакууме описываем, а работаем в конечном итоге с реальной ПЛИС и её примитивами. Я не защищаю автора статьи с проблемами переприсвоения тактового сигнала. Но задержка при неблокирующем присваивании просто напросто упрощает понимание диаграмм. Факт в том, что по активному фронту на триггере образуются новые данные, но в реальности это не так, новые данные появляются позже. Это задержка может быть больше или меньше, но она строго больше нуля и меньше периода (желательно). Хорошо, когда эта задержка в реальности меньше периода с учётом всех накладных расходов (logic delay + routing delay), это означает, что мы уложились по таймингам.


          1. nerudo
            13.08.2018 10:59

            Вся модель симуляции с дельта-задержками была придумана для того, чтобы избежать этих велосипедов с добавлением задержек в каждом операторе.
            Вы улучшаете читаемость диаграмм (якобы; лично у меня нет проблем с чтением их без задержек), ухудшаете читаемость кода, особенно для VHDL, который и так несколько перегружен. Потом какой-нибудь джуниор начнет писать
            #1 a<=b вместо a<= #1 b; и станет совсем интересно.
            Ну и могу еще раз отметить — задержки в аппаратуре куда как многообразнее. Понятие клокового дерева в последних микросхемах FPGA крайне размыто. И два соседних в коде триггера могут находиться в разных клоковых регионах, тактируясь разными физическими сигналами. Какие там окажутся задержки и фактический hold-time для триггера, который вы обставляете этими задержками — одному богу известно.


          1. old_bear
            13.08.2018 12:36

            Вы как раз описали механизм delta delay, который уже есть в языке и без которого в принципе симуляция не будет функционировать.
            Уложились вы по таймингам или нет, вам сообщает софт производителя fpga. К симуляции работы синхронной логики это не имеет никакого отношения. Всё что от вас требуется на этапе RTL-кода — ставить не больше асинхронной логики между двумя регистрами, чем может уложиться в нужный тайминг. Это «не больше» достигается проверочной сборкой проекта и опытом. Со временем первая составляющая уменьшается в пользу второй.
            Вы же просто смешиваете разные уровни и задачи FPGA-дизайна, якобы для наглядности и упрощения процесса. А на практике такая путаница ожидаемо приводит к ошибкам, т.к. вместо того, чтобы всегда следовать простому правилу «регистр -> регистр асинхронная логика -> регистр» и забыть про всё лишнее кроме логики, вам нужно постоянно следить, чтобы вас не накрыло вашим велосипедом с пачкой костылей на багажнике.


            1. old_bear
              13.08.2018 13:02

              *«регистр -> асинхронная логика -> регистр"


              1. Inanity
                13.08.2018 14:12

                На самом деле вы всё верно говорите, и я с вами в общем плане согласен.

                Вы же просто смешиваете разные уровни и задачи FPGA-дизайна, якобы для наглядности и упрощения процесса.
                Почему я смешиваю уровни? Синтезатор игнорирует эти задержки. Возможно для вас будет аргументом то, что я хотел бы видеть диаграммы при симуляции так, как если бы к реальному железу был подключен реальный логический анализатор. Логический анализатор показал бы, что состояние на выходе триггера изменяется с задержкой, а не строго по активному фронту. И всё! Если мы не имеем ввиду gate level, то ни для какой другой задачи эта задержка не нужна. Ещё, наверное, стоит сказать, что правильнее эту задержку определять как «parameter», предоставляя пользователю право обнулять это значение.


                1. old_bear
                  13.08.2018 14:24

                  Когда я последний раз пользовался логическим анализатором (не осциллографом) он, что характерно, показывал изменения сигналов именно по фронту того сигнала, который был выбран в качестве синхро-входа. :)
                  Как именно вам легче воспринимать диаграммы — это индивидуальный вопрос и я не претендую вам в нём указывать. Но беда в том, что использование задержек для придания некой красивости, может вылиться в маскирование ошибок в коде.


  1. dsmv2014 Автор
    13.08.2018 11:14

    Хочу ещё раз отметить. Я занимаюсь реальной работой по проектированию ПЛИС в течении длительного времени. В статье приведён простой пример который демонстрирует РЕАЛЬНУЮ проблему. Я охотно верю, что если один разработчик делает один проект то проект может быть идеальным. У нас не так. У нас много проектов и каждом до 90% чужого кода. Отследить что бы не было переприсваивания клока практически невозможно. Однажды я очень сильно об это обжёгся, я очень много времени потратил на поиск подобной ошибки.
    Так что утверждения что это надуманная проблема я считаю ошибочными, это РЕАЛЬНАЯ проблема.
    Решения у этой конкретной проблемы может быть только два:

    1. Вставляем задержки
    2. Делаем присваивание клока через alias или assign

    Я выбрал первый способ. Второй я считаю ненедёжным. Ну а лучше всего использовать оба.


    1. old_bear
      13.08.2018 12:42

      У нас много проектов и каждом до 90% чужого кода. Отследить что бы не было переприсваивания клока практически невозможно.

      Вы простите, но это высказывание эквивалентно варианту «у нас много С-разработчиков пишут общий код и отследить, чтобы в коде не было undefined behaviour невозможно».
      Если тим-лид в софтовом проекте скажет такое руководству, то либо его нужно увольнять, либо срочно отправлять на курсы повышения квалификации в области управления проектами.


      1. dsmv2014 Автор
        13.08.2018 13:02

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

        P.S. Если тимлид утверждает что в коде совершенно нет «undefined behaviour» то это должно очень сильно насторожить руководство.


        1. old_bear
          13.08.2018 13:09

          Это уже уход в область управления проектами и организацией в целом.

          Совершенно с вами согласен — описываемая вами ситуация является прямым следствием грубых ошибок в управлении проектом. В других условиях такая ситуация возникнуть не может. Попытка замазать эти ошибки всяческими костылями — это ещё более грубая ошибка.
          Вне зависимости от тимлида есть только два решения этой проблемы — задержки и правильное присваивание. Вопрос — что выбираем?

          Для меня второй вариант является очевидным выбором. И я немного в шоке, что для кого-то, имеющего отношение к FPGA-дизайну, это не так.
          Если тимлид утверждает что в коде совершенно нет «undefined behaviour» то это должно очень сильно насторожить руководство.

          Не надо коверкать мои слова. Если тим-лид утверждает, что undefined behaviour — это нормальная составляющая в коде, вот тогда руководство должно не только сильно насторожиться но и сильно напрячься.