Jump to content
    

verilog из наглядного i<=i+1 счетчика синтезировать быстрый

12 минут назад, Zig сказал:

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

Сравнение будет всегда с нулем (или единицей), а загружаться счетчик должен вашим *_cntmax-1 (или *_cntmax).

Сравнение с нулем в структуре ПЛИС реализуется на схемах ускоренного переноса и будет работать существенно быстрее многоразрядного компаратора с произвольным значением.

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

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

Share this post


Link to post
Share on other sites

Приветствую!

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

25 minutes ago, Zig said:

Сравнение с нулем в структуре ПЛИС реализуется на схемах ускоренного переноса и будет работать существенно быстрее многоразрядного компаратора с произвольным значением.

Увы это не так.  Для этого достаточно проанализировать когда   на выходе  схемы ускоренного переноса в структуре  счетчика появляется  1.

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

localparam CNT_WH = $clog2(CNT_DIV_MAX);

logic [CNT_WH-1:0] cnt     ;
logic              cnt_last;

always @(posedge clk) begin
  {cnt_last, cnt} <= {cnt_last, cnt} + 1'b1;

  if (rst || cnt_last) begin
    {cnt_last, cnt} <= 2**CNT_WH+1-CNT_DIV;
  end

  // {cnt_last, cnt} <= {cnt_last, cnt} - 1'b1;

  // if (rst || cnt_last) begin
  //   {cnt_last, cnt} <= CNT_DIV-2;
  // end
end

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

16 minutes ago, Zig said:

Если вам нужно считать до разных значений, то для времянок будет лучше сделать счетчик считающий вниз. Сравнение будет всегда с нулем (или единицей), а загружаться счетчик должен вашим *_cntmax-1 (или *_cntmax).

Это имеет смыл именно для упрощения логики,  если нужно считать  для разных начальных значений счетчика меняющихся динамически во время работы.  А внешнему к счетчику компаратору  одинаково с чем сравнивать.  К тому же  сейчас многоразрядные компараторы тоже могут синтезироаться с использованием carry-chain для объединения частичных  результатов с LUT.

Удачи! Rob.

 

Share this post


Link to post
Share on other sites

9 минут назад, Flip-fl0p сказал:

Не уверен, что это всегда так.

Про рекомендацию применения счетчиков вниз читал в старых рекомендациях Xilinx еще на серии Spartan (просто Spartan, без индекса). В них получить 44-х разрядный счетчик на частоте 27 МГц без ухищрений было сложно.

 

Рекомендация к применению вычитающих счетчиков объяснялась примерно так.

Счетчик - это регистр к выходу которого подключен сумматор с единицей. Выход сумматора подключен к входу регистра. При счетчике, с модулем счета не совпадающем со степенью 2, на выход сумматора подключается компаратор, выход которого синхронно сбрасывает регистр. Соответственно у нас в путь задержек от регистра к нему же к слоям логики сумматора добавляются слои логики компаратора, понижая максимальную частоту. Ухищрение: Побороть это можно поставив компаратор на значение на единицу меньше требуемого и задержав результат сравнения (сброса регистра) на такт.

При счете вниз мы получаем следующую схему. Мультиплексор - регистр - вычитатель единицы. На один вход мультиплексора подаем значение модуля счета, на другой - результат вычитателя. Управление мультиплексором - выход схемы ускоренного переноса счетчика. За счет того что ускоренный перенос в ПЛИС Xilinx выполнен по специальным линиям от CLB (Configurable Logic Block) к CLB по колонке задержка по этим линиям получалась меньше, чем в компараторе на LUT.

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

29 минут назад, Flip-fl0p сказал:

И сравнивал две абсолютно одинаковые по поведению схемы.

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

 

Как обстоят дела со схемами ускоренного переноса у Intel не знаю. Нужно изучать структуру ПЛИС.

Share this post


Link to post
Share on other sites

Приветствую!

4 minutes ago, Zig said:

Про рекомендацию применения счетчиков вниз читал в старых рекомендациях Xilinx еще на серии Spartan (просто Spartan, без индекса). В них получить 44-х разрядный счетчик на частоте 27 МГц без ухищрений было сложно.

Рекомендация к применению вычитающих счетчиков объяснялась примерно так. ...

Это вы что то спутали  - что вниз что вверх структура счетчика одинакова.  Даже наоборот -  счет вниз  на самом деле это счет вверх   cnt <= cnt - K  ->  cnt <= cnt + ~K + 1'b1.  Если по каким то причинам   разрешение счета  будет реализовано  как управление K (например К это регистр в вашей логике)  то это потребует разводки  нескольких цепей от источника К к каждому разряду счетчика. А это доп проблемы и задержки. Для счет вверх нужна только 1 цепь к младшему разряду.  

На стары семействах  если скорости не хватает то основная техника ускорения это  pipeline счетчика  - разбиваем счетчик на 2 ( или более)  меньших по разрядности с регистром  в цепи переноса в старшие разряды и  проблем нет. 

Удачи! Rob.

Share this post


Link to post
Share on other sites

3 минуты назад, RobFPGA сказал:

Приветствую!

Это вы что то спутали  - что вниз что вверх структура счетчика одинакова.  Даже наоборот -  счет вниз  на самом деле это счет вверх   cnt <= cnt - K  ->  cnt <= cnt + ~K + 1'b1.  Если по каким то причинам   разрешение счета  будет реализовано  как управление K (например К это регистр в вашей логике)  то это потребует разводки  нескольких цепей от источника К к каждому разряду счетчика. А это доп проблемы и задержки. Для счет вверх нужна только 1 цепь к младшему разряду.  

На стары семействах  если скорости не хватает то основная техника ускорения это  pipeline счетчика  - разбиваем счетчик на 2 ( или более)  меньших по разрядности с регистром  в цепи переноса в старшие разряды и  проблем нет. 

Удачи! Rob.

Ещё может помочь - атрибут подключить сигнал разрешения (сигнал переноса) в явном виде к регистрам. В противном случае синтезатор сигнал разрешения может через lut реализовать.

Share this post


Link to post
Share on other sites

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity Pipeline_Counter is
    generic (
        width_g: natural := 64; -- Must be divisible by parts_g.
        parts_g: natural := 4
    );
    
    port (
        clk:            in std_logic;
        rst_n:          in std_logic;
        
        clear:          in std_logic;
        enable:         in std_logic;
        
        count:          out std_logic_vector(width_g - 1 downto 0);
        tick:           out std_logic
    );
end Pipeline_Counter;

architecture rtl of Pipeline_Counter is
    constant part_width_c:      natural := width_g / parts_g;
    signal almost_tick_r:       std_logic_vector(parts_g - 1 downto 0);
    signal count_r:             std_logic_vector(width_g - 1 downto 0);
begin
    count <= count_r;

    process (clk, rst_n)
        variable part_v:        unsigned(part_width_c - 1 downto 0);
        variable tick_v:        std_logic;
    begin
        if rst_n = '0' then
            count_r <= (others => '0');
            almost_tick_r <= (others => '0');
            tick <= '0';
        elsif rising_edge(clk) then
            
            tick_v := enable;
            for i in 0 to parts_g - 1 loop
                part_v := unsigned(count_r((i + 1) * part_width_c - 1 downto i * part_width_c));
                
                if tick_v = '1' then
                    -- Value is max - 1?
                    if part_v = to_unsigned(2**part_width_c - 2, part_width_c) then
                        almost_tick_r(i) <= '1';
                    else
                        almost_tick_r(i) <= '0';
                    end if;
                
                    part_v := part_v + 1;
                end if;
                
                count_r((i + 1) * part_width_c - 1 downto i * part_width_c) <=
                    std_logic_vector(part_v);
                
                tick_v := tick_v and almost_tick_r(i);
            end loop;
            tick <= tick_v;
            
            if clear = '1' then
                count_r <= (others => '0');
                almost_tick_r <= (others => '0');
                tick <= '0';
            end if;
        end if;
    end process;
end architecture;

 

testbench

 

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb_pipeline_counter is
end entity;

architecture testbench of tb_pipeline_counter is
    signal clk, rst_n:  std_logic;
    signal clear:       std_logic;
    signal enable:      std_logic;
    signal count:       std_logic_vector(15 downto 0);
    signal tick:        std_logic;
begin
    cnt1:       entity work.Pipeline_Counter
        generic map (width_g => 16, parts_g => 4)
        port map (clk, rst_n, clear, enable, count, tick);
    
    process
        procedure clock is
            constant PERIOD: time := 1 us;
        begin
            clk <= '0'; wait for PERIOD/2; clk <= '1'; wait for PERIOD/2;
        end procedure;
    begin
        rst_n <= '0';
        clear <= '0';
        enable <= '0';
        clock;
        rst_n <= '1';
        enable <= '1';
        
        for i in 0 to 65535 loop
            assert unsigned(count) = to_unsigned(i, 16)
                report "Wrong count, " &
                    integer'image(to_integer(unsigned(count))) &
                    " expected " & integer'image(i);
            
            assert tick = '0' report "Unexpected tick";
        
            clock;
        end loop;
        
        assert count = X"0000" report "Expected to roll over";
        assert tick = '1' report "Expected tick";
        
        clock;
        
        assert count = X"0001";
        
        clear <= '1';
        clock;
        clear <= '0';
        
        assert count = X"0000" report "Counter should clear";
        
        clock;
        
        assert count = X"0001";
        
        report "Simulation ended" severity note;
        wait;
    end process;
end architecture;

 

Share this post


Link to post
Share on other sites

В 05.04.2020 в 11:13, AltairNsk сказал:

Добрый день

Осваиваю плисоводство, на плате MaxII EPM570T100, соответственно quartus 13.0sp1 webedition.

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


//input bit_cntmax, word_cntmax

if (bit_cnt < bit_cntmax) bit_cnt <= bit_cnt + 1;
else bit_cnt <= 0;

if (bit_cnt == bit_cntmax) begin
    if (word_cnt < word_cntmax) word_cnt <= word_cnt + 1;
    else word_cnt <= 0;
end

if (bit_cnt == bit_cntmax && word_cnt == word_cntmax) begin
    if (msg_cnt < MSG_LENGTH) msg_cnt <= msg_cnt + 1;
    else msg_cnt <= 0;
end
        
//output sig = f(bit_cnt, word_cnt, msg_cnt)   

Привычно глазам, в симуляторе тикает как надо, но в железе не успевает.

Можно ли как-то объяснить синтезатору, что тут нужно делать именно счетчики, а не регистры+сумматор+компаратор? Или смириться и привыкать вставлять мегафункции?

 

 

Если Вы хотите сделать именно счетчик, то и описывайте его как счетчик.

if we = '1' then cnt <= data;

elsif ce =  '1' then cnt <= cnt + 1;

end if;

А сигналы "we" и "ce" формируйте отдельно. 

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

Если есть возможность не применять мегафункции, не применяйте их. 

Share this post


Link to post
Share on other sites

Итого выкинул из проекта совсем лишнюю логику (лишний счетчик, два десятка регистров). В процессе проверил на железе с осциллографом, исправил грубые ошибки (в симуляторе глядел, но плохо). И методом последовательного тыка настроек компилятора получилось уложить в 100 МГц. Счетчики пока не переделывал.

Share this post


Link to post
Share on other sites

В 05.04.2020 в 11:23, Flip-fl0p сказал:

Хотие быстрый счетчик? 

1. Используйте счетчик, который считает от 0 до 2**n -1, который обнуляется переполнением.


			if (rising_edge(clk)) then      
				cnt <= cnt + "1";   
			end if; 

+1!  И в модели, и в железе работает.

А меня на этом форуме тапками закидали за такой счётчик.

Share this post


Link to post
Share on other sites

33 минуты назад, MrGalaxy сказал:

+1!  И в модели, и в железе работает.

А меня на этом форуме тапками закидали за такой счётчик.

А можно ссылочку. Интересно почитать аргументы других участников форума.

Share this post


Link to post
Share on other sites

14 минут назад, Flip-fl0p сказал:

А можно ссылочку. Интересно почитать аргументы других участников форума.

Я уж и забыл про тот диалог, Вы мне своим постом напомнили. :)

Насилу нашёл, аж самому интересно стало.

https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=155020&app=forums&module=forums&id=155020

Share this post


Link to post
Share on other sites

49 минут назад, MrGalaxy сказал:

Насилу нашёл, аж самому интересно стало.

Там вам всё разжевали.

Ещё раз.

1. Если у вас счётчик -- сигнал/переменная -- вектор, то всё хорошо: он переполняется и из макисмума переходит в ноль.

2. При поведенческом моделировании, если у вас счётчик -- сигнал/переменная -- целый тип с указанным диапазоном, то не всё хорошо: он не переполняется и из макисмума не переходит в ноль. При выходе за границы диапазона генерируется ошибка. Если же симулятор кривой, счётчик продолжит считать дальше.

3. При временно́м моделировании (после синтеза и тем более после P&R), если у вас счётчик -- сигнал/переменная -- целый тип с указанным диапазоном, то от целого ничего не осталось, он превратился в вектор. Тогда см. п. 1.

Share this post


Link to post
Share on other sites

11 minutes ago, andrew_b said:

если у вас счётчик -- сигнал/переменная -- целый тип с указанным диапазоном

А если ещё напрячься и почитать стандарт IEEE Std 1364, то на стр. 32 можно увидеть замечательную фразу:

Quote

An integer is a general-purpose variable used for manipulating quantities that are not regarded as hardware registers.

Так что формально, делать счетчик на integer нельзя.

Share this post


Link to post
Share on other sites

6 минут назад, andrew_b сказал:

3. При временно́м моделировании (после синтеза и тем более после P&R), если у вас счётчик -- сигнал/переменная -- целый тип с указанным диапазоном, то от целого ничего не осталось, он превратился в вектор. Тогда см. п. 1.

Думается мне, если заданный диапазон не кратен степени двойки, счетчик скорее всего обнулится позже, при превышении диапазона созданного вектора. К примеру, если диапазон был 0..12, то обнуление 4-битного вектора будет после значения 15. Т.е. не "всё хорошо".

Share this post


Link to post
Share on other sites

1 час назад, MrGalaxy сказал:

Я уж и забыл про тот диалог, Вы мне своим постом напомнили. :)

Насилу нашёл, аж самому интересно стало.

https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=155020&app=forums&module=forums&id=155020

Тогда понятно. Вам все правильно сказали. Я сам наступал на эти грабли в начале изучения VHDL: 

 

 

Но хочу обратить ваше внимание на строчку  cnt <= cnt + "1"; 

Наша единичка в кавычках заключена не просто так. А потому-что ранее счетчик объявлен как 

signal cnt : unsigned(max_width - 1 downto 0) := (others => '0');

Ибо я ярый поклонник библиотеки IEEE.numeric_std.all; И никаких сторонних библиотек у меня найти невозможно. Ну разве что в исключительных случаях пользую    IEEE.math_real.all

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...