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

Добрый день всем!

Пишу ПИ-регулятор, требуется профессиональная критика) Камень - 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

Моделсим:

tb_pid.png

 

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


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

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

Я бы быстрее написал самостоятельно, чем применил Ваш код. Ибо разбираться, что происходит внутри кода, зачем и почему желания нет никакого...

Вполне возможно, что код отличный. И работает хорошо...

Я обычно проделываю "двойную" работу, и описываю все максимально подробно. И начиню проектирование на листке бумаги. Рисую алгоритмы, блок схемы, графы автоматов. И только потом перевожу на HDL. И не стесняюсь на комментарии. Предпочитаю комментировать так, чтобы листинг можно было читать как книгу.

Ибо практика показала, что я максимум месяц-два помню то, что я написал. Потом забываю начисто. Только сфотографированные листы с которых я начинал проектирование - где описано все что можно, позволяют быстро вспомнить, что было сделано, зачем, почему.

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


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

Пишу ПИ-регулятор, требуется профессиональная критика) Камень - cyclone III, использовано 4 аппаратных умножителя.

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

 

    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;

 

 

 

 

 

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


Ссылка на сообщение
Поделиться на другие сайты
И вместо кейсов на 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

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


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

Использование "case" предпочтительнее (когда это возможно), т.к. при синтезе получается 1 MUX, а при использовании конструкций типа "if ... else" синтезится N-ое количество последовательных MUX-ов. Помимо увеличения логики в схеме, увеличиваются задержки на каждом из элементов.

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


Ссылка на сообщение
Поделиться на другие сайты
    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

 

В чем вообще разница между стэйт-машиной и регистром в данном случае? Что предпочтительнее использовать? Результат симуляции в обоих случаях одинаковый.

 

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


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

Во втором случае у вас будет совершенно другая схема синтезироваться, в отличии от case.

Вот тут немного написано про это: https://electronics.stackexchange.com/quest...=google_rich_qa

Сами читали, на что сослались? Всё одно сведётся к логической функции нескольких переменных и ляжет в LUT. Результат будет тот же самый. Соревноваться с оптимизирующим компилятором бессмысленно.

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


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

Основной смысл - в картинках :rolleyes:

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

Тут Вы правы это я немного поспешил. Тут же не приоритетный шифратор. Эх...

Хотя всё-равно считаю, что описывать через case правильнее, с точки зрения стиля написания. Читается проще.

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


Ссылка на сообщение
Поделиться на другие сайты
Основной смысл - в картинках :rolleyes:

 

Тут Вы правы это я немного поспешил. Тут же не приоритетный шифратор. Эх...

Хотя всё-равно считаю, что описывать через case правильнее, с точки зрения стиля написания. Читается проще.

 

По поводу описания есть еще один не совсем очевидный момент. Не всегда описание, которое принимается синтезатор, может понять симулятор. Иногда при написании кода приходится помнить, что ModelSim понимает не все конструкции (по крайней мере для VHDL так, не знаю как для Verilog).

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


Ссылка на сообщение
Поделиться на другие сайты
Иногда при написании кода приходится помнить, что ModelSim понимает не все конструкции (по крайней мере для VHDL так, не знаю как для Verilog).
Я уверен, что вас не затруднит подтвердить слова кодом.

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


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

 

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

 

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

 

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?;

 

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


Ссылка на сообщение
Поделиться на другие сайты
На память могу ошибиться. но вот как щас помню

 

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

 

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 почти ничего не знаю.

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


Ссылка на сообщение
Поделиться на другие сайты
Про vhdl'2008 почти ничего не знаю.

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

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


Ссылка на сообщение
Поделиться на другие сайты
А можно нескромный вопрос. А каковы объективные причины не применять VHDL2008 ? Я пока не встретил каких-либо неудобств, или косяков.
Во-первых, я не вижe там какого-то особого профита.

Ну кроме разве что не всегда работающего присваивания выходного сигнала с порта, внутрь модуля.
А кто виноват?

Вот-вот, синтезатор или не полностью его поддерживает. Или может совсем не поддерживать (в случае, если вы пишете код для АСИКа).

А '93 поддерживают все.

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


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

 

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

 

Моделсим его частично поддерживает, по крайней мере в версии 10.2

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

 

Во-первых, я не вижe там какого-то особого профита.

А кто виноват?

Вот-вот, синтезатор или не полностью его поддерживает. Или может совсем не поддерживать (в случае, если вы пишете код для АСИКа).

А '93 поддерживают все.

 

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

 

process(all)

 

И case удобно применять для описания приоритетного шифратора. Жизнь упрощает, хотя конечно же и без всего этого можно

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


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

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация