Search
Write a publication
Pull to refresh

Comments 43

Хм… а почему Вы не стали использовать код грея для передачи указателей фифо через домен? Я думал это стандартное, проверенное временем решение, которое используют все.
Код Грея может спасти только при очень близких частотах. А вот при переходе от 250 МГц к 100 МГц он уже не поможет.
Счётчик на 250 МГц успеет насчитать несколько значений за один такт 100 МГц. И я не понимаю как здесь может помочь год Грея.
Это не важно сколько он насчитает.

В любой момент времени вы всегда будите видеть либо текущее значение, либо прошлое. Только если у вас шина со значением настолько расползается по времени, что по одним битам успеют придти два значения пока по другим ползет одно. Но это нереальный случай.

А имея в любой момент времени либо старое либо новое значение, при это за счет счетчика грея отличающиеся только на 1 бит, вы не можете неправильно обработать заполнение фифо.

| Только если у вас шина со значением настолько расползается по времени

При асинхронных сигналах тактовой частоты всегда будут ситуации когда разные разряды шины будут защёлкнуты в разное время. Т.е. в другом домене будет часть нового значения а часть старого. А часть вообще будет в метастабильном состоянии. И в этом случае год Грея не поможет.
Ваша реализация точно так же не дает никаких гарантий. В теории сдвиг фазы может превышать может превышать любое число тактовых периодов, если вы не написали constraint синтезатору. В общем реализация с Греем лучше т.к. требует меньше тактов на пересинхронизацию.
Это не так. У меня есть компонент ctrl_retack_counter_m12 Он корректно переводит шину data_in на data_out за несколько тактов. В нём сначала фиксируется значение на data_in, взводится флаг и фиксируется значение на data_out. Далее всё повторяется.
ну смотрите, у вас есть вектор сигналов data который пишется на одном клоке, а читается на другом. Если вы не напишете constraint на этот сигнал, то разные сигналы в этом векторе могут прийти с разной задержкой. На низкой частоте в маленьком дизайне скорей всего все будет правильно работать. Но на высоких частотах задержка может превышать несколько тактов и вы рискуете получить неправильное значение. Я в другом комменте скинул ссылку на статью про этот кейс. Т.е. constraint нужно писать всегда. А раз пишем constraint, то проще делать на грее, т.к. его можно пересинхронизировать обычным двойным флопом
Теперь я понял что вы опасаетесь. Но смотрите что происходит.
Автомат pr_st1 фиксирует data и взводит flag1
flag1 переводится в другой домен — flag1to2
flag1to2 обнаруживается автоматом pr_st2
автомат pr_st2 переходит в состояние s2 и фиксирует данные на выходе.
Т.е. есть как минимум два такта на прохождение данных от data к data_out.

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

P.S. Но код Грея всё равно не спасёт. Или здесь я тоже не прав?
Хм… а по моему, при передаче кодом грея в метастабильном состоянии может оказаться только один бит, тот который в данный момент меняется. Остальные же изменились раньше и всегда верные.
Я не спорю как будет:) я знаю ответ:).
Код грея делает так что одно значение от другого будет отличаться ровно 1 битом. В любой момент времени вы можете получить либо новое, либо старое значение. Даже в случае мета-стабильности у вас в итоге триггер свалиться к новому или старому значению и это всегда даст правильный ответ.

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

вот у вас есть счетчик который меняется
0 1 2 3 4 5 6 7 8 9 10
пусть он доходит в другой домен с задержкой в 2 такта
0 0 0 1 2 3 4 5 6 7 8 9
пусть другой домен выбирает каждый 10 отсчет
0 0 0 0 0 0 0 0 0 7 7 7 7…

но обратите внимание что когда мы меняли 0 на 7, мы на входе который защелкивает данные имели то 6 7 8, То есть мы могли защелкнуть 6, или переход 6-7 или 7 или переход 7-8, мы не могли видеть перехода 6 — 8, мы не могли видеть часть от 6 и часть от 7, так как считаем что период изменения больше разбежки шины.
так вот 6, 6-7, 7, 7-8, в коде Грея все эти ситуации будут превращены в 6 или 7 или 8, 6-7 и 7-8 свалиться в одно из устойчивых.

И не важно с какой частотой вы выбираете и с какой общей задержкой приходят данные

Давайте рассмотрим такую ситацию: клок записи 250 МГц, клок чтения 100 МГц.
Задержки на шине передачи от от одного счётчика к другому распределились так:
bit0 — 1.1 ns
bit1 — 1.2 ns
bit2 — 1.4 ns
Это всё законно для частоты 250 МГц.
Теперь случай 1: фронт клока чтения отстал на 1.0 ns — все три бита получат старое значение
Следующий случай: фронт отстал на 1.3 ns — bit0 и bit1 — новое, bit 2 — старое
Ещё случай: фронт отстал ровно на 1.2 ns — bit2 — старое, bit0 — новое, bit1 — в метастабильном.
А эти случаи будут идти постоянно.
Отлично, теперь мы к этому добавляем кодирование кодом грея
старое значение {bit0, bit1, bit2} отличается от нового {bit0, bit1, bit2} только одним битом
то есть у нас 3 варианта
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, New}
для первого случая мы получим
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
второго случая
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
то есть 2 варианта изменились, а третий передался просто старым
для 3 случая
{bit0, bit1, bit2} -> {New, bit1, bit2}
{bit0, bit1, bit2} -> {bit0, New или bit1, bit2}
{bit0, bit1, bit2} -> {bit0, bit1, bit2}
то есть первый вариант прошел, второй вариант либо прошол либо остался старым
третий вариант остался старым

Не так. Обратите внимание, что частота записи 250 МГц, а частота чтения 100 МГц. Т.е. новое значение изменилось больше чем на один бит.
сие невозможно:) разбежка между битами 0.3 нСек, а период задающего клока 4 нСек. Ни при каких условиях вы не можете на другом конце шины в один момент времени получить не соседние значения, которые отличаются не более чем на 1 бит.

то что шина в 100 МГц видит данные в 2.5 раза реже не означает что данные в шине пропадают в промежутки между клоками…

Постройте симуляцию и посмотрите.
Внимательно посмотрел и признаю — я не прав. Действительно год Грея можно использовать при переходе между тактовыми доменами.
Благодарю за советы.
Дальше можно обсудить будет ли реализация на коде Грея более эффективной.
есть ограничения: код Грея будет работать только для FIFO глубиной степени 2^n, иначе при переходе максимум-минимум будут многобитывае изменения.

второе ограничение: если вы хотите иметь флаги частичного заполнения, то вам придется сделать обратные преобразователи код Грея — обычный код, чтобы сравнивать уровни заполнения.

Пересинхронизация будет удобнее, но чуть объемнее. Надо все биты через 2 триггера пропускать, а не один флаг, с другйо стороны нет логики на флаге. Расширенный бит счетчика делает очень удобный анализ полно-пусто, без частичных уровней заполнения можно все сделать в коде Грея без обратного преобразования.

Похоже код Грея мне не поможет. Настройка флагов обязательно нужна.
преобразователь в код грея и обратно — это небольшая комбинаторная схема. Скорее всего она имеет меньший или сравнимый размер с логикой формирования флагов синхронизации.

Внутри домена у вас нормальный счетчик адреса, в другой домен счетчик идет через преобразователь в код Грея, переходит в этом виде между доменами и на той стороне восстанавливается опять в нормальный вид. Так решено пожалуй 99% FIFO через которые делают пересинхронизацию. Это стандартное и облизанное со всех сторон решение.
Счётчик Грея как раз и нужен для того, чтобы у Вас изменялся только 1 бит.
И тогда, очевидно, возможны только 2 ситуации:
1) Бит уже успел измениться (будет защёлкнуто новое значение счётчика)
2) Бит ещё не успел измениться (будет защёлкнуто предыдущее значение счётчика)
Я бы изменил название на «Как работает асинхронное FIFO» для ясности
Рекомендую ознакомиться http://www.zimmerdesignservices.com/mydownloads/no_mans_land_20130328.pdf В некоторых случаях с большим clock skew даже правильно написанное асихронное фифо не будет правильно работать без timing constraints
Посмотрел, но понял зачем так делать
то есть — не понял зачем так делать
Это память типа «первым вошёл-первым ушёл» (first input – first output).

Вообще-то «first in — first out».
А почему вы у себя в коде пишете:

flag2to1 <= flag2 after 1 ns when rising_edge( clk1 );


А если кому-то потребуется использовать ваше fifo в дизайне который работает на частоте больше 1 ГГц, оно нормально будет симулироваться?
Ну если на 1 ГГц то надо написать after 0.1 ns, или меньше.
Я всегда пишу after, так лучше видно сигналы на временной диаграмме и предохраняет от расхождения с работой в реальной аппаратуре.
Эти задержки накапливаются. Где-то в другом месте кто-то может написать еще одно присвоение, потом еще.

В общем весь продуктовый RTL который я видел был написан без задержек. Если хочется увидеть какие-то «мнимые» задержки на временной диаграмме, досточно просто включить отображение дельта-циклов.
Мы пишем задержки только после синхронных присвоений под CLK, поэтому они не накапливаются. При следующем защелкивании — она снова будет 1нс. Про 1ГГц — верно подмечено, нужно весь код заменить на что-то типа 0.1нс. Поэтому я в своих проектах ввел переменную td, которой присваивается значение этой задержки.
«Мы» это стандарт на кодирование в компании или просто чья-то светлая идея?
Изначально — это «светлая идея», которая затем переросла в негласный стандарт компании.

К сожалению, он не всеми соблюдается, но мы стремимся к единообразию и соблюдению определенных правил написания кода. В свое время я написал такой документ, но сами понимаете — протащить его наверх и сделать это стандартом крайне непросто. Несмотря на то, что многие поддержали эту идею, а коллективные обсуждения по этому вопросу возникали неоднократно. :)
Это всё на основе реального опыта. Было несколько случаев, когда результаты моделирования резко отличались от реальной работы. Разбор выявил узкое место. Это присваивание тактовых частот. Присваивание тактовых частот также производится с дельта-задержкой и это приводит к трудно обнаруживаемым проблемам. В итоге — проще написать aftger 1 ns.
Обычно для моделирования всяких clock-гейтов и прочей логики на клоках используются присвоения с immediate notification, тогда event скедулиться в текущий дельта-цикл. Не помню деталей VHDL, нужно спеку смотреть. Последнее время пользуюсь только SystemC.

В общем использование задержек в синтезируемом коде приводит к путтанице. Особенно если у вас в перемешку RTL с gate-level с timing аннотациями. В общем я за свою жизнь уже настродался с интеграцией кода вроде вашего, поэтому у меня на него больная реакция.
По стандарту языка конструкцию after синтезатор игнорирует. Так что никаких проблем нет.
Да, и моделированием после трассировки мы не занимаемся.
Обычно накопления не происходит. Задержка добавляется только при защёлкивании на триггере.
Почему так делаю я написал здесь.
Шёл 2017 год. Люди изобретали двухклоковое фифо, открывая попутно для себя свойства кода Грея, допуская ошибки связанные с метастабильностью в цепях сброса и пересечении клоковых доменов.

Clock domain crossing: http://www.sunburst-design.com/papers/CummingsSNUG2008Boston_CDC.pdf
Async fifo design: http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf

Это не фундаментальные труды по схемотехнике и разработке цифровых схем, но дают представление о предмете.

И еще, боюсь показаться грубым или заносчивым, но скажите пожалуйста, что подвигает Вас писать статьи? Что решения описанные в этой, что в предыдущей статье содержат классические ошибки начинающих (начиная от неглубокой проработки теории и специфики задач и, вследствии, принятие и реализация несоответствующих схемных решений, щедро приправленых синдромом NIH). И все бы ничего, если бы это был студенческий энтузиазм, но в профиле ж написано «инженер-разработчик».
Век живи — век учись (правда всё равно не поможет)
При всех недостатках — у меня есть о чём рассказать и показать.
Хотя бы для организации дискуссии.
Тогда нужно поддержать и «афтар пешы ещо» =).
Реально же много толковых людей, но они предпочитают «набигать и критиковать» (прямо как я), а вот написать грамотную и связную статью — выше сил.
Цитата: «Но что делать если что-то не устраивает в стандартных решениях? Ответ один – разобраться и написать самому». Дальше можно не читать! Если у нас начальники лаборатории такую ересь пишут, то тогда я понимаю почему Элон Маск за 7 лет поднимает сверхтяж, а в «Роскосмосе» за 20 лет не могут :(
Sign up to leave a comment.

Articles