увеличение ограничения PPA дизайна

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

Итак, для моего дизайна: я получил максимальную частоту 85 МГц в циклоне 4 FPGA с использованием всего 8500 логических элементов, 55% FPGA.

основная проблема, которая, как я думаю, сделала мой дизайн таким большим, заключается в том, что я написал код в иерархическом порядке, много «elsif» и переменных. и еще одна вещь, которая, я думаю, могла бы быть лучше, это если бы quartus реализовал мой дизайн памяти как память, а не с логическим элементом, даже если это единственный массив из 16 слов по 32 бита. что, по вашему мнению, я могу улучшить?

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;

entity padding is
port(       clk     :   in      std_logic;
            rst     :   in      std_logic;
            ward    :   in      std_logic_vector(31 downto 0);
            ready   :   out     std_logic;
            hash    :   out     std_logic_vector(255 downto 0));
end;

architecture padding of padding is

component sha256 
    port (      clk         :  in   std_logic;
                rst         :  in   std_logic;
                enable      :  in   std_logic;
                ward            :  in   std_logic_vector(31 downto 0);
                k               :  in   std_logic_vector(31 downto 0);
                h0              :  in   std_logic_vector(31 downto 0);
                h1              :  in   std_logic_vector(31 downto 0);
                h2              :  in   std_logic_vector(31 downto 0);
                h3              :  in   std_logic_vector(31 downto 0);
                h4              :  in   std_logic_vector(31 downto 0);
                h5              :  in   std_logic_vector(31 downto 0);
                h6              :  in   std_logic_vector(31 downto 0);
                h7              :  in   std_logic_vector(31 downto 0);
                ready           :  out  std_logic;
                digest      :  out  std_logic_vector(255 downto 0));
end component;

type kconst is array ( 0 to 63 ) of std_logic_vector(31 downto 0);
type mem    is array ( 0 to 15 ) of std_logic_vector(31 downto 0);

signal k                : kconst := (x"428a2f98", x"71374491", x"b5c0fbcf", x"e9b5dba5", x"3956c25b", x"59f111f1", x"923f82a4", x"ab1c5ed5",
                                             x"d807aa98", x"12835b01", x"243185be", x"550c7dc3", x"72be5d74", x"80deb1fe", x"9bdc06a7", x"c19bf174",
                                             x"e49b69c1", x"efbe4786", x"0fc19dc6", x"240ca1cc", x"2de92c6f", x"4a7484aa", x"5cb0a9dc", x"76f988da",
                                             x"983e5152", x"a831c66d", x"b00327c8", x"bf597fc7", x"c6e00bf3", x"d5a79147", x"06ca6351", x"14292967",
                                             x"27b70a85", x"2e1b2138", x"4d2c6dfc", x"53380d13", x"650a7354", x"766a0abb", x"81c2c92e", x"92722c85",
                                             x"a2bfe8a1", x"a81a664b", x"c24b8b70", x"c76c51a3", x"d192e819", x"d6990624", x"f40e3585", x"106aa070",
                                             x"19a4c116", x"1e376c08", x"2748774c", x"34b0bcb5", x"391c0cb3", x"4ed8aa4a", x"5b9cca4f", x"682e6ff3",
                                             x"748f82ee", x"78a5636f", x"84c87814", x"8cc70208", x"90befffa", x"a4506ceb", x"bef9a3f7", x"c67178f2");

signal first_mem        : mem:= (   x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000",
                                    x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000"); 

signal second_mem       : mem:= (   x"00000000", x"00000000", x"00000000", x"00000000", x"80000000", x"00000000", x"00000000", x"00000000",
                                    x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000000", x"00000280");

signal enable           : std_logic;
signal enable1          : std_logic;
signal enable2          : std_logic;
signal r_d              : std_logic;
signal k_in             : std_logic_vector(31 downto 0);
signal ward_in          : std_logic_vector(31 downto 0);
signal ward_in1         : std_logic_vector(31 downto 0);
signal ward_in2         : std_logic_vector(31 downto 0);
signal h0,h1,h2,h3  : std_logic_vector(31 downto 0);
signal h4,h5,h6,h7  : std_logic_vector(31 downto 0);

signal temp             : std_logic_vector(255 downto 0);
signal temp1            : std_logic_vector(255 downto 0);
signal gama0            : std_logic_vector(31 downto 0);
signal gama1            : std_logic_vector(31 downto 0);
signal gama2            : std_logic_vector(31 downto 0);
signal gama3            : std_logic_vector(31 downto 0);
signal gama4            : std_logic_vector(31 downto 0);
signal gama5            : std_logic_vector(31 downto 0);

begin

sha1: sha256 port map(  
                clk         ,
                rst         ,
                enable      ,
                ward_in     ,
                k_in            ,
                h0              ,
                h1              ,
                h2              ,
                h3              ,
                h4              ,
                h5              ,
                h6              ,
                h7              ,
                enable1         ,
                temp            );
sha2: sha256 port map(  
                clk         ,
                rst         ,
                enable1     ,
                ward_in1        ,
                k_in            ,
                temp(255 downto 224),
                temp(223 downto 192),
                temp(191 downto 160),
                temp(159 downto 128),
                temp(127 downto 96 ),
                temp(95 downto 64  ),
                temp(63 downto 32  ),
                temp(31 downto 0   ),
                enable2         ,
                temp1           );
sha3: sha256 port map(  
                clk         ,
                rst         ,
                r_d         ,
                ward_in2        ,
                k_in            ,
                h0              ,
                h1              ,
                h2              ,
                h3              ,
                h4              ,
                h5              ,
                h6              ,
                h7              ,
                ready           ,
                hash            );

h0  <= x"6a09e667";
h1  <= x"bb67ae85";
h2  <= x"3c6ef372";
h3  <= x"a54ff53a";
h4  <= x"510e527f";
h5  <= x"9b05688c";
h6  <= x"1f83d9ab";
h7  <= x"5be0cd19";             

process (clk,rst)
variable i : integer;
variable j : integer;
variable m : integer;
variable n : integer;
variable l : integer;
begin
    if rst = '0' then
        enable      <= '0';
        i := 0;
        j := 0;
        m := 9;
        n := 15;
        l := 8; 
    elsif clk'event and clk = '1' then
        if j = 16 then
           j := 0;
        end if;
        if m = 16 then
           m := 0;
        end if;
        if n = 16 then
           n := 0;
        end if;
        if l = 16 then
           l := 0;
        end if;
        if i  = 193 then
           i := 0;
        elsif i  > 144  then
            first_mem(n) <= gama4 + first_mem(l) + gama5 + first_mem(n);
            ward_in2     <= gama4 + first_mem(l) + gama5 + first_mem(n);
            k_in         <= k(i-129);
        elsif i  > 136 then
            ward_in2           <= first_mem(n);
            k_in             <= k(i-129);  
        elsif i  = 136 then
            first_mem(n)   <= temp1(31 downto 0);
            ward_in2           <= temp1(31 downto 0);
            k_in             <= k(i-129);
        elsif i  = 135 then
            first_mem(n)   <= temp1(63 downto 32);
            ward_in2           <= temp1(63 downto 32);
            k_in             <= k(i-129);
        elsif i  = 134 then
            first_mem(n)   <= temp1(95 downto 64);
            ward_in2           <= temp1(95 downto 64);
            k_in             <= k(i-129);
        elsif i  = 133 then
            first_mem(n)   <= temp1(127 downto 96);
            ward_in2           <= temp1(127 downto 96);
            k_in             <= k(i-129);
        elsif i  = 132 then
            first_mem(n)   <= temp1(159 downto 128);
            ward_in2           <= temp1(159 downto 128);
            k_in             <= k(i-129);
        elsif i  = 131 then
            first_mem(n)   <= temp1(191 downto 160);
            ward_in2           <= temp1(191 downto 160);
            k_in             <= k(i-129);
        elsif i  = 130 then
            first_mem(n)   <= temp1(223 downto 192);
            ward_in2           <= temp1(223 downto 192);
            k_in             <= k(i-129);
        elsif i  = 129 then
            first_mem(15) <= x"00000100";
            first_mem(14) <= x"00000000";
            first_mem(13) <= x"00000000";
            first_mem(12) <= x"00000000";
            first_mem(11) <= x"00000000";
            first_mem(10) <= x"00000000";
            first_mem(9) <= x"00000000";
            first_mem(8) <= x"80000000";
            first_mem(n) <= temp1(255 downto 224);
            ward_in2         <= temp1(255 downto 224);
            k_in             <= k(i-129);
        elsif i  = 128 then 
        elsif i  > 79  then
            second_mem(j) <= gama2 + second_mem(m) + gama3 + second_mem(j);
            ward_in1      <= gama2 + second_mem(m) + gama3 + second_mem(j);
            k_in          <= k(i-64);       
        elsif i  > 63  then
            enable       <= '0';
            ward_in1         <= second_mem(j);
            k_in         <= k(i-64);
        elsif i  > 19  then
            first_mem(j) <= gama0 + first_mem(m) + gama1 + first_mem(j);
            ward_in      <= gama0 + first_mem(m) + gama1 + first_mem(j);
            k_in         <= k(i);
            enable      <= '1';
        elsif i  > 15  then
            second_mem(j)<= ward;
            first_mem(j) <= gama0 + first_mem(m) + gama1 + first_mem(j);
            ward_in      <= gama0 + first_mem(m) + gama1 + first_mem(j);
            k_in         <= k(i);   
            enable      <= '1';
        elsif i  >= 0   then
            first_mem(i) <= ward;
            ward_in      <= ward;
            k_in         <= k(i);
            enable      <= '1';
        end if;
        i := i + 1;
        j := j + 1;
        m := m + 1;
        n := n + 1;
        l := l + 1;
    end if;
end process;

process (clk, rst)
begin
    if rst = '0' then
        r_d <= '0';
    elsif clk'event and clk = '1' then
        r_d <= enable2;
    end if;
end process;

process (clk, rst)
variable f: integer;
variable j: integer;
variable l: integer;
variable m: integer;
begin
    if rst = '0' then
        f := 2;
        j := 15;
        l := 1;
        m := 14;
    elsif clk'event and clk = '1' then
        if j = 16 then
            j := 0;
        end if;
        if f = 16 then
            f := 0;
        end if;
        if l = 16 then
            l := 0;
        end if;
        if m = 16 then
            m := 0;
        end if;
        gama0 <= ((first_mem(f)(6 downto 0) & first_mem(f)(31 downto 7)) xor (first_mem(f)(17 downto 0) & first_mem(f)(31 downto 18)) xor ("000" & first_mem(f)(31 downto 3)));
        gama1 <= ((first_mem(j)(16 downto 0) & first_mem(j)(31 downto 17)) xor (first_mem(j)(18 downto 0) & first_mem(j)(31 downto 19)) xor ("0000000000" & first_mem(j)(31 downto 10)));
        gama4 <= ((first_mem(l)(6 downto 0) & first_mem(l)(31 downto 7)) xor (first_mem(l)(17 downto 0) & first_mem(l)(31 downto 18)) xor ("000" & first_mem(l)(31 downto 3)));
        gama5 <= ((first_mem(m)(16 downto 0) & first_mem(m)(31 downto 17)) xor (first_mem(m)(18 downto 0) & first_mem(m)(31 downto 19)) xor ("0000000000" & first_mem(m)(31 downto 10)));
        gama2 <= ((second_mem(f)(6 downto 0) & second_mem(f)(31 downto 7)) xor (second_mem(f)(17 downto 0) & second_mem(f)(31 downto 18)) xor ("000" & second_mem(f)(31 downto 3)));
        gama3 <= ((second_mem(j)(16 downto 0) & second_mem(j)(31 downto 17)) xor (second_mem(j)(18 downto 0) & second_mem(j)(31 downto 19)) xor ("0000000000" & second_mem(j)(31 downto 10)));
        f := f + 1;
        j := j + 1;
        l := l + 1;
        m := m + 1;
    end if;
end process;

end;

person shmulikm    schedule 04.01.2018    source источник
comment
Во-первых: это не минимально воспроизводимый пример. Нам не хватает многих деклараций. Если вы хотите просмотреть код, вы должны предоставить весь код. Во-вторых, вы используете переменные для тактовых операторов: это плохой дизайн для реализации. Вы должны использовать переменные только для временных вычислений в процессе. Все, что охватывает несколько записей процесса, должно быть сигналом. Ваш дизайн также может быть большим, потому что вы не используете оперативную память: поскольку вы одновременно обращаетесь к нескольким значениям в first_mem, он реализован с использованием регистров. Прочтите руководство по синтезу Altera для получения рекомендаций по кодированию.   -  person JHBonarius    schedule 04.01.2018
comment
Также кажется, что вы используете переменную i как один большой комбинированный конечный автомат и логический селектор, что приводит к странным конструкциям, таким как k_in <= k(i-129);. Не делай этого. Разработайте обычный конечный автомат (примеры Google VHDL fsm) и внутренне используйте отдельный счетчик для выбора времени и элементов.   -  person JHBonarius    schedule 04.01.2018
comment
Большое спасибо за ответ, я редактирую полный код. и я просмотрю свой дизайн и попытаюсь изменить его в соответствии с вашими предложениями.   -  person shmulikm    schedule 04.01.2018
comment
Сожалею, что не могу подробно рассказать о своем отзыве. У меня есть крайний срок, и есть слишком много, чтобы комментировать. Одна вещь об оперативной памяти: если вы хотите использовать оперативную память, вам нужно не только помнить, что у нее есть только 1 или 2 порта, которые вы можете использовать одновременно, но также и то, что для получения данных требуется дополнительный такт. Таким образом, для first_mem(n) <= gama4 + first_mem(l) + gama5 + first_mem(n); вам нужен один такт, чтобы получить first_mem(n), и один тактовый цикл, чтобы записать first_mem(n)... И вам, вероятно, также потребуется 1-2 тактовых цикла, чтобы выполнить сложение. Благодаря продуманному замыслу часто можно предвидеть.   -  person JHBonarius    schedule 04.01.2018
comment
@JHBonarius Проблема с этим вопросом в том, что он субъективен. Вы говорите, что используете переменные для тактовых операторов: это плохой дизайн для реализации. Я не согласен.   -  person Matthew Taylor    schedule 04.01.2018
comment
@JHBonarius я дал ему 16 циклов для чтения, поэтому я не думаю, что в этом должна быть проблема, но я попытался понять, почему он реализует регистрацию, а не оперативную память, в соответствии с тем, что вы сказали, доступ к нескольким значениям в first_mem единственный раз, когда я сделать то, что в я = 129 и что загрузить баран. но он не реализовал second_mem как оперативную память, и я не получил там несколько значений.   -  person shmulikm    schedule 04.01.2018
comment
@MatthewTaylor Я использовал переменную, чтобы упростить написание кода, я надеялся, что это не вызовет проблем, но дизайн очень большой, как бы вы это сделали?   -  person shmulikm    schedule 04.01.2018
comment
@MatthewTaylor Хорошо, это не фиксированная истина. Я сделал свое заявление для общего случая, когда люди с опытом работы с программным обеспечением, плохо знакомым с VHDL, склонны использовать переменные там, где они должны использовать сигналы (в случае синтезируемой логики). Некоторые инструменты синтеза испортятся, если вы это сделаете (по крайней мере, так было в прошлом). Увы, я не могу отредактировать свой комментарий.   -  person JHBonarius    schedule 04.01.2018
comment
P.S. здесь приведен пример настоящей двухпортовой блочной ОЗУ.   -  person JHBonarius    schedule 04.01.2018
comment
@shmulikm Я анализировал ваш код, и если будет очень сложно заставить его использовать блочную ОЗУ: вы просто хотите делать слишком много вещей одновременно. Как я уже сказал: first_mem(n) <= gama4 + first_mem(l) + gama5 + first_mem(n); уже займет 3 такта при конвейерной обработке и использовании блочной ОЗУ. Но тогда второй процесс одновременно обращается к first_mem! Кроме того, код на самом деле написан не так, чтобы его можно было сопровождать, поэтому трудно понять, что происходит везде. Я думаю, вам нужно вернуться к чертежной доске.   -  person JHBonarius    schedule 04.01.2018
comment
@JHBonarius о, теперь я понимаю, что вы имеете в виду, поскольку мне нужны данные одновременно (те же часы), я не могу использовать оперативную память (думаю, мне просто нужно инвестировать в какой-то процесс и регистры в архитектуре конвейера). Ну, прямо сейчас код работает нормально, он просто медленный и большой, поэтому я не буду менять часть памяти и попытаюсь сосредоточиться на части if, я не вижу, как использование fsm улучшит мой дизайн, более того, будет вероятно, такой же размер (?) и рыхлый будет сложным в моем дизайне, нет?   -  person shmulikm    schedule 04.01.2018
comment
@user1155120 user1155120 да, его результат был подтвержден. код делает то, что должен делать. PPA означает мощность, предпочтение и площадь. Я прошу помощи asic, так как я хочу улучшить реализацию моего RTL при подготовке asic-дизайна, FPGA должен быть только прототипом перед чипом.   -  person shmulikm    schedule 05.01.2018


Ответы (1)


elsif, то есть «приоритетное кодирование/декодирование», повлияет на частоту вашего дизайна. Со всеми доступными логическими ресурсами, которые у вас остались, вы можете рассмотреть оператор case... если вам действительно не нужно приоритетное кодирование/декодирование. Даже в этом случае, если вы можете позволить себе компромиссы между задержкой, вы можете выполнять декодирование за несколько тактов (конвейерно декодировать), и ваш дизайн, вероятно, увеличит частоту... в конечном счете, вам нужно запустить отчет о времени и посмотреть на медленные пути. понять узкие места.

Если вы действительно хотите использовать ОЗУ вместо FF, вы можете вывести ОЗУ (создать массив), или, если это не сработает для вас, вы можете вручную создать экземпляр ОЗУ для конкретного устройства... и, затем, конечно, добавить логику управления для него. если примитив, поместите его в черный ящик, чтобы позже заменить его на «тот же» примитив библиотеки ASIC

Что касается «переменных», обсуждение такое же, как «VHDL» против «Verilog» или «синхронный» против «асинхронного» сброса, в основном просто мнения, и мое: «Я не фанат переменных в синтезируемых RTL"... они допустимы для синтеза, но они "исчезают" во время синхронизации, так что, если вы когда-нибудь захотите посмотреть список соединений и сравнить его с вашим RTL, вы сможете вручную отслеживать соединения. Обычно нет веской причины иметь переменные, поскольку они ничего не представляют с точки зрения аппаратного обеспечения и запутывают дизайн по сравнению со списком соединений. Мне нравится видеть логические типы wire/net/regs, чтобы было понятно, что вы создаете в HW. Но, как вам угодно, я просто съеживаюсь, когда вижу их.

В том же духе, с точки зрения массивов, я не большой поклонник "объединения сигналов в массивы"... люди будут утверждать, что это "быстрее" и "легче" иметь дело, но для меня это еще больше запутывает дизайн. Опять же, это не является незаконным, но когда дело доходит до OPC (чужого кода), это может быть очень раздражающим, пытаясь отследить сигналы не только внутри модуля, но и массивы между портами... и затем, если они нарезают эти массивы, или уничтожить их иначе, это становится еще более раздражающим. Что-то вроде этого репортажа :)

В конечном счете, вы можете делать все, что хотите, и особенно в FPGA, некоторые люди склонны уделять меньше внимания деталям того, что будет создано по сравнению с ASIC. Если вы проектируете ASIC, я бы сказал, что вы должны ошибаться в сторону большей педантичности и иметь возможность смотреть на свой RTL и знать, что (в некоторой степени) будет создано, и, таким образом, иметь возможность оценить ворота. считай, если надо. С этой целью я настоятельно рекомендую потратить время на то, чтобы нарисовать ваш проект в программе для рисования (например, Visio), чтобы включить вентили, FF, декодеры, Muxes, FSM, псевдокод, где это уместно, детали ваших часов и деревьев сброса, и все Логика пересечения CDC и т. д., включая имена сигналов. Как только вы это сделаете, вам останется только перевести его в RTL... скорее всего, в качестве бонуса для тех, кто разделяет мое мнение о переменных, вы не найдете никаких переменных на своем рисунке и, следовательно, в своем RTL. :)

person CapnJJ    schedule 12.04.2018
comment
вау это было потрясающе! спасибо, попробую сделать как вы сказали. обычно я вижу в уме RTL до того, как начну программировать, но, поскольку я новичок в больших микросхемах, RTL в моем уме многое упускает. - person shmulikm; 13.04.2018
comment
Что ж, я немного отвлекся, так как это было больше похоже на то, как подходить к дизайну ИС в общих чертах, но я рад, что вы что-то извлекли из этого. - person CapnJJ; 16.04.2018