~ cd 2.2. синтезатор
На этой странице мы рассмотрим готовый скрипт синтеза ПЛИС iCE40 — synth_ice40. Мы рассмотрим простую конструкцию на каждом шаге, изучим вызываемые команды и то, что они делают с конструкцией. Хотя synth_ice40 специфичен для платформы iCE40, большинство операций, которые мы будем обсуждать, являются общими для большинства скриптов синтеза ПЛИС. Таким образом, этот документ обеспечит хорошее базовое понимание того, как выполняется синтез в Yosys, независимо от используемой архитектуры.
2.2.1. Демонстрационная конструкция
Для начала давайте посмотрим на конструкцию, которую мы будем синтезировать:
// address generator/counter
module addr_gen
#( parameter MAX_DATA=256,
localparam AWIDTH = $clog2(MAX_DATA)
) ( input en, clk, rst,
output reg [AWIDTH-1:0] addr
);
initial addr <= 0;
// async reset
// increment address when enabled
always @(posedge clk or posedge rst)
if (rst)
addr <= 0;
else if (en) begin
if (addr == MAX_DATA-1)
addr <= 0;
else
addr <= addr + 1;
end
endmodule //addr_gen
// Define our top level fifo entity
module fifo
#( parameter MAX_DATA=256,
localparam AWIDTH = $clog2(MAX_DATA)
) ( input wen, ren, clk, rst,
input [7:0] wdata,
output reg [7:0] rdata,
output reg [AWIDTH:0] count
);
// fifo storage
// sync read before write
wire [AWIDTH-1:0] waddr, raddr;
reg [7:0] data [MAX_DATA-1:0];
always @(posedge clk) begin
if (wen)
data[waddr] <= wdata;
rdata <= data[raddr];
end // storage
// addr_gen for both write and read addresses
addr_gen #(.MAX_DATA(MAX_DATA))
fifo_writer (
.en (wen),
.clk (clk),
.rst (rst),
.addr (waddr)
);
addr_gen #(.MAX_DATA(MAX_DATA))
fifo_reader (
.en (ren),
.clk (clk),
.rst (rst),
.addr (raddr)
);
// status signals
initial count <= 0;
always @(posedge clk or posedge rst) begin
if (rst)
count <= 0;
else if (wen && !ren)
count <= count + 1;
else if (ren && !wen)
count <= count - 1;
end
endmodule
2.2.2. Загрузка конструкции
$ yosys fifo.v
-- Parsing `fifo.v' using frontend ` -vlog2k' --
1. Executing Verilog-2005 frontend: fifo.v
Parsing Verilog input from `fifo.v' to AST representation.
Storing AST representation for module `$abstract\addr_gen'.
Storing AST representation for module `$abstract\fifo'.
Successfully finished Verilog frontend
yosys fifo.v
-- Парсинг `fifo.v' с помощью фронтэнда ` -vlog2k' --
1. Осуществление фронтэнда Verilog-2005: fifo.v
Парсинг входных данных Verilog из файла `fifo.v' в AST-представление.
Кэширование AST-представления для модуля `$abstract\addr_gen'.
Кэширование AST-представления для модуля `$abstract\fifo'.
Успешно завершено создание фронтенда Verilog.
2.2.3. Элаборация
Теперь, когда мы находимся в интерактивной оболочке, мы можем вызывать команды Yosys напрямую. Наша общая цель — вызвать synth_ice40 -top fifo, но пока мы можем выполнить каждую из команд по отдельности, чтобы лучше понять, как каждая часть вносит свой вклад в поток. Мы также начнем только с одного модуля — addr_gen.
В нижней части вывода справки для synth_ice40 находится полный список команд, вызываемых этим скриптом.
Начнем с раздела, озаглавленного » begin»:
read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v
hierarchy -check -top <top>
proc
read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v загружает модели ячеек iCE40, что позволяет нам включить в наш проект IP-блоки, специфичные для платформы. PLL являются распространенным примером, когда нам может понадобиться напрямую ссылаться на SB_PLL40_CORE, а не полагаться на последующие передачи отображения.
Поскольку в нашем простом проекте не используется ни один из этих IP-блоков, мы можем пока пропустить эту команду. Поскольку эти модели ячеек также понадобятся, когда мы начнем размещать на аппаратном обеспечении, нам все равно придется загрузить их позже.
Примечание: +/ — это динамическая ссылка на каталог общего доступа Yosys. По умолчанию это /usr/local/share/yosys. Если используется локально собранная версия Yosys из исходного каталога, то это будет папка share в том же каталоге.
2.2.3.1. Модуль addr_gen
Поскольку мы только начинаем, давайте вместо этого начнем с hierarchy -top addr_gen. Эта команда объявляет, что модулем верхнего уровня является addr_gen, а все остальное можно отбросить.
module addr_gen
#( parameter MAX_DATA=256,
localparam AWIDTH = $clog2(MAX_DATA)
) ( input en, clk, rst,
output reg [AWIDTH-1:0] addr
);
initial addr <= 0;
// async reset
// increment address when enabled
always @(posedge clk or posedge rst)
if (rst)
addr <= 0;
else if (en) begin
if (addr == MAX_DATA-1)
addr <= 0;
else
addr <= addr + 1;
end
endmodule //addr_gen
Примечание: hierarchy всегда должна быть первой командой после чтения конструкции. Указав верхний модуль, hierarchy также установит для него атрибут (* top *). Он используется другими командами, которым необходимо знать, какой модуль является верхним.
yosys> hierarchy -top addr_gen
2. Executing HIERARCHY pass (managing design hierarchy).
3. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'.
Generating RTLIL representation for module `\addr_gen'.
3.1. Analyzing design hierarchy..
Top module: \addr_gen
3.2. Analyzing design hierarchy..
Top module: \addr_gen
Removing unused module `$abstract\fifo'.
Removing unused module `$abstract\addr_gen'.
Removed 2 unused modules.
yosys> hierarchy -top addr_gen
2. Выполнение передачи HIERARCHY (управление иерархией конструкции).
3. Формирование AST-фронтенда в режиме derive с использованием предварительно разобранного AST для модуля `\addr_gen'. Генерация RTLIL-представления для модуля `\addr_gen'.
3.1. Анализ иерархии конструкции...
Модуль верхнего уровня: \addr_gen
3.2. Анализ иерархии конструкции...
Верхний модуль: \addr_gen
Удаление неиспользуемого модуля `$abstract\fifo'.
Удаление неиспользуемого модуля `$abstract\addr_gen'.
Удалены 2 неиспользуемых модуля.
Теперь наша схема addr_gen выглядит следующим образом:

Такие простые операции, как addr + 1 и addr == MAX_DATA-1, можно извлечь из нашего always блока в исходном тексте модуля addr_gen. Это дает нам выделенные ячейки $add и $eq, которые мы видим. Но управляющая логика (например, if … else) и элементы памяти (например, addr <= 0) не так просты. Они помещаются в «процессы», показанные на схеме как PROC. Обратите внимание, как вторая строка ссылается на номера строк начала/конца соответствующего блока always. В случае начального блока вместо этого мы видим PROC ссылается на строку 0.
Чтобы справиться с ними, давайте представим следующую команду: proc — преобразование процессов в нетлисты. proc — это макрокоманда, подобная synth_ice40. Вместо того чтобы непосредственно изменять конструкцию, она вызывает серию других команд. В случае proc эти подкоманды работают над преобразованием поведенческой логики процессов в мультиплексоры и регистры. Давайте посмотрим, что произойдет, когда мы ее запустим. Пока что мы будем вызывать proc –noopt, чтобы предотвратить некоторые автоматические оптимизации, которые обычно происходят.

Теперь есть несколько новых ячеек, которые были выделены из нашего always. Операторы if теперь моделируются ячейками $mux, а регистр использует ячейку $adff. Если мы посмотрим на вывод терминала, то увидим, как вызываются все различные команды proc_*. Мы рассмотрим каждую из них более подробно в разделе
2.2.3.1.1. Преобразование процедурных блоков.
Обратите внимание, что в левом верхнем углу модуля addr_gen после proc -noopt у нас есть плавающий провод, образовавшийся в результате первоначального присвоения 0 проводу addr. Однако, это начальное присвоение не поддается синтезу, поэтому его нужно будет очистить, прежде чем мы сможем сгенерировать для физического оборудования. Мы можем сделать это сейчас, вызвав clean. Мы также собираемся вызвать opt_expr, которая обычно вызывается в конце proc. Мы можем вызвать обе команды одновременно, разделив их точкой с запятой и пробелом: opt_expr; clean.

Вы также можете заметить, что выделенная ячейка $eq, содержащая 255, изменилась на 8’11111111. Значения констант представлены в формате <ширина_бита>'<биты>, причем для 32-битных значений вместо этого используется десятичное число.
Это указывает на то, что входная константа была уменьшена с 32-битной до 8-битной ширины. Это побочный эффект выполнения opt_expr, который выполняет сворачивание констант и простую перезапись выражений.
Примечание: clean — удаляет неиспользуемые ячейки и провода, также может быть вызвана с двумя точками с запятой после любой команды, например, мы могли бы вызвать opt_expr;; вместо opt_expr; clean. Вы можете заметить, что некоторые скрипты будут оканчиваться строку символом ;;. Перед проверкой промежуточных модулей полезно провести очистку, чтобы удалить плавающие части цепи, которые остались, и в некоторых случаях это может уменьшить объем обработки, требуемый последующими командами.
2.2.3.1.2. Полный пример
Теперь давайте вернемся и проверим нашу полную конструкцию с помощью команды hierarchy -check -top fifo. Передавая опцию -check, мы также говорим команде hierarchy, что если конструкция включает в себя любые не “black box”(не примитивы) модулей, не имеющих реализации, он должен возвращать ошибку.
Обратите внимание, что если мы попытаемся выполнить эту команду сейчас, то получим ошибку. Это потому, что мы уже удалили все модули, кроме addr_gen. Мы могли бы перезапустить наш сеанс оболочки, но вместо этого давайте воспользуемся двумя новыми командами:
- design — сохранение, восстановление и сброс текущего конструкции
- read_verilog — чтение модулей из файла Verilog.
yosys> design -reset
yosys> read_verilog fifo.v
11. Executing Verilog-2005 frontend: fifo.v
Parsing Verilog input from `fifo.v' to AST representation.
Generating RTLIL representation for module `\addr_gen'.
Generating RTLIL representation for module `\fifo'.
Successfully finished Verilog frontend.
yosys> hierarchy -check -top fifo
12. Executing HIERARCHY pass (managing design hierarchy).
12.1. Analyzing design hierarchy..
Top module: \fifo
Used module: \addr_gen
Parameter \MAX_DATA = 256
12.2. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'.
Parameter \MAX_DATA = 256
Generating RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
Parameter \MAX_DATA = 256
Found cached RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
12.3. Analyzing design hierarchy..
Top module: \fifo
Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
12.4. Analyzing design hierarchy..
Top module: \fifo
Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
Removing unused module `\addr_gen'.
Removed 1 unused modules.
Перевод:
yosys> design -reset
yosys> read_verilog fifo.v
11. Исполнение фронтенда Verilog-2005: fifo.v
Обработка входных данных Verilog из файла `fifo.v' в AST-представление.
Генерирование RTLIL-представления для модуля `\addr_gen'.
Генерирование RTLIL-представления для модуля `\fifo'.
Успешно завершено создание фронтенда Verilog.
yosys> hierarchy -check -top fifo
12. Выполнение передачи HIERARCHY (управление иерархией конструкции).
12.1. Анализ иерархии конструкции...
Модуль верхнего уровня: \fifo
Используемый модуль: \addr_gen
Параметр \MAX_DATA = 256
12.2. Распаковка AST-фронтенда в режиме derive с использованием предварительно обработанного AST для модуля `\addr_gen'.
Параметр \MAX_DATA = 256
Генерация RTLIL-представления для модуля `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
Параметр \MAX_DATA = 256
Найдено кэшированное RTLIL-представление для модуля `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
12.3. Анализ иерархии конструкции...
Модуль верхнего уровня: \fifo
Используемый модуль: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
12.4. Анализ иерархии конструкции...
Модуль верхнего уровня: \fifo
Используемый модуль: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
Удаление неиспользуемого модуля `\addr_gen'.
Удален 1 неиспользуемый модуль.
Примечание: Число перед выводом команды увеличивается с каждым ее выполнением. Не волнуйтесь, если ваши числа не совпадают с нашими! Вывод, который вы видите, происходит из того же скрипта, который был использован для генерации изображений в этом документе, включенного в исходный текст под именем fifo.ys. В нем выполняются дополнительные команды, которые вы не видите, но не бойтесь попробовать их сами или поиграть с разными командами. Вы всегда можете начать с чистого листа, вызвав exit или нажав ctrl+d (т.е. EOF) и снова запустив интерактивный терминал Yosys. ctrl+c (т.е. SIGINT) также завершит терминальную сессию, но вернет ошибку вместо того, чтобы правильно завершить работу.
Мы также можем запустить proc сейчас, чтобы завершить полную секцию begin. Поскольку схема конструкции довольно большая, мы покажем только путь данных для вывода rdata. Если вы хотите увидеть всю схему целиком, вы можете сделать это с помощью команды show — генерировать схемы с помощью graphviz. Обратите внимание, что команда show работает только с одним модулем, поэтому вам может понадобиться вызвать ее с помощью show fifo.

Выделенный блок fifo_reader содержит экземпляр модуля addr_gen после proc -noopt, который мы рассматривали ранее. Обратите внимание, что тип показан как $paramod\\addr_gen\\MAX_DATA=s32’….. Это «параметрический модуль»: экземпляр модуля addr_gen с параметром MAX_DATA, установленным на заданное значение.
Другой выделенный блок — это ячейка $memrd. На данном этапе синтеза мы еще не знаем, какой тип памяти будет реализован, но мы знаем, что rdata <= data[raddr]; может быть реализовано как чтение из памяти. Обратите внимание, что ячейка $memrd здесь асинхронная, с неопределенными тактовым и разрешающим сигналами, которые показаны как входы 1’x.
2.2.4 Сглаживание
На этом этапе синтеза есть еще несколько команд, которые мы могли бы выполнить. В synth_ice40 мы получаем следующие:
flatten
tribuf -logic
deminout
Во-первых, это flatten. Подобное сглаживание конструкций может позволить оптимизировать работу модулей, которая в противном случае была бы упущена. Давайте запустим flatten;; на нашей конструкции.
yosys> flatten
15. Executing FLATTEN pass (flatten design).
Deleting now unused module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.
<suppressed ~2 debug messages>
yosys> clean
Removed 3 unused cells and 28 unused wires
yosys> flatten
15. Выполнение передачи FLATTEN (сглаживание конструкции).
Удаление неиспользуемого модуля $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.
<подавлено ~2 отладочных сообщения>
yosys> clean
Удалите 3 неиспользуемых ячейки и 28 неиспользуемых проводов.

Кусочки немного сдвинулись, но мы видим, что модуль addr_gen после proc -noopt из предыдущей версии заменил блок fifo_reader в выводе rdata после proc. Мы также видим, что вывод addr был переименован в fifo_reader.addr и объединен с проводом raddr, идущим в ячейку $memrd. Это объединение проводов произошло во время вызова clean, что мы можем видеть в выводе flatten;;.
Примечание: Обычно flatten и clean объединяются в один вывод yosys> flatten;;, но здесь они появляются отдельно как побочный эффект использования echo для генерации вывода в стиле терминала.
В зависимости от целевой архитектуры, на этом этапе синтеза могут также использоваться такие команды, как tribuf с параметрами -logic и deminout . Они удаляют конструкции tristate и inout соответственно, заменяя их логикой, пригодной для отображения на ПЛИС. Поскольку в нашем примере таких конструкций нет, выполнение этих команд не изменит наш проект.
2.2.5. Сoarse-grainпредставление
На этом этапе конструкция представлена в сoarse-grain виде. Он все еще выглядит узнаваемым, а ячейки представляют собой операторы уровня слов с параметризуемой шириной. Именно на этом этапе синтеза мы выполняем такие действия, как сокращение констант, переписывание выражений и обрезание неиспользуемых частей проводов.
Здесь же мы преобразуем наши FSM и блоки, такие как DSP или память. Такие элементы должны быть выведены на основе паттернов в конструкции, и для каждого из них существуют специальные проходы. На обнаружение этих паттернов также могут повлиять оптимизация и другие преобразования, выполненные ранее.
Примечание: Хотя в потоке iCE40 секция flatten и секция put proc находятся в секции begin, некоторые скрипты синтеза вместо этого включают их в эту секцию.
2.2.5.1. Часть 1
В потоке iCE40 мы начинаем со следующих команд:
opt_expr
opt_clean
check
opt -nodffe -nosdff
fsm
opt
Мы уже встречались с opt_expr , а opt_clean — это то же самое, что и clean, но с более подробным выводом. Передача check выявляет несколько очевидных проблем, которые впоследствии приведут к ошибкам. Вызов его здесь позволяет нам быстрее потерпеть неудачу, а не тратить время на то, что заведомо невозможно.
Далее следует opt -nodffe -nosdff, выполняющая набор простых оптимизаций конструкции. Эта команда также обеспечивает включение только определенного подмножества типов FF для подготовки к следующей команде: fsm извлечение и оптимизация конечных автоматов.
До сих пор путь данных для rdata оставался неизменным, поскольку rdata выводится после flatten;;. Однако следующий вызов opt приводит к изменениям. В частности, вызов opt_dff без параметра -nodffe -nosdff способен сложить одну из ячеек $mux в $adff, чтобы сформировать ячейку $adffe; показано ниже:
yosys> opt_dff
17. Executing OPT_DFF pass (perform DFF optimizations).
Adding EN signal on $procdff$59 ($adff) from module fifo (D = $0\count[8:0], Q = \count).
Adding EN signal on $flatten\fifo_writer.$procdff$66 ($adff) from module fifo (D = $flatten\fifo_writer.$procmux$53_Y, Q = \fifo_writer.addr).
Adding EN signal on $flatten\fifo_reader.$procdff$66 ($adff) from module fifo (D = $flatten\fifo_reader.$procmux$53_Y, Q = \fifo_reader.addr).
Перевод:
yosys> opt_dff
17. Выполнение OPT_DFF pass (выполнение DFF-оптимизации).
Добавление сигнала EN на $procdff$59 ($adff) из модуля fifo (D = $0\count[8:0], Q = \count).
Добавление сигнала EN на $flatten\fifo_writer.$procdff$66 ($adff) из модуля fifo (D = $flatten\fifo_writer.$procmux$53_Y, Q = \fifo_writer.addr).
Добавление сигнала EN на $flatten\fifo_reader.$procdff$66 ($adff) из модуля fifo (D = $flatten\fifo_reader.$procmux$53_Y, Q = \fifo_reader.addr).

2.2.5.2. Часть 2
Следующая группа команд выполняет ряд оптимизаций:
peepopt
opt_clean
share
techmap -map +/cmp2lut.v -D LUT_WIDTH=4
opt_expr
opt_clean
memory_dff [-no-rw-check]
Во-первых, это wreduce — уменьшение размера слова операций, если это возможно. Если мы запустим эту функцию, то получим следующее:
yosys> wreduce
19. Executing WREDUCE pass (reducing word size of cells).
Removed top 31 bits (of 32) from port B of cell fifo.$add$fifo.v:66$29 ($add).
Removed top 23 bits (of 32) from port Y of cell fifo.$add$fifo.v:66$29 ($add).
Removed top 31 bits (of 32) from port B of cell fifo.$sub$fifo.v:68$32 ($sub).
Removed top 23 bits (of 32) from port Y of cell fifo.$sub$fifo.v:68$32 ($sub).
Removed top 1 bits (of 2) from port B of cell fifo.$auto$opt_dff.cc:195:make_patterns_logic$72 ($ne).
Removed cell fifo.$flatten\fifo_writer.$procmux$55 ($mux).
Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$36 ($add).
Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$36 ($add).
Removed cell fifo.$flatten\fifo_reader.$procmux$55 ($mux).
Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$36 ($add).
Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$36 ($add).
Removed top 23 bits (of 32) from wire fifo.$add$fifo.v:66$29_Y.
Removed top 24 bits (of 32) from wire fifo.$flatten\fifo_reader.$add$fifo.v:19$36_Y.
Removed top 24 bits (of 32) from wire fifo.$flatten\fifo_writer.$add$fifo.v:19$36_Y.
yosys> show -notitle -format dot -prefix rdata_wreduce o:rdata %ci*
20. Generating Graphviz representation of design.
Writing dot description to `rdata_wreduce.dot'.
Dumping selected parts of module fifo to page 1.
yosys> opt_clean
21. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \fifo..
Removed 0 unused cells and 5 unused wires.
<suppressed ~1 debug messages>
yosys> memory_dff
22. Executing MEMORY_DFF pass (merging $dff cells to $memrd).
Checking read port `\data'[0] in module `\fifo': merging output FF to cell.
Write port 0: non-transparent.
yosys> wreduce
19. Выполнение прохода WREDUCE (уменьшение размера слова в ячейках).
Удалили 31 верхний бит (из 32) из порта B ячейки fifo.$add$fifo.v:66$29 ($add).
Удалены верхние 23 бита (из 32) из порта Y ячейки fifo.$add$fifo.v:66$29 ($add).
Удалены верхние 31 бит (из 32) из порта B ячейки fifo.$sub$fifo.v:68$32 ($sub).
Удалены верхние 23 бита (из 32) из порта Y ячейки fifo.$sub$fifo.v:68$32 ($sub).
Удалены верхние 1 бит (из 2) из порта B ячейки fifo.$auto$opt_dff.cc:195:make_patterns\_logic$72 ($ne).
Удалена ячейка fifo.$flatten\fifo_writer.$procmux$55 ($mux).
Удалены верхние 31 бит (из 32) из порта B ячейки fifo.$flatten\fifo_writer.$add$fifo.v:19$36 ($add).
Удалены верхние 24 бита (из 32) из порта Y ячейки fifo.$flatten\fifo_writer.$add$fifo.v:19$36 ($add).
Удалена ячейка fifo.$flatten\fifo_reader.$procmux$55 ($mux).
Удалили 31 верхний бит (из 32) из порта B ячейки fifo.$flatten\fifo_reader.$add$fifo.v:19$36 ($add).
Удалены верхние 24 бита (из 32) из порта Y ячейки fifo.$flatten\fifo_reader.$add$fifo.v:19$36 ($add).
Удалены верхние 23 бита (из 32) из проволочного fifo.$add$fifo.v:66$29_Y.
Удалены верхние 24 бита (из 32) из проводного fifo.$flatten\fifo_reader.$add$fifo.v:19$36_Y.
Удалены верхние 24 бита (из 32) из проводного fifo.$flatten\fifo_writer.$add$fifo.v:19$36_Y.
yosys> show -notitle -format dot -prefix rdata_wreduce o:rdata %ci\*
Генерация представления конструкции в Graphviz. Запись точечного описания в `rdata_wreduce.dot'. Выгрузка выбранных частей модуля fifo на страницу 1.
yosys> opt_clean
Выполнение pass OPT_CLEAN (удаление неиспользуемых ячеек и проводов). Нахождение неиспользуемых ячеек или проводов в модуле \fifo...
Удалено 0 неиспользуемых ячеек и 5 неиспользуемых проводов.
<подавлено ~1 отладочное сообщение>
yosys> memory_dff
22. Выполнение передачи MEMORY_DFF (объединение ячеек $dff в $memrd).
Проверка порта чтения `\data'[0] в модуле `\fifo': объединение выходных FF в ячейку.
Порт записи 0: отсутствует.
Если посмотреть на путь данных для rdata, то наиболее актуальными из этих уменьшений ширины являются те, которые влияют на fifo. $flatten\fifo_reader.$add$fifo.v. Это ячейка $add, увеличивающая адрес fifo_reader. Мы можем посмотреть на схему и увидеть, что вывод этой ячейки теперь изменился.
Следующие две (новые) команды — peepopt — коллекция peephole-оптимизаторов и share — выполнение разделения ресурсов на основе sat. Ни одна из них не влияет на нашу конструкцию, поэтому пропустим их. techmap -map +/cmp2lut.v -D LUT_WIDTH=4 оптимизирует определенные операторы сравнения, преобразуя их в LUT.
Следующей командой будет memory_dff — объединение входных/выходных DFF в порты чтения памяти.

yosys> memory_dff
22. Executing MEMORY_DFF pass (merging $dff cells to $memrd).
Checking read port `\data'[0] in module `\fifo': merging output FF to cell.
Write port 0: non-transparent.
Перевод:
yosys> memory_dff
22. Выполнение pass MEMORY_DFF (объединение ячеек $dff в $memrd).
Проверка порта чтения `\data'[0] в модуле `\fifo': объединение выходных FF в ячейку.
Порт записи 0: отсутствует.

Как следует из названия, memory_dff объединяет вывод $dff с ячейкой $memrd и преобразует ее в $memrd_v2 (выделено). При этом порт CLK также подключается к входу clk, поскольку теперь это синхронное чтение памяти с соответствующими входами разрешения (EN=1’1) и сброса (ARST=1’0 и SRST=1’0).
2.2.5.3. Часть 3
Третья часть потока synth_ice40 — это серия команд для сопоставления с DSP. По умолчанию поток iCE40 не будет отображаться на аппаратные блоки DSP и будет выполняться только при вызове с параметром -dsp: synth_ice40 -dsp. Хотя в нашем примере нет ничего, что можно было бы сопоставить с DSP, мы все же можем вкратце рассмотреть приведенные здесь команды и описать, что они делают.
wreduce t:$mul
techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 -D DSP_NAME=$__MUL16X16 (if -dsp)
select a:mul2dsp (if -dsp)
setattr -unset mul2dsp (if -dsp)
opt_expr -fine (if -dsp)
wreduce (if -dsp)
select -clear (if -dsp)
ice40_dsp (if -dsp)
chtype -set $mul t:$__soft_mul (if -dsp)
wreduce t:$mul снова выполняет уменьшение ширины, на этот раз только для ячеек типа $mul. techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v … -D DSP_NAME=$__MUL16X16 использует techmap для отображения ячеек $mul на $__MUL16X16, которые, в свою очередь, отображаются на iCE40 SB_MAC16. Все множители, которые не совместимы с преобразованием в $__MUL16X16, перемаркируются в $__soft_mul, прежде чем chtype изменит их обратно на $mul.
Во время преобразования mul2dsp некоторые промежуточные сигналы помечаются атрибутом mul2dsp. Вызвав select a:mul2dsp, мы ограничиваем действие следующих команд только ячейками и проводами, используемыми для этих сигналов. setattr удаляет ненужный теперь атрибут mul2dsp. opt_expr мы уже встречали для сокращения констант и простого переписывания выражений, опция -fine просто обеспечивает более тонкую оптимизацию. Затем мы в последний раз выполняем уменьшение ширины и очищаем выборку.
Наконец, у нас есть ice40_dsp: подобно команде memory_dff, которую мы видели в предыдущем разделе, эта команда объединяет все окружающие регистры в ячейку SB_MAC16. Сюда входят не только входные/выходные регистры, но и регистры конвейера(pipeline) и даже post-adder, где это применимо: превращая умножение + сложение в одно умножение-суммирование.
2.2.5.4. Часть 4
Это подводит нас к четвертой и последней части потока синтеза iCE40:
alumacc
opt
memory -nomap [-no-rw-check]
opt_clean
Если раньше для каждого типа арифметических операций была своя ячейка, например $add, то теперь мы хотим разделить их на ячейки $alu и $macc, которые помогут выявить возможности для повторного использования логики. Для этого мы запустим alumacc, который, как мы видим, произведет следующие изменения в нашем примере:
yosys> alumacc
24. Executing ALUMACC pass (create $alu and $macc cells).
Extracting $alu and $macc cells in module fifo:
creating $macc model for $add$fifo.v:66$29 ($add).
creating $macc model for $flatten\fifo_reader.$add$fifo.v:19$36 ($add).
creating $macc model for $flatten\fifo_writer.$add$fifo.v:19$36 ($add).
creating $macc model for $sub$fifo.v:68$32 ($sub).
creating $alu model for $macc $sub$fifo.v:68$32.
creating $alu model for $macc $flatten\fifo_writer.$add$fifo.v:19$36.
creating $alu model for $macc $flatten\fifo_reader.$add$fifo.v:19$36.
creating $alu model for $macc $add$fifo.v:66$29.
creating $alu cell for $add$fifo.v:66$29: $auto$alumacc.cc:485:replace_alu$87
creating $alu cell for $flatten\fifo_reader.$add$fifo.v:19$36: $auto$alumacc.cc:485:replace_alu$90
creating $alu cell for $flatten\fifo_writer.$add$fifo.v:19$36: $auto$alumacc.cc:485:replace_alu$93
creating $alu cell for $sub$fifo.v:68$32: $auto$alumacc.cc:485:replace_alu$96
created 4 $alu and 0 $macc cells.
yosys> alumacc
1. Выполнение передачи ALUMACC (создание ячеек $alu и $macc).
Извлечение ячеек $alu и $macc в модуль fifo:
Создание модели $macc для $add$fifo.v:66$29 ($add).
Создание $macc модели для $flatten\fifo_reader.$add$fifo.v:19$36 ($add).
Создание $macc модели для $flatten\fifo_writer.$add$fifo.v:19$36 ($add).
Создание $macc модели для $sub$fifo.v:68$32 ($sub).
Создание модели $alu для $macc $sub$fifo.v:68$32.
Создание модели $alu для $macc $flatten\fifo_writer.$add$fifo.v:19$36.
Создание модели $alu для $macc $flatten\fifo_reader.$add$fifo.v:19$36. создание модели $alu для $macc $add$fifo.v:66$29.
Создание ячейки $alu для $add$fifo.v:66$29: $auto$alumacc.cc:485:replace_alu$87
создание ячейки $alu для $flatten\fifo_reader.$add$fifo.v:19$36: $auto$alumacc.cc:485:replace_alu$90
создание ячейки $alu для $flatten\fifo_writer.$add$fifo.v:19$36: $auto$alumacc.cc:485:replace_alu$93
Создание ячейки $alu для $sub$fifo.v:68$32: $auto$alumacc.cc:485:replace_alu$96 создало 4 ячейки $alu и 0 ячеек $macc.
После того как эти ячейки вставлены, вызов opt может объединить ячейки, которые теперь идентичны, но могли быть пропущены, например, из-за разницы между $add и $sub.
Другой новой командой в этой части является memory — перевод памяти в базовые ячейки.
До этого момента чтение и запись в память были совершенно разными ячейками, работающими с одной и той же памятью только абстрактно. memory_collect объединяет все чтения и записи для блока памяти в одну ячейку.
Взглянув на схему после запуска memory_collect, мы видим, что наша ячейка $memrd_v2 была заменена ячейкой $mem_v2 с именем data, тем же именем, которое мы использовали в fifo.v. Там, где раньше у нас был один набор сигналы адреса и разрешения, теперь у нас есть один набор для чтения (RD_*) и один для записи (WR_*), а также вход WR_DATA и выход RD_DATA.


2.2.5.5. Заключение
Дойдя до конца грубозернистого представления, мы также могли бы добраться до этого места, выполнив synth_ice40 -top fifo -run :map_ram после загрузки конструкции. Опция -run <from_label>:<to_label> с пустой <from_label> запускается с секции begin, а <to_label> запускается до секции map_ram, но включая ее.
2.2.6. Сопоставление аппаратных средств
Остальные разделы посвящены различным типам аппаратного обеспечения и в большей степени зависят от архитектуры, чем предыдущие. Поэтому мы рассмотрим каждый раздел лишь вкратце.
Если вы пропустили вызов read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v ранее, сделайте это сейчас.
2.2.6.1. Блоки памяти
Для отображения на блоки памяти используется комбинация memory_libmap и techmap.
Листинг 2.16: секция map_ram
memory_libmap -lib +/ice40/brams.txt -lib +/ice40/spram.txt [-no-auto-huge] [-no-auto-block] (-no-auto-huge unless -spram, -no-auto-block if -nobram)
techmap -map +/ice40/brams_map.v -map +/ice40/spram_map.v
ice40_braminit

Секция map_ram преобразует общий $mem_v2 в iCE40 SB_RAM40_4K (выделено). Мы также видим, что адрес памяти был изменен, а биты данных были переупорядочены (или перемешаны). Также теперь есть ячейка $mux, управляющая значением rdata. В файле fifo.v мы записали память в режиме чтения перед записью, однако SB_RAM40_4K имеет неопределенное поведение при чтении с одного и того же адреса и записи на него в одном и том же цикле. В результате была добавлена дополнительная логика, чтобы сгенерированная схема соответствовала поведению verilog.
yosys> debug memory_libmap -lib +/ice40/brams.txt -lib +/ice40/spram.txt -no-auto-huge
4. Executing MEMORY_LIBMAP pass (mapping memories to cells).
Memory fifo.data mapping candidates (post-geometry):
- logic fallback
- cost: 2048.000000
- $__ICE40_RAM4K_:
- option HAS_BE 0
- emulation score: 7
- replicates (for ports): 1
- replicates (for data): 1
- mux score: 0
- demux score: 0
- cost: 78.000000
- abits 11 dbits 2 4 8 16
- chosen base width 8
- swizzle 0 1 2 3 4 5 6 7
- emulate read-first behavior
- write port 0: port group W
- widths 2 4 8
- read port 0: port group R
- widths 2 4 8 16
- emulate transparency with write port 0
- $__ICE40_RAM4K_:
- option HAS_BE 1
- emulation score: 7
- replicates (for ports): 1
- replicates (for data): 1
- mux score: 0
- demux score: 0
- cost: 78.000000
- abits 11 dbits 2 4 8 16
- byte width 1
- chosen base width 8
- swizzle 0 1 2 3 4 5 6 7
- emulate read-first behavior
- write port 0: port group W
- widths 16
- read port 0: port group R
- widths 2 4 8 16
- emulate transparency with write port 0
Memory fifo.data mapping candidates (after post-geometry prune):
- logic fallback
- cost: 2048.000000
- $__ICE40_RAM4K_:
- option HAS_BE 0
- emulation score: 7
- replicates (for ports): 1
- replicates (for data): 1
- mux score: 0
- demux score: 0
- cost: 78.000000
- abits 11 dbits 2 4 8 16
- chosen base width 8
- swizzle 0 1 2 3 4 5 6 7
- emulate read-first behavior
- write port 0: port group W
- widths 2 4 8
- read port 0: port group R
- widths 2 4 8 16
- emulate transparency with write port 0
mapping memory fifo.data via $__ICE40_RAM4K_
Ячейка $__ICE40_RAM4K_ определена в файле techlibs/ice40/brams.txt, а отображение на SB_RAM40_4K выполняется с помощью techmap, используя techlibs/ice40/brams_map.v. Любые оставшиеся ячейки памяти затем преобразуются в триггеры (thelogic fallback) с помощью memory_map .
opt -fast -mux_undef -undriven -fine
memory_map
opt -undriven -fine
Примечание: Визуальный беспорядок на выходном порту RDATA (выделено) является досадным побочным эффектом opt_clean для битов данных, подвергшихся выборке. При подключении входного порта $mux непосредственно к RDATA для уменьшения количества проводов, провод $techmap579\data.0.0.RDATA становится визуально более сложным.

2.2.6.2. Арифметика
Использует техкарту для отображения базовой арифметической логики на аппаратное обеспечение. Здесь наблюдается некоторый всплеск ячеек, поскольку многобитные $mux и $adffe заменяются однобитными $_MUX_ и $_DFFE_PP0P_, а $alu заменяется примитивными $_OR_ и $_NOT_ вентилями и ячейкой $lut.

ice40_wrapcarry
techmap -map +/techmap.v -map +/ice40/arith_map.v
opt -fast
abc -dff -D 1 (only if -retime)
ice40_opt
2.2.6.3. Flip-flops
Преобразуйте FF в типы, поддерживаемые аппаратно, с помощью dfflegalize , а затем используйте techmap для их отображения.
В нашем примере это преобразует ячейки $_DFFE_PP0P_ в SB_DFFER. Мы также запускаем здесь simplemap, чтобы преобразовать все оставшиеся ячейки, которые не удалось отобразить на аппаратное обеспечение, в примитивы уровня вентилей. Это включает оптимизацию ячеек $_MUX_, где один из входов является константой 1’0, заменяя ее ячейкой $_AND_.
Листинг 2.19: секция map_ffs
dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_DFF_?P?_ 0 -cell $_DFFE_?P?P_ 0 -cell $_SDFF_?P?_ 0 -cell $_SDFFCE_?P?P_ 0 -cell $_DLATCH_?_ x -mince -1
techmap -map +/ice40/ff_map.v
opt_expr -mux_undef
simplemap
ice40_opt -full

2.2.6.4. LUTs
abc и techmap используются для отображения LUT. Преобразование примитивных типов ячеек для использования ячеек $lut и SB_CARRY.
Листинг 2.20: секция map_luts
abc (only if -abc2)
ice40_opt (only if -abc2)
techmap -map +/ice40/latches_map.v
simplemap (if -noabc or -flowmap)
techmap -map +/gate2lut.v -D LUT_WIDTH=4 (only if -noabc)
flowmap -maxlut 4 (only if -flowmap)
read_verilog -D ICE40_HX -icells -lib -specify +/ice40/abc9_model.v
abc9 -W 250
ice40_wrapcarry -unwrap
techmap -map +/ice40/ff_map.v
clean
opt_lut -tech ice40

Наконец, мы используем techmap для сопоставления общих ячеек $lut с ячейками iCE40 SB_LUT4.
Листинг 2.21: секция map_cells
techmap -map +/ice40/cells_map.v (skip if -vpr)
clean

2.2.6.5. Другие клетки
Следующие команды также могут быть использованы для отображения других ячеек:
- hilomap
Некоторые архитектуры требуют специальных ячеек-источников для управления постоянными значениями hi или lo. Эта команда заменяет простые константы экземплярами таких ячеек-источников. - iopadmap
Вводы/выводы верхнего уровня обычно должны быть реализованы с помощью специальных ячеек I/O-pad. Эта команда вставляет такие ячейки в проект.
Эти команды обычно находятся либо в секции map_cells, либо после секции check, в зависимости от потока.
2.2.7. Заключительные шаги
На следующем этапе работы с синтезатором iCE40 выполняется проверка правильности и окончательное приведение в порядок:
autoname
hierarchy -check
stat
check -noinit
blackbox =A:whitebox
Новыми командами здесь являются:
- autoname — автоматически присваивать имена объектам,
- stat — вывести некоторую статистику
- blackbox — преобразование модулей в модули blackbox.
yosys> stat -top fifo
17. Printing statistics.
=== fifo ===
Number of wires: 95
Number of wire bits: 262
Number of public wires: 95
Number of public wire bits: 262
Number of ports: 7
Number of port bits: 29
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 138
$scopeinfo 2
SB_CARRY 26
SB_DFF 26
SB_DFFER 25
SB_LUT4 58
SB_RAM40_4K 1
Обратите внимание, что параметр -top fifo здесь необязателен. stat автоматически будет использовать модуль с установленным атрибутом top, которым был fifo, когда мы вызывали hierarchy. Если ни один модуль не отмечен как top, то статистика будет показана для каждого выбранного модуля.
Вывод статистики также полезен в качестве своеобразной проверки на рациональность: Поскольку мы уже запустили proc , мы не ожидаем, что будут какие-либо процессы. Мы также ожидаем, что данные будут использовать аппаратную память(примитив); если бы вместо SB_RAM40_4K мы увидели большое количество используемых флип-флопов, то могли бы заподозрить неладное.
yosys> stat
2. Printing statistics.
=== fifo ===
Number of wires: 28
Number of wire bits: 219
Number of public wires: 9
Number of public wire bits: 45
Number of ports: 7
Number of port bits: 29
Number of memories: 1
Number of memory bits: 2048
Number of processes: 3
Number of cells: 9
$add 1
$logic_and 2
$logic_not 2
$memrd 1
$sub 1
addr_gen 2
=== addr_gen ===
Number of wires: 8
Number of wire bits: 60
Number of public wires: 4
Number of public wire bits: 11
Number of ports: 4
Number of port bits: 11
Number of memories: 0
Number of memory bits: 0
Number of processes: 2
Number of cells: 2
$add 1
$eq 1
Обратите внимание, что fifo и addr_gen перечислены отдельно, а статистика для fifo показывает 2 модуля addr_gen. Поскольку память еще не была отображена, мы также видим, что имеется 1 память с 2048 битами памяти, что соответствует нашей памяти данных шириной 8 бит с 256 значениями (8 * 256 = 2048).
2.2.7.1. Выходные данные синтеза
В потоке синтеза iCE40 доступны следующие режимы вывода:
- write_blif — запись конструкции в BLIF-файл
- write_edif — запись проекта в файл нетлиста EDIF*
- *write_json — запись конструкции в файл JSON.
Например, если мы вызвали synth_ice40 -top fifo -json fifo.json, наш синтезированный конструкция fifo будет выведен в виде fifo.json. Затем мы можем прочитать этот конструкцию обратно в Yosys с помощью read_json, но не забудьте сначала использовать design -reset или открыть новый интерактивный терминал. Полученный JSON-вывод также может быть загружен в nextpnr для выполнения размещения и маршрутизации; но это выходит за рамки данной документации.