|
Эффективный широкий pipelined mux, как альтеровский LPM_MUX, но для Xilinx |
|
|
|
Jun 24 2018, 05:57
|
Частый гость
 
Группа: Свой
Сообщений: 82
Регистрация: 7-02-07
Из: Беларусь, г. Минск
Пользователь №: 25 149

|
Здравствуйте. В процессе миграции проекта с Quartus в Vivado столкнулся с неприятной проблемой. В Quartus проекте используется мегафункция LPM_MUX – т.е. эффективный параметризируемый Mux оптимизированный под конкретное семейство FPGA с возможностью pipelining. Аналогичного IP Core у Xilinx найти не удалось (может я плохо искал?). Изначально в проекте использовал обычный Mux «общего назначения», ключевая часть которого выглядела приблизительно так (модуль целиком в аттаче bus_mux.vhd): Код -- Asynchronous Mux MUX_P_ASYNC: process(sel, data_in) variable idx: integer := 0; begin idx := conv_integer(sel); mux_data <= data_in((idx+1)*(BUS_WIDTH)-1 downto idx*(BUS_WIDTH)); end process;] Это простая альтернатива длинным case структурам, которая обычно даёт такой же результат. Однако в данном проекте этот подход был неэффективным, т.к. мультиплексоры должны быть весьма широкие – где-то от 40 до 150 входных шин, каждая шина 32/64 бита. Таких мультиплексоров несколько сотен, и они достаточно тесно «взаимосвязаны». Всё это приводило к высокой насыщенности в кристалле (congested design). В результате в процессе раскладки Routing зачастую или просто загибался, или в результате имел низкую частоту (где-то 50 МГц, тогда как целевая частота в диапазоне 100-200 МГц). Решить проблему помогло добавление pipeline регистров. Т.е. в альтеровской мегафункции LPM_MUX можно просто параметром установить количество ступеней pipeline, и наше одно большое асинхронное дерево MUX разбивается на каскады с промежуточными регистрами между ними. Т.к. аналогичного IP Core для Xilinx найти не удалось, озадачился поиском альтернатив. Нашёл достаточно неплохой xapp522-mux-design-techniques (автор небезызвестный Ken Chapman). Там достаточно хорошо описывается, как наиболее эффективно реализовать мультиплексоры на базе основных «кирпичиков» Configurable Logic Blocks (CLBs) (для Spartan-6 FPGAs, Virtex-6 FPGAs, and 7 series FPGAs). И есть даже примеры исходников ( Reference Design Files). Проблема только в том что: 1) Прилагаемые примеры описывают максимум Mux 16:1 (т.е. 16 входов, один выход). Б ольшие муксы предлагается компоновать из более мелких. 2) Само описание MUX-а низкоуровневое, специфичное для вышеупомянутых семейств, по сути, просто конструктор элементов CLB (дерево из LUT6, MUXF7, MUXF8). На картинке пример реализации 8:1 MUX:  И прилагаемом в xapp-е примерах в коде прямо так и описываются все эти примитивы (особенно вставляют строки инициализации LUT6, т.е. .INIT (64'hFF00F0F0CCCCAAAA) ). Ниже пример кода для 16:1 MUX (standard_mux16.v). CODE /////////////////////////////////////////////////////////////////////////////////////////// // // Format of this file. // // The module defines the implementation of the logic using Xilinx primitives. // These ensure predictable synthesis results and maximise the density of the // implementation. The Unisim Library is used to define Xilinx primitives. It is also // used during simulation. // The source can be viewed at %XILINX%\verilog\src\unisims\ // /////////////////////////////////////////////////////////////////////////////////////////// //
`timescale 1 ps / 1ps
module standard_mux16 ( input [15:0] data_in, input [3:0] sel, output data_out);
// /////////////////////////////////////////////////////////////////////////////////////////// // // Wires used in standard_mux16 // /////////////////////////////////////////////////////////////////////////////////////////// //
wire [3:0] data_selection; wire [1:0] combiner;
// /////////////////////////////////////////////////////////////////////////////////////////// // // Start of standard_mux16 circuit description // /////////////////////////////////////////////////////////////////////////////////////////// //
LUT6 #( .INIT (64'hFF00F0F0CCCCAAAA)) selection0_lut( .I0 (data_in[0]), .I1 (data_in[1]), .I2 (data_in[2]), .I3 (data_in[3]), .I4 (sel[0]), .I5 (sel[1]), .O (data_selection[0]));
LUT6 #( .INIT (64'hFF00F0F0CCCCAAAA)) selection1_lut( .I0 (data_in[4]), .I1 (data_in[5]), .I2 (data_in[6]), .I3 (data_in[7]), .I4 (sel[0]), .I5 (sel[1]), .O (data_selection[1]));
MUXF7 combiner0_muxf7 ( .I0 (data_selection[0]), .I1 (data_selection[1]), .S (sel[2]), .O (combiner[0])) ;
LUT6 #( .INIT (64'hFF00F0F0CCCCAAAA)) selection2_lut( .I0 (data_in[8]), .I1 (data_in[9]), .I2 (data_in[10]), .I3 (data_in[11]), .I4 (sel[0]), .I5 (sel[1]), .O (data_selection[2]));
LUT6 #( .INIT (64'hFF00F0F0CCCCAAAA)) selection3_lut( .I0 (data_in[12]), .I1 (data_in[13]), .I2 (data_in[14]), .I3 (data_in[15]), .I4 (sel[0]), .I5 (sel[1]), .O (data_selection[3]));
MUXF7 combiner1_muxf7 ( .I0 (data_selection[2]), .I1 (data_selection[3]), .S (sel[2]), .O (combiner[1])) ;
MUXF8 combiner_muxf8 ( .I0 (combiner[0]), .I1 (combiner[1]), .S (sel[3]), .O (data_out)) ;
endmodule
/////////////////////////////////////////////////////////////////////////////////////////// // // END OF FILE standard_mux16.v // /////////////////////////////////////////////////////////////////////////////////////////// Понятное дело, что такой код весьма далёк от generic кода общего вида. И чтобы подогнать под большие MUX-ов произвольного размера, да ещё и с добавлением промежуточных pipeline регистров для повышения производительности, нужно приложить определённые усилия. Если идти по такому пути, то написание более универсального MUX-а видится приблизительно так. Для примера возьмём MUX 150:1. Т.к. один CLB реализует максимум 16:1, то разбиваем наши 160 входов на ceil(150/16)=10 групп (9 полных 16:1 мультиплексоров, один неполный 6:1). Они образуют первый каскад, в которую можно «вставлять» промежуточные регистры (используя регистры тех же CLB, что и задействованы в имплементации самих MUX-ов). 10 выходов первого каскада заводим на каскад 2-ого уровня (MUX 10:1), с регистром на выходе если надо. Т.е. вроде можно заморочиться и просто описать этот алгоритм в HDL. Но, честно говоря, я на 100% не уверен, что такой способ наиболее эффективный с точки зрения производительности (может есть и более оптимальные решения). Ну и естественно, в идеале хотелось бы чего-то более простого и универсального. Тем более, что мне нужно это реализовать для двух семейств FPGA (Zynq7000 и UltraScale). А в семействе UltraScale CLB имеют другую архитектуру и могут реализовывать до 32:1 MUX. Опять-таки придётся это отдельным случаем описывать. В идеале хотелось бы иметь некий код общего назначения, понятный без вникания в детали архитектуры конкретного семейства и абстрагированный от CLB. Может можно как-то аттрибутами запихать generic код в примитивы CLB (правда с промежуточными регистрами накладки получаются). Так вот, исходя из всего вышеперечисленного, хотелось бы услышать мнения/критику, как бы наиболее эффективно (с точки зрения производительности) и не сильно проблематично с точки зрения написания кода (а хотелось бы ещё и красиво) реализовать такой конфигурируемый широкий мультиплексор с pipeline регистрами.Может кому уже приходилось сталкиваться с подобным, и можете поделиться набитыми шишками? Или подкинет кто каких полезных ссылок? Буду рад помощи.
|
|
|
|
|
 |
Ответов
|
Jun 26 2018, 11:42
|
Частый гость
 
Группа: Свой
Сообщений: 82
Регистрация: 7-02-07
Из: Беларусь, г. Минск
Пользователь №: 25 149

|
Ещё в процессе экспериментов выяснилось, что не всегда синтезатор оптимально размещает муксы используя специальные внутренние примитивы (MUXF8, MUXF9). Не знаю, можно ли вставлять сразу картинки большего качества, пока получилось через ссылки и в аттаче.Начнём с семейства Zynq7000. Как уже писалось выше 1 слайс (поправка именно Slice; в CLB два Slice, но сути это не меняет) используя все 4xLUT6, 2xMUXF и 1xMUXF8 можно превратить в mux 16:1  Когда из кода общего вида делаю синтез для mux с 16 входами, то как раз получается такой «канонический» результат:  Однако когда делаем синтез для 32 входов, картина меняется. Почему-то MUXF8 не задействованы, а лишь MUXF7 (хотя ничто не мешает просто продублировать как было в mux_16_1 два раза и объединить через LUT).  Что ещё более интересно, для 64 входов (mux_64_1) вновь синтезируется корректно, задействует MUX8 (т.е. повторяем четыре раза mux_16_1 и объединяем через LUT):  И кстати вот ещё картинка, где показано, как для mux_64_1 работает атрибут retiming_backward и «задвигает» добавочные регистры пайплана на промежуточную стадию (до входа на конечный LUT):  Для муксов большего размера ситуация повторяется - каждые дополнительные 32 входа MUXF8 то используется, то нет. -------------------------------------------------------------------------------------------------------------------------------------------- В семействе Zynq UltraScale+ ситуация немного другая, чуть хуже. Начнём с того, что тут другая архитектура CLB – два слайса объединены в один и поэтому можно на базе одного CLB делать mux 32:1 (используя примитив MUXF9):  Однако, какого бы размера муксы не пытался синтезировать, ни в одном случае не удавалось задействовать MUXF9. Для примера вот так синтезируется mux_32_1 (нет ни MUXF8, ни MUXF9):  В муксах большего размера ситуация похожая как в семейтсве Zynq7000 (т.е. MUXF8 то используется, то нет). Но MUXF9 увидеть ни разу не удалось. Перепробовал все стратегии синтеза – не помогло. Даже в одном случае для стратегии AlternateRoutability вместо всех MUXF[5-8] использовались LUT (что и соответствует заданной стратегии). Вроде бы должна помочь стратегия AreaOptimized_high, в описании которой присутствует фраза “area optimized mux optimization” - но результаты не менялись. Начал смотреть в документации, как можно заставить использовать эти самые MUXF[7-9]. Вроде нашёл подходящую опцию. В ug901, Глава 3 “Using Block Synthesis Strategies” имеется перечень поддерживаемых Вивадо опций стратегии блочного синтеза. Среди них есть опция: Код MUXF_MAPPING INTEGER 0/1 • 0 – Disable MUXF7/F8/F9 inference • 1 – Enable MUXF7/F8/F9 inference Попробовал использовать – не помогло (убедился, что xdc файл с этой опцией действительно читается и парсится синтезатором). Прогонялось в Vivado 2017.4 Вопрос: может есть идеи, как ещё можно попытаться задействовать эти самые MUXF[8-9] там, где им положено бы быть, не запихивая их туда вручную из кода? Возможно в более новых версиях Вивадо ситуация может быть другая, но пока проверить это имею возможности. По идее есть смысл спросить на форуме Xilinx, но что-то тамошние ответы или отсутствуют, или не шибко-то помогают… P.S.: в аттаче pdf файлы генерируемых схематик, которые легче масштабировать.
Сообщение отредактировал Vengin - Jun 26 2018, 11:51
|
|
|
|
|
Jun 27 2018, 09:57
|
Частый гость
 
Группа: Свой
Сообщений: 180
Регистрация: 17-02-09
Из: Санкт-Петербург
Пользователь №: 45 001

|
Цитата(Vengin @ Jun 26 2018, 14:42)  Однако когда делаем синтез для 32 входов, картина меняется. Почему-то MUXF8 не задействованы, а лишь MUXF7 (хотя ничто не мешает просто продублировать как было в mux_16_1 два раза и объединить через LUT). .. Что ещё более интересно, для 64 входов (mux_64_1) вновь синтезируется корректно, задействует MUX8 (т.е. повторяем четыре раза mux_16_1 и объединяем через LUT): Синтезатор не глуп и придерживается принципа "бритвы оккама". В том случае, в котором Вы сетуете на отсутствие MUX8 всё равно без ЛУТа не обойтись, а значит эти муксы - лишние сущности. Цитата(Vengin @ Jun 26 2018, 14:42)  В муксах большего размера ситуация похожая как в семейтсве Zynq7000 (т.е. MUXF8 то используется, то нет). Но MUXF9 увидеть ни разу не удалось. Здесь может быть всё, что угодно - от синтезатора, который просто "не знает" про MUXF9 или немного не корректно по каким-либо причинам обсчитывает через него времянки, до хардварных с ними проблем, о которых предпочитают не говорить, но дали указание "низя". Может как-нибудь проверю симплифаем, да сейчас лень.. Да и вообще, сознательное использование специфических элементов, типа этих муксов, carry логики или линий связи между дсп накладывает кучу ограничений на описание. Главное из которых, ИМХО, это выносить описание этих элементов в отдельные модули и жёстко контролировать оптимизацию на всех уровнях, чтобы какой-нибудь ресинтез не уничтожил все Ваши труды. К тому же такое низкоуровневое описание имеет смысл в действительно больших и быстрых проектах, а так же с чётким осознанием цели их применения.
|
|
|
|
|
Jun 28 2018, 07:07
|
Частый гость
 
Группа: Свой
Сообщений: 82
Регистрация: 7-02-07
Из: Беларусь, г. Минск
Пользователь №: 25 149

|
Цитата(TRILLER @ Jun 27 2018, 12:57)  Синтезатор не глуп и придерживается принципа "бритвы оккама". В том случае, в котором Вы сетуете на отсутствие MUX8 всё равно без ЛУТа не обойтись, а значит эти муксы - лишние сущности. В этом есть смысл, хотя опять-таки всплывают нюансы. Вот, например, как для mux_32_1 при использовании только MUXF7 происходит добавление pipeline регистров атрибутом retiming_backward:  Как видно, на промежуточной стадии добавляется аж 6 регистров – 4 для сигналов данных и 2 для «задержки» сигнала выбора sel (которые синтезатор вставляет по собственной инициативе, в коде их нет). Если бы первый каскад заканчивался не на четырёх MUXF7, а на двух MUXF8, то промежуточных регистров было бы по идее только 3 (2 для сигнала данных, 1 для sel). Короче чем дальше в лес…
|
|
|
|
|
Jun 28 2018, 07:45
|
Профессионал
    
Группа: Свой
Сообщений: 1 214
Регистрация: 23-12-04
Пользователь №: 1 643

|
Приветствую! Цитата(Vengin @ Jun 28 2018, 10:07)  В этом есть смысл, хотя опять-таки всплывают нюансы. Вот, например, как для mux_32_1 при использовании только MUXF7 происходит добавление pipeline регистров атрибутом retiming_backward: Как видно, на промежуточной стадии добавляется аж 6 регистров – 4 для сигналов данных и 2 для «задержки» сигнала выбора sel (которые синтезатор вставляет по собственной инициативе, в коде их нет). Если бы первый каскад заканчивался не на четырёх MUXF7, а на двух MUXF8, то промежуточных регистров было бы по идее только 3 (2 для сигнала данных, 1 для sel). Короче чем дальше в лес… Экономить на регистрах тут смысла нет - так как критично по ресурсам будет число LUT. Удачи! Rob.
|
|
|
|
Сообщений в этой теме
Vengin Эффективный широкий pipelined mux Jun 24 2018, 05:57 iosifk Цитата(Vengin @ Jun 24 2018, 08:57) т.к. ... Jun 24 2018, 07:00 Vengin Цитата(iosifk @ Jun 24 2018, 10:00) Возьм... Jun 24 2018, 07:34  iosifk Цитата(Vengin @ Jun 24 2018, 10:34) В тео... Jun 24 2018, 07:43   Vengin Цитата(iosifk @ Jun 24 2018, 10:43) Разве... Jun 24 2018, 07:47    RobFPGA Приветствую!
Цитата(iosifk @ Jun 24 2018... Jun 24 2018, 08:19     Vengin Цитата(RobFPGA @ Jun 24 2018, 11:19) Все ... Jun 24 2018, 08:31      RobFPGA Приветствую!
Цитата(Vengin @ Jun 24 2018,... Jun 24 2018, 09:16       Vengin Цитата(RobFPGA @ Jun 24 2018, 12:16) Это ... Jun 24 2018, 10:47        blackfin Цитата(Vengin @ Jun 24 2018, 13:47) На да... Jun 24 2018, 10:53         Vengin Цитата(blackfin @ Jun 24 2018, 13:53) (*r... Jun 24 2018, 11:02        RobFPGA Приветствую!
Цитата(Vengin @ Jun 24 2018,... Jun 24 2018, 11:53         Vengin Цитата(RobFPGA @ Jun 24 2018, 14:53) Для ... Jun 24 2018, 13:18          RobFPGA Приветствую!
Цитата(Vengin @ Jun 24 2018,... Jun 25 2018, 09:54 Vengin Поэкспериментировал немного с атрибутом retiming_b... Jun 25 2018, 13:12 RobFPGA Приветствую!
Цитата(Vengin @ Jun 26 2018,... Jun 26 2018, 12:36   blackfin Цитата(Vengin @ Jun 28 2018, 10:07) Как в... Jun 28 2018, 07:40    Vengin Цитата(RobFPGA @ Jun 28 2018, 10:45) Прив... Jun 28 2018, 07:56     RobFPGA Приветствую!
Цитата(Vengin @ Jun 28 2018... Jun 28 2018, 08:57   TRILLER Цитата(Vengin @ Jun 28 2018, 10:07) Как в... Jun 28 2018, 07:48 blackfin Попробовал сделать на LUT6 4-дерево из MUX 4:1 для... Jun 26 2018, 12:53 Vengin Цитата(blackfin @ Jun 26 2018, 15:53) Поп... Jun 26 2018, 13:09  blackfin Цитата(Vengin @ Jun 26 2018, 16:09) Т.е.,... Jun 26 2018, 13:13 RobFPGA Приветствую!
Цитата(blackfin @ Jun 26 201... Jun 26 2018, 13:50 blackfin Цитата(Vengin @ Jun 24 2018, 08:57) ... м... Jun 28 2018, 08:09 Vengin Цитата(blackfin @ Jun 28 2018, 11:09) На ... Jun 28 2018, 11:43  blackfin Цитата(Vengin @ Jun 28 2018, 14:43) Тогда... Jun 28 2018, 12:00   Vengin Цитата(blackfin @ Jun 28 2018, 15:00) В т... Jun 28 2018, 12:08
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|