我想使用扩展的欧几里德算法为GCD计算编写一个模块。但主要的问题是,如果没有达到最低(RTL)水平,我完全不知道该怎么做。我的意思是让FSM有三种状态:
然而,当我尝试将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)解决方案的情况下正确执行此操作?
我当前的波形:
答案 0 :(得分:3)
您似乎缺少设计中的一些时钟元素。
根据我对你的设计的理解,你似乎期望一旦状态进入计算,它应该继续迭代a
和b
的值,直到b
达到0。但是你实际上在时钟边缘计时的唯一事物是状态变量,因此从一个状态到下一个状态没有a和b的记忆。如果你想让a
和b
之类的变量从一个时钟周期到下一个时钟周期都有内存,那么你也需要锁存这些变量:
我对你的程序进行了一些修改,它可能不是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