Пишем под FPGA без HDL. Сравнение высокоуровневых средств разработки

    В последнее время производители FPGA и сторонние компании активно развивают методы разработки для FPGA, отличающиеся от привычных подходов использованием высокоуровневых средств разработки.

    Являясь FPGA-разработчиком, в качестве основного инструмента я использую язык описания аппаратуры (HDL) Verilog, но растущая популярность новых методов вызвала у меня большой интерес, поэтому в данной статье я решил сам разобрать что к чему.

    Эта статья — не руководство или инструкция к пользованию, это мой обзор и выводы о том, что могут дать различные высокоуровневые средства разработки FPGA-разработчику или программисту, который хочет окунуться в мир FPGA. Для того, чтобы сравнить самые интересные на мой взгляд средства разработки, я написал несколько тестов и проанализировал полученные результаты. Под катом — что из этого вышло.

    Зачем нужны высокоуровневые средства разработки для FPGA


    • Ускорить разработку проекта
      — за счет переиспользования уже написанного на языках высокого уровня кода;
      — за счет использования всех достоинств языков высокого уровня, при написании кода с нуля;
      — за счет уменьшения времени компиляции и верификации кода.
    • Возможность создавать универсальный код, который будет работать на любом семействе FPGA.
    • Снизить порог вхождения в разработку для FPGA, например уход от понятий “тактовая частота” и других низкоуровневых сущностей. Возможность писать код для FPGA разработчику, не знакомому с HDL.

    Откуда берутся средства высокоуровневой разработки


    Сейчас многих манит идея высокоуровневой разработки. Этим заняты как энтузиасты, такие как, например, Quokka и кодогенератор на Python, так и корпорации, такие как Mathworks, и производители FPGA Intel и Xilinx

    Каждый для достижения цели использует свои методы и инструменты. Энтузиасты в борьбе за идеальный и прекрасный мир используют свои любимые языки разработки, такие как Python или C#. Корпорации, пытаясь угодить клиенту, предлагают свои или адаптируют существующие инструменты. Mathworks предлагают свой инструмент HDL coder для генерации HDL кода из m-скриптов и моделей Simulink, а Intel и Xilinx — компиляторы для распространенного C/C++.

    На данный момент большего успеха достигли обладающие значительными финансовыми и человеческими ресурсами компании, в то время как энтузиасты несколько отстают. Данную статью я посвящу рассмотрению продукта HDL coder от Mathworks и HLS Compiler от Intel.

    А как же Xilinx
    В данной статье я не рассматриваю HLS от Xilinx, по причине разных архитектур и САПР Intel и Xilinx, что делает невозможным провести однозначное сравнение результатов. Но хочу отметить, что Xilinx HLS, как и Intel HLS, предоставляет компилятор C/C++ и концептуально они схожи.

    Начнем сравнения HDL coder от Mathworks и HLS Compiler от Intel, решив несколько задач с использованием разных подходов.

    Сравнение средств высокоуровневой разработки


    Тест первый. “Два умножителя и сумматор”


    Решение данной задачи не имеет практической ценности, но хорошо подходит в качестве первого теста. Функция принимает 4 параметра, перемножает первый со вторым, третий с четвертым и складывает результаты умножения. Ничего сложного, но посмотрим, как с этим справятся наши испытуемые.

    HDL coder от Mathworks


    Для решения такой задачи m-скрипт выглядит следующим образом:

    function [out] = TwoMultAdd(a,b,c,d)
        out = (a*b)+(c*d);
    end
    

    Посмотрим, что предлагает нам Mathworks для конвертации кода в HDL.

    Я не буду подробно рассматривать работу с HDL-coder, остановлюсь только на тех настройках, которые в дальнейшем буду изменять для получения разных результатов в FPGA, и над изменениями которых придется подумать MATLAB-программисту, которому требуется запустить свой код в FPGA.

    Итак, первое, что необходимо сделать, это задать тип и диапазон входных значений. В FPGA нет привычных char, int, float, double. Разрядность числа может быть любой, логично выбирать ее, исходя из диапазона входных значений, который планируется использовать.

    image
    Рисунок 1

    MATLAB проводит проверку типов переменных, их значений и сам подбирает правильные разрядности для шин и регистров, — это действительно удобно. Если проблем с разрядностью и типизацией нет, можно переходить к следующим пунктам.

    image
    Рисунок 2

    В HDL Code Generation есть несколько вкладок, где можно выбрать язык, в который произойдет конвертация (Verilog или VHDL); стиль кода; названия сигналов. Самая интересная вкладка, на мой взгляд, — это Optimization, — с ней я и буду проводить эксперименты, но позже, а пока оставим все значения по умолчанию и посмотрим, что получится у HDL coder “из коробки”.

    Нажимаем кнопку Run и получаем следующий код:

    `timescale 1 ns / 1 ns
    
    module TwoMultAdd_fixpt
              (a,
               b,
               c,
               d,
               out);
    
      input   [7:0] a;  // ufix8
      input   [7:0] b;  // ufix8
      input   [7:0] c;  // ufix8
      input   [7:0] d;  // ufix8
      output  [16:0] out;  // ufix17
    
      wire [15:0] TwoMultAdd_fixpt_mul_temp;  // ufix16
      wire [16:0] TwoMultAdd_fixpt_2;  // ufix17
      wire [15:0] TwoMultAdd_fixpt_mul_temp_1;  // ufix16
      wire [16:0] TwoMultAdd_fixpt_3;  // ufix17
    
    
      //HDL code generation from MATLAB function: TwoMultAdd_fixpt
      //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      //                                                                          %
      //           Generated by MATLAB 9.2 and Fixed-Point Designer 5.4           %
      //                                                                          %
      //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      assign TwoMultAdd_fixpt_mul_temp = a * b;
      assign TwoMultAdd_fixpt_2 = {1'b0, TwoMultAdd_fixpt_mul_temp};
      assign TwoMultAdd_fixpt_mul_temp_1 = c * d;
      assign TwoMultAdd_fixpt_3 = {1'b0, TwoMultAdd_fixpt_mul_temp_1};
      assign out = TwoMultAdd_fixpt_2 + TwoMultAdd_fixpt_3;
    
    endmodule  // TwoMultAdd_fixpt
    

    Выглядит код неплохо. MATLAB понимает, что запись всего выражения в одну строку на Verilog — плохая практика. Создает отдельные wire для умножителя и сумматора, придраться особо не к чему.

    Настораживает, что отсутствует описание регистров. Так получилось потому, что мы и не просили об этом HDL-coder, а все поля в настройках оставили в значениях по умолчанию.

    Вот что из такого кода синтезирует Quartus.


    Рисунок 3

    Никаких проблем, все как и было задумано.

    В FPGA мы реализуем синхронные схемы, и все-таки хотелось бы видеть регистры. HDL-coder предлагает механизм для размещения регистров, но где их разместить — решать разработчику. Мы можем разместить регистры на входе умножителей, на выходе умножителей перед сумматором или на выходе сумматора.

    Для синтезирования примеров я выбрал семейство FPGA Cyclone V, где для реализации арифметических действий используются специальные DSP блоки со встроенными сумматорами и умножителями. Выглядит DSP блок следующим образом:


    Рисунок 4

    DSP блок имеет входные и выходные регистры. Нет необходимости пытаться защелкнуть результаты умножения в регистре до сложения, это только нарушит архитектуру (в определенных случаях такой вариант возможен и даже нужен). Как поступать с входным и выходным регистром решать разработчику, исходя из требований к задержке (latency) и необходимой максимальной частоте. Я принял решение использовать только выходной регистр. Для того, чтобы этот регистр был описан в коде, генерируемом HDL-coder’ом, во вкладке Options в HDL coder необходимо поставить галочку напротив Register output и повторно запустить конвертацию.

    Получается следующий код:

    `timescale 1 ns / 1 ns
    
    module TwoMultAdd_fixpt
              (clk,
               reset,
               clke_ena_i,
               a,
               b,
               c,
               d,
               clke_ena_o,
               out);
    
      input   clk;
      input   reset;
      input   clke_ena_i;
      input   [7:0] a;  // ufix8
      input   [7:0] b;  // ufix8
      input   [7:0] c;  // ufix8
      input   [7:0] d;  // ufix8
      output  clke_ena_o;
      output  [16:0] out;  // ufix17
    
      wire enb;
      wire [16:0] out_1;  // ufix17
      wire [15:0] TwoMultAdd_fixpt_mul_temp;  // ufix16
      wire [16:0] TwoMultAdd_fixpt_2;  // ufix17
      wire [15:0] TwoMultAdd_fixpt_mul_temp_1;  // ufix16
      wire [16:0] TwoMultAdd_fixpt_3;  // ufix17
      reg [16:0] out_2;  // ufix17
    
      //HDL code generation from MATLAB function: TwoMultAdd_fixpt
      //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      //                                                                          %
      //           Generated by MATLAB 9.2 and Fixed-Point Designer 5.4           %
      //                                                                          %
      //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      assign TwoMultAdd_fixpt_mul_temp = a * b;
      assign TwoMultAdd_fixpt_2 = {1'b0, TwoMultAdd_fixpt_mul_temp};
      assign TwoMultAdd_fixpt_mul_temp_1 = c * d;
      assign TwoMultAdd_fixpt_3 = {1'b0, TwoMultAdd_fixpt_mul_temp_1};
      assign out_1 = TwoMultAdd_fixpt_2 + TwoMultAdd_fixpt_3;
    
      assign enb = clke_ena_i;
    
      always @(posedge clk or posedge reset)
        begin : out_reg_process
          if (reset == 1'b1) begin
            out_2 <= 17'b00000000000000000;
          end
          else begin
            if (enb) begin
              out_2 <= out_1;
            end
          end
        end
    
      assign clke_ena_o = clke_ena_i;
      assign out = out_2;
    endmodule  // TwoMultAdd_fixpt
    

    Как видно, в коде есть принципиальные отличия по сравнению с предыдущим вариантом. Появился always-блок, который и является описанием регистра (как раз то, что мы хотели). Для работы always-блока появились также входы модуля clk (тактовая частота) и reset (сброс). Видно, что выход сумматора защелкивается в триггере, описанном в always. Также есть пара сигналов разрешения ena, но они нам не очень интересны.

    Посмотрим на схему, которую теперь синтезирует Quartus.


    Рисунок 5

    И снова результаты хорошие и ожидаемые.

    На рисунке ниже представлена таблица использованных ресурсов — держим ее в уме.


    Рисунок 6

    За это первое задание Mathworks получает зачет. Все не сложно, предсказуемо и с желаемым результатом.

    Я довольно подробно описал простой пример, привел схему DSP-блока и описал возможности применения настройки использования регистров в HDL-coder, отличных от настроек “по умолчанию”. Это сделано не просто так. Этим я хотел подчеркнуть, что даже в таком простом примере при использования HDL-coder знания архитектуры FPGA и основ цифровой схемотехники необходимы, а настройки необходимо изменять осознанно.

    HLS Compiler от Intel


    Давайте попробуем собрать код с той же функциональностью, написанный на С++, и посмотрим, что в итоге синтезируется в FPGA с помощью HLS compiler.

    Итак, код на С++

    component unsigned int TwoMultAdd(unsigned char a, unsigned char b, unsigned char c, unsigned char d)
    {
    	return (a*b)+(c*d);
    }
    

    Типы данных я выбрал так, чтобы избежать переполнения переменных.

    Есть продвинутые методы задания разрядностей, но наша цель — проверить возможность собрать под FPGA написанные в стиле С/С++ функции без внесения изменений, все из коробки.

    Так как HLS compiler — родной инструмент Intel, собираем код специальным компилятором и проверяем результат сразу в Quartus.

    Посмотрим на схему, которую синтезирует Quartus.


    Рисунок 7

    Компилятор создал регистры на входе и выходе, но самая суть скрыта в модуле обертке. Начинаем разворачивать обертку и… видим еще, еще и еще вложенные модули.

    Структура проекта выглядит так.


    Рисунок 8

    Очевиден намек от Intel: “руками не лезть!”. Но мы попробуем, тем более функционал не сложный.

    В недрах дерева проекта |quartus_compile|TwoMultAdd:TwoMultAdd_inst|TwoMultAdd_internal:twomultadd_internal_inst|TwoMultAdd_fu
    nction_wrapper:TwoMultAdd_internal|TwoMultAdd_function:theTwoMultAdd_function|bb_TwoMultAdd_B1_start:
    thebb_TwoMultAdd_B1_start|bb_TwoMultAdd_B1_start_stall_region:thebb_TwoMultAdd_B1_start_stall_region|i
    _sfc_c1_wt_entry_twomultadd_c1_enter_twomultadd:thei_sfc_c1_wt_entry_twomultadd_c1_enter_twomultad
    d_aunroll_x|i_sfc_logic_c1_wt_entry_twomultadd_c1_enter_twomultadd13:thei_sfc_logic_c1_wt_entry_twom
    ultadd_c1_enter_twomultadd13_aunroll_x|Mult1 находится искомый модуль.

    Можем посмотреть на схему искомого модуля, синтезированную Quartus’ом.


    Рисунок 9

    Какие выводы можно сделать из этой схемы.

    Видно, что произошло то, чего мы пытались избежать при работе в MATLAB: синтезирован регистр на выходе умножителя — это не очень хорошо. Из схемы DSP блока (Рисунок 4) видно, что есть только один регистр на его выходе, а значит каждое умножение придется производить в отдельном блоке.

    В таблице использованных ресурсов показано, к чему это приводит.


    Рисунок 10

    Сравним результаты с таблицей HDL coder (Рисунок 6).

    Если с использованием большего числа регистров можно мириться, то расходовать драгоценные DSP блоки на таком простом функционале очень неприятно.

    Но есть огромный плюс в Intel HLS по сравнению с HDL coder. При настройках “по умолчанию” HLS compiler развел в FPGA синхронный дизайн, хоть и израсходовал больше ресурсов. Такая архитектура возможна, видно, что Intel HLS настроен на достижение максимальной производительности, а не на экономию ресурсов.

    Посмотрим как наши испытуемые поведут себя с более сложными проектами.

    Тест второй. “Поэлементное перемножение матриц с суммированием результата”


    Данная функция широко используется в обработке изображений: так называемый, “матричный фильтр”. Реализуем его с использованием высокоуровневых средств.

    HDL coder от Mathwork


    Работа сразу начинается с ограничения. HDL Сoder не может принимать на вход функций 2-D матрицы. С учетом того, что MATLAB — это инструмент работы именно с матрицами, — то это серьезный удар по всему наследованному коду, что может стать серьезной проблемой. Если код пишется с нуля, это неприятная особенность, которую надо учитывать. Так что приходится разворачивать все матрицы в вектор и реализовывать функции с учетом векторов на входе.

    Код для функции в MATLAB выглядит следующим образом

    function [out] = mhls_conv2_manually(target,kernel)
        len = length(kernel);
        mult = target.*kernel;
        summ = sum(mult);
        out = summ/len;
    end
    

    Сгенерированный HDL код получился очень раздутым и содержит сотни строк, поэтому приводить его здесь я не буду. Сразу посмотрим какую схему синтезирует Quartus из этого кода.


    Рисунок 11

    Выглядит данная схема неудачно. Формально она рабочая, но я предполагаю, что работать она будет на очень низкой частоте, и вряд ли ее можно использовать в реальном железе. Но любое предположение должно быть проверено. Для этого разместим регистры на входе и выходе данной схемы и при помощи Timing Analyzer оценим реальное положение дел. Для проведения анализа необходимо указать желаемую рабочую частоту схемы, чтобы Quartus знал, к чему стремиться при разводке, и в случае неудачи предоставил отчеты о нарушениях.

    Задаем частоту 100МГц, посмотрим, что у Quartus получится выжать из предложенной схемы.


    Рисунок 12

    Видно, что получилось немногое: 33 МГц выглядят несерьезно. Задержка в цепочке из умножителей и сумматоров около 30 нс. Чтобы избавиться от этого “бутылочного горлышка”, необходимо использовать конвейер: вставляем регистры после арифметических операций, уменьшая тем самым критический путь.

    HDL coder дает нам такую возможность. Во вкладке Options можно задать Pipeline variables. Так как рассматриваемый код написан в MATLAB стиле, нет возможности конвейеризировать переменные (кроме переменных mult и summ), что нас не устраивает. Необходимо вставить регистры в промежуточные цепи, скрытые в нашем HDL коде.

    Причем ситуация с оптимизацией могла быть и хуже. Например, ничего не мешает нам написать код

    out = (sum(target.*kernel))/len;

    он вполне адекватен для MATLAB, но полностью лишает нас возможности оптимизации HDL.

    Выход следующий — править код руками. Это очень важный момент, так как мы отказываемся от наследования и начинаем переписывать m-скрипт, причем НЕ в MATLAB стиле.

    Новый код выглядит следующим образом

    function [out] = mhls_conv2_manually(target,kernel)
        len = length(kernel);
        mult = target.*kernel;    
        summ_1 = zeros([1,(len/2)]);
        summ_2 = zeros([1,(len/4)]);
        summ_3 = zeros([1,(len/8)]);       
        
        for i=0:1:(len/2)-1
            summ_1(i+1) = (mult(i*2+1)+mult(i*2+2));
        end        
        for i=0:1:(len/4)-1
            summ_2(i+1) = (summ_1(i*2+1)+summ_1(i*2+2));
        end   
        for i=0:1:(len/8)-1
            summ_3(i+1) = (summ_2(i*2+1)+summ_2(i*2+2));
        end    
        out = summ_3/len;
    end
    

    В Quartus собираем сгенерированный HDL Coder’ом код. Видно, что количество слоев с примитивами уменьшилось, и схема выглядит гораздо лучше.


    Рисунок 12

    При правильной компоновке примитивов частота вырастает почти в 3 раза, до 88 МГц.


    Рисунок 13

    Теперь последний штрих: в настройках Optimization указываем summ_1, summ_2 и summ_3 как элементы конвейера. Собираем получившийся код в Quartus. Схема меняется следующим образом:


    Рисунок 14

    Максимальная частота снова увеличивается и теперь ее значение около 195 МГц.


    Рисунок 15

    Столько же ресурсов на кристалле займет такой дизайн? На рисунке 16 показана таблица использованных ресурсов для описанного случая.


    Рисунок 16

    Какие выводы можно сделать после рассмотрения этого примера?

    Главный недостаток HDL coder — использовать MATLAB код в чистом виде вряд ли получится.
    Отсутствует поддержка матриц как входов функции, разводка кода в MATLAB стиле посредственная.

    Главная опасность в отсутствии регистров в сгенерированном без дополнительных настроек коде. Без этих регистров, даже получив формально работающий HDL код без синтаксических ошибок, использование такого кода в современных реалиях и разработках нежелательно.

    Желательно сразу писать код, заточенный под конвертацию в HDL. В таком случае можно получать вполне приемлемые по скорости и ресурсоемкости результаты.

    Если вы MATLAB разработчик, не спешите нажимать кнопку Run и собирать ваш код под FPGA, помните, что ваш код будет синтезирован в реальную схему. =)

    HLS Compiler от Intel


    Для того-же функционала я написал следующий код на С/С++

    
    component unsigned int conv(unsigned char *data, unsigned char *kernel)
    {
        unsigned int mult_res[16];
        unsigned int summl;
    	summl = 0;
    
    	for (int i = 0; i < 16; i++)
    	{	
    		mult_res[i] = data[i] * kernel[i];
    		summl = summl+mult_res[i];
    	}
    	return summl/16;
    }
    

    Первое, что бросается в глаза, это количество использованных ресурсов.


    Рисунок 17

    Из таблицы видно, что использован всего 1 DSP блок, значит что-то пошло не так, и умножения не выполняются параллельно. Также удивляет количество использованных регистров, и задействована даже память, но это оставим на совести HLS compiler.

    Хочется отметить, что HLS compiler развел неоптимальную, использующую огромное количество лишних ресурсов, но все же рабочую схему, которая, по отчетам Quartus, заработает на приемлемой частоте, и такого провала как у HDL coder не будет.


    Рисунок 18

    Попробуем улучшить ситуацию. Что для этого нужно? Правильно, закрыть глаза на наследование и залезть в код, но пока что совсем немного.

    HLS есть специальные директивы для оптимизации кода под FPGA. Вставляем директиву unroll, которая должна развернуть наш цикл в параллель:

    
    #pragma unroll
    	for (int i = 0; i < 16; i++)
    	{	
    		mult_res[i] = data[i] * kernel[i];
    	}
    


    Посмотрим как на это отреагировал Quartus


    Рисунок 19

    Первым делом обращаем внимание на количество DSP блоков — их 16, а значит умножения выполняются параллельно.

    Ура! unroll работает! Но с тем, как сильно выросла утилизация других ресурсов, уже тяжело мириться. Схема стала абсолютно нечитаемой.


    Рисунок 20

    Я полагаю, это произошло из-за того, что никто не указал компилятору на то, что нам вполне подходят вычисления в числах с фиксированной точкой, и он честно на логике и регистрах реализовал всю математику с плавающей точкой. Нужно объяснить компилятору что от него требуется, и для этого мы снова погружаемся в код.

    Для целей использования fixed-point реализованы шаблонные классы.


    Рисунок 21

    Если говорить своими словами, мы можем использовать переменные, разрядность которых задаем вручную с точностью до бита. Для тех, кто пишет на HDL, к этому не привыкать, но программисты С/С++, наверное, схватятся за голову. Разрядности, как в MATLAB, в данном случае никто не подскажет, и считать количество бит должен сам разработчик.

    Посмотрим как это выглядит на практике.

    Редактируем код следующим образом:

    
    component ac_fixed<16,16,false> conv(ac_fixed<8,8,false> *data, ac_fixed<8,8,false> *kernel)
    {
    	ac_fixed<16,16,false>mult_res[16];
        ac_fixed<32,32,false>summl;
    
    	
    	#pragma unroll
    	for (int i = 0; i < 16; i++)
    	{	
    		mult_res[i] = data[i] * kernel[i];
    	}
    	for (int i = 0; i < 16; i++)
    	{
    		summl = summl+mult_res[i];		
    	}
    	return summl/16;
    }
    

    И вместо жутких макарон из рисунка 20 получаем вот такую красоту:


    Рисунок 22

    К сожалению, нечто странное продолжает происходить с использованными ресурсами.


    Рисунок 23

    Но при подробном рассмотрении отчетов видно, что непосредственно интересующий нас модуль выглядит более чем адекватно:


    Рисунок 24

    Огромное потребление регистров и блочной памяти связано с большим количеством периферийных модулей. Глубокий смысл их существования мне пока до конца не понятен, и с этим нужно будет разобраться, но проблема решаема. В крайнем случае можно аккуратно вырезать из общей структуры проекта один интересующий нас модуль, что избавит нас от периферийных модулей, пожирающих ресурсы.

    Тест третий. “Переход из цветового пространства RGB в цветовое пространство HSV”


    Начиная писать эту статью, я не ожидал, что она окажется такой объемной. Но я не могу отказаться от третьего, и последнего в рамках данной статьи, примера.

    Во-первых, это реальный пример из моей практики, и именно из-за него я начал смотреть в сторону высокоуровневых средств разработки.

    Во-вторых, из первых двух примеров мы могли бы сделать предположение, что чем сложнее дизайн, тем хуже высокоуровневые средства справляются с задачей.

    Я же хочу продемонстрировать, что это суждение ошибочно, и на самом деле чем сложнее задача, тем больше проявляются достоинства высокоуровневых средств разработки.

    В прошлом году в работе над одним из проектов меня не устраивала приобретенная на Aliexpress камера, а именно — цвета были недостаточно насыщенными. Один из популярных способов варьировать насыщенность цветов — это перейти из цветового пространства RGB в пространство HSV, где один из параметров и есть насыщенность. Помню, как я открыл формулу перехода и глубоко вздохнул… Реализация подобных вычислений в FPGA не является чем-то экстраординарным, но на написание кода потратить время, конечно придется. Итак, формула перехода из RGB в HSV выглядит следующим образом:


    Рисунок 25

    Реализация такого алгоритма в FPGA займет не дни, но часы, причем все это нужно делать очень аккуратно ввиду специфики HDL, а реализация на C++ или в MATLAB займет, я думаю, минуты.

    На C++ можно написать код прямо в лоб и все равно получить рабочий результат.
    Я написал следующий вариант на С++

    
    struct color_space{
    	unsigned char rh;
    	unsigned char gs;
    	unsigned char bv;
    };
    
    component color_space rgb2hsv(color_space rgb_0)
    {
    	color_space hsv;
    	float h,s,v,r,g,b;
    	float max_col, min_col;
    
    	r = static_cast<float>(rgb_0.rh)/255;
    	g = static_cast<float>(rgb_0.gs)/255;
    	b = static_cast<float>(rgb_0.bv)/255;
    
    	max_col = std::max(std::max(r,g),b);
    	min_col = std::min(std::min(r,g),b);
    	//расчет H
    	if (max_col == min_col)
    		h = 0;
    	else if (max_col==r && g>=b)
    		h = 60*((g-b)/(max_col-min_col));
    	else if (max_col==r && g<b)
    		h = 60*((g-b)/(max_col-min_col))+360;
    	else if (max_col==g)
    		h = 60*((b-r)/(max_col-min_col))+120;
    	else if (max_col==b)
    		h = 60*((r-g)/(max_col-min_col))+240;
    	//расчет S
    	if (max_col == 0)
    		s = 0;
    	else
    	{
    		s = (1-(min_col/max_col))*100;
    	}
    	//расчет V
    	v = max_col*100;
    
    	hsv.rh = static_cast<char>(h);
    	hsv.gs = static_cast<char>(s);
    	hsv.bv = static_cast<char>(v);
    	return hsv;
    }
    

    И Quartus успешно имплементировал полученный результат, что видно из таблицы использованных ресурсов.


    Рисунок 26

    Частота очень неплохая


    Рисунок 27

    С HDL coder все немного сложнее.

    Чтобы не раздувать статью, я не буду приводить m-скрипт для этой задачи, он не должен вызывать сложности. Написанный в лоб m-скрипт вряд ли можно успешно использовать, но если отредактировать код и правильно указать места для конвейеризации, получим рабочий результат. Это, конечно, займет несколько десятков минут, но не часы.

    В C++ тоже желательно расставить директивы и перевести вычисления в фиксированную точку, что также займет не очень много времени.

    А значит, используя высокоуровневые средства разработки, мы экономим время, и чем сложнее алгоритм, тем больше времени экономится — так будет продолжаться, пока мы не упремся в ограничения объема ресурсов FPGA или жесткие ограничения по скорости вычислений, где придется браться за HDL.

    Заключение


    Что можно сказать в заключении.

    Очевидно, золотой молоток еще не изобретен, но есть дополнительные инструменты, которые вполне можно использовать при разработке.

    Главным достоинством высокоуровневых средств, по моему мнению, является скорость разработки. Получить достаточное качество за сроки, порой на порядок меньшие, чем при разработке с использованием HDL, это реальность.

    К таким достоинствам, как использование для FPGA наследованного кода и подключение к разработке для FPGA программистов без предварительной подготовки я отношусь настороженно. Для получения удовлетворительных результатов придется отказаться от многих привычных приемов программирования.

    Еще раз хочу отметить, что данная статья — это лишь поверхностный взгляд на высокоуровневые средства разработки для FPGA.

    В HLS compiler есть большие возможности для оптимизаций: прагмы, специальные библиотеки с оптимизированными функциями, описания интерфейсов, в интернете много статей про “best practices” и т.д. Фишка MATLAB, которая не была рассмотрена, это возможность прямо из GUI сгенерировать, например, фильтр, не написав ни одной строчки кода, просто указав желаемые характеристики, что еще ускоряет время разработки.

    Кто победил в сегодняшнем исследовании? Мое мнение — это Intel HLS compiler. Он генерирует рабочий дизайн даже из не оптимизированного кода. HDL coder без вдумчивого анализа и переработки кода я бы использовать побоялся. Также хочу отметить, что HDL coder достаточно старый инструмент, но как мне известно, он так и не приобрел широкого признания. А вот HLS, хоть и молод, но видно, что производители FPGA делают на него большую ставку, думаю мы увидим его дальнейшее развитие и рост популярности.

    Представители фирмы Xilinx уверяют, что развитие и внедрение высокоуровневых средств — это единственная возможность в будущем вести разработку для все бОльших и бОльших чипов FPGA. Традиционные средства просто не будут справляться с этим, и вероятно, Verilog/VHDL уготована судьба ассемблера, но это в будущем. А сейчас у нас в руках есть инструменты разработки (со своими плюсами и минусами), выбирать которые мы должны исходя из задачи.

    Буду ли я использовать высокоуровневые средства разработки в своей работе? Скорее да, сейчас их развитие идет семимильными шагами, поэтому надо как минимум не отставать, но объективных причин немедленно бросать HDL я не вижу.

    В конце еще раз хочу отметить, что на данном этапе развития высокоуровневых средств проектирования пользователю ни на минуту нельзя забывать, что он пишет не исполняемую в процессоре программу, а создает схему с реальными проводами, триггерами и логическими элементами.
    Share post

    Similar posts

    Comments 24

      +2
      Представители фирмы Xilinx уверяют, что развитие и внедрение высокоуровневых средств — это единственная возможность в будущем вести разработку для все бОльших и бОльших чипов FPGA. Традиционные средства просто не будут справляться с этим, и вероятно, Verilog/VHDL уготована судьба ассемблера, но это в будущем.

      ИМХО это неправда. Не вижу проблем использования HDL языков при создании сложных СФ-блоков. Они достаточно высокоуровневые и вполне читаемые. С наглядностью того же Verilog при соблюдении минимальной культуры написания кода вообще мало какой из современных языков может сравниться.
      А вот настоящую на мой взгляд причину я слышал на какой-то конфе в США. Заключается она в том, что большим парням не нравится то что HDL-разработчиков мало, их приходится хантить и много платить. А разработчиков ПО — много, и с ними проблем нету. Пользуясь тем что FPGA ныне ломовые и особо в оптимизации по ресурсам не нуждаются — им очень нравится мысль всех HDL-разработчиков поувольнять нафиг и посадить писать плиски 3х-копеечных индусов.
      И видимо в конечном итоге так все и будет. HDL останется только в компаниях, занимающихся микроэлектроникой, а со временем уйдет и оттуда.
        0
        Однако, прочитав статью, я для себя понял, что пользоваться вот этим всем нельзя. Ерунда получается. Хотят высокоуровневый язык, чтоб «не думать», но не получается: то тут галочку в настройках нужно поставить то тут снять, потом проверить в неллисте, что получилось, если получилось плохо, то переделывать… Ну и смысл в этом какой?

        Никакой высокоуровневый язык не сможет автоматически поставить регистры в логических функциях так, как это бы сделал инженер. Просто потому, что в чистом языке типа C++ этой информации нет. Компилятору нужны какие-то дополнительные настройки, но лучше бы эти настройки вписывать по месту прямо в код программы (но тогда получится Verilog/VHDL).

        На мой взгляд, с помощью C-подобных высокоуровневых инструментов можно с некоторой осторожностью описывать модули в которых данные двигаются только вперед.
        Собственно в этой статье как раз такой пример функции r=a*b+c*d;

        В принципе, если я правильно понимаю, кажется, что так думают и создатели вот этих инструментов.
        Например, Khonos SPIR compiler:
        Standard Portable Intermediate Representation for Parallel Compute and Graphics.
        SPIR, это такой байткод, который генерируется из OpenCL программы (или из программы пиксельного шейдера для видеокарты). Программа по возможности преобразуется в форму SSA (https://en.wikipedia.org/wiki/Static_single_assignment_form)
        то есть каждая переменная должна использоваться только один раз при движении данных в функции только вперед. Вот тогда каждая SSA переменная и получается регистром в FPGA.

        Я даже как-то думал написать преобразователь SPIR байткода в Verilog. Думаю это не сложная задача.
          +1
          Однако, прочитав статью, я для себя понял, что пользоваться вот этим всем нельзя. Ерунда получается. Хотят высокоуровневый язык, чтоб «не думать», но не получается: то тут галочку в настройках нужно поставить то тут снять, потом проверить в неллисте, что получилось, если получилось плохо, то переделывать… Ну и смысл в этом какой?


          Верификация проще, если доверять синтезу в САПРе, а также использовать стандартный интерфейс (я думаю, все поддерживают AXI), то можно верифицировать модуль на уровне матлаба или С. Что гораздо быстрее и не требует покупки Questasim/VCS. В матлабе, конечно, такая функциональность тоже дорогая (я думаю, все равно не дороже САПР от Cadence/Mentor), а вот в случае других HLS, верификация на С будет сравнительно быстрой и дешевой.

          Никакой высокоуровневый язык не сможет автоматически поставить регистры в логических функциях так, как это бы сделал инженер. Просто потому, что в чистом языке типа C++ этой информации нет. Компилятору нужны какие-то дополнительные настройки, но лучше бы эти настройки вписывать по месту прямо в код программы (но тогда получится Verilog/VHDL).

          На мой взгляд, с помощью C-подобных высокоуровневых инструментов можно с некоторой осторожностью описывать модули в которых данные двигаются только вперед.


          Говорят, что Simulink -> System Generator -> Intel/Xilinx позволяет делать что угодно, причем моделировать можно на уровне симулинка, что опять же попроще, чем в RTL. Разбираться с настройками придется и в САПР для верилога тоже.
            0
            А зачем SPIR нужен применительно к FPGA, если Altera/Xilinx уже давно как нативно умеют в OpenCL?
              0
              Я когда SPIR начинал заниматься, еще не было бесплатного OpenCL для Альтеры. Думал сам сделаю таким образом… Но я честно говоря думаю Альтеровский OpenCL на SPIR и построен. Ибо какой-никакой, но стандарт.
                +1
                В нём уши от HLS по полной торчат. Про SPIR там я не знаю.
            0
            С наглядностью того же Verilog при соблюдении минимальной культуры написания кода вообще мало какой из современных языков может сравниться


            Штоа? В языке без пользовательских типов данных, структур, многомерных массивов в портах, параметризации наличия/отсутствия портов, с бесконечными строками инлайн-вэйверов типа «spyglass disable_block» для затыкания линтеров? Где нельзя понять ни строчки, если ты не сам их вчера написал?
              0
              Где нельзя понять ни строчки, если ты не сам их вчера написал?

              Ой ли. То то я уже лет 5 ковыряю чужие открытые IP и даже правлю в них бажики.
              И как-то до сих пор получалось обходиться без встроенных инструкций и многомерных массивов в портах.
              Каюсь один раз мне помог бы массив — писал параметризуемый коммутатор AXI. Пришлось шины собирать в трубу [NUM*64:0] и внутри блока через Generate раскладывать по интерфейсам.
            0
            В контексте статьи к вопросу о необходимости понимания железа и понимания работы HLS при использовании таких средств интересным может оказаться чужой опыт. По ссылке диссер, где ребята реализовывали нейронную сеть (что-то вроде SqueezeNet) на ПЛИС и пытались использовать для этого HLS, во что воткнулись и как пытались это разруливать (спойлер: не разрулили).
              +1
              Около 4-5 лет назад довольно активно использовал Xilinx HLS, если руку набить, то модули получались по качеству сравнимыми с аналогами, написанными на верилоге, так что инструмент вполне рабочий. Но у него, как и у решения Альтеры есть большой минус — зависимость от вендора ПЛИС, что ограничивает переиспользование кода. Опять же, я не заметил сильной экономии времени в масшабах всей разработки устройства. Да, первая версия IP блока появляется в 3 раза быстрее, но для получения производительности, сравнимой с ручным кодированием на hdl, нужно было потратить сравнимое время.

              Есть хорошие инструменты, которые должны работать с разными целевыми архитектурами — такие как маршрут Simulink-System Generator-Intel/Xilinx, а также платформо-независимые среды Mentor Catapult или Cadence Stratus, но их использование пока ограничивается еще и стоимостью.
                0
                Лет пять назад пытался в матлабе синтезировать какую-то нейронную сеть (какую не помню, чисто из интереса), сначала в Си — заработало. Потом тнкнул галочку Verilog, Matlab задумался и сказал что не может синтезировать блоки экспоненты (по сути ядро нейрона).
                Возможно, там надо было поиграться с настройками и тд и тп., но у меня такой задачи небыло. Потом потыкал еще примеры от матлаб, они конечно синтезировались, но без подстроек выглядили довольно топорно.
                Может с той поры что-то изменилось, но как и у автора сложилось впечатление что в деле создания хайлевел инструментов для ПЛИС, еще работать и работать.
                  0
                  с экспонентой лучше поступить так:
                  1. всё равно в плавающей точке на фпга вычислять очень расточительно поэтому делают квантизацию например в 16 бит и вычисляют в фикс точке, где реально экспонента будет изменяться в 8-10 битах.
                  2. заранее рассчитывают табличку экспоненты и в ПЛИС размещают ROM-Lookup table трансляцию X в Y согласно предварительно рассчитанной табличке экспоненты получая результат
                    0
                    Вот тоже встречаю варианты для ПЛИС с табуляцией функций, в т.ч. сигмоида, только вместо плавающей точки всё чаще попадаются с числами с фиксированной точкой.
                      0
                      Спасибо за совет, если еще раз столкнусь с таким буду знать куда копать.
                      В своем проекте, который тогда реализовывал на плис, я и вовсе переделал весь конвеер в целочисленные вычисления получилось очень быстро и дешево (по ресурсам). А так преобразовать всю математику, где можно, в фиксируемую точку перед проектировании самой схемы, это обязательный шаг.
                      Только в одном месте, где нужно было делить два числа, решил заюзать плавающую точку, благо ресурсов было много и это был последний этап.
                    0

                    ИМХО автор неправильно подошел к определению высокоуровнего средства разработки под FPGA. FPGA — это логика, сигналы, схема, только зашитые в один чип. А схемы лучше не писать, их нужно рисовать. Описывая схему в C или m-коде — вы просто пытаетесь преобразовать один текст в другой. Причем ни C ни m-код никак не лучше не приспособлены для FPGA, чем VHDL/Verilog. Вариант автоматической генерации кода из m-скриптов или C был просто придуман для того, чтобы разработчики могли хоть что-то из своего запихивать в FPGA, но для новых проектов это костыли.
                    А вот HDL Coder/DSP System Generator в связке с Simulink дает гораздо более лучшие результаты, чем m-скрипт или C. Мало того, там все можно промоделировать и проверить работу алгоритма, не генеря код. Потому что умножитель там — это блок. Сигналы — это линии между блоками. Задержка — это задержка, регистр — регистр и т.д.
                    Мы уже почти 10 лет используем Simulink для программирования FPGA и код получается > достаточно эффективный при гораздо более высокой скорости разработки.


                    Этим я хотел подчеркнуть, что даже в таком простом примере при использования HDL-coder знания архитектуры FPGA и основ цифровой схемотехники необходимы

                    Тут вы правы, единственное, что подчеркну — в указанной фразе нет слов VHDL или Verilog. Т.е. знание архитектуры FPGA, основ цифровой схемотехники не означает необходимость изучения этих языков. И это важно, так как указанные вещи легко изучит любой электронщик, или схемотехник. И начнет программировать FPGA хорошо. А вот наоборот не всегда получается.

                      0
                      спасибо за интересный каммент, плюсую и хочу воспользоваться как поводом развязать дискуссию:
                      Затравка: графика конечно хорошо, и я сам бы рад был бы, получить отличный инструмент. Но есть пара нюансов, хотелось бы выяснить решены ли они:
                      1. Наглядность: исходный код более нагляден чем схемы. Схемы распологаются в 2д и содержат стопятьсот переходов и именованных шин куда попало, а код линеен. И опять же таки оформление в 2д на порядок сложнее чем текста, степеней свободы больше и поэтому будешь тратить просто уйму времени на именно оформление и редко когда получается хотябы терпимо, обычно это просто «блевотина из макарон»

                      2. Удобность: почти в любой среде разработки можно подсветить переменную или имя блока или тип блока и он подсветит, сделает анализ: сколько где и как используется, отрефакторит, переименует и тд. Это очень важно: «код который не нужно изменять — мёртвый код».

                      3. Отслеживание изменений: Как понять что было изменено без сравнения фото? И как понять какие были изменения? в случае 2д схемы можно чуток пораздвигать для наглядности и вроде бы не изменилось по факту (да да а бывает вроде бы казалось бы не изменилось да вот подсоединилось и во совсем не заметно при беглом просмотре тех же эл.принципиальных схем-что бесит)

                      4. Контроль целостности: в 2д схемах невозможно всё впихнуть на лист и часто доп параметры скрыты в менюшках и тд, ладно если ты делаешь часики, а если нейрочип на пару тыщь дсп блоков и 2 миллиона регистров это критично, стандартный кейс — возвращаешься к своему коду через год, в побочной ветке для особого заказчика и попробуй найти что именно для него было сделано кастомное.

                      5. Контроль сборки: во всех средах есть текстовые файлы того как куда чего будет расположено и сконфигурировано, их удобнее править, чем например тыкать в стопятьсот опций одного только пина, а пинов например тыщи… в тексте можно задать все свойства сразу группам по маске или регэкспу.

                      6. Командная работа: с текстом работать проще, всё равно все интерфейсы выраждаются в виде списков бит регистров параметров и тд. Камменты тоже это текст, описание действий тоже текст и тд — всё текст!

                      7. метапрограммирование: как делать шаблоны функций? как сделать поведенческое описание функции: задав только входы и выходы в зависимости от состояний и с условиями перехода между состояниями? Как сделать универсальный генератор например тысячи дсп блоков произвольной ширины и конфигурации? (например известно что некоторый дсп умножают биты шириной 3х99 а другие 15х7 а третьи 3х3 и их лучше в лутах реализовать и тд) Про структуры полностью определяемые входными данными я вообще молчу.

                      8. Как переносить текстовый код от других отделов, например математиков-теоритиков: они дадут опять таки текстовый код от питона или си или какого нибудь R. А верилог например легко реализуется на си и обратно, нужно лишь чуток механически переписать синтаксис.

                      9. совместимость с другими утилитами: они все на вход принимают именно текст, да тот же гитхаб, конверторы кода, и тд

                      И самое главное, последний гвоздь преткновения в крышку скепсиса:
                      10. Симуляция: я спокойно могу просимулировать всю структуру нейрочипа в сборе со скоростью миллиард умножений в секунду получив потактовую точность симуляции и срезы сигналов в нужных точках и времени. Одновременно прогнав весь валидационный датасет за адекватное время гарантировав что у меня всё работает во всех случаях побитово точно с моделю что мне дали — это единственный гарантированный способ понять ты завершил работу или нет.

                      Как ВСЁ это сделать графически?
                      (извиняюсь за резкую критику но разработка под FPGA это инженерная индустрия для больших команд, больших наборов данных и пока не решить хотя-бы большую часть этих вопросов смысла в граф редакторах я не вижу, но я рад новым знаниям и с удовольствием попробую что либо новое. Я был рад пользоваться SoC билдером от альтеры ровно до тех пор пока не разболелись глаза от пиксель хантинга (слишком много попытались впихнуть в маленький экран) и открытия стопятьсот окошек.)
                        0
                        Воспользуюсь возможностью присоединится к дискуссии).
                        По поводу 2D схем, тут не всё так однозначно: предыдущий автор указывает на высокоуровневые схемы, где целые вычислительные компоненты, фильтры и прочее, они на порядки проще для понимания, чем исходный код. Что более важно, они понятны специалистам по цифровой обработке сигналов, математикам и прочим предметным специалистам, а то, что им не надо будет погружаться в VHDL/Verilog, имхо, большой плюс. В то же время, код гораздо проще, чем копание в низкоуровневых схемах RTL.
                        По поводу средств рефакторинга, здесь всё не так уж и радужно, например в Xilinx Vivado интелисенс появился только в версии года где-то 2017.
                        Имхо, графические инструменты нужны не для замены текстовых, а для их гармоничного дополнения. В Xilinx есть тулза IP Integrator, в ней можно выбирать IP-ядра из библиотеки ядер, и мышкой расставлять на экране, а затем соединять шинами, добавлять клоки, сбросы, конфигурировать адреса. Большинство ядер поддерживают интерфейс AXI. В результате, за пару кликов мыши можно сгенерить вполне себе рабочую инфраструктуру и не писать однотипный инфраструктурный код (boilerplate code), т.е. время разработки можно сократить существенно и не переизобретать велосипед в стандартных вещах, а заниматься написанием своих основных ядер, кастомной логики, которые затем легко в эту инфраструктуру интегрируются.
                          0
                          Что более важно, они понятны специалистам по цифровой обработке сигналов, математикам и прочим предметным специалистам, а то, что им не надо будет погружаться в VHDL/Verilog, имхо, большой плюс.

                          как дополнительное средство для общения с разработчиками математики и алгоритмов если его использовать полностью согласен очень хорошая штука и лучше чем голый верилог. Но надо знать меру.

                          В то же время, код гораздо проще, чем копание в низкоуровневых схемах RTL.

                          Только вот в них приходится копаться чтоб обеспечить верифицируемость, а в нашей фирме единственный способ доказать что реализация соответствует теоретической модели — полное побитовое совпадение выхода с эталонной моделью и соблюдение ТЗ — т.е. достаточно быстрая реализация, а этого добиться можно только настройкой глубже чем визуальная схема. Простейший пример: часто бывают узкие места, например один вид памяти требует 100 тактов для начала передачи пакета, а другой вид памяти например 101 тактов, при их взаимном соединении можно получить 100100 тактов ожидания, вот тут и приходится погружаться в дебри настройки визуальной среды а иногда переписывать это на верилоге
                          (правда сейчас визуальные среды разработки понимают смысл поточности и не требуют каждый раз декодировать адрес если пакеты идут одни за другим — получше стало, да и согласование не такое грубое делают — выходит вместо 100100 тактов всего 102 например, но раньше, лет 10 назад был ужас-ужас, падение скорости было порой просто адовое).

                          А насчёт совместимости с текстом: раньше когда я начинал осваивать ООП в 90ых ещё до делфи был визуальный редактор для TurboVision, вот там можно было контролы и диаграммы выделить, скопировать, вставить в код и поправить и обратно в визуальный редактор диаграм и потоков данных вставить уже с правками, потом они эту систему купили и применили в делфи — было очень удобно и наглядно, интересно, есть ли подобное сейчас, а то, то что я видел получается слабо читаемым (например на XML)
                          0
                          а код линеен

                          Без ветвлений что ли?

                          Вот насчет версионирования да, проблемы вырастают и при сравнениях (diff) и при совместной разработке.
                            0
                            нет, я про то что ветвления если и есть, то они либо снизу, либо сверху по тексту,
                            но не как где то там далеко-придалеко за краем экрана справа притаилась шина мелким шрифтом среди полсотни других имён и шин.
                              0
                              Но шина (графическая связь) всё же нагляднее, чем флаг, который выставляется в одном месте кода, сбрасывается где-то в другом, а читается еще в нескольких.

                              «далеко-далеко мелким шрифтом» — это, в принципе, одинаковая проблема что для диаграмм что для кода, и решается одинаково — правильной декомпозицией по уровням детализации. Код разбивают на модули, классы, функции — так и схема тоже разбивается на блоки и субблоки.
                                0
                                1. но ошибок, недоглядок и упущений при использовании 2д схем с высоким уровнем детализации всё таки существенно выше, т.е. это хороший показатель что гораздо больше людей не замечают что либо где нибудь. А детализацию не всегда можно понизить успешно — порой смысла нет от десятка блоков соединённых полусотней сигналов каждый. Порой доходит до смешного: например есть УправляющийАвтомат и сотня АЛУ узкой специализации каждый, все сигналы в итоге описываются документом и в конце концов все на диаграмму соеденений УА и АЛУ вообще забивают и начинается правка именно текстового перечня под тыщу страниц с детальным описанием сигналов в зависимости от специализации АЛУ.
                                Это был пример неудачной практики который я застал. Мне интересно было бы взглянуть на удачный пример.

                                2. Порой необходимо как то изобразить перечисления например от M экземпляров или, в зависимости от N каналов или иных факторов, как это поудачнее сделать чем например у меня получилось примерно так (слева дерево сумматоров, справа массив умножителей с накопителями — всё обычная линейная алгебра 1д и 2д КИХи но параметризируемые по количеству и ширине шины):
                                prntscr.com/o8vnxo
                                вышло вообще криво, интересно как правильно изобразить подобное, в научных статьях тоже особо не выходит хорошо отобразить подобное.
                            0

                            Ой, вы как-то сильно много вопросов затронули в одном комментарии, боюсь можем потерять ветвь дискуссии очень быстро. Замечу, что я работаю в домене систем управления на основе ПЛИС в основном для силовой электроники, поэтому может быть буду некомпетентен по некоторым вопросам.


                            1. Наглядность: исходный код более нагляден чем схемы. Схемы распологаются в 2д и содержат стопятьсот переходов и именованных шин куда попало, а код линеен. И опять же таки оформление в 2д на порядок сложнее чем текста, степеней свободы больше и поэтому будешь тратить просто уйму времени на именно оформление и редко когда получается хотябы терпимо, обычно это просто «блевотина из макарон»

                            Говнокод можно сделать и в коде и в 2D легко. Схемы также легко ложатся на иерархическую архитектуру, поэтому красиво оформить можно и в 2D. Еще раз хотелось бы отметить — так как из-за особенностей ПЛИС сигналы и блоки в схемах также превращаются в физические сигналы и блоки в ПЛИС, то схема очень легко читается. Ограничение по степеням свободы, наверное, на руку, так как труднее сделать неправильно.
                            Что еще интересно — тот же Matlab предлагает специальные инструменты, например, для рисования автоматов состояний. Это вещь, я скажу, потому, что никакой HDL код не будет нагляднее, чем диаграмма в Stateflow.


                            1. Удобность: почти в любой среде разработки можно подсветить переменную или имя блока или тип блока и он подсветит, сделает анализ: сколько где и как используется, отрефакторит, переименует и тд. Это очень важно: «код который не нужно изменять — мёртвый код».

                            Это есть и в графических средах и достаточно давно.


                            1. Отслеживание изменений: Как понять что было изменено без сравнения фото? И как понять какие были изменения? в случае 2д схемы можно чуток пораздвигать для наглядности и вроде бы не изменилось по факту (да да а бывает вроде бы казалось бы не изменилось да вот подсоединилось и во совсем не заметно при беглом просмотре тех же эл.принципиальных схем-что бесит)

                            Это также есть, я где-то приводил комментарий на Хабре, как это работает. Да, наверное хуже текста, но оно очень быстро совершенствуется.


                            1. Контроль сборки: во всех средах есть текстовые файлы того как куда чего будет расположено и сконфигурировано, их удобнее править, чем например тыкать в стопятьсот опций одного только пина, а пинов например тыщи… в тексте можно задать все свойства сразу группам по маске или регэкспу.

                            Тут надо понимать — контроль сборки и исполняемые алгоритмы для меня это разные вещи. Конечно контроль сборки можно и нужно делать в тексте. Но во многих случаях вы настроите все для проекта один раз и будете возвращаться к этому этапу очень редко. На это будет тратиться мало времени.


                            1. Командная работа: с текстом работать проще, всё равно все интерфейсы выраждаются в виде списков бит регистров параметров и тд. Камменты тоже это текст, описание действий тоже текст и тд — всё текст!

                            Я уже написал выше: в ПЛИС интерфейс — это сигналы, а сигналы — это линии и стрелки, описание действий — это вентили и регистры. Эти вещи научились рисовать задолго до текста.


                            1. метапрограммирование: как делать шаблоны функций? как сделать поведенческое описание функции: задав только входы и выходы в зависимости от состояний и с условиями перехода между состояниями? Как сделать универсальный генератор например тысячи дсп блоков произвольной ширины и конфигурации? (например известно что некоторый дсп умножают биты шириной 3х99 а другие 15х7 а третьи 3х3 и их лучше в лутах реализовать и тд) Про структуры полностью определяемые входными данными я вообще молчу.

                            Помоему вы описываете IP Core Generatorы. Т.е. если вам нужен умножитель 3х99, задаете это в параметрах блока и он автоматически синтезируется. Мало того, в большинстве случаев достаточно задать типы входных сигналов, например Ufix15_7(15 бит, 7 бит после запятой) и Ufix 31_7 и генератор кода автоматически сделает нужный умножитель из DSP блоков или LUTов, как вы захотите, выбрав флажек. Может я не правильно понял, но тут нет проблемы вообще.


                            1. Как переносить текстовый код от других отделов, например математиков-теоритиков: они дадут опять таки текстовый код от питона или си или какого нибудь R. А верилог например легко реализуется на си и обратно, нужно лишь чуток механически переписать синтаксис.

                            Не знаю, как у вас, но мои математики работают в Matlab/Simulink, так как моделирование. Они свои алгоритмы там месяцами гоняют прежде чем отдать на имплементацию, так как стоимость ошибки возрастает на порядки. Поэтому я получаю на входе модель с тестовыми кейсами, на которой я могу проверить все, что угодно без переписывания и создания новых багов.


                            1. совместимость с другими утилитами: они все на вход принимают именно текст, да тот же гитхаб, конверторы кода, и тд

                            Не встречался с данными проблемами. Используем git. Matlab уже давно перешел на Xml.


                            1. Симуляция

                            Эээ, Матлаб вообще-то на этом уже лет 20 как специализируется. Преимущество там — наличие кучи тулбоксов для нужного физического домена — например мы используем Simscape/Power Systems, чтобы проверять наши PLL и алгоритмы защиты. Для нейросетей там тоже точно что-то есть.
                            Верифицируемость — это именно наш конек тоже, мы моделируем побитно и поциклово без проблем, причем еще до того, как сгенерируется код. У людей возникает скепсис поначалу, как же ж так без Modelsimа и в продакшн, но когда они сами убеждаются, что код 100% покрыт тестами и работает и в симуляции и в железе, то быстро начинают доверять.


                            но разработка под FPGA это инженерная индустрия для больших команд, больших наборов данных

                            У меня три человека в отделе лабают на Virtex-7 легко. По PCIe там что-то в районе 8Гб/с трафика летает в реальном времени. Без модельно-ориентированного подхода мне надо было бы, наверное, человек 20 и раз в 5 больше времени на разработку.


                            PS интересно, почему shvlad — автор поста не подключился ни к одной из дискуссий?

                              +1
                              Плюсую, сам до поры рисовал все в графике.
                              Но потом пришла необходимость максимально быстро найти найменьшее число во входном векторе на 128 значений. Решается задачка вообщем-то просто, строим пирамидку из компараторов и получаем результат всего за 7 (+2) тактов.
                              Но вот после рисований этой пирамидки и правки кучи опечаток с номерами шин, я понял что пора окончательно переходить в Verilog/VHDL где тоже самое делается в пару десят строчек c generate.
                              Графику использую только на верхнем уровнем типа: Video In Module — CPU Module — Screen Module с минимумом линий.

                          Only users with full accounts can post comments. Log in, please.