Да, там как раз недотриггеры, то есть совсем не триггеры.
При оптимизации, цепочки сдвиговых регистров упаковываются в примитивы типа srl16 или srlc32e. Эти сдвиговые регистры построены вокруг технологии LUTRAM. Центральная идея технологии LUTRAM в том, что Look Up Table 5-в-1 функционально работает так же, как память с пятью битами адреса на входе и одним битом данных на выходе. Когда вы загружаете в LUT таблицу истинности, вы задаёте значение 1 бита на выходе для любой возможной комбинации 5 бит на входе. Если 5 бит на входе рассматривать как адрес, то LUT 5-в-1 будет работать как память шириной 1 бит и глубиной 32 значения. В базовом сценарии таблица истинности для LUT вычисляется на этапе имплементации прошивки и загружается в процессе загрузки битстрима в кристалл, т.е. является фиксированной всё время работы кристалла, то есть работает как LUTROM (Read Only Memory). Однако Xilinx добавил в свои LUT-ы возможность менять "таблицу истинности" run-time, но не более одного бита за такт (возможно изменить только тот бит таблицы истинности, на который указывают 5 бит "адреса" на входе LUT-a). В итоге внутри LUT-а можно сохранять данные, и LUT может функционировать как эквивалент регистра (если вы не меняете 5 бит "адреса" на входе LUT-а), или как эквивалент памяти на 32 бита (если вы управляете адресами на входе). На основе этой же идеи собраны упомянутые примитивы сдвиговых регистров.
TLDR - если ваш код оптимизировался в примитив сдвигового регистра, то вы по сути изменяете таблицу истинности внутри LUT-a.
Xilinx не раскрывает детали реализации этого механизма, поэтому единственное что можно утверждать наверняка - появление метастабильности внутри этой схемы может привести к непредсказуемым последствиям. Соответственно, чтобы кристалл работал предсказуемо, следует избегать таких ситуаций.
p.s. аппаратный cdc, который упомянул @KeisN13 в своём ответе - это совсем другая история. Примитив называется HARD_SYNC, физически расположен снаружи слайсов, содержит в себе цепочку 1-битных регистров, каждый из которых обладает более короткой постоянной времени выхода из метастабильного состояния. Таких регистров в кристалле очень мало, ресурс ценный, тянуть дорожки до них далеко, предполагаю что оптимизатор никогда их не заиспользует по собственному желанию.
FIFO, реализованную с вашим интерфейсом, можно поставить в цепочку axi-stream модулей без дополнительных переходников. Однако при подключении придется держать в голове маппинг сигналов вашего и axi-stream интерфейсов. Выглядит так, что переименование сигналов в axi-stream style добавит удобства при интеграции c axi-stream модулями, при этом имеет околонулевую стоимость реализации.
Про интерфейсы согласен только отчасти: APB, AHB и AXI-4 действительно являются громоздкими, и использование их на уровне модулей FIFO приведет к неоправданным тратам ресурсов на протокольную логику. Однако AXI-stream на порядок проще в имплементации, особенно в минимальной комплектации (data,valid,ready).
pop в смысле вашей реализации выглядит эквивалентным cигналу s_tready интерфейса axi-stream, can_push - эквивалентным сигналу s_tvalid. Не планируете ли вы перейти на обозначения axi-stream интерфейса для улучшения совместимости со сторонним кодом?
В рамках данной статьи, приведенные «конкретные аргументы против» рассматриваются только в контексте создания кода с нуля. Остается непонятным, как предложения из статьи повлияют на процесс модификации проекта под изменяющиеся требования, на удобство реюза блоков из старого проекта в новом, на время, требуемое одному разработчику на поддержку кода, написанного другим разработчиком. Именно в этом контексте хотелось бы узнать, какой у автора бэкграунд, чтобы хотя бы косвенно оценить, с какими проблемами он уже мог сталкиваться в процессе разработки, а с какими скорее всего еще не сталкивался.
Воу, круто, спасибо за инфу.
Да, там как раз недотриггеры, то есть совсем не триггеры.
При оптимизации, цепочки сдвиговых регистров упаковываются в примитивы типа srl16 или srlc32e. Эти сдвиговые регистры построены вокруг технологии LUTRAM. Центральная идея технологии LUTRAM в том, что Look Up Table 5-в-1 функционально работает так же, как память с пятью битами адреса на входе и одним битом данных на выходе. Когда вы загружаете в LUT таблицу истинности, вы задаёте значение 1 бита на выходе для любой возможной комбинации 5 бит на входе. Если 5 бит на входе рассматривать как адрес, то LUT 5-в-1 будет работать как память шириной 1 бит и глубиной 32 значения. В базовом сценарии таблица истинности для LUT вычисляется на этапе имплементации прошивки и загружается в процессе загрузки битстрима в кристалл, т.е. является фиксированной всё время работы кристалла, то есть работает как LUTROM (Read Only Memory). Однако Xilinx добавил в свои LUT-ы возможность менять "таблицу истинности" run-time, но не более одного бита за такт (возможно изменить только тот бит таблицы истинности, на который указывают 5 бит "адреса" на входе LUT-a). В итоге внутри LUT-а можно сохранять данные, и LUT может функционировать как эквивалент регистра (если вы не меняете 5 бит "адреса" на входе LUT-а), или как эквивалент памяти на 32 бита (если вы управляете адресами на входе). На основе этой же идеи собраны упомянутые примитивы сдвиговых регистров.
TLDR - если ваш код оптимизировался в примитив сдвигового регистра, то вы по сути изменяете таблицу истинности внутри LUT-a.
Xilinx не раскрывает детали реализации этого механизма, поэтому единственное что можно утверждать наверняка - появление метастабильности внутри этой схемы может привести к непредсказуемым последствиям. Соответственно, чтобы кристалл работал предсказуемо, следует избегать таких ситуаций.
p.s. аппаратный cdc, который упомянул @KeisN13 в своём ответе - это совсем другая история. Примитив называется HARD_SYNC, физически расположен снаружи слайсов, содержит в себе цепочку 1-битных регистров, каждый из которых обладает более короткой постоянной времени выхода из метастабильного состояния. Таких регистров в кристалле очень мало, ресурс ценный, тянуть дорожки до них далеко, предполагаю что оптимизатор никогда их не заиспользует по собственному желанию.
этот эффект известен как плазменный резонанс
похоже на опечатку, эффект называется плазмонный резонанс.
понятно, спасибо за разъяснения.
Я имел в виду полную функциональную эквивалентность и совместимость между сигналами вашего интерфейса и сигналами axi-stream интерфейса:
// input stream
push_data = s_tdata
push = s_tvalid
can_push = s_tready
//output stream
pop_data = m_tdata
can_pop = m_tvalid
pop = m_tready
FIFO, реализованную с вашим интерфейсом, можно поставить в цепочку axi-stream модулей без дополнительных переходников. Однако при подключении придется держать в голове маппинг сигналов вашего и axi-stream интерфейсов. Выглядит так, что переименование сигналов в axi-stream style добавит удобства при интеграции c axi-stream модулями, при этом имеет околонулевую стоимость реализации.
Про интерфейсы согласен только отчасти: APB, AHB и AXI-4 действительно являются громоздкими, и использование их на уровне модулей FIFO приведет к неоправданным тратам ресурсов на протокольную логику. Однако AXI-stream на порядок проще в имплементации, особенно в минимальной комплектации (data,valid,ready).
Здравствуйте, Юрий.
pop в смысле вашей реализации выглядит эквивалентным cигналу s_tready интерфейса axi-stream, can_push - эквивалентным сигналу s_tvalid. Не планируете ли вы перейти на обозначения axi-stream интерфейса для улучшения совместимости со сторонним кодом?
Блокирующее присваивание — это оператор "="
Неблокирующее присваивание — оператор "<="
Описано в пунктах 10.4.1 и 10.4.2 стандарта на SystemVerilog от 2012 года.