Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: П-И-регулятор
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Языки проектирования на ПЛИС (FPGA)
Sprite
Добрый день всем!
Пишу ПИ-регулятор, требуется профессиональная критика) Камень - cyclone III, использовано 4 аппаратных умножителя.
Код:
Код
module pid
(
    input     clk,
    input     ena,
    input     signed [15:0] in_ustavka,
    input     signed [15:0] in_fakt,
    input     signed [15:0] Ki,
    input     signed [15:0] Kp,
    input     signed [15:0] out_min,
    input     signed [15:0] out_max,
    output     reg signed [31:0] out
);
    reg signed [31:0] I_reg;
    reg signed [31:0] F_reg;
    reg signed [31:0] I_prev_reg;
    reg [1:0] F_limits_reg;
    reg [1:0] I_limits_reg;
    reg    [1:0] ena_reg;

    reg    state;
    
    initial
    begin
        ena_reg = 2'b00;
        I_prev_reg = 31'd1000;
        out = 0;
        state = 1'b0;
    end
    
    wire ena_risingEdge;
    wire [1:0] w_F_limits;
    wire [1:0] w_I_limits;
    wire signed [31:0] w_F;
    wire signed [31:0] w_I;
    wire signed [31:0] w_P;
    wire signed [15:0] w_err;
    

    //---------------------------------
    // Front detector
    //---------------------------------
    always @(posedge clk)
    begin
        ena_reg <= {ena_reg[0], ena};
    end
    assign ena_risingEdge = (ena_reg == 2'b01);

    //---------------------------------
    // PID-calculating
    //---------------------------------
    assign w_err = in_ustavka - in_fakt;
    assign w_I = Ki*w_err + I_prev_reg;
    assign w_P = Kp*w_err;
    assign w_F = w_I + w_P;
    assign w_F_limits[0] = (w_F < out_min)? 1'b1 : 1'b0;
    assign w_F_limits[1] = (w_F > out_max)? 1'b1 : 1'b0;
    assign w_I_limits[0] = (w_I < out_min)? 1'b1 : 1'b0;
    assign w_I_limits[1] = (w_I > out_max)? 1'b1 : 1'b0;
    
    
    always @ (posedge clk)
    begin
        case (state)
        //--------------------------------
        // Gets I and F values
        //--------------------------------
        1'b0:
            if (ena_risingEdge)
                begin
                    I_reg <= w_I;
                    F_reg <= w_F;
                    I_limits_reg <= w_I_limits;
                    F_limits_reg <= w_F_limits;
                    state <= 1'b1;
                end
            else
                state <= 1'b0;
        //--------------------------------
        // Integral and out limitation
        //--------------------------------
        1'b1:
            begin
                case (F_limits_reg)
                    2'b01:        out <= out_min;
                    2'b10:         out <= out_max;
                    default:    out <= F_reg;
                endcase                
                case (I_limits_reg)
                    2'b01:        I_prev_reg <= out_min;
                    2'b10:         I_prev_reg <= out_max;
                    default:    I_prev_reg <= I_reg;
                endcase                        

                state <= 1'b0;
            end
        //--------------------------------
        default:
            state <= 1'b0;
        endcase
        //--------------------------------
    end

Моделсим:

Flip-fl0p
Цитата(Sprite @ Jun 11 2018, 11:25) *

Понимаете, код должен быть написан таким образом, чтобы его было можно применить без правок, либо с минимальными правками.
Я бы быстрее написал самостоятельно, чем применил Ваш код. Ибо разбираться, что происходит внутри кода, зачем и почему желания нет никакого...
Вполне возможно, что код отличный. И работает хорошо...
Я обычно проделываю "двойную" работу, и описываю все максимально подробно. И начиню проектирование на листке бумаги. Рисую алгоритмы, блок схемы, графы автоматов. И только потом перевожу на HDL. И не стесняюсь на комментарии. Предпочитаю комментировать так, чтобы листинг можно было читать как книгу.
Ибо практика показала, что я максимум месяц-два помню то, что я написал. Потом забываю начисто. Только сфотографированные листы с которых я начинал проектирование - где описано все что можно, позволяют быстро вспомнить, что было сделано, зачем, почему.
nice_vladi
Цитата(Sprite @ Jun 11 2018, 09:25) *
Добрый день всем!
Пишу ПИ-регулятор, требуется профессиональная критика) Камень - cyclone III, использовано 4 аппаратных умножителя.

На профессионала не потяну, НО biggrin.gif

Код
    assign w_I = Ki*w_err + I_prev_reg; // 32 бита = (16 бит * 16 бит) + 32 бита -- возможно переполнение

Всегда дико заморочно следить за разрядностями, но иногда из-за этого все ломается.

А вместо машины на два состояния, которая щелкает каждый такт (если я правильно понял) можно сделать простой однобитный счетчик, который будет по какому-то событию перекидываться 0-1-0-1... мне кажется и лаконичнее и писанины меньше (хотя, наверное, дело религии).

И вместо кейсов на 2-3 состояния красивше писать if-else. Опять же лаконичнее и писанины меньше (но, наверное, это тоже дела религиозные))).
Т.е. получаем вместо:
Код
case (F_limits_reg)
    2'b01:        out <= out_min;
     2'b10:         out <= out_max;
     default:    out <= F_reg;
endcase


Что-то вроде:

Код
    if (F_limits_reg == 2'b01)         out <= out_min;
    else if (F_limits_reg == 2'b10)  out <= out_max;
      else                                     out <= F_reg;





Flip-fl0p
Цитата(nice_vladi @ Jun 12 2018, 08:40) *
И вместо кейсов на 2-3 состояния красивше писать if-else. Опять же лаконичнее и писанины меньше (но, наверное, это тоже дела религиозные))).
Т.е. получаем вместо:
Код
case (F_limits_reg)
    2'b01:        out <= out_min;
     2'b10:         out <= out_max;
     default:    out <= F_reg;
endcase


Что-то вроде:

Код
    if (F_limits_reg == 2'b01)         out <= out_min;
    else if (F_limits_reg == 2'b10)  out <= out_max;
      else                                     out <= F_reg;


Тут Вы сильно заблуждаетесь.
Во втором случае у вас будет совершенно другая схема синтезироваться, в отличии от case.
Вот тут немного написано про это: https://electronics.stackexchange.com/quest...=google_rich_qa
Nieve
Использование "case" предпочтительнее (когда это возможно), т.к. при синтезе получается 1 MUX, а при использовании конструкций типа "if ... else" синтезится N-ое количество последовательных MUX-ов. Помимо увеличения логики в схеме, увеличиваются задержки на каждом из элементов.
Sprite
Цитата(nice_vladi @ Jun 12 2018, 12:40) *
Код
    assign w_I = Ki*w_err + I_prev_reg; // 32 бита = (16 бит * 16 бит) + 32 бита -- возможно переполнение

Всегда дико заморочно следить за разрядностями, но иногда из-за этого все ломается.

Спасибо за замечание! Но значение I_prev_reg, а значит и интегральная составляющая (w_I) у меня ограничивается максимальным и минимальным значениями (out_max и out_min), которые имеют разрядность 16 бит, так что криминала быть не должно.

Цитата
А вместо машины на два состояния, которая щелкает каждый такт (если я правильно понял) можно сделать простой однобитный счетчик, который будет по какому-то событию перекидываться 0-1-0-1... мне кажется и лаконичнее и писанины меньше (хотя, наверное, дело религии).

Не совсем понял фразу "щелкает каждый такт", но мысль уловил. Переписал немного добавив регистр (flag):
Код
    reg flag;
    always @ (posedge clk)
    begin
        //--------------------------------
        // Gets I and F values
        //--------------------------------
        if (ena_pulse)
            begin
                I_reg <= w_I;
                F_reg <= w_F;
                I_limits_reg <= w_I_limits;
                F_limits_reg <= w_F_limits;
                flag <= 1'b1;
            end
        //--------------------------------
        // Integral and out limitation
        //--------------------------------
        else if (flag)
        begin
            case (F_limits_reg)
                2'b01:        out <= out_min;
                2'b10:         out <= out_max;
                default:    out <= F_reg;
            endcase                
            case (I_limits_reg)
                2'b01:        I_prev_reg <= out_min;
                2'b10:         I_prev_reg <= out_max;
                default:    I_prev_reg <= I_reg;
            endcase                        
            flag <= 1'b0;
        end
    end


В чем вообще разница между стэйт-машиной и регистром в данном случае? Что предпочтительнее использовать? Результат симуляции в обоих случаях одинаковый.
andrew_b
Цитата(Flip-fl0p @ Jun 12 2018, 08:53) *
Тут Вы сильно заблуждаетесь.
Во втором случае у вас будет совершенно другая схема синтезироваться, в отличии от case.
Вот тут немного написано про это: https://electronics.stackexchange.com/quest...=google_rich_qa
Сами читали, на что сослались? Всё одно сведётся к логической функции нескольких переменных и ляжет в LUT. Результат будет тот же самый. Соревноваться с оптимизирующим компилятором бессмысленно.
Flip-fl0p
Цитата(andrew_b @ Jun 12 2018, 14:00) *
Сами читали, на что сослались?

Основной смысл - в картинках rolleyes.gif
Цитата
Результат будет тот же самый. Соревноваться с оптимизирующим компилятором бессмысленно.

Тут Вы правы это я немного поспешил. Тут же не приоритетный шифратор. Эх...
Хотя всё-равно считаю, что описывать через case правильнее, с точки зрения стиля написания. Читается проще.
gin
Цитата(Flip-fl0p @ Jun 12 2018, 21:40) *
Основной смысл - в картинках rolleyes.gif

Тут Вы правы это я немного поспешил. Тут же не приоритетный шифратор. Эх...
Хотя всё-равно считаю, что описывать через case правильнее, с точки зрения стиля написания. Читается проще.


По поводу описания есть еще один не совсем очевидный момент. Не всегда описание, которое принимается синтезатор, может понять симулятор. Иногда при написании кода приходится помнить, что ModelSim понимает не все конструкции (по крайней мере для VHDL так, не знаю как для Verilog).
andrew_b
Цитата(gin @ Jun 14 2018, 10:56) *
Иногда при написании кода приходится помнить, что ModelSim понимает не все конструкции (по крайней мере для VHDL так, не знаю как для Verilog).
Я уверен, что вас не затруднит подтвердить слова кодом.
gin
Цитата(andrew_b @ Jun 14 2018, 11:08) *
Я уверен, что вас не затруднит подтвердить слова кодом.


На память могу ошибиться. но вот как щас помню

объявляем сигнал:

Код
signal cnt : natural range 0 to 5


затем его используем в каком-нить процессе:

Код
if (cnt = cnt'high) then


Квартус это проглатывал, а вот Modelsim ругался.
Версию моделсима щас уже не вспомню


Так же моделсим вот на такую контсрукцию ругается

Код
case? <expression> is
    when <constant_expression> =>
        -- Sequential Statement(s)
    when <constant_expression> =>
        -- Sequential Statement(s)
    when others =>
        -- Sequential Statement(s)
end case?;

andrew_b
Цитата(gin @ Jun 14 2018, 11:22) *
На память могу ошибиться. но вот как щас помню

объявляем сигнал:

Код
signal cnt : natural range 0 to 5


затем его используем в каком-нить процессе:

Код
if (cnt = cnt'high) then


Квартус это проглатывал, а вот Modelsim ругался.
Я тоже на такое натыкался, но лень было выяснять, Моделсим тут прав или нет. Я просто ввёл новый тип.

Цитата
Так же моделсим вот на такую контсрукцию ругается

Код
case? <expression> is
     when <constant_expression> =>
         -- Sequential Statement(s)
     when <constant_expression> =>
         -- Sequential Statement(s)
     when others =>
         -- Sequential Statement(s)
end case?;
Про vhdl'2008 почти ничего не знаю.
Flip-fl0p
Цитата(andrew_b @ Jun 14 2018, 12:32) *
Про vhdl'2008 почти ничего не знаю.

А можно нескромный вопрос. А каковы объективные причины не применять VHDL2008 ? Я пока не встретил каких-либо неудобств, или косяков. Ну кроме разве что не всегда работающего присваивания выходного сигнала с порта, внутрь модуля.
andrew_b
Цитата(Flip-fl0p @ Jun 14 2018, 13:23) *
А можно нескромный вопрос. А каковы объективные причины не применять VHDL2008 ? Я пока не встретил каких-либо неудобств, или косяков.
Во-первых, я не вижe там какого-то особого профита.
Цитата
Ну кроме разве что не всегда работающего присваивания выходного сигнала с порта, внутрь модуля.
А кто виноват?
Вот-вот, синтезатор или не полностью его поддерживает. Или может совсем не поддерживать (в случае, если вы пишете код для АСИКа).
А '93 поддерживают все.
gin
Цитата(andrew_b @ Jun 14 2018, 12:32) *
Я тоже на такое натыкался, но лень было выяснять, Моделсим тут прав или нет. Я просто ввёл новый тип.

Про vhdl'2008 почти ничего не знаю.


Моделсим его частично поддерживает, по крайней мере в версии 10.2
Там даже отдельный technote есть с описанием того, что и как поддерживается для vhdl2008

Цитата(andrew_b @ Jun 14 2018, 15:09) *
Во-первых, я не вижe там какого-то особого профита.
А кто виноват?
Вот-вот, синтезатор или не полностью его поддерживает. Или может совсем не поддерживать (в случае, если вы пишете код для АСИКа).
А '93 поддерживают все.


В 2008 стандарте есть удобные фишки, тот же список чувствительности процесса можно задать как

Код
process(all)


И case удобно применять для описания приоритетного шифратора. Жизнь упрощает, хотя конечно же и без всего этого можно
andrew_b
Цитата(gin @ Jun 14 2018, 16:19) *
В 2008 стандарте есть удобные фишки, тот же список чувствительности процесса можно задать как
Код
process(all)
Это мелочь. И чтение выхода тоже мелочь. На самом деле в '93 есть всё, что мне нужно.

Но это всё офтоп.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.