diff --git a/Labs/01. Tcl/README.md b/Labs/01. Tcl/README.md index 65f93b7..a967d74 100644 --- a/Labs/01. Tcl/README.md +++ b/Labs/01. Tcl/README.md @@ -27,6 +27,25 @@ Если вы не помните как создавать проекты, то обратитесь к [материалам АПС с прошлого семестра](https://github.com/MPSU/APS/tree/master/Vivado%20Basics). + **Примечание** + +Vivado часто сохраняет в Tcl-скрипте абсолютные пути к исходным файлам, например `C:/Users/.../Downloads/...`. Такой скрипт обычно корректно работает только на том компьютере, где он был создан. При переносе проекта в другую папку или на другой компьютер пути могут стать недействительными. + +Для повышения переносимости проекта абсолютные пути можно заменить на относительные. В рамках данной лабораторной работы это не является обязательным требованием, однако в дальнейшем такой подход может быть полезен для улучшения переносимости скрипта. + +Например: + +```tcl +# Абсолютный путь +set file "C:/Users/name/Downloads/demo.sv" + +# Относительный путь +set file [file join "." "rtl" "demo.sv"] +``` + +Второй вариант удобнее тем, что при сохранении структуры проекта Tcl-скрипт проще переносить между каталогами и компьютерами. + + ## Экспорт скрипта для автоматизированного создания проекта в Vivado Давайте теперь мы создадим свой первый Tcl скрипт. Этот скрипт будет сгенерирован Vivado и будет предназначаться для восстановления проекта. Затем мы будем менять и дополнять этот скрипт. diff --git a/Labs/06.AXI-Stream/README.md b/Labs/06.AXI-Stream/README.md index 7afb7fc..dd7cd4e 100644 --- a/Labs/06.AXI-Stream/README.md +++ b/Labs/06.AXI-Stream/README.md @@ -78,6 +78,9 @@ module simple_valid_ready_slave end end + assign ready_o = ready_ff; + assign saved_data_o = data_ff; + endmodule ``` @@ -149,7 +152,7 @@ endmodule У вас может возникнуть логичный вопрос: а почему бы просто не поставить регистры на сигнал `ready`, ничего не меняя? Вы можете построить временную диаграмму работы такого решения и убедиться, что оно, к сожалению, окажется неработоспособно. -Тем не менее, существует несколько вариантов решения проблемы с критическим пути по линии `ready`. Самый простой вариант представлен ниже: +Тем не менее, существует несколько вариантов решения проблемы с критическим путем по линии `ready`. Самый простой вариант представлен ниже: ```verilog module valid_ready_pipe_2 @@ -424,7 +427,7 @@ AXI Stream (Advanced eXtensible Interface Stream) — это протокол д 4. `TREADY` (Ready — Готовность приёмника). Указывает, что приёмник готов принять данные. Этот сигнал генерируется Slave. Передача данных происходит только при одновременной активности `TVALID` и `TREADY` (handshake). При этом в спецификации интерфейса AXI Stream существует важные правила, определяющие работу сигналов `TVALID` и `TREADY` -* Slave может удерживать `TREADY` = 1 во то время, когда `TREADY` = 0. +* Slave может удерживать `TREADY` = 1 в то время, когда `TVALID` = 0. * Slave может опускать `TREADY` в 0 в любой момент времени и "слушающий этот сигнал" Master должен это учитывать. * Однажды выставив `TVALID` в 1 Master более **не имеет права** выставлять `TVALID` в 0 до момента, пока не произойдёт рукопожатие и транзакция. То есть, Master не имеет права "передумать", выставляя флаг валидности. @@ -675,7 +678,7 @@ endmodule assign grant[0] = req[0]; assign grant[1] = ~req[0] & req[1]; assign grant[2] = ~req[0] & ~req[1] & req[2]; -assign grant[3] = ~req[0] & ~req[1] & req[2] & ~req[3]; +assign grant[3] = ~req[0] & ~req[1] & ~req[2] & req[3]; ``` Второй способ полезен при описании конфигурируемых дизайнов, где количество портов (а значит и элементов, требующих арбитража) может параметризоваться. Данный пример не является законченным и синтезируемым, но он отлично поясняет идею, которую можно развить с помощью конструкции `for`. @@ -823,7 +826,7 @@ endmodule ![](./pic/rr_arb.png) - Мы добавили указатель `ptr_ff`, который указывает текущий приоритет. При этом, следующее значение указателя всегда выбирается таким, чтобы он указывал на следующий порт относительно того, на котором в данный момент происходит транзакция. Таким образом, приоритет порта с транзакцией на следующий такт понизится до наименьшего. -- В сердце показанного примера до сих пор находится статический арбитр, но мы делаем интересный трюк: сначала мы циклический сдвигаем вектор `req` вправо на количество бит из текущего значения `ptr_ff`, то есть, мы поворачиваем входной вектор `req` так, чтобы `ptr_ff`-й элемент имел максимальный статичный приоритет, в результате получая вектор `req_rotated`. Результат вычисления приоритета `grant_rotated` нельзя просто так использовать для управления портами, его нужно "повернуть обратно", выполнив циклический сдвиг влево на те же `ptr_ff` бит. Таким образом, мы хитрым образом вращаем биты вокруг статического приоритета, чтобы получить динамический арбитраж. Всё гениальное -- просто! +- В сердце показанного примера до сих пор находится статический арбитр, но мы делаем интересный трюк: сначала мы циклически сдвигаем вектор `req` вправо на количество бит из текущего значения `ptr_ff`, то есть, мы поворачиваем входной вектор `req` так, чтобы `ptr_ff`-й элемент имел максимальный статичный приоритет, в результате получая вектор `req_rotated`. Результат вычисления приоритета `grant_rotated` нельзя просто так использовать для управления портами, его нужно "повернуть обратно", выполнив циклический сдвиг влево на те же `ptr_ff` бит. Таким образом, мы хитрым образом вращаем биты вокруг статического приоритета, чтобы получить динамический арбитраж. Всё гениальное -- просто! Стоит обратить внимание на то, что формирование сигналов `req_rotated` и `grant` можно описать более красивым и изящным способом, который, помимо всего прочего, легко ложится на параметризуемый дизайн с переменным числом портов. @@ -843,7 +846,7 @@ assign grant[3:0] = grant_double[7:4]; ### Коммутация потоков данных -Теперь, когда мы знакомы с концепциями fork и join, знаем про арбитраж, мы можем приступить к разработке блока коммутатора, имеющего N входов и M выходов и способного обеспечить связность любого входа с любым выходом. Такую схему, с начиная с зари развития телефонии, принято называть **crossbar switch**. +Теперь, когда мы знакомы с концепциями fork и join, знаем про арбитраж, мы можем приступить к разработке блока коммутатора, имеющего N входов и M выходов и способного обеспечить связность любого входа с любым выходом. Такую схему, начиная с зари развития телефонии, принято называть **crossbar switch**. Суть простейшего коммутатора заключается в наборе блоков fork и join, подключенных с топологией соединений "каждый с каждым". Пример такого коммутатора на 3 входных и 3 выходных показан на рисунке ниже. @@ -981,7 +984,7 @@ module axis_join_param ## Контрольные вопросы 1. Зачем нужен сигнал `ready` в интерфейсе valid-ready? 2. Что такое рукопожатие (handshake)? -3. Что такой обратное давление (backpressure)? +3. Что такое обратное давление (backpressure)? 4. Какая проблема возникает с сигналом `ready` при соединении большого количества модулей в конвейер? 5. Какие способы развязки критического пути по backpressure вы знаете? Опишите их. 6. Что такое кредитный механизм? Для чего он нужен? Как он работает?