目录
1. 前言
2. 运行环境
3.实现原理
4. 实现过程
5. 代码
顶层代码
输入模块
状态机模块
计算模块
显示模块
约束文件
6. 功能仿真
1. 前言
近期在学习EDA课程,课上学习了一部分关于使用Verilog语言实现求两个数的最大公约数的算法,课后在完成作业的时候看到了一篇关于在ZYBO Z7实现四位全加器的博客,于是参考该代码的输入及显示方式,实现了使用ZYBO Z7来计算最大公约数的功能,但是ZYBOZ7开发板的拨码只有4个,所以只能计算2个4位数的最大公约数。
部分参考程序:
四位全加器的FPGA实现_hfutstudenthuang的博客-CSDN博客_fpga四位加法器
2. 运行环境
1. 软件:vivado 2018.3
2. FPGA开发板:ZYBO Z7
3.实现原理
最大公约数的算法流程图如下:
在以上过程中,存在使用除法求余数的步骤。用硬件实现除法开销较大,所以使用减法来实现,除法取余数本质上是不断做减法直到被除数小于除数。
4. 实现过程
RTL图如下:
其中共包括4个模块,即
输入模块:通过FPGA上的拨码即按钮进行两个数据的输入,以及使用开关控制结果出输出及复位。
状态机模块:规模较小的数字系统/子系统的控制逻辑一般采用状态机来实现。根据需求,对应的计算过程可以分为等待输入、计算、等待输出等三步。
计算模块:计算过程。
显示模块:通过LED灯来显示输入数据、运算结果。
5. 代码
顶层代码
`timescale 1ns/100ps//时间精度
//定义值
`define A_SEL_X 2'bxx
`define A_SEL_IN 2'b00
`define A_SEL_B 2'b01
`define A_SEL_SUB 2'b10
`define B_SEL_X 1'bx
`define B_SEL_IN 1'b0
`define B_SEL_A 1'b1
//顶层模块
module gcd_rtl_top #(parameter W=4)(input clk, //时钟信号input reset, //复位信号input wire bottom1, //开关(控制数据1输入)input wire bottom2, //开关2(控制数据2输入)input wire bottom3, //开关3(控制输出结果)input wire [W-1:0] data, //当前的数据output wire [3:0] display_result,//输出结果output result_rdy //运算状态
);//定义中间数据类型wire [W-1:0] result_data; //运算结果wire A_en;wire B_en;wire [1:0] A_sel;wire B_sel;wire B_zero;wire A_lt_B;wire [W-1:0] operand_A; //数据1wire [W-1:0] operand_B; //数据2//调用子模块
//输入模块
data_in data_in(.clk(clk),.reset(reset),.bottom1(bottom1),.bottom2(bottom2),.data(data),.operand_A(operand_A),.operand_B(operand_B));//状态机模块
gcdGCDUnitCtrl gcdUC(.clk(clk), .reset(reset), .A_en(A_en), .B_en(B_en),.A_sel(A_sel), .B_sel(B_sel), .result_rdy(result_rdy), .B_zero(B_zero), .A_lt_B(A_lt_B));//计算模块
gcdGCDUnitDpath gcdUD(.clk(clk),.operand_A(operand_A), .operand_B(operand_B), .result_data(result_data), .A_en(A_en),.B_en(B_en),.A_sel(A_sel),.B_sel(B_sel),.B_zero(B_zero), .A_lt_B(A_lt_B));//显示模块
display display(.clk(clk),.reset(reset),.bottom1(bottom1),.bottom2(bottom2),.bottom3(bottom3),.result_data(result_data),.display_result(display_result),.result_rdy(result_rdy),.data(data));
endmodule
输入模块
`timescale 1ns / 1ps
//定义输入/输出
module data_in(input clk,input reset,input bottom1, input bottom2,input [3:0] data, output reg[3:0] operand_A=1,output reg[3:0] operand_B=1);always @(posedge clk or posedge reset)beginif(reset) //按下复位键时数据1,2都为0.beginoperand_A<= 0;operand_B<= 0;endelsebeginif(bottom1) //bottom1按下时,数据1接收当前输入数据beginoperand_A <= data;endelse if(bottom2) //bottom2按下时,数据2接收当前输入数据beginoperand_B <= data;endendend
endmodule
状态机模块
`timescale 1ns/100ps
`define A_SEL_X 2'bxx
`define A_SEL_IN 2'b00
`define A_SEL_B 2'b01
`define A_SEL_SUB 2'b10
`define B_SEL_X 1'bx
`define B_SEL_IN 1'b0
`define B_SEL_A 1'b1
module gcdGCDUnitCtrl
#(parameter W = 4)//数据位数(input clk,input reset,input B_zero, input A_lt_B, output reg A_en, output reg B_en, output reg [1:0] A_sel, output reg B_sel, output reg result_rdy
);localparam WAIT = 2'd0;localparam CALC = 2'd1;localparam DONE = 2'd2; //三种状态reg [1:0] state_next; //下次状态wire [1:0] state; //当前状态//复位信号vcRDFF_pf state_pf( .clk (clk),.reset_p (reset),.d_p (state_next),.q_np (state) );always @(*)begin //控制信号 A_sel = `A_SEL_X; //定义初始值A_en = 1'b0; B_sel = `B_SEL_X;B_en = 1'b0;result_rdy = 1'b0; //表示计算未结束case ( state )WAIT: begin //位于WAIT状态,读取数据A_sel = `A_SEL_IN;A_en = 1'b1;B_sel = `B_SEL_IN;B_en = 1'b1;endCALC: //位于计算状态begin if ( A_lt_B ) //当A_lt_B=1时beginA_sel = `A_SEL_B;A_en = 1'b1;B_sel = `B_SEL_A;B_en = 1'b1;endelse if ( !B_zero ) //当B_zero不为0时beginA_sel = `A_SEL_SUB;A_en = 1'b1;endendDONE: result_rdy = 1'b1; //位于计算结束状态endcaseendalways @(*) //运算过程beginstate_next = state; // 默认保持当前状态case ( state )WAIT :state_next = CALC; CALC :if ( B_zero) beginstate_next = DONE;endDONE :state_next = WAIT;endcase
end
endmodule
`timescale 1ns / 1ps
module vcRDFF_pf
( input clk,input reset_p,input[1:0] d_p,output reg[1:0] q_np
);localparam WAIT = 2'd0;localparam CALC = 2'd1;localparam DONE = 2'd2;always @( posedge clk or posedge reset_p )
beginif( reset_p ) //当复位为1时,状态为WAITq_np <= WAIT;elseq_np <= d_p; //当复位为0时,状态为下一状态
endendmodule
计算模块
module gcdGCDUnitDpath #(parameter W = 4)
( input clk,input [W-1:0] operand_A, input [W-1:0] operand_B, output [W-1:0] result_data, input A_en, input B_en, input [1:0] A_sel, input B_sel, output B_zero, output A_lt_B
);wire [W-1:0] B;wire [W-1:0] sub_out;wire [W-1:0] A_out;// 3-1多路复用器vcMux3#(W) A_mux( .in0 (operand_A),.in1 (B),.in2 (sub_out),.sel (A_sel),.out (A_out) );wire [W-1:0] A;// register with enablevcEDFF_pf#(W) A_pf( .clk (clk),.en_p (A_en),.d_p (A_out),.q_np (A) );wire [W-1:0] B_out;// 2-1多路复用器vcMux2#(W) B_mux( .in0 (operand_B),.in1 (A),.sel (B_sel),.out (B_out) );// register with enablevcEDFF_pf#(W) B_pf( .clk (clk),.en_p (B_en),.d_p (B_out),.q_np (B) );assign B_zero = (B==0); assign A_lt_B = (A < B); assign sub_out = A - B;assign result_data = A;
endmodule
`timescale 1ns/100ps
`define A_SEL_X 2'bxx
`define A_SEL_IN 2'b00
`define A_SEL_B 2'b01
`define A_SEL_SUB 2'b10
`define B_SEL_X 1'bx
`define B_SEL_IN 1'b0
`define B_SEL_A 1'b1
module vcMux3 #(parameter W = 4)
(input[W-1:0] in0,input[W-1:0] in1,input[W-1:0] in2,input[1:0] sel,output reg[W-1:0] out
); always @(*)begincase(sel)`A_SEL_IN : out <= in0;`A_SEL_B : out <= in1;`A_SEL_SUB : out <= in2;endcaseend
endmodule
`timescale 1ns/100ps
`define A_SEL_X 2'bxx
`define A_SEL_IN 2'b00
`define A_SEL_B 2'b01
`define A_SEL_SUB 2'b10
`define B_SEL_X 1'bx
`define B_SEL_IN 1'b0
`define B_SEL_A 1'b1module vcMux2 #(parameter W = 4)
(input [W-1:0] in0,input [W-1:0] in1,input sel,output reg[W-1:0] out
);always @(sel)begincase(sel)`B_SEL_IN : out <= in0;`B_SEL_A : out <= in1;endcaseend
endmodule
`timescale 1ns/100ps
`define A_SEL_X 2'bxx
`define A_SEL_IN 2'b00
`define A_SEL_B 2'b01
`define A_SEL_SUB 2'b10
`define B_SEL_X 1'bx
`define B_SEL_IN 1'b0
`define B_SEL_A 1'b1
module vcEDFF_pf #(parameter W = 4)
(input clk,input en_p,input[W-1:0] d_p,output reg[W-1:0] q_np
);always @(posedge clk)beginif(en_p) beginq_np <= d_p;endend
endmodule
显示模块
`timescale 1ns / 1ps
module display(input clk,input reset,input bottom1,input bottom2,input bottom3,input carry_out,input [3:0] result_data,output reg [3:0] display_result,input result_rdy,input [3:0] data);always @(posedge clk or posedge reset)beginif(reset) //按下复位键,输出清0begindisplay_result <= 0;endelsebeginif(bottom3&result_rdy) //bottom3按下时,显示求和结果以及进位输出begindisplay_result <= result_data;endelse if(bottom1) //bottom1按下时,显示输入数据begindisplay_result <=data;endelse if(bottom2) //bottom2按下时,显示输入数据begindisplay_result <= data;endendend
endmodule
约束文件
##Clock signal
set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports { clk }];
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clk }];##Switches
set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { data[0] }];
set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { data[1] }];
set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { data[2] }];
set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { data[3] }]; ##Buttons
set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { reset }];
set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { bottom1 }];
set_property -dict { PACKAGE_PIN K19 IOSTANDARD LVCMOS33 } [get_ports { bottom2 }];
set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { bottom3 }]; ##LEDs
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { display_result[0] }];
set_property -dict { PACKAGE_PIN M15 IOSTANDARD LVCMOS33 } [get_ports { display_result[1] }];
set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { display_result[2] }];
set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { display_result[3] }]; ##RGB LED 5
set_property -dict { PACKAGE_PIN Y11 IOSTANDARD LVCMOS33 } [get_ports { result_rdy }];
6. 功能仿真
编写仿真文件:
`timescale 1ns / 1ps
module gcd_rtl_test;
parameter W = 4;
reg clk = 0;
reg reset = 1;
reg bottom1;
reg bottom2;
reg bottom3;
reg [W-1:0] data;
wire [W-1:0] display_result;
gcd_rtl_top #(4) gcd_rtl(.clk(clk),.reset(reset),.bottom1(bottom1),.bottom2(bottom2),.bottom3(bottom3),.display_result(display_result),.data(data)
);
always #10 clk =~clk;initial beginclk <= 0;reset <= 1;bottom1 <= 0;bottom2 <= 0;bottom3 <= 0;#20 reset = 0; #20 bottom1<=1;data <= 4'd6; #20 bottom1<=0;bottom2<=1;data<= 4'd9; #20 bottom1<=0;bottom2<=0;#20bottom3 <= 1;endendmodule
点击run simulation仿真结果如下:
当bottom1按下时,显示结果为6,即第一个输入。
当bottom2按下时,显示结果为9,即第二个输入。
当bottom3按下时,显示为3,运算正确。
7. 结果
点击generate bitstream生成比特流文件;
将zybo z7连接电脑,之后点击open hardware manager,点击open target - auto connect。最后将比特流文件烧录进ZYBO Z7。绿灯亮起,即可以正常使用功能。
拨动开关输入数据4'b 0110(6),按下右下角从右向左第二个开关(即bottom1),显示0110.
拨动开关输入数据4'b 1001(9),按下右下角从右向左第三个开关(即bottom2),显示1001.
按下右下角从右向左第四个开关(即bottom3),显示结果0011(3)。
按下右下角从右向左第一个开关(即reset),显示归0.