Comments 54
Когда то много лет назад я потратил очень много времени на поиск несоответствия между результатами моделирования и реальной работой. Причина — вот такое присваивание клока. Обнаружить это в чужом коде практически нереально. А в наших проектах чужого или старого кода очень много. Установка задержек проблему решает. Ну и кроме того на временной диаграмме сигналы с задержками выглядят гораздо понятней.
Крайне надуманная проблема, в т.ч. как вопрос для собеседования. Ну т.е. если вы только что искали такую проблему в коде и живете этим — то вам очевидно. Человек же с улицы не факт что вспомнит об этом по наводящим вопросам. Когда же вы ткнете его носом в этот код, недоуменно посмотрит на вас: «Ну да, есть такой нюанс. Но зачем кто-то так делает?»
PS Послезавтра кто-нибудь напишет «clk2 <= clk1 after 5 ns;» тоже не знаю зачем и всю эту эпопею можно начинать сначала.
По поводу использования задержек внутри боевого кода активно согласен с предыдущими ораторами — это отличный способ выстрелить себе в ногу. Логично проверить на собеседовании, что кандидат в курсе, что это плохо-плохо (и что за переназначение тактового сигнала путём присваивания отрывают руки). А вот придумывать тестовые задачи на это плохо-плохо попахивает глупостью.
По поводу использования задержек внутри боевого кода активно согласен с предыдущими ораторами — это отличный способ выстрелить себе в ногу.
А собственно говоря почему? Наличие задержек делает временную диаграмму более наглядной. Гарантирует, что не будет проблемы при подобном переприсваивании клока.
Синтезатор задержки всегда игнорирует.
Неужели это так трудно понять? Все эти «примеры» эквивалентны элегантному способу выстрелить себе в печень левой рукой стоя на голове. И обсуждения формы костыля, который позволяет при выстреле точно попасть в нужную долю этой печени.
В реальной жизни, где нужно написать rtl-код, который исполняется в железе одинаково с симулятором, всё это не нужно.
Переприсваивание клоков не может быть корректно синтезировано.
Это не так. Синтезатор прекрасно понимает эту конструкцию и делает эту цепь общей.
P.S. Не думаю, что имеет смысл продолжать это бесплодное обсуждение с вами.
Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно, хотя в реальности это триггер и новое значение на его выходе формально образуется через время tCQ (clock to q delay). Без этой задержки события в симуляторе будут происходить на такт раньше, что может привести к неправильному пониманию работы схемы при симуляции. В то время как с задержкой мы видим то, что происходит в реальности. Собственно исходя из этих соображений можно привести массу примеров того, когда результаты симуляции отличаются от железа.
Так уж получилось, что на широких просторах интернета мне на глаза попадались исходники фирменных ядер Xilinx, Altera, Mictrotronix, SLS и т.д. Очень часто вижу задержки при неблокирующем присваивании. У Xilinx вообще все ядра симулируются с задержкой на триггерах в 100ps.
- Никак не могут «события в симуляторе происходить на такт раньше» если тест написан правильно. Если неправильно — задержки не спасут.
- Мы не видим что происходит в реальности, т.к. в реальности задержки куда как многообразнее и с ними справляется трассировщик на пару с временным анализатором
- Задержки ухудшают читаемость кода, если вы пишете не триггер, а что-то более сложное.
- У меня сейчас нет доступа к рабочей машине, чтобы посмотреть исходники, но не замечал особого обилия задержек кроме как в коде предназначенном для timing simulation. Тот же github можно взять для примера.
Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно
Погуглите по словам «delta delay».
Без этой задержки события в симуляторе будут происходить на такт раньше, что может привести к неправильному пониманию работы схемы при симуляции.
Если вам не сложно, приведите пример этого чудесного явления. Я ещё не встречался с таким явлением в коде, который написан с соблюдением стандарта.
У Xilinx вообще все ядра симулируются с задержкой на триггерах в 100ps.
Это просто универсальные модели, в которые потом из sdf-файла вливаются реальные задержки при пост-симуляции.
Собственно исходя из этих соображений можно привести массу примеров того, когда результаты симуляции отличаются от железа.
В синхронных схемах (т.е. в 99% случаев для проектов для FPGA), результаты железа будут отличаться от реальности только при нарушении таимингов и/или при некорректно заданных таймингах для специфичных случаев межклокового взаимодействия и сигналов с многотактовым таймингом.
Если вам не сложно, приведите пример этого чудесного явления. Я ещё не встречался с таким явлением в коде, который написан с соблюдением стандарта.
Пример в этой статье. Вот фрагмент кода:
always @(posedge clk1)
begin
c = b;
b = a;
end
Если в нём поменять местами строки с присваиванием, то результат будет разный. Это опять же связано с отсутствием дельта задержки.
В синхронных схемах (т.е. в 99% случаев для проектов для FPGA), результаты железа будут отличаться от реальности только при нарушении таимингов и/или при некорректно заданных таймингах для специфичных случаев межклокового взаимодействия и сигналов с многотактовым таймингом.
Не так. Один из примеров — в данной статье.
Если в нём поменять местами строки с присваиванием, то результат будет разный.
Разумеется он будет разный, в полном соответствии со стандартом языка.
Не так.
Высосанный из пальца пример, как не надо делать ни в коем случае, как то опровергает моё утверждение, что в 99% случаях rtl-кода для FPGA так делать не надо?
Это опять же связано с отсутствием дельта задержки.
Неверно. Это связано с тем, что неблокирующие присваивания как раз сделаны для того, чтобы можно было сразу забрать результат и использовать его в следующем присваивании, но нужно следить за порядком выполнения операций. Вместо того чтобы использовать правильный инструмент (блокирующее присваивание), вы пытаетесь его симулировать с помощью задержек.
Неблокирующее присваивание без задержки выполняется в симуляторе мгновенно
Да нет, симулятор как раз их вносит. Почитайте у Полякова (названия сейчас увы не помню, книжка про верилог и vhdl). Там это хорошо объясняется на примере RS-триггера из двух элементов И-НЕ.
Лично я всегда использую задержки при неблокирующем присваивании
Если Вас не затруднит, не могли бы Вы выложить куда-нибудь образцы кода? Тема мне кажется вполне достойная.
Вы улучшаете читаемость диаграмм (якобы; лично у меня нет проблем с чтением их без задержек), ухудшаете читаемость кода, особенно для VHDL, который и так несколько перегружен. Потом какой-нибудь джуниор начнет писать
#1 a<=b вместо a<= #1 b; и станет совсем интересно.
Ну и могу еще раз отметить — задержки в аппаратуре куда как многообразнее. Понятие клокового дерева в последних микросхемах FPGA крайне размыто. И два соседних в коде триггера могут находиться в разных клоковых регионах, тактируясь разными физическими сигналами. Какие там окажутся задержки и фактический hold-time для триггера, который вы обставляете этими задержками — одному богу известно.
Уложились вы по таймингам или нет, вам сообщает софт производителя fpga. К симуляции работы синхронной логики это не имеет никакого отношения. Всё что от вас требуется на этапе RTL-кода — ставить не больше асинхронной логики между двумя регистрами, чем может уложиться в нужный тайминг. Это «не больше» достигается проверочной сборкой проекта и опытом. Со временем первая составляющая уменьшается в пользу второй.
Вы же просто смешиваете разные уровни и задачи FPGA-дизайна, якобы для наглядности и упрощения процесса. А на практике такая путаница ожидаемо приводит к ошибкам, т.к. вместо того, чтобы всегда следовать простому правилу «регистр -> регистр асинхронная логика -> регистр» и забыть про всё лишнее кроме логики, вам нужно постоянно следить, чтобы вас не накрыло вашим велосипедом с пачкой костылей на багажнике.
Вы же просто смешиваете разные уровни и задачи FPGA-дизайна, якобы для наглядности и упрощения процесса.Почему я смешиваю уровни? Синтезатор игнорирует эти задержки. Возможно для вас будет аргументом то, что я хотел бы видеть диаграммы при симуляции так, как если бы к реальному железу был подключен реальный логический анализатор. Логический анализатор показал бы, что состояние на выходе триггера изменяется с задержкой, а не строго по активному фронту. И всё! Если мы не имеем ввиду gate level, то ни для какой другой задачи эта задержка не нужна. Ещё, наверное, стоит сказать, что правильнее эту задержку определять как «parameter», предоставляя пользователю право обнулять это значение.
Как именно вам легче воспринимать диаграммы — это индивидуальный вопрос и я не претендую вам в нём указывать. Но беда в том, что использование задержек для придания некой красивости, может вылиться в маскирование ошибок в коде.
Так что утверждения что это надуманная проблема я считаю ошибочными, это РЕАЛЬНАЯ проблема.
Решения у этой конкретной проблемы может быть только два:
- Вставляем задержки
- Делаем присваивание клока через alias или assign
Я выбрал первый способ. Второй я считаю ненедёжным. Ну а лучше всего использовать оба.
У нас много проектов и каждом до 90% чужого кода. Отследить что бы не было переприсваивания клока практически невозможно.
Вы простите, но это высказывание эквивалентно варианту «у нас много С-разработчиков пишут общий код и отследить, чтобы в коде не было undefined behaviour невозможно».
Если тим-лид в софтовом проекте скажет такое руководству, то либо его нужно увольнять, либо срочно отправлять на курсы повышения квалификации в области управления проектами.
Вне зависимости от тимлида есть только два решения этой проблемы — задержки и правильное присваивание. Вопрос — что выбираем?
P.S. Если тимлид утверждает что в коде совершенно нет «undefined behaviour» то это должно очень сильно насторожить руководство.
Это уже уход в область управления проектами и организацией в целом.
Совершенно с вами согласен — описываемая вами ситуация является прямым следствием грубых ошибок в управлении проектом. В других условиях такая ситуация возникнуть не может. Попытка замазать эти ошибки всяческими костылями — это ещё более грубая ошибка.
Вне зависимости от тимлида есть только два решения этой проблемы — задержки и правильное присваивание. Вопрос — что выбираем?
Для меня второй вариант является очевидным выбором. И я немного в шоке, что для кого-то, имеющего отношение к FPGA-дизайну, это не так.
Если тимлид утверждает что в коде совершенно нет «undefined behaviour» то это должно очень сильно насторожить руководство.
Не надо коверкать мои слова. Если тим-лид утверждает, что undefined behaviour — это нормальная составляющая в коде, вот тогда руководство должно не только сильно насторожиться но и сильно напрячься.
Эта проблема совершенно не имеет отношения к управлению проектом. Это проблема знания матчасти. А именно знания случая когда моделирование и синтез даст разные результаты.
Переприсваивание клока не является ошибкой, тем более грубой. Синтезатор это корректно обрабатывает, код будет работать. Симулятор также всё обработает, результат будет, но немного другой. Но как говориться — что просили, то и получили.
Я кстати не первый раз публично поднимаю тему с задержками и всегда получаю волну критики. Основной аргумент — лень добавить несколько слов. А то что это позволяет решить реальные проблемы и в итоге сэкономить кучу времени как то упускается из виду. Здесь действует принцип — раз я с этим не сталкивался значит этого нет. Ну а я сталкивался.
Напишите мне пожалуйста, эквивалентом какого аппаратного элемента FPGA является переприсвоение тактового сигнала в RTL-коде. На всякий случай отмечу, что мы не говорим сейчас о gate level-е и ASIC-ах, а именно про RTL-код и FPGA.
А если вы не затруднитесь ссылкой на coding rules от любого из производителей FPGA, где подтверждается, что переприсовение тактового сигнала — это допустимая
конструкция для их синтезатора и рассказывается во что она будет гарантировано синтезирована, то будет совсем чудесно.
Заранее спасибо.
Для определённости — я говорю о Xilinx и синоезаторах из пакетов ISE и Vivado.
Конструкция VHDL: clk1 <= clk2; будет реализована одной цепью. Цепь может получить имя одного из сигналов, но скорее всего будет называться по имени источника.
Я сейчас не могу привести ссылку на кодестайл, не уверен что это вообще есть в описаниях. Но реально это работает.
А повторять как мантру «это проходит в синтезаторе Х» ничем не отличается от варианта «этот undefined behaviour приводит к нужному нам поведению в компиляторе Х версии Y и поэтому мы его будем использовать с коде».
За сим я откланиваюсь. Не вижу смысла продолжать дискуссию, по крайней мере до тех пор, пока вы не приведёте более весомые аргументы, чем «у меня это проходит» и «я много лет так делаю».
Вы задали вопрос — эквивалентом какого аппаратного элемента FPGA является переприсвоение тактового сигнала.
Я отвечаю, причём уже много раз, это будет одна и та же цепь.
Разве я не понятно ответил?
Или вы не понимаете проблему?
Обсуждать это дальше я смысла не вижу. Всего вам наилучшего.
А ничего что я про VHDL пишу?
Вы конечно можете не отвечать, я не навязываюсь. Но хочу ещё раз отметить, синтезатор задержки ИГНОРИРУЕТ. Причём все, включая дельта задержку.
Поэтому любое присваивание, что через assign/alias, что простое присваивание сигнала в vhdl приведёт к одному результату. Это будет одна цепь, без всяких мифических элементов задержки на величину delta. Это кстати тоже необходимое знание для разработчика FPGA.
На мой взгляд это просто и очевидно. Неужели я так плохо объясняю?
То, что вы в итоге добавляете задержки в присвоения, по сути имеет ту же заложенную проблему — подобные присвоения не будут эквивалентными результатам синтеза.
из-за особенностей моделирования у присвоения «clk2 <= clk1» нет прямого эквивалента в синтезе, т.к. в моделировании такая конструкция создаёт задержку (пусть и дельта), а в синтезе — нет.
Совершенно верно. «clk2 <= clk1» преобразуется в цепь без задержек в соответствии со стандартом языка. Ссылки на стандарт ниже.
Но вот статья именно об этом — разная интерпретация языковых конструкций синтезатором и симулятором. Заметьте — корректных конструкций.
То, что вы в итоге добавляете задержки в присвоения, по сути имеет ту же заложенную проблему — подобные присвоения не будут эквивалентными результатам синтеза.
Не так. Добавление задержек соответствует результатам синтеза. Конечно при условии что задержка меньше периода тактового сигнала.
Ну и опять же если у вас присвоения задержек есть на синхросигналах и на сигналах сброса, то добавление задержек не будет соответствовать результатам синтеза, т.к. величины задержек будут разными. Синтез строит clock tree и reset tree, добавляя буферы так, чтобы итоговая задержка по цепям сброса и синхронизации соответствовала распространению данных. В системах с большим количеством (да в общем-то просто больше одного) асинхронных друг другу частотных доменов это может быть сложно отмоделировать правильно, а внесённая руками задержка может изменить работу схемы.
Естественно, за исключением случаев, когда они необходимы (например, в моделях внешней памяти и т.п.).
Согласен с приведёнными выше аргументами про «выстрел в ногу» и маскирование ошибки.
Поэтому повторяться не буду.
Дополню только следующими пунктами:
- Для синтезируемого подмножества Verilog есть стандарт IEEE Std. 1364.1 — 2002 «Standard for Verilog Register Transfer Level Synthesis».
В этом стандарте, в Annex B, пункт B.12 Timing delays сказано:
Synthesis tools ignore time delay in a model. Adding time delays to a Verilog pre-synthesis simulation can cause a mismatch between pre-synthesis and post-synthesis simulations and in general should be avoided.
- Есть ещё статья от всем известных Sunburst Design:
www.sunburst-design.com/papers/CummingsSNUG2002Boston_NBAwithDelays.pdf
Там авторы не настолько категорично настроены против использования задержек. В качестве доводов «за» они называют следующее:
- Более удобную отладку с временными диаграммами
- Решение некоторых проблем при смешанной RTL/Gate-level симуляции
Доводов «против» также хватает.
Мое мнение по первому пункту «за» — неудобство отладки по временным диаграммам без использования дополнительной задержки полностью уходит примерно через неделю практики. И для инженеров с серьёзным опытом это точно не должно быть причиной для использования задержек.
По второму пункту — да, это так. Но в статье и комментариях разбирается не этот случай.
Общий вывод по статье.
Самое плохое, на мой взгляд, то, когда начинающих FPGA-разработчиков вместо понимания сути вещей (как именно работают симулятор и синтезатор, какие конструкции есть в языках и когда и как их нужно правильно применять) учат «хитростям», которые позволяют избегать проблем, если повезёт. И которые дают ложное ощущение безопасности.
Именно такое впечатление у меня сложилось после прочтения материала.
Поэтому поставил статье минус, хоть обычно я двумя руками «за» все статьи по тематике FPGA.
Я уже говорил свою позицию лично Дмитрию (dsmv2014).
Он взрослый адекватный инженер, поэтому нормально воспринял мою критику.
Надеюсь, что он также не будет на меня в обиде за то, что я написал своё мнение в комментарии.
Чисто технически задержки тянут за собой как минимум две проблемы:
1. Задержку можно забыть написать в каком-то присвоении, и это притащит за собой обратно все те же самые проблемы, от которых мы хотели избавиться задержками.
2. Если добавлять задержки *везде*, то при достаточном количестве вложенной логики можно умудриться перескочить через такт, что тоже ни к чему хорошему не приведёт.
С синхросигналами в HDL вообще нужно работать осторожно и внимательно, конкретно вот пример
clk2 <= clk1;ведь ничего не даёт в схеме, а только создаёт проблему. Я бы еще понял
clk2 <= not clk1;или какую-то другую логику, но в таком случае это уже модификация синхросигнала, и работа с такими вещами требуется соответствующая.
Добавлю, что занимаюсь в основном разработкой под ASIC, но часто с верификацией на FPGA, так что знаком с обоими «мирами».
Я надеюсь, что Вы не будете в обиде за мнение о статье.
Мой комментарий содержит две части, в каждой из них делается акцент на разных проблемах.
Часть первая (содержание статьи)
Вы подняли интересную и важную тему (сравнение конструкций языка в симуляции и в железе, и что делать, чтобы они совпадали). Мне кажется, можно было бы улучшить статью, если изначально предложить код, и попросить читателя провести “симуляцию” кода в голове и на железе, а затем уже обсудить почему получаются расхождения. А затем предложить еще примеры, когда происходят различия. Но это всё мелочи.
Главная проблема в том, что Вы не даете доказательств. Например, ссылки на стандарт, который обсуждается в статье. На мой взгляд очень важно делать отсылки на конкретные части конкретного документа, когда мы говорим про нюансы языка/окружения. Читатель должен иметь возможность перечитать это на английском языке и “перепроверить” перевод/понимание автора статьи. Мы бездумного сравниваем примеры VHDL и Verilog, но может там есть разница в симуляции? Было бы здорово указать марку и версию симулятора, в котором производились “измерения”. В качестве доказательства, что в железе будет по-другому можно было бы приложить скриншот из SignalTap/ChipScope.
Дельта-задержка это не единственный “нюанс” в симуляции. Там есть “слоты”, “регионы”, “события” и так далее. Я ожидаю от статьи, где разбираются задержки и симуляция, описание как действительно работает симулятор, с всеми фазами, ограничениями/допущениями, либо ссылки на другую статью на русском языке где это хорошо описано. Такую статью можно было бы использовать как референсную, когда кто-то что-то забыл про симуляцию и решил освежить память и прочитать это на русском языке с хорошими примерами.
К сожалению, эта статья скрывает все подробности работы симулятора, а начинающего разработчика, который хочет разобраться как работает симулятор, она только запутает, т.к. нет ссылок на конкретный стандарт и литературу, из которой и делаются выводы, например:
Вот здесь начинается самое интересное — когда надо изменить значение b. Казалось бы надо изменить его сейчас, в этот момент времени. Но у нас параллельные процессы. Может быть, нам ещё понадобиться значение b для расчёта других сигналов. И вот здесь появляется понятие дельта задержки.
Сейчас статья напоминает неформальное обсуждение за пивом.
А: Слушай, ну помнишь, есть дельта-цикл, из-за этого мы ставим задержки.
B: А, да, точно я что-то про это читал. Ну, да, вроде логично, спасибо, тоже буду ставить.
Она не помогает разобраться и точно понять что происходит и почему, и как это перепроверить/доказать.
Так же я считаю плохой практикой различные примеры вносить под комментарий и предалагать читателю что-то раскоментировать чтобы получить другой эффект. На мой взгляд это должно быть или под дефайном/параметром, а еще лучше каждый пример расположить в отдельном файле/модуле.
Часть вторая (выводы)
Я исхожу из того, что мы обсуждаем разработку под FPGA с соблюдением принципов синхронного дизайна, а так же лучших практик от вендоров с которыми работаем. Если нет, то эта часть комментария нерелевантна и ее можно не читать.
Как известно, самая важная часть в статье – это её заключение или вывод.
В качестве вывода Вы предлагаете везде в RTL коде писать задержки. Я с этим не согласен. Аргументы против задержек в синтезируемом коллеги собрали выше, я не буду повторяться и попытаюсь сделать акцент на другом.
Пример, который Вы привели слишком вымышленный. Сложно поверить, что в модуль заходит клок, из него делается второй клок и часть триггеров питается от одного “клока”, а часть от другого.
Потому что это нарушение принцпов синхронного дизайна: формирование клоков и ресетов должно быть в отдельных модулях, отделённых от логики.
Допустим, наоборот, два клока приходят на входе модуля (которые по факту являются одинаковыми и переназначаются в другом модуле), то возникает вопрос: почему значение d берется с триггера b, хотя они находятся в разных клоковых доменах и не происходит корректной пересинхронизации между клоковыми доменами. А если это всё-таки один клок, то зачем заводить на модуль два клока?
Пример хороший для того, чтобы показать какие есть нюансы и как работает симулятор, но это не тот пример ради которого я бы мог сделать вывод, что да, теперь нужно писать везде задержки.
Я допускаю, что есть кейс в реальном проекте где нужны задержки. Это может быть какой-то низкоскоростной протокол, где есть несколько клоков и данные как-то хитро передаются и защелкиваются, и там нужно клоки мультиплексировать.
Если был бы разобран такой пример (со всеми констрейнами) и было бы “доказано”, что без использования задержек в конкретном случае нельзя (либо нужно написать в десять раз больше кода), то я бы согласился, что это полностью оправдано в данном конкретном случае, и использование задержек допустимо в модулях, где происходит некоторая “магия”, а когда уже все “синхронизировано” и идет поток данных, уже можно без них обойтись. Но из этого нельзя сделать вывод, что когда у меня один клок в системе и полностью синхронная схема я должен везде расставлять задержки.
В комментариях Вы написали, что написание задержек это отчасти некоторое легаси, и Вы напоролись на некоторые проблемы из-за старого кода и теперь приходится их везде писать. Наследие неидеального кода – это проблема, которая встречалась многим разработчикам. В фразе “я делаю так, потому что у нас такое наследие, и нет бюджета всё исправить” я не вижу ничего плохого, такое, к сожалению, бывает, и идеальная картинка в книгах (советах из интернета) рушится с тем, что происходит в жизни.
На самом деле вопрос же заключается в том, что если удалить все исходники, уволить всех коллег, оставить только Вас и набрать в FPGA команду студентов, которые ничего не знают, то Вы тоже будете им предлагать писать задержки во всех модулях и во всех проектах или нет? При условии, что Вы их руководитель/учитель/ментор и Вам дают большой бюджет и никто со сроками не торопит.
TLDR: содержание статьи слабое, т.к. подробно не разобрано как работает симулятор. Вывод статьи дается на суперсложном странном примере, который может встретиться при плохой организации разработки или легаси. Его нельзя распространять на всю индустрию.
Ну и некоторые комментарии. Я не претендую на полное описание процесса симуляции. Идея статьи появилась в результате обсуждения вопросов для собеседования. Я придумал два вопроса и дал на них ответы. Но похоже по этим вопросам мало кто сможет пройти собеседование :-)
Синтезатор задержки игнорирует. Это есть в стандарте
1076.6 IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis
Вот выдержка из стандарта:
![](https://habrastorage.org/webt/5z/o3/pa/5zo3payhm67vurs8jvnzu3e65d8.png)
Это есть в ug901-vivado-synthesis.pdf:
![](https://habrastorage.org/webt/4g/zw/cp/4gzwcpwceu4_sbqs1zhmy12pvli.png)
Так что формально вставлять задержки в боевой код можно. В соответствии со стандартом синтезатор их проигнорирует.
Интересный вопрос:
На самом деле вопрос же заключается в том, что если удалить все исходники, уволить всех коллег, оставить только Вас и набрать в FPGA команду студентов, которые ничего не знают, то Вы тоже будете им предлагать писать задержки во всех модулях и во всех проектах или нет? При условии, что Вы их руководитель/учитель/ментор и Вам дают большой бюджет и никто со сроками не торопит.
Да, я будет вставлять задержки и буду обучать учеников. Собственно это я делаю. Правда не все поддаются.
За 18 лет я не смог привыкнуть к виду диаграммы в которой фронт клока и изменение сигнала визуально совпадают. Может надо ещё подождать?
Я написал максимально простой пример, разные варианты я совершенно сознательно поместил под комментарий. Это гораздо проще изучить. Конечно его можно усложнить, сделать отдельные компоненты с разной реализацией. Но это только усложнит понимание.
Но если доводить до аппаратуры то это придётся сделать. Может быть я это сделаю.
Ещё раз про то откуда берётся переприсвоение тактового сигнала. В основном это когда копипастом помещается код из другого компонента. Добавляется кусок кода, добавляются сигналы. Если название не совпадает, то и происходит переприсваивание. Другой вариант, если есть несколько источников а выбирается один. Имеется ввиду не мультиплексор а простое назначение сигнала. Это конечно можно сделать через alias но кто про него помнит?
В основном это происходит на этапах финальной отладки проекта, когда про модель уже забывают.
Поддержание проекта в виде пригодном для моделирования это достаточно сложная проблема. Но это уже тема для отдельной статьи.
Ещё раз о задержках в исходном коде проекта FPGA или простой вопрос для собеседования на вакансию разработчика FPGA