3

我想为 GCD 计算编写一个模块,使用扩展的欧几里得算法。但主要问题是我完全不知道如何在不达到最低(RTL)级别的情况下做到这一点。我的意思是让 FSM 具有三种状态:

  1. IDLE(等待输入)
  2. 计算(根据需要尽可能多的时钟周期)
  3. FINISHED(准备读取输出)。

但是,当我尝试将 FSM 和计算分成单独的进程时,如下所示:

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

当我尝试将计算放在第二个进程中时,在 COMPUTING 状态的情况下,它只工作一次 - 在 verilog 中是正确的,因为在计算完成之前,状态不会改变,因此不会再次调用该进程.

但是,当我将计算放在第一个过程中时,没有任何不美观的方法可以将计算限制为仅纠正状态,这会导致错误的输出(一旦 FSM 处于 FINISHED 状态,输出就已经不正确 -更进一步)。

所以,我的问题是 - 如何在不使用 FSM + 数据路径(低级 RTL)解决方案的情况下正确地做到这一点?

我当前的波形:

波形

4

1 回答 1

3

您的设计中似乎缺少一些时钟元素。

根据我对您的设计的了解,您似乎期望一旦状态进入 COMPUTING,它应该继续迭代 和 的值a直到b达到b0。但是您实际上在时钟边沿上计时的唯一东西是状态变量,所以从一个状态到下一个状态没有 a 和 b 的记忆。如果您想要像ab这样的变量具有从一个时钟周期到下一个时钟周期的内存,那么您还需要锁存这些变量:

我对你的程序做了一些修改,它可能不是 100% 正确的,但你应该看看我在做什么。看看这是否有意义,您如何在第二个块中执行组合逻辑,但是您在 posedge 上注册值,以便您可以在下一个时钟周期开始时使用它们。

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
于 2014-05-31T01:17:15.787 回答