Verilog中的行为算法(GCD) - 可能吗?

时间:2014-05-31 00:21:59

标签: verilog hdl register-transfer-level

我想使用扩展的欧几里德算法为GCD计算编写一个模块。但主要的问题是,如果没有达到最低(RTL)水平,我完全不知道该怎么做。我的意思是让FSM有三种状态:

  1. IDLE(等待输入)
  2. 计算(根据需要使用多个时钟周期)
  3. 已完成(准备阅读输出)。
  4. 然而,当我尝试将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的方法中是正确的,因为在计算完成之前,状态不会改变,所以过程不是'再次打电话。

    然而,当我在第一个过程中放置​​计算时,没有任何非丑陋的方法来限制计算只能纠正STATE,这会导致输出错误(一旦FSM处于FINISHED状态,输出就是已经不正确了 - 更进了一步。)

    所以,我的问题是 - 如何在不使用FSM +数据路径(低级RTL)解决方案的情况下正确执行此操作?

    我当前的波形: Waveform

1 个答案:

答案 0 :(得分:3)

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

根据我对你的设计的理解,你似乎期望一旦状态进入计算,它应该继续迭代ab的值,直到b达到0。但是你实际上在时钟边缘计时的唯一事物是状态变量,因此从一个状态到下一个状态没有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