Перейти к содержанию
    

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

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Основное  правило повышения частоты - уменьшение длинны критических путей. Отсюда  не использовать   непосредственно результат сравнения в условиях. А добавлять регистр  как в примере 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.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

 

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

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

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

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

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

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

4 minutes ago, Zig said:

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

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

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

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

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

Удачи! Rob.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 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" формируйте отдельно. 

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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


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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Ещё раз.

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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 нельзя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...