Поведенческие алгоритмы (GCD) в Verilog - возможно?

Я хочу написать модуль для вычислений GCD, используя расширенный алгоритм Евклида. Но главная проблема в том, что я совершенно не знаю, как это сделать, не дойдя до самого нижнего (RTL) уровня. Я имею в виду, что автомат будет иметь три состояния:

  1. IDLE (ожидает ввода)
  2. ВЫЧИСЛЕНИЕ (столько тактов, сколько необходимо)
  3. ЗАВЕРШЕНО (готово к чтению вывода).

Однако когда я пытаюсь разделить конечный автомат и вычисления на отдельные процессы, например:

module modinv(clk, reset, number, prime, finished, gcd, inverse_fail, inverse);

    input [31:0] number, prime;
    input wire clk, reset;

    output integer gcd, inverse;
    output reg finished, inverse_fail;

    parameter [2:0] IDLE = 3'b001, COMPUTING = 3'b010, END = 3'b100;
    reg [2:0] state, state_next;

    integer a, b, c, q, p, r;

    always @ (posedge clk, posedge reset)
    begin
        if (reset == 1)
            begin
                state <= IDLE;
            end
        else
            begin              
                state <= state_next;
            end
    end

    always @(state or b)
    begin
        finished <= 0;
        inverse_fail <= 0;

        case (state)
            IDLE:
                begin
                    a <= number;
                    b <= prime;
                    p <= 1;
                    r <= 0;
                    state_next <= COMPUTING;
                end
            COMPUTING:
                begin
                    c = a % b;
                    q = a / b;
                    a = b;
                    b = c;
                    r = p - q * r;
                    p = r;

                    if (b == 0)
                        begin
                            state_next <= END;
                        end
                    else
                        begin
                            state_next <= COMPUTING;
                        end
                end
            END:
                begin
                    gcd <= a;
                    inverse <= p;
                    finished <= 1;
                    if (gcd != 1)
                        begin
                            inverse_fail <= 1;
                        end
                end
        endcase
    end

endmodule

И когда я пытаюсь поместить вычисление во второй процесс, в случае состояния ВЫЧИСЛЕНИЕ, он работает только один раз - что правильно в средствах Verilog, потому что до тех пор, пока вычисления не будут выполнены, состояние не меняется, поэтому процесс не вызывается снова. .

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

Итак, у меня вопрос - как это сделать правильно, не используя решение FSM + datapath (низкоуровневый RTL)?

Моя текущая форма волны:

Форма волны


person Kacper Banasik    schedule 31.05.2014    source источник
comment
Пожалуйста, покажите, что у вас есть на данный момент. Попробуйте изменить always @ (state) на always @*.   -  person Greg    schedule 31.05.2014
comment
Обновлено с кодом и формой волны   -  person Kacper Banasik    schedule 31.05.2014


Ответы (1)


Кажется, вам не хватает некоторых синхронизированных элементов в вашем дизайне.

Судя по тому, что я понимаю о вашем дизайне, вы, кажется, ожидаете, что, когда состояние переходит в ВЫЧИСЛЕНИЕ, оно должно продолжать повторять значения a и b, пока b не достигнет 0. Но единственное, что вы на самом деле синхронизируете на фронте тактового сигнала находится переменная состояния, поэтому нет памяти для значений a и b от одного состояния к другому. Если вы хотите, чтобы такие переменные, как a и b, имели память от одного тактового цикла до следующего, вам также необходимо зафиксировать эти переменные:

Я внес некоторые изменения в вашу программу, возможно, это не на 100% правильно, но вы должны увидеть, к чему я клоню. Посмотрите, имеет ли это смысл, как вы выполняете комбинационную логику во втором блоке, но вы регистрируете значения в позе, чтобы вы могли использовать их в начале следующего тактового цикла.

module modinv(clk, reset, number, prime, finished, gcd, inverse_fail, inverse);

    input [31:0] number, prime;
    input wire clk, reset;

    output integer gcd, inverse;
    output reg finished, inverse_fail;

    parameter [2:0] IDLE = 3'b001, COMPUTING = 3'b010, END = 3'b100;
    reg [2:0] state, state_next;

    integer a, b, c, q, p, r;
    integer a_next, b_next, p_next, r_next;

    always @ (posedge clk, posedge reset)
    begin
        if (reset == 1)
            begin
                state <= IDLE;
                a <= 0;
                b <= 0;
                p <= 0;
                r <= 0;
            end
        else
            begin              
                state <= state_next;
                a     <= a_next;
                b     <= b_next;
                p     <= p_next;
                r     <= r_next;
            end
    end

    always @* //just use the auto-triggered '@*' operator
    begin
        finished <= 0;
        inverse_fail <= 0;

        case (state)
            IDLE:
                begin
                    a_next <= number;
                    b_next <= prime;
                    p_next <= 1;
                    r_next <= 0;
                    state_next <= COMPUTING;
                end
            COMPUTING:
                begin
                    c = a % b;
                    q = a / b;
                    a_next = b;
                    b_next = c;
                    r_next = p - q * r;
                    p_next = r;

                    if (b == 0)
                        begin
                            state_next <= END;
                        end
                    else
                        begin
                            state_next <= COMPUTING;
                        end
                end
            END:
                begin
                    gcd <= a;
                    inverse <= p;
                    finished <= 1;
                    if (gcd != 1)
                        begin
                            inverse_fail <= 1;
                        end
                end
        endcase
    end

endmodule
person Tim    schedule 31.05.2014