Это вы говорите про то что если мы делаем AXI-stream блок то все выходы должны иметь регистры - это да. Ну тут разного рода FIFO (в том числе глубиной 1) и skid-buffer'ы помогают. :-)
Опс, а какая у вас версия икаруса, не 12я случаем?
Я меня 11я.
В 12й версии поменяли многое в отношении $queue. На основе этого $queue отрисовывается очередь модели. Это как раз сценарии 2 и 5 где эта model присутствует и визуализируется.
Спасибо. Если будут идеи того что можно еще завизуализировать пишите. В принципе можно добавить любые сценарии от конвейера процессора до нейронных сетей. Так как все это все равно работает на симуляторе то все по сути дела написано на верилоге.
cocotb может забирать значения переменных из верилога и питоном отрисовывать псевдографикой. При этом rtl не меняется.
На самом деле тут самое большое спасибо и респект создателям cocotb. :-)
Пока отвечу по поводу пункта два на счет краша сценариев 2 и 5:
Перед запуском этих сченариев попробуйте развернуть терминальное окно на весь экран. И уже в развернутом окне попробовать запустить сценарии 2 и 5.
Тут проблема скорее всего в том что псевдографика отрисовывается при помощи curses и если curses пытается рисовать за пределами терминального окна то отрабатывает exception и скрипт вылетает. При этом на экране мы ошибки можем не увидеть так как сам экран "захвачен" curses.
Если увеличение размера терминального окна не помогает то можете запустить проблемные сченарии с перенаправлением stdout в файл следующим образом:
Для сценария2:
foo@debian:~$ make -B -f ./Makefile_sc2 >> ./outfile
Для сценария5:
foo@debian:~$ make -B -f ./Makefile_sc5 >> ./outfile
Это даст возможность увидеть какая ошибка приведа в крашу посмотрев в outfile.
По поводу пункта один могу уточнить?
То есть если у меня терминальное окно скажем 85x20 (я его мышкой раздвинул до этих размеров). Далее я запускаю bash скрипт в ктором стоит размер 120x32 то что произойдет? Скрипт ведь не сможет раздвинуть терминальное окно до 120x32?
Хотя тут есть очень интересная идея, проверять размер терминального окна непосредственно из bash скрипта до запуска cocotb и питона. Если терминальное окно маленькое и недостаточное для отображения сценария то выдавать ошибку с рекомендацией увеличить окно и перезапустить bash скрипт.
Отличная статья, спасибо. Хотел бы добавить что еще очень важный coverpoint это то что мы наблюдали появление valid при отсутствующем в предыдущем такте ready.
Двухклоковое FIFO это для clock domain crossing (там специальный FIFO с Gray counters). Тут все работает в одном клоковом домене.
Если Вы хотите использовать almost full то разница между full и almost full должна быть меньше или равна длине конвейера. То есть FIFO говорит что хватит слать так как я почти заполнен предполагая худший вариант что на пути к FIFO в конвейере уже находятся еще несколько транзакций по приходу которых в FIFO не останется свободного места.
В схеме с кредитами мы не заморачиваемся длинной конвейера вообще, а просто выставляем кредитный счетчик в максимальный размер FIFO в самом начале. Если транзакция вошла в конвейер то счечик уменшается (credit -1). Если транзакция покинула FIFO то идет возврат кредита в счетчик (credit +1). Ну и конечно еще одно преимущество что FIFO не нужно генерировать сигнал almost_full а только (ready (что есть notFull) который кстати не используется внутри конвейера!!!) и valid (что есть notEMPTY). Ready для upstream блока генерится если счетчик не равен 0.
То есть в схеме с кредитми мы можем варьировать количество ступеней в конвейере как хотим - ничего нигде менять в схеме не нужно. В схеме с almost_full если мы меняем число ступеней в конвейере соответственно меняем порог almost full на FIFO - неудобно.
По поводу обратного сигнала: если обратный сигнал кредита (credit +1) идет обратно через регистр(ы) то ничего страшного не произойдет если переход кредитного счетчика в более благоприятное состояние произойдет позже, место ведь в FIFO уже освободилось.
В схеме с almost_full вычисление кредита и генерация ready на upstream локализовано у FIFO. В схеме с кредитным счетчиком вычисление и генерация ready на upstream локализовано на кредитном счетчике. Почему Вы решили что оно находится в двух местах?
Немного философии: Концепция skid-buffer поощеряет использование конструкции if (ready) внутри каждого always блока. Здесь возможно нужен подход подобный тому который когда-то предпринял Никлаус Вирт сказав что goto в языке программирования высокого уровня - вреден. Возможно и тут нужно так же, дать FIFO и запретить использование сигнала ready на write интерфейсе, тогда все на кредиты перейдут мгновенно. :-)
Тут еще может быть и разница в толковании терминов. Я пологал что skid-buffer это "половина от FIFO глубиной два". То есть если ready всегда в единице то skid-buffer позволяет valid/data идти напрямую. FIFO вносит латентность по valid/data в любом случае. Просто данная латентность уже как правило заложена в конвейере в виде регистров. Так что если поставить FIFO и компенсировать излишнюю латентность конвейера удалением ступенией из конвейера то как Вы и сказали - Skid buffer и FIFO глубиной два эквивалентны. Просто лично для меня Skid buffer это еще одна дополнительная концепция для запоминания. FIFO же прост, универсален и этим прекрасен. Один раз поняв как генерируется логика для FULL (aka ~ready) и EMPTY (aka ~valid) не обязательно запоминать тонкости его дезайна. Можно проснуться в три часа ночи и за 3 минуты нарисовать любой FIFO (как с read enable так и AXI compatible first word fall through) и на любую глубину. Со skid-buffer там уже придется гуглить :-) Ну это лично мое имхо.
:-) я правда не до конца понимаю больших преимуществ skid-buffer перед "синхронным FIFO глубиной два". Иными словами я не могу себе представить случая где нельзя было бы обойтись FIFO и нужен был бы skid-buffer. Конечно иметь skid-buffer в арсенале разработчика полезно - просто не понимаю почему skid-buffer "уделяется столько почестей".
Просто я про skid-buffer слышу чуть ли не от каждого "первого встречного" при этом многие люди не знают как сделать синхронную FIFO с нуля что на мой взгляд приоритет номер один для любого разработчика. Это скорее некий социальный феномен который я наблюдаю ну или мне просто так показалось.
Видимо подразумевается что разница между almost full и full это количество стадий в конвейере. То есть при изменении длины конвейера предлагается переделывать FIFO - издевательство одним словом. С кредитами о таком вообще думать не нужно.
Кстати можно еще на собеседовании просить спроектировать простой AXI stream broadcast с одним входом и несколькими выходами. Вроде на первый взгляд элементарная задача но без кредитов такое реализовать ой как не просто - особенно если приемники находятся далеко от передатчика. Так что да, кредиты о которых Вы так много говорите координально улучшают качество жизни хардварного разработчика.
Юрий, если бы еще документация была человеческая. Непонятно где этому учиться.
Приведу пример: есть один большой FPGA вендор с синим логотипом, (не буду называть имя вслух) который выпускает очень продвинутые FPGA с технологией "регистры повсюду" - это множество "простых" флопов на фабрике FPGA для оптимизации тайминга. Эти флопы в отличие от регистров в ALM не имеют сигналов reset и clock enable. Казалось бы - идеальное место для предлагаемых Вами конвейеров с кредитными счетчиками.
Тем не менее в официальном гайде для разработчиков приводится вот такая картинка увидев которую сразу становится "очень понятно" как дезайнить конвейеры без backpressure что бы в них использовались флопы с фабрики без clock enable. Вот картинка:
Мало того что решение мягко говоря уступает по удобности использования конвейерам с кредитами так еще и документация подразумевает что каждый прохожий сразу догадается что "Please try to slow down soon" это almost full от FIFO что в инверсии даст ready который можно "притянуть" на вход конвейера. Вот это как пример того что для людей не работающих в Apple и NVidia и не обсуждающих данные детали с коллегами на кофе брейках а просто читающих открытые источники тонкости и трюки хардварного дезайна можно сказать недоступны.
В AXI ready может ждать valid (то есть ready может как бы подтвердить valid-ные данные). А вот valid от ready зависить не должен. Тогда и Ваша задачка "по идее" должна решаться достаточно просто:
Данная фраза прояснила концепцию и преимущества данного подхода: "Не нужно использовать сигнал full как ~ready и не нужно протаскивать его наверх через стадии, так как верх заведомо не пришлет данных столько, чтобы FIFO переполнилось."
Мало того на обратной красной линнии "+1 credit" можно ставить "флопы" для улучшения тайминга, которые просто отсрочат переход кредитного счетчика в более "благоприятное" значение после выхода транзакции из FIFO"!!!
Так же теперь становится очевидной важность верификации данных схем!!! Не только функционально но и с точки зрения производительности конвейера.
Книга интересная но достаточно быстро погружает в сложные детали. Так же не видя всех деталей схемы конвейера с кредитными счетчиками (в частности без понимания того, что происходит с сигналами valid/ready внутри конвейера достаточно сложно понять преимущества.
Если взять "старый подход" к проектированию конвейера то есть абстрагироваться и представить что каждая стадия конвейера это маленький IP-core с AXI-stream на входе и AXI-stream на выходе то непонятно почему конвейер будет "двигаться или останавливаться целиком"?
Предположим что некоторые стадии конвейера могут останавливаться (то есть сами генерировать сигналы valid/ready, а не транслировать их). Таким образом остановившиеся стадии слева будут пораждать "пузыри" (сигнал valid=0) для стадий справа. Мы уже видим асинхронное движение, разве нет?
Так же между стадиями конвейера мы всегда можем поставить "удалитель пузырей". А в случае с проблем с таймингами (особенно если комбинаторная логика по сигналу ready очень длинная) можем поставить FIFO (неглубокий) который "прервет" цепочку комбинаторной логики не только по линии "valid" но и по обратной линии "ready".
Взяв к примеру схему асинхронного FIFO от Clifford Cummings и убрав оттуда логику для "clock-domain-crossing" можем получить синхронный FIFO на любую глубину - комбинаторные линии "valid" и "ready" прерваны, так как в FIFO они генерируются на основе значений счетчиков "write_counter" и "read_counter".
Нарисую схему FIFO для полноты картины.
То есть смотрите в итоге, мы ведь можем спроектировать отдельные стадии конвейера так что они сами будут генерировать сигналы "valid/ready", так же можем использовать "удалители пузырей" и FIFO для буферизации и решения проблем с таймингами?
Правильно ли мое понимание что "старый подход" может быть превращен в "новый" если мы дополним старую схему кредитным счетчиком который будет инкрементировать значение счетчика при входе новой транзакции в конвейер при условии (valid=1 && ready=1), и дикрементировать значение при выходе транзакции с конвейера при том же условии (valid=1 && ready=1)? Как я понимаю это нужно для того что бы сообщить другим блокам на upstream'е насколько занят конвейер.
Еще раз хочу сказать огромное спасибо за Вашу готовность общаться, делиться опытом и знаниями!
Это вы говорите про то что если мы делаем AXI-stream блок то все выходы должны иметь регистры - это да. Ну тут разного рода FIFO (в том числе глубиной 1) и skid-buffer'ы помогают. :-)
А почему вы не хотите сделать сумматор комбинаторно?
Тем более AXI-stream сам выдаст вам слагаемые на каналах A и В и будет их держать до подтверждения сигналом ready:
О супер! Спасибо.
У меня такой набор:
python v3.11
cocotb v1.8.0
icarus_verilog v11.0
Тут кстати еще возможна несовместимость старой версии икаруса и cocotb.
У вас есть возможность проверить на 11й версии икаруса?
А что дает resize?
У меня в линуксе его нет по умолчанию. Говорит поставьте xterm.
Тем не менее я в start.bash скрипт добавил проверку размера терминала.
Опс, а какая у вас версия икаруса, не 12я случаем?
Я меня 11я.
В 12й версии поменяли многое в отношении $queue. На основе этого $queue отрисовывается очередь модели. Это как раз сценарии 2 и 5 где эта model присутствует и визуализируется.
Спасибо. Если будут идеи того что можно еще завизуализировать пишите. В принципе можно добавить любые сценарии от конвейера процессора до нейронных сетей. Так как все это все равно работает на симуляторе то все по сути дела написано на верилоге.
cocotb может забирать значения переменных из верилога и питоном отрисовывать псевдографикой. При этом rtl не меняется.
На самом деле тут самое большое спасибо и респект создателям cocotb. :-)
Спасибо за обратную связь:
Пока отвечу по поводу пункта два на счет краша сценариев 2 и 5:
Перед запуском этих сченариев попробуйте развернуть терминальное окно на весь экран. И уже в развернутом окне попробовать запустить сценарии 2 и 5.
Тут проблема скорее всего в том что псевдографика отрисовывается при помощи curses и если curses пытается рисовать за пределами терминального окна то отрабатывает exception и скрипт вылетает. При этом на экране мы ошибки можем не увидеть так как сам экран "захвачен" curses.
Если увеличение размера терминального окна не помогает то можете запустить проблемные сченарии с перенаправлением stdout в файл следующим образом:
Для сценария2:
foo@debian:~$ make -B -f ./Makefile_sc2 >> ./outfile
Для сценария5:
foo@debian:~$ make -B -f ./Makefile_sc5 >> ./outfile
Это даст возможность увидеть какая ошибка приведа в крашу посмотрев в outfile.
По поводу пункта один могу уточнить?
То есть если у меня терминальное окно скажем 85x20 (я его мышкой раздвинул до этих размеров). Далее я запускаю bash скрипт в ктором стоит размер 120x32 то что произойдет? Скрипт ведь не сможет раздвинуть терминальное окно до 120x32?
Хотя тут есть очень интересная идея, проверять размер терминального окна непосредственно из bash скрипта до запуска cocotb и питона. Если терминальное окно маленькое и недостаточное для отображения сценария то выдавать ошибку с рекомендацией увеличить окно и перезапустить bash скрипт.
Отличная статья, спасибо. Хотел бы добавить что еще очень важный coverpoint это то что мы наблюдали появление valid при отсутствующем в предыдущем такте ready.
Двухклоковое FIFO это для clock domain crossing (там специальный FIFO с Gray counters). Тут все работает в одном клоковом домене.
Если Вы хотите использовать almost full то разница между full и almost full должна быть меньше или равна длине конвейера. То есть FIFO говорит что хватит слать так как я почти заполнен предполагая худший вариант что на пути к FIFO в конвейере уже находятся еще несколько транзакций по приходу которых в FIFO не останется свободного места.
В схеме с кредитами мы не заморачиваемся длинной конвейера вообще, а просто выставляем кредитный счетчик в максимальный размер FIFO в самом начале. Если транзакция вошла в конвейер то счечик уменшается (credit -1). Если транзакция покинула FIFO то идет возврат кредита в счетчик (credit +1). Ну и конечно еще одно преимущество что FIFO не нужно генерировать сигнал almost_full а только (ready (что есть notFull) который кстати не используется внутри конвейера!!!) и valid (что есть notEMPTY). Ready для upstream блока генерится если счетчик не равен 0.
То есть в схеме с кредитми мы можем варьировать количество ступеней в конвейере как хотим - ничего нигде менять в схеме не нужно. В схеме с almost_full если мы меняем число ступеней в конвейере соответственно меняем порог almost full на FIFO - неудобно.
По поводу обратного сигнала: если обратный сигнал кредита (credit +1) идет обратно через регистр(ы) то ничего страшного не произойдет если переход кредитного счетчика в более благоприятное состояние произойдет позже, место ведь в FIFO уже освободилось.
В схеме с almost_full вычисление кредита и генерация ready на upstream локализовано у FIFO. В схеме с кредитным счетчиком вычисление и генерация ready на upstream локализовано на кредитном счетчике. Почему Вы решили что оно находится в двух местах?
Немного философии: Концепция skid-buffer поощеряет использование конструкции if (ready) внутри каждого always блока. Здесь возможно нужен подход подобный тому который когда-то предпринял Никлаус Вирт сказав что goto в языке программирования высокого уровня - вреден. Возможно и тут нужно так же, дать FIFO и запретить использование сигнала ready на write интерфейсе, тогда все на кредиты перейдут мгновенно. :-)
Тут еще может быть и разница в толковании терминов. Я пологал что skid-buffer это "половина от FIFO глубиной два". То есть если ready всегда в единице то skid-buffer позволяет valid/data идти напрямую. FIFO вносит латентность по valid/data в любом случае. Просто данная латентность уже как правило заложена в конвейере в виде регистров. Так что если поставить FIFO и компенсировать излишнюю латентность конвейера удалением ступенией из конвейера то как Вы и сказали - Skid buffer и FIFO глубиной два эквивалентны. Просто лично для меня Skid buffer это еще одна дополнительная концепция для запоминания. FIFO же прост, универсален и этим прекрасен. Один раз поняв как генерируется логика для FULL (aka ~ready) и EMPTY (aka ~valid) не обязательно запоминать тонкости его дезайна. Можно проснуться в три часа ночи и за 3 минуты нарисовать любой FIFO (как с read enable так и AXI compatible first word fall through) и на любую глубину. Со skid-buffer там уже придется гуглить :-) Ну это лично мое имхо.
:-) я правда не до конца понимаю больших преимуществ skid-buffer перед "синхронным FIFO глубиной два". Иными словами я не могу себе представить случая где нельзя было бы обойтись FIFO и нужен был бы skid-buffer. Конечно иметь skid-buffer в арсенале разработчика полезно - просто не понимаю почему skid-buffer "уделяется столько почестей".
Просто я про skid-buffer слышу чуть ли не от каждого "первого встречного" при этом многие люди не знают как сделать синхронную FIFO с нуля что на мой взгляд приоритет номер один для любого разработчика. Это скорее некий социальный феномен который я наблюдаю ну или мне просто так показалось.
Если без FIFO, то как вариант самодвижушийся конвейер с critical path по ready. Эта схема должна работать:
Видимо подразумевается что разница между almost full и full это количество стадий в конвейере. То есть при изменении длины конвейера предлагается переделывать FIFO - издевательство одним словом. С кредитами о таком вообще думать не нужно.
Кстати можно еще на собеседовании просить спроектировать простой AXI stream broadcast с одним входом и несколькими выходами. Вроде на первый взгляд элементарная задача но без кредитов такое реализовать ой как не просто - особенно если приемники находятся далеко от передатчика. Так что да, кредиты о которых Вы так много говорите координально улучшают качество жизни хардварного разработчика.
Юрий, если бы еще документация была человеческая. Непонятно где этому учиться.
Приведу пример: есть один большой FPGA вендор с синим логотипом, (не буду называть имя вслух) который выпускает очень продвинутые FPGA с технологией "регистры повсюду" - это множество "простых" флопов на фабрике FPGA для оптимизации тайминга. Эти флопы в отличие от регистров в ALM не имеют сигналов reset и clock enable. Казалось бы - идеальное место для предлагаемых Вами конвейеров с кредитными счетчиками.
Тем не менее в официальном гайде для разработчиков приводится вот такая картинка увидев которую сразу становится "очень понятно" как дезайнить конвейеры без backpressure что бы в них использовались флопы с фабрики без clock enable. Вот картинка:
Мало того что решение мягко говоря уступает по удобности использования конвейерам с кредитами так еще и документация подразумевает что каждый прохожий сразу догадается что "Please try to slow down soon" это almost full от FIFO что в инверсии даст ready который можно "притянуть" на вход конвейера. Вот это как пример того что для людей не работающих в Apple и NVidia и не обсуждающих данные детали с коллегами на кофе брейках а просто читающих открытые источники тонкости и трюки хардварного дезайна можно сказать недоступны.
В AXI ready может ждать valid (то есть ready может как бы подтвердить valid-ные данные). А вот valid от ready зависить не должен. Тогда и Ваша задачка "по идее" должна решаться достаточно просто:
В целях поддержания беседы, попробую привести свой вариант gearbox (пока без testbench):
Фантастика, Юрий спасибо огромное.
Данная фраза прояснила концепцию и преимущества данного подхода: "Не нужно использовать сигнал full как ~ready и не нужно протаскивать его наверх через стадии, так как верх заведомо не пришлет данных столько, чтобы FIFO переполнилось."
Мало того на обратной красной линнии "+1 credit" можно ставить "флопы" для улучшения тайминга, которые просто отсрочат переход кредитного счетчика в более "благоприятное" значение после выхода транзакции из FIFO"!!!
Так же теперь становится очевидной важность верификации данных схем!!! Не только функционально но и с точки зрения производительности конвейера.
Юрий спасибо огромное за развернутый ответ.
Книга интересная но достаточно быстро погружает в сложные детали. Так же не видя всех деталей схемы конвейера с кредитными счетчиками (в частности без понимания того, что происходит с сигналами valid/ready внутри конвейера достаточно сложно понять преимущества.
Если взять "старый подход" к проектированию конвейера то есть абстрагироваться и представить что каждая стадия конвейера это маленький IP-core с AXI-stream на входе и AXI-stream на выходе то непонятно почему конвейер будет "двигаться или останавливаться целиком"?
Предположим что некоторые стадии конвейера могут останавливаться (то есть сами генерировать сигналы valid/ready, а не транслировать их). Таким образом остановившиеся стадии слева будут пораждать "пузыри" (сигнал valid=0) для стадий справа. Мы уже видим асинхронное движение, разве нет?
Так же между стадиями конвейера мы всегда можем поставить "удалитель пузырей". А в случае с проблем с таймингами (особенно если комбинаторная логика по сигналу ready очень длинная) можем поставить FIFO (неглубокий) который "прервет" цепочку комбинаторной логики не только по линии "valid" но и по обратной линии "ready".
Взяв к примеру схему асинхронного FIFO от Clifford Cummings и убрав оттуда логику для "clock-domain-crossing" можем получить синхронный FIFO на любую глубину - комбинаторные линии "valid" и "ready" прерваны, так как в FIFO они генерируются на основе значений счетчиков "write_counter" и "read_counter".
Нарисую схему FIFO для полноты картины.
То есть смотрите в итоге, мы ведь можем спроектировать отдельные стадии конвейера так что они сами будут генерировать сигналы "valid/ready", так же можем использовать "удалители пузырей" и FIFO для буферизации и решения проблем с таймингами?
Правильно ли мое понимание что "старый подход" может быть превращен в "новый" если мы дополним старую схему кредитным счетчиком который будет инкрементировать значение счетчика при входе новой транзакции в конвейер при условии (valid=1 && ready=1), и дикрементировать значение при выходе транзакции с конвейера при том же условии (valid=1 && ready=1)? Как я понимаю это нужно для того что бы сообщить другим блокам на upstream'е насколько занят конвейер.
Еще раз хочу сказать огромное спасибо за Вашу готовность общаться, делиться опытом и знаниями!