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

Cyclone III, констрейны

Доброго дня всем!

 

Есть модуль ПИ-регулятора, проект собирается на cyclone III, таймквест ругается, выдает слаки.

 

Код:

module pid
(
    input     clk,
    input     use_pid,                //Если 1 - включается ПИД-регулятор, иначе выдаем pwm_cur = pwm_fixed
    input     [24:0] pwm_fixed,        //Фиксированное значение ШИМ (используется при use_pid=0)
    input     start_pulse,            //Импульс начала вычислений
    input     [11:0] ADC_REF,            //АЦП уставки, max:  4096 (12 bit)
    input     [11:0] ADC_FAKT,        //АЦП фактическое, max:  4096 (12 bit)
    input     [15:0] Ki,                //Интегральный коэффициент
    input     [15:0] Kp,                //Пропорциональный коэффициент
    input     [24:0] pwm_min,            //Минимальная граница ШИМ
    input     [24:0] pwm_max,            //Макчимальная граница ШИМ    
    
    output     reg [24:0] pwm_cur,        //Выходное значение ШИМ
    output     reg is_pwm_max,            //Если 1 - то ШИМ максимальный
    output     end_pulse                //Импульс окончания вычислений
);
    reg signed [25:0] I_reg;
    reg signed [26:0] F_reg;
    reg signed [24:0] I_prev_reg;
    reg [24:0] pwm_fixed_reg;
    reg [1:0] F_limits_reg;
    reg [1:0] I_limits_reg;
    reg [11:0] adc_ref_reg;
    reg [11:0] adc_fakt_reg;
    reg signed [12:0] err_reg;
    reg [9:0] Kp_reg;
    reg [9:0] Ki_reg;
    
    initial
    begin
        I_prev_reg = 25'd1000;
        is_pwm_max = 0;
        pwm_cur = 0;
    end
    
    
    wire [1:0] w_F_limits;
    wire [1:0] w_I_limits;
    wire signed [25:0] w_I;
    wire signed [23:0] w_P;
    wire signed [26:0] w_F;
    wire signed [12:0] w_err;
    wire w_start_pulse2;
    wire w_start_pulse3;
    wire w_start_pulse4;
    wire w_start_pulse5;    
    wire w_start_pulse6;
    //---------------------------------
    // PID-calculating
    //---------------------------------
    assign w_err =$signed({1'b0, ADC_REF}) - $signed({1'b0, ADC_FAKT});        //max: 13 bit
    assign w_I = use_pid? ($signed({1'b0, Ki_reg}) * err_reg + I_prev_reg) : ($signed({1'b0, pwm_fixed_reg}));    //max: (10+1) * 13 + 25 = 26 bit
    assign w_P = $signed({1'b0, Kp_reg}) * err_reg;                            //max: (10+1) * 13 = 24 bit
    assign w_F = use_pid? (w_I + w_P) : ($signed({1'b0, pwm_fixed_reg}));        //max: 26 + 24 = 27 bit;
    assign w_F_limits[0] = (w_F < $signed({1'b0, pwm_min}))? 1'b1 : 1'b0;
    assign w_F_limits[1] = (w_F > $signed({1'b0, pwm_max}))? 1'b1 : 1'b0;
    assign w_I_limits[0] = (w_I < $signed({1'b0, pwm_min}))? 1'b1 : 1'b0;
    assign w_I_limits[1] = (w_I > $signed({1'b0, pwm_max}))? 1'b1 : 1'b0;

    //============================================
    delay            delay1(.in(start_pulse),
                        .clk(clk),
                        .out(w_start_pulse2));
    delay            delay2(.in(w_start_pulse2),
                        .clk(clk),
                        .out(w_start_pulse3));
    delay            delay3(.in(w_start_pulse3),
                        .clk(clk),
                        .out(w_start_pulse4));
    delay            delay4(.in(w_start_pulse4),
                        .clk(clk),
                        .out(w_start_pulse5));        
    delay            delay5(.in(w_start_pulse5),
                        .clk(clk),
                        .out(w_start_pulse6));                            
    delay            delay6(.in(w_start_pulse6),
                        .clk(clk),
                        .out(end_pulse));                            
    
    //============================================
    always @ (posedge clk)
    begin
        //========================================
        // Начало вычислений, захватываем входные данные
        //========================================
        if (start_pulse)
            begin
                Ki_reg = Ki[9:0];
                Kp_reg = Kp[9:0];
                err_reg <= w_err;
                pwm_fixed_reg <= pwm_fixed;
            end
        //========================================
        // Даем 3 такта на вычисления, помещаем результат в регистры
        //========================================    
        else if (w_start_pulse4)
        begin
            I_reg <= w_I;
            F_reg <= w_F;
            I_limits_reg <= w_I_limits;
            F_limits_reg <= w_F_limits;
        end
        //========================================
        // Выдаем результат, учитывая ограничения
        //========================================
        else if (w_start_pulse5)        
        begin
            //Ограничиваем выходное воздействие
            case (F_limits_reg)
                2'b01:        
                    begin 
                        pwm_cur <= pwm_min;
                        is_pwm_max <= 0;
                    end
                2'b10:         
                    begin
                        pwm_cur <= pwm_max;
                        is_pwm_max <= 1;
                    end
                default:    
                    begin
                        pwm_cur <= F_reg[24:0];
                        is_pwm_max <= 0;
                    end                        
            endcase
            //Ограничиваем интегральную составляющую
            case (I_limits_reg)
                2'b01:        I_prev_reg <= pwm_min;
                2'b10:         I_prev_reg <= pwm_max;
                default:    I_prev_reg <= I_reg[24:0];
            endcase
        end        
    end
    //============================================
    
endmodule

 

Модуль delay:

module delay
#(parameter WIDTH = 1)
(
    input    [WIDTH-1:0] in, 
    input    clk,
    output     reg [WIDTH-1:0] out
);

always @(posedge clk)
   out <= in;

endmodule

 

Идея такова: в момент прихода стартового импульса (start_pulse) происходит захват входных значений в регистры, цепочка из элементов delay производит задержку входного сигнала на 6 тактов для того, чтобы вся эта "колбаса" успела посчитаться к моменту выдачи импульса завершения (end_pulse).

Таймквест выдает ошибки:

errors.png

Но регистр err_reg захватывается на 3 такта раньше чем производится вычисление F_reg, неужели 3 такта недостаточно для умножения?

Файл констрейнов:

derive_clock_uncertainty
create_clock -name clk -period 36.1MHz [get_ports {clk}]
create_generated_clock -name clk_216MHz -source [get_ports {clk}] -multiply_by 6 [get_nets {pll_ena_inst1|pll1|altpll_component|auto_generated|wire_pll1_clk[0]}]
create_generated_clock -name clk_adc -source [get_ports {clk}] -divide_by 17 -multiply_by 15 [get_nets {pll_ena_inst1|pll1|altpll_component|auto_generated|wire_pll1_clk[1]}]
set_false_path -from pll_ena_inst1|pll1|altpll_component|auto_generated|pll1|clk[1] -to adc_inst:adc_inst1|sync:sync_adc|sync[0]

Какие констрейны прописать для устранения ошибок?

Заранее спасибо!

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


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

    delay            delay1(.in(start_pulse),
                        .clk(clk),
                        .out(w_start_pulse2));
    delay            delay2(.in(w_start_pulse2),
                        .clk(clk),
                        .out(w_start_pulse3));
    delay            delay3(.in(w_start_pulse3),
                        .clk(clk),
                        .out(w_start_pulse4));
    delay            delay4(.in(w_start_pulse4),
                        .clk(clk),
                        .out(w_start_pulse5));        
    delay            delay5(.in(w_start_pulse5),
                        .clk(clk),
                        .out(w_start_pulse6));                            
    delay            delay6(.in(w_start_pulse6),
                        .clk(clk),
                        .out(end_pulse));

 

Вот это прям сильно) Зачем на такую простую операцию столько модулей? На мой взгляд - лишняя писанина... все это можно заменить парой строк:

 

    reg   [4:0] shft_reg;

        shft_reg <= shft_reg << 1 | start_pulse; // либо shft_reg <= {shft_reg[3:0], start_pulse}; /*мне так больше нравится*/

 

И, если сильно хочется, можно этот сдвиговый регистр утащить в отдельный модуль. При таком описании в качестве сигналов разрешения (w_start_pulsе...) будут использоваться биты сдвигового регистра (shft_reg[...]).

 

Но регистр err_reg захватывается на 3 такта раньше чем производится вычисление F_reg, неужели 3 такта недостаточно для умножения?

 

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

 

derive_clock_uncertainty
create_clock -name clk -period 36.1MHz [get_ports {clk}]
create_generated_clock -name clk_216MHz -source [get_ports {clk}] -multiply_by 6 [get_nets {pll_ena_inst1|pll1|altpll_component|auto_generated|wire_pll1_clk[0]}]
create_generated_clock -name clk_adc -source [get_ports {clk}] -divide_by 17 -multiply_by 15 [get_nets {pll_ena_inst1|pll1|altpll_component|auto_generated|wire_pll1_clk[1]}]
set_false_path -from pll_ena_inst1|pll1|altpll_component|auto_generated|pll1|clk[1] -to adc_inst:adc_inst1|sync:sync_adc|sync[0]

 

Я бы описал входные клоки (опорные):

create_clock -name clk -period 36.1MHz [get_ports {clk}]

 

И потом командой создаются все клоки PLL:

derive_pll_clocks

 

Для удобства и красоты им можно присвоить имена:

set clk_216MHz pll_ena_inst1|pll1|altpll_component|auto_generated|pll1|clk[0]

set clk_adc pll_ena_inst1|pll1|altpll_component|auto_generated|pll1|clk[1]

 

А еще стоит открыть handbook на вашу модель циклона и посмотреть, сколько максимальная частота работы умножителей. Может быть, они не умеют в 216 МГц.

 

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


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

Особо в код не вникал, но если у вас данные меняются каждые 3 такта - то для того, чтобы подсказать это синтезатору обычно применяют мультициклы:

set_multicycle_path

Ибо по-умолчанию анализатор считает что данные меняются каждый такт.

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


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

Еще чуть добавлю. Очень неплохо было бы если бы автор темы сообщал решил ли он проблему или нет. И как он решил возникшую проблему.

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

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


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

Спасибо за оперативные ответы!

Извиняюсь, что пишу так поздно. Проблема была в невыставленном set_multicycle_path. Я почему то полагал, что TimeQuest чуть умнее и сам додумается, т.к. вычисления разнесены по цепочке D-триггеров, но как выяснилось - нужно все прописывать самому. Пришлось добавить целую портянку кода:

set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM3} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM3} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM5} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM5} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[24]_OTERM217} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[24]_OTERM217} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]_OTERM235} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]_OTERM235} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM153_OTERM223_OTERM251} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM153_OTERM223_OTERM251} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM155_OTERM221_OTERM239_OTERM299} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM155_OTERM221_OTERM239_OTERM299} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]_OTERM281} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]_OTERM281} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]_OTERM287} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]_OTERM287} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]_OTERM291} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]_OTERM291} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[*]_OTERM295} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[*]_OTERM295} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|I_limits_reg[0]_OTERM13_OTERM215_OTERM233_OTERM275} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|I_limits_reg[0]_OTERM13_OTERM215_OTERM233_OTERM275} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM149_OTERM227_OTERM247_OTERM307} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM149_OTERM227_OTERM247_OTERM307} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM149_OTERM227_OTERM245_OTERM303} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_limits_reg[0]_OTERM7_OTERM149_OTERM227_OTERM245_OTERM303} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_reg[12]_OTERM325} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|F_reg[12]_OTERM325} -setup -end 4
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|I_limits_reg[0]_OTERM13_OTERM215_OTERM233_OTERM273} -setup -end 4
set_multicycle_path -from {pid:pid1|Ki_reg[*]} -to {pid:pid1|I_limits_reg[0]_OTERM13_OTERM215_OTERM233_OTERM273} -setup -end 4

Ситуация выправилась, но осталась куча слаков по Hold, вот один из них:

hold.png

 

Если я правильно понимаю, то Hold time (th) - это время, которое сигнал должен удерживаться после фронта clk приёмника. Как понимать эту диаграмму? Данные (Data Arrival) пришли раньше фронта Latch clock, что не так?

 

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


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

Задам вопрос по другому. Есть 1 общий клок и цепочка D-триггеров:

 

delay.png

 

В момент прихода импульса на первый триггер (1) в регистры заносятся некоторые данные, например err_reg[]. Далее с ними проводятся манипуляции (сложение/умножение/сравнение) и к моменту поступления импульса на шестой триггер (6) берется результат (F_limits_reg[]). Периодичность поступления импульсов на цепочку триггеров намного больше 6.

Как правильно описать данный констрейн с помощью set_multicycle_path?

Делаю так:

set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|F_limits_reg[*]*} -setup -end 6

 

На картинке снизу изображения временного отчета по setup(слева) и hold(справа).

setup_hold.png

 

По setup все более-менее понятно: мы указываем когда защелкиваются данные, соответственно сами данные должны прибыть ДО Latch-фронта.

А вот с hold не знаю что делать, смысл его для меня пока загадка. Из каких соображений определяется констрейн для hold?

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


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

Вобщем посмотрел Time-report простейшей цепи и сделал вывод: setup и hold отстают друг от друга на один такт. В моем случае - та же простейшая цепь с одним клоком, просто данные захватываются с задержкой в 6 тактов. Поставил hold-time на 1 меньше setup и варнинги пропали. Осталось легкое недопонимание относительно hold(

set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|I_limits_reg[*]*} -hold -end 6
set_multicycle_path -from {pid:pid1|err_reg[*]} -to {pid:pid1|I_limits_reg[*]*} -setup -end 7

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


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

Всё правильно, разница в один такт должна быть.Здесь смотрели?

Multicycles_explained_v1.0.pdf

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


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

Спасибо всем, кто принимал участие в обсуждении!

Все получилось, остался один вопрос: при компиляции проекта выдается ограничение по частоте в 200Мгц:

fmax.png

 

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

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


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

Спасибо всем, кто принимал участие в обсуждении!

Все получилось, остался один вопрос: при компиляции проекта выдается ограничение по частоте в 200Мгц:

fmax.png

 

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

Лучше читать описание к микросхеме.

Тут у Вас либо ограничение клокового дерева, либо применяется какой-то блок, который имеет ограничение в 200 Mhz. Например, очень частым блоком, ограничивающим максимальную частоту является блок памяти.

Первая цифра максимальной частоты в проекте (Fmax) - это максимальная теоретическая частота для конкретного проекта на конкретной FPGA без учёта ограничений на спец блоки, и клоковое дерево. Вторая цифра (restricted Fmax - максимальная частота с учетом ограничения на спец блоки и клоковое дерево).

Приведу простой пример: если создать небольшой сдвиговый регистр на триггерах, у него Fmax может показать под 800 Mhz. Но restricted Fmax будет например 400 Мгц.

Т.е триггеры могли бы и на 800 Мгц щелкать. Но клоковое дерево выше 400 Мгц через себя не пропустит.

Иными словами: максимальная частота проекта - это меньшее из этих значений (Fmax и restricted Fmax).

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


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

Лучше читать описание к микросхеме.

Тут у Вас либо ограничение клокового дерева, либо применяется какой-то блок, который имеет ограничение в 200 Mhz. Например, очень частым блоком, ограничивающим максимальную частоту является блок памяти.

Первая цифра максимальной частоты в проекте (Fmax) - это максимальная теоретическая частота для конкретного проекта на конкретной FPGA без учёта ограничений на спец блоки, и клоковое дерево. Вторая цифра (restricted Fmax - максимальная частота с учетом ограничения на спец блоки и клоковое дерево).

Иосиф Григорьевич, спасибо!

Все ясно и понятно! Тему можно считать закрытой.

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


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

Иосиф Григорьевич, спасибо!

Все ясно и понятно! Тему можно считать закрытой.

Вы меня перепутали с многоуважаемым iosifk :biggrin:

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


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

Вы меня перепутали с многоуважаемым iosifk :biggrin:
Прошу прощения :laughing: Но за советы все равно спасибо!

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


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

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

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

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

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

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

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

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

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

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