一、原理
1.使用辗转相除法求最大公约数:两个数的最小公约数等于较小的数和两个数之差的最大公约数,因此可以让两个数相减,得到的结果与其中较小的数再相减,直到两个数相等为止;
2.最小公倍数等于两个数的乘积除以最大公约数。
二、代码
//Description:求最大公因数(gcd)和最小公倍数(lcm)
module gcd_lcm
#(
parameter DWIDTH=8
)
(
input clk,
input rst_n,
input [DWIDTH-1:0] data_a,
input [DWIDTH-1:0] data_b,
input en,
output reg [DWIDTH-1:0] gcd,
output reg [DWIDTH*2-1:0] lcm,
output ready,
output data_valid
);
reg [2:0] cs;
reg [2:0] ns;
reg [DWIDTH-1:0] a_buf;
reg [DWIDTH-1:0] b_buf;
reg done;
reg [DWIDTH*2-1:0] mul;
parameter IDLE=3'b001,
SUB=3'b010,
END=3'b100;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
cs<=IDLE;
end
else
begin
cs<=ns;
end
always@(*)
begin
ns=IDLE;
case(cs)
IDLE:
begin
if(en)
begin
ns=SUB;
end
else
begin
ns=IDLE;
end
end
SUB:
begin
if(a_buf!=b_buf)
begin
ns=SUB;
end
else
begin
ns=END;
end
end
END:
begin
ns=IDLE;
end
default:ns=IDLE;
endcase
end
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
a_buf<=0;
b_buf<=0;
done<=0;
gcd<=0;
lcm<=0;
end
else case(cs)
IDLE:
begin
a_buf<=data_a;
b_buf<=data_b;
mul<=data_a*data_b;
done<=0;
end
SUB:
begin
if(a_buf>b_buf)
begin
a_buf<=a_buf-b_buf;
b_buf<=b_buf;
end
else if(a_buf<b_buf)
begin
b_buf<=b_buf-a_buf;
a_buf<=a_buf;
end
else
begin
a_buf<=a_buf;
b_buf<=b_buf;
end
end
END:
begin
done<=1'b1;
gcd<=a_buf;
lcm<=mul/a_buf;
end
default:
begin
a_buf<=0;
b_buf<=0;
done<=0;
end
endcase
assign data_valid=done;
assign ready=(cs==IDLE);
endmodule
三、测试文件
`timescale 1ns/1ps //时间单位、仿真精度
`define clock_period 20 //系统时钟周期
module gcd_lcm_tb;
//=============================<端口>=======================================
parameter DWIDTH=8;
reg clk;
reg rst_n;
reg [DWIDTH-1:0] data_a;
reg [DWIDTH-1:0] data_b;
reg en;
wire [DWIDTH-1:0] gcd;
wire [DWIDTH*2-1:0] lcm;
wire ready;
wire data_valid;
//=============================<模块例化>===================================
gcd_lcm
#(
.DWIDTH(DWIDTH)
)
GCD_LCM(
.clk(clk),
.rst_n(rst_n),
.data_a(data_a),
.data_b(data_b),
.en(en),
.gcd(gcd),
.lcm(lcm),
.ready(ready),
.data_valid(data_valid)
);
//==========================<时钟激励>=======================================
initial clk=1;
always# (`clock_period/2) clk=~clk;
//==========================<设计激励信号>===================================
initial begin
rst_n=0;data_a=0;data_b=0;en=0;
#201;rst_n=1;
#100;en=1;data_a=150;data_b=3;
#20;en=0;
@(posedge ready);
#101;en=1;data_a=252;data_b=105;
#20;en=0;
@(posedge ready);
#1000;
$stop;
end
endmodule
四、Modelsim Tcl脚本
quit -sim
.main clear
vlib ./lib/
vlib ./lib/design/
vmap design ./lib/design/
vlog -work design ./../*.v
vsim -t ns -voptargs=+acc design.gcd_lcm_tb
add wave -divider {tb}
add wave gcd_lcm_tb/*
add wave -divider {gcd_lcm}
add wave gcd_lcm_tb/GCD_LCM/*
run -all