ZYBO Z7 实现计算最大公约数

目录

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;        //数据1
	wire [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)begin
		if(reset)                              //按下复位键时数据1,2都为0.
			begin
				operand_A<= 0;
				operand_B<= 0;
			end
		else
			begin
				if(bottom1)                    //bottom1按下时,数据1接收当前输入数据
					begin
						operand_A <= data;
					end
				else if(bottom2)               //bottom2按下时,数据2接收当前输入数据
					begin
						operand_B <= data;
					end
			end
	end
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;
		  end
		CALC:                      //位于计算状态
		  begin                    
			if ( A_lt_B )          //当A_lt_B=1时
			     begin
				    A_sel = `A_SEL_B;
				    A_en     = 1'b1;
				    B_sel = `B_SEL_A;
				    B_en      = 1'b1;
			     end
			else if ( !B_zero )      //当B_zero不为0时
			     begin
				    A_sel = `A_SEL_SUB;
				    A_en      = 1'b1;
			     end
          end
		DONE: result_rdy = 1'b1;      //位于计算结束状态
	  endcase
	end

	always @(*)                         //运算过程
	begin
	  state_next = state;              // 默认保持当前状态
	 case ( state )
	    WAIT :
		state_next = CALC;             
	    CALC :
	      if ( B_zero) begin
		state_next = DONE;
		end
	    DONE :
		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 )
begin
    if( reset_p )                   //当复位为1时,状态为WAIT
        q_np <= WAIT;
    else
        q_np <= d_p;                //当复位为0时,状态为下一状态
end

endmodule

计算模块

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 enable
	vcEDFF_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 enable
	vcEDFF_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 @(*)
	begin
		case(sel)
		`A_SEL_IN : out <= in0;
		`A_SEL_B : out <= in1;
		`A_SEL_SUB : out <= in2;
		endcase
	end
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 vcMux2 #(parameter W = 4)
(
	input [W-1:0] in0,
	input [W-1:0] in1,
	input sel,
	output reg[W-1:0] out
);
	always @(sel)
	begin
		case(sel)
		`B_SEL_IN : out <= in0;
		`B_SEL_A : out <= in1;
		endcase
	end
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)
	begin
		if(en_p) begin
			q_np <= d_p;
		end
	end
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)
	begin
		if(reset)                                     //按下复位键,输出清0
			begin
				display_result <= 0;
			end
		else
			begin
				if(bottom3&result_rdy)                  //bottom3按下时,显示求和结果以及进位输出
					begin
						display_result <= result_data;
					end
				else if(bottom1)                        //bottom1按下时,显示输入数据
					begin
						display_result <=data;
					end
				else if(bottom2)                        //bottom2按下时,显示输入数据
					begin
						display_result <= data;
					end
			end
	end
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 begin
  clk <= 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;
    #20
	    bottom3 <= 1;
  end

endmodule

点击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.

猜你喜欢

转载自blog.csdn.net/weixin_44800859/article/details/121487792
今日推荐