Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Cyclone III, констрейны
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Языки проектирования на ПЛИС (FPGA)
Sprite
Доброго дня всем!

Есть модуль ПИ-регулятора, проект собирается на 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).
Таймквест выдает ошибки:

Но регистр 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]

Какие констрейны прописать для устранения ошибок?
Заранее спасибо!
nice_vladi
Цитата(Sprite @ Jul 24 2018, 04:52) *
Код
    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 МГц.
Flip-fl0p
Особо в код не вникал, но если у вас данные меняются каждые 3 такта - то для того, чтобы подсказать это синтезатору обычно применяют мультициклы:
Код
set_multicycle_path

Ибо по-умолчанию анализатор считает что данные меняются каждый такт.
Flip-fl0p
Еще чуть добавлю. Очень неплохо было бы если бы автор темы сообщал решил ли он проблему или нет. И как он решил возникшую проблему.
Ведь форум - это не только место где могут помочь, подсказать и дать пинка в верном направлении, но также это хорошая база знаний, где можно найти решение проблемы, если она встречалась ранее wink.gif
Sprite
Спасибо за оперативные ответы!
Извиняюсь, что пишу так поздно. Проблема была в невыставленном 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 time (th) - это время, которое сигнал должен удерживаться после фронта clk приёмника. Как понимать эту диаграмму? Данные (Data Arrival) пришли раньше фронта Latch clock, что не так?
Sprite
Задам вопрос по другому. Есть 1 общий клок и цепочка D-триггеров:



В момент прихода импульса на первый триггер (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 все более-менее понятно: мы указываем когда защелкиваются данные, соответственно сами данные должны прибыть ДО Latch-фронта.
А вот с hold не знаю что делать, смысл его для меня пока загадка. Из каких соображений определяется констрейн для hold?
Sprite
Вобщем посмотрел 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
_Anatoliy
Цитата(Sprite @ Jul 30 2018, 15:19) *

Всё правильно, разница в один такт должна быть.Здесь смотрели?
Sprite
_Anatoliy, спасибо большое! beer.gif
Посмотрю обязательно!
Sprite
Спасибо всем, кто принимал участие в обсуждении!
Все получилось, остался один вопрос: при компиляции проекта выдается ограничение по частоте в 200Мгц:


Где именно ограничивается минимальный период и можно ли это поправить?
Flip-fl0p
Цитата(Sprite @ Aug 3 2018, 06:32) *
Спасибо всем, кто принимал участие в обсуждении!
Все получилось, остался один вопрос: при компиляции проекта выдается ограничение по частоте в 200Мгц:


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

Лучше читать описание к микросхеме.
Тут у Вас либо ограничение клокового дерева, либо применяется какой-то блок, который имеет ограничение в 200 Mhz. Например, очень частым блоком, ограничивающим максимальную частоту является блок памяти.
Первая цифра максимальной частоты в проекте (Fmax) - это максимальная теоретическая частота для конкретного проекта на конкретной FPGA без учёта ограничений на спец блоки, и клоковое дерево. Вторая цифра (restricted Fmax - максимальная частота с учетом ограничения на спец блоки и клоковое дерево).
Приведу простой пример: если создать небольшой сдвиговый регистр на триггерах, у него Fmax может показать под 800 Mhz. Но restricted Fmax будет например 400 Мгц.
Т.е триггеры могли бы и на 800 Мгц щелкать. Но клоковое дерево выше 400 Мгц через себя не пропустит.
Иными словами: максимальная частота проекта - это меньшее из этих значений (Fmax и restricted Fmax).
Sprite
Цитата(Flip-fl0p @ Aug 3 2018, 10:57) *
Лучше читать описание к микросхеме.
Тут у Вас либо ограничение клокового дерева, либо применяется какой-то блок, который имеет ограничение в 200 Mhz. Например, очень частым блоком, ограничивающим максимальную частоту является блок памяти.
Первая цифра максимальной частоты в проекте (Fmax) - это максимальная теоретическая частота для конкретного проекта на конкретной FPGA без учёта ограничений на спец блоки, и клоковое дерево. Вторая цифра (restricted Fmax - максимальная частота с учетом ограничения на спец блоки и клоковое дерево).

Иосиф Григорьевич, спасибо!
Все ясно и понятно! Тему можно считать закрытой.
Flip-fl0p
Цитата(Sprite @ Aug 3 2018, 15:19) *
Иосиф Григорьевич, спасибо!
Все ясно и понятно! Тему можно считать закрытой.

Вы меня перепутали с многоуважаемым iosifk biggrin.gif
Sprite
Цитата(Flip-fl0p @ Aug 4 2018, 15:04) *
Вы меня перепутали с многоуважаемым iosifk biggrin.gif
Прошу прощения laughing.gif Но за советы все равно спасибо!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.