Verilog 锁相环参数动态自动生成,Xilinx MMCM 和 PLL 动态配置频率

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46621272/article/details/130484100


Verilog 锁相环参数动态自动生成,Xilinx MMCM 和 PLL 动态配置频率


前言

  • 本文介绍了Xilinx MMCM 和 PLL 的动态配置的方法
  • Verilog 代码计算生成 PLL_M、PLL_D、PLL_N
  • 动态输出一个自定义频率的时钟
  • 提供 VIVADO 仿真工程下载

简介

  • 本文直接采用 Xilinx MMCM/PLL 原语,没有采用 Clock Wizard IP
  • 模块综合后的电路图

在这里插入图片描述

  • 例化代码
    在这里插入图片描述
  • 动态配置的代码参考了官方的 xapp888 的例程,在文章后面有相关下载的链接
  • verilog 根据频率值(精确到HZ)自动计算 M/N/D 等锁相环参数

Verilog 代码 pll_cfg_x1.v 自动计算生成 PLL_M、PLL_D、PLL_N

  • 本代码经过了一些优化,能在 133MHz 下以较小的时间穷举计算出锁相环的各参数值。
  • 优化后的计算代码可能比较难理解,可以下载本文的VIVADO 仿真工程,在压缩包中有个未优化的原始计算代码(只能跑25MHz)
  • 计算中的约束条件如下

XILINX 7系列PLL时钟的条件约束
FIN 最小 10MHz 最大 800MHz,这个频率越大,最后计算出的频率越精准
FVCO_MIN 最小600MHz
FVCO_MAX 最大1200MHz - 1600MHz FPGA -1,-2,-3 芯片不同的后缀频率不同
PLL_D 输入分频系数,整数,1-106, 输入的分频频率 FIN/PLL_D , (FIN/PLL_D) >= 10MHz
PLL_D 的取值范围整数1-106
FVCO 为锁相环频率,FVCO >= FVCO_MIN && FVCO <= FVCO_MAX
FVCO = (FIN/PLL_D)*PLL_M
PLL_M 是个10bit的变量,是个定点小数,最小分辨率是 0.125
例如 PLL_M = 10’b01_1101_0011 就是 0x1d3/8=467/8=58.375
PLL_M 的取值范围小数 2.0-64.0, 10’b00_0001_0000-10’b10_0000_0000
clk_out 是 PLL 的输出频率,clk_out <= 800MHz
clk_out= FVCO/PLL_N=(FIN/PLL_D)*PLL_M/PLL_N
PLL_N 是输出分频系数
PLL_N 的取值范围整数1.0-128.0,最小分辨率是 0.125
例如 PLL_N = 10’b01_1101_0011 就是 0x1d3/8=467/8=58.375
多个 clk_out 输出是对应有多个 PLL_N,这几个输出共用这同一组 FIN,PLL_M,PLL_D,FVCO 系数

  • pll_cfg_x1.v
// XILINX 7系列PLL时钟的条件约束
// FIN 最小 10MHz 最大 800MHz,这个频率越大,最后计算出的频率越精准
// FVCO_MIN 最小600MHz 
// FVCO_MAX 最大1200MHz - 1600MHz FPGA -1,-2,-3 芯片不同的后缀频率不同
// PLL_D 输入分频系数,整数,1-106, 输入的分频频率  FIN/PLL_D , (FIN/PLL_D) >= 10MHz
// PLL_D 的取值范围整数1-106
// FVCO 为锁相环频率,FVCO >= FVCO_MIN && FVCO <= FVCO_MAX
// FVCO = (FIN/PLL_D)*PLL_M/8
// PLL_M 是个10bit的变量,是个定点小数,最小分辨率是 0.125
// 例如 PLL_M = 10'b01_1101_0011 就是 0x1d3/8=467/8=58.375
// PLL_M 的取值范围小数 2.0-64.0, 10'b00_0001_0000-10'b10_0000_0000
// clk_out 是 PLL 的输出频率,clk_out <= 800MHz
// clk_out= FVCO/PLL_N=(FIN/PLL_D)*PLL_M/PLL_N/8 
// PLL_N 是输出分频系数
// PLL_N 的取值范围整数1-128
// 多个 clk_out 输出是对应有多个 PLL_N,这几个输出共用这同一组 FIN,PLL_M,PLL_D,FVCO 系数

//在计算频率时,一些 0 误差的计算,结果很快就能输出(不到10ms)//一些频率无法做到 0 误差,只能尽可能的精确。在最糟糕的时候可能会有 600-700ms 的计算时间(100MHz)
module	pll_cfg_x1	#
(
	parameter	iCLK_N		= 1,				///< 输出数量 1-7
	parameter	FVCO_MIN	= 600*1000*1000,	///< XILINX 7 系FPGA FVCO 最小 600MHz
	parameter	FVCO_MAX	= 1440*1000*1000,	///< XILINX 7 系FPGA FVCO 最大频率-1 1200MHz,-2 1440MHz,-3 1600MHz
	parameter	FIN			= 100*1000*1000		///< 100MHz 输入频率
)
(
	input					clk,		//这个时钟是 DRP 参考时钟,和PLL 实际的输入时钟可以不同
										//这个代码中有些乘除法计算,建议用频率较低的时钟
	input					rst_n,
	input					star_cp,	//计算开始,这是个脉冲,高电平有效
	input	[31:0]			fout_freq,		//输出频率的频率值,单位HZ,如果有多个输出,选频率最高的值
	input	[iCLK_N*8-1:0]	divx,		//各路输出的分频比//这是个二维数组
	output	[15:0]			pll_m,		//计算输出结果,倍频值
	output	[15:0]			pll_d,		//计算输出结果,输入分频值
	output	[iCLK_N*16-1:0]	pll_n,		//计算输出结果,输出分频值//这是个二维数组
	output					pll_err,
	output					pll_done
);
//比如输出多个频率,out1=100MHz,out2=300MHz,out3=50MHz,out4=25MHz,
//fout_freq=300_000_000	//300MHz
//divx[07:00]	= fout_freq/out1=300/100=3;	//这必须能整除
//divx[15:08]	= fout_freq/out2=300/300=1;	//这必须能整除
//divx[23:16]	= fout_freq/out3=300/50 =6;	//这必须能整除
//divx[31:24]	= fout_freq/out3=300/25 =12;	//这必须能整除
//divx 的取值范围在 1-128 ,并且 divx * pll_n 必须小于等于 128
//pll_m 输出是个定点小数,二进制保留 3 位。最小颗粒是0.125 
//比如 pll_m 输出 197,这个值实际是 197/8=24.625

//D			=	1~80;
//M			=	2.0~64.0;	//步长0.125
//N			=	1.0~128.0;	//定点小数 iCLK_N==1 时步长为0.125,iCLK_N > 1 时步长为1//( Fin / D )	>= 10*10^6;			//10000000(10MHz)
//Fvco		=	( Fin / D  ) * M;
//fout_freq		=	Fvco/N;

	genvar		n;
	localparam	PLL_IDLE	= 0;
	localparam	PLL_MAX		= 1;
	localparam	PLL_START	= 2;
	localparam	PLL_OK		= 3;
	localparam	PLL_ERROR	= 4;
	localparam	F_MIN		= FVCO_MIN;
	localparam	F_MAX		= FVCO_MAX;
	localparam	PLL_N_STEP	= iCLK_N == 1 ? 1:8;

	reg		[15:0]	p_m		= 16;
	reg		[15:0]	p_n		= 1;
	reg		[15:0]	p_d		= 1;
	reg		[15:0]	s_m;
	reg		[15:0]	s_n;
	reg		[15:0]	s_d;
	reg				p_err	= 0;

	reg		[15:0]	s_m_r;
	reg		[15:0]	s_n_r[iCLK_N-1:0];
	reg		[15:0]	s_d_r;
	wire	[7:0]	divx_i[iCLK_N-1:0];
	
	reg				p_done	= 0;
	reg		[3:0]	st= 0;
	reg		[7:0]	i= 0;
	
	wire	[31:0]	fin_d;
	reg		[37:0]	f_vfo_m;
	reg		[37:0]	f_vfo_n;
	wire	[31:0]	differ_y;
	reg		[31:0]	differ_x = -1;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	wire	[15:0]	f_d_max;
	wire	[15:0]	f_m_max;
	reg		[15:0]	f_n_max;
	reg		[31:0]	fout_mult;

	wire	[31:0]	fd_rom	[255:0];		//建立一个表,用查表法来计算除法
	for(n=1;n<256;n=n+1)
	begin:for_rom_01
		assign	fd_rom[n]	= FIN/n;		//用查表法计算FIN/p_d
	end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	assign	pll_done	= p_done;
	assign	pll_err		= p_err;

	for(n=0;n<iCLK_N;n=n+1)
	begin:for_xx1	
		assign	divx_i[n]				= divx[(n+1)*8-1:n*8];
		assign	pll_n[(n+1)*16-1:n*16]	= s_n_r[n] > 128*8 ? 128*8:s_n_r[n];
	end

	assign	pll_m		= s_m_r;
	assign	pll_d		= s_d_r;
//	assign	f_vfo_n		= fout_freq/8*p_n;
	assign	fin_d		= fd_rom[p_d];			//用查表法计算//FIN/p_d;
//	assign	f_vfo_m		= fd_rom[p_d]/8*p_m;	//用查表法计算//FIN/p_d/8*p_m;
	assign	differ_y	= f_vfo_m > f_vfo_n ? (f_vfo_m-f_vfo_n):(f_vfo_n-f_vfo_m);

	assign	f_d_max		= FIN/10_000_000;
	assign	f_m_max		= FVCO_MAX/FIN*p_d*8;

	always @(posedge clk)
	begin
		if(rst_n == 0)
		begin
			st			<= PLL_IDLE;
			p_err		<= 0;
			p_done		<= 0;
		end
		else
		case(st)
		PLL_IDLE:
		begin
			f_vfo_n	<= fout_freq;//fout_freq*p_n
			f_vfo_m	<= fin_d/8*16;

			p_d			<= 1;
			p_m			<= 16;
			p_n			<= 8;
			p_done		<= 0;
			differ_x	<= -1;
			f_n_max		<= 8;
			fout_mult	<= fout_freq;
			if(star_cp == 1)
				st		<= PLL_MAX;
		end
		PLL_MAX:
		begin
			if(f_n_max < 128*8 && fout_mult < F_MAX)
			begin
				f_n_max		<= f_n_max + PLL_N_STEP;
				fout_mult	<= fout_mult + fout_freq/8*PLL_N_STEP;
			end
			else
				st			<= PLL_START;
		end
		PLL_START:
		begin
			p_err		<= 0;
			p_done		<= 0;

			if(p_d > 106 || p_d > f_d_max)
				st	<= PLL_ERROR;
			else if(fin_d >= 10*1000*1000 && f_vfo_m >= F_MIN && f_vfo_m <= F_MAX && f_vfo_n >= F_MIN && f_vfo_n <= F_MAX && p_n != 9)
			begin
				if(differ_y < differ_x)
				begin
					s_d			<= p_d;
					s_m			<= p_m;
					s_n			<= p_n;
					differ_x	<= differ_y;
				end
				if(differ_y == 0)
					st	<= PLL_OK;
			end

			if(p_m < 512 && p_m <= f_m_max)
			begin
				p_m	<= p_m + 1;
				f_vfo_m<= f_vfo_m+fin_d/8;
			end
			else
			begin
				p_m		<= 16;
				if(p_n < 128*8 && p_n <= f_n_max)
				begin
					p_n	<= p_n + PLL_N_STEP;
					f_vfo_n	<= f_vfo_n + fout_freq/8*PLL_N_STEP;	//fout_freq*p_n
					f_vfo_m	<= fin_d/8*16;
				end
				else
				begin
					p_n	<= 8;
					f_vfo_n	<= fout_freq;
					f_vfo_m	<= fd_rom[p_d+1]/8*16;
					if(p_d <= 106 && p_d <= f_d_max)
						p_d	<= p_d + 1;
				end
			end
		end
		PLL_OK:
		begin
			st		<= PLL_IDLE;
			p_done	<= 1;
			s_d_r	<= s_d;
			s_m_r	<= s_m;
			for(i=0;i<iCLK_N;i=i+1)
				s_n_r[i]	<= s_n * divx_i[i];
		end
		PLL_ERROR:
		begin
			if(differ_x == 32'hffff_ffff)
			begin
				p_err	<= 1;
				st		<= PLL_IDLE;
			end
			else
				st		<= PLL_OK;
		end
		default:
		begin
			st		<= PLL_IDLE;
			p_done	<= 0;
		end
		endcase
	end

endmodule


Verilog 代码 pll.v

  • 这代码是例化锁相环参数计算和例化 MMCM 动态加载的代码
  • 当输入频率 fout_freq 的值发生变化时,自动启动锁相环参数的计算和加载
module	pll	#
(
	parameter	iCLK_N		= 2,				///< *输出数量 1-7
	parameter	FVCO_MIN	= 600*1000*1000,	//600MHz
	parameter	FVCO_MAX	= 1440*1000*1000,	//-1 1200MHz,-2 1440MHz,-3 1600MHz
	parameter	FIN			= 100*1000*1000		//输入频率100MHz
)
(
	input						drp_clk,
	input						rst_n,
	input	[31:0]				fout_freq,
	input	[iCLK_N*8-1:0]		divx,		//各路输出的分频比列

	input   					clk_in,
    output						pll_locked,
	output  [iCLK_N-1:0]		clk_out
);


	wire	[15:0]			pll_m;
	wire	[15:0]			pll_d;
	wire	[iCLK_N*16-1:0]	pll_n;
	wire					pll_done;
	reg		[31:0]			ss_r1=0;
	reg		[31:0]			ss_r2=0;
	reg						star_cp=0;
////////////////////////////////////////////////////////////////////////////////////////////////
//这段代码,是监测 fout_freq 是否发生变化,变化后产生 star_cp 脉冲,启动 PLL 设置
////////////////////////////////////////////////////////////////////////////////////////////////
	always @(posedge drp_clk)
	begin
		if(rst_n == 0)
		begin
			ss_r1	<= 0;
			ss_r2	<= 0;
		end
		else if(pll_locked == 1)
		begin
			ss_r1	<= fout_freq;
			ss_r2	<= ss_r1;
		end
	end

	always @(posedge drp_clk)
	begin
		if(rst_n == 0)
			star_cp	<= 0;
		else if(ss_r1 != ss_r2)		//监测 fout_freq 发生了变化
			star_cp	<= 1;			//产生 star_cp 脉冲,启动 PLL 设置
		else
			star_cp	<= 0;
	end
////////////////////////////////////////////////////////////////////////////////////////////////
pll_cfg_x1	#
(
	.iCLK_N		(iCLK_N),
	.FVCO_MIN	(FVCO_MIN),
	.FVCO_MAX	(FVCO_MAX),
	.FIN		(FIN)
)
pll_cfg_ux
(
	.clk		(drp_clk),
	.rst_n		(rst_n),
	.star_cp	(star_cp),
	.fout_freq	(fout_freq),
	.divx		(divx),
	.pll_m		(pll_m),
	.pll_d		(pll_d),
	.pll_n		(pll_n),
	.pll_err	(),
	.pll_done	(pll_done)
);

pll_set		#
(
	.iCLK_N		(iCLK_N),
	.iFREQ_IN	(FIN),		///< *输入时钟频率
	.iPLL_M		(297.0),	///< *倍频系数
	.iPLL_D		(5),		///< *输入分频系数
	.iPLL_N0	(5),		///< *输出分频系数
	.iPLL_N1	(5)			///< *输出分频系数

)
pll_set_ux
(
	.star_cp	(pll_done),
	.drp_clk	(drp_clk),
    .clk_in		(clk_in),
	.rst_n		(rst_n),
	.m_pll_n	(pll_n),
	.m_pll_m	(pll_m),
	.m_pll_d	(pll_d),
    .pll_locked	(pll_locked),
	.clk_out	(clk_out)
);

endmodule



Verilog 代码 pll_set.v

  • MMCM 动态加载的代码,主要参考了官方的 xapp888 中的代码
//下面这组缺省的参数是输入100MHz,FVCO=iFREQ_IN/iPLL_D*iPLL_M/8=1250MHz,输出=FVCO/iPLL_N=125MHz
module	pll_set		#
(
	parameter	iFREQ_IN	= 100*1000*1000,		///< *输入时钟频率
	parameter	iPLL_M		= 400.0,				///< *倍频系数
	parameter	iPLL_D		= 4,					///< *输入分频系数
	parameter	iPLL_N0		= 10,					///< *输出分频系数
	parameter	iPLL_N1		= 64,					///< *输出分频系数
	parameter	iPLL_N2		= 64,					///< *输出分频系数
	parameter	iPLL_N3		= 64,					///< *输出分频系数
	parameter	iPLL_N4		= 64,					///< *输出分频系数
	parameter	iPLL_N5		= 64,					///< *输出分频系数
	parameter	iPLL_N6		= 64,					///< *输出分频系数
	parameter	iCLK_N		= 1						///< *输出数量
)
(
	input					star_cp,
	input   				drp_clk,
    input   				clk_in,
	input   				rst_n,
	input   [iCLK_N*16-1:0]	m_pll_n,
	input   [15:0]			m_pll_m,
	input   [15:0]			m_pll_d,
    output					pll_locked,
	output	[iCLK_N-1:0]	clk_out
);

	parameter	BANDWIDTH	= "OPTIMIZED";	//// "HIGH", "LOW" or "OPTIMIZED"	"LOW_SS"
	genvar	n;

	wire	[15:0]		m_clkout_d[6:0];

	for(n=0;n<7;n=n+1)
	begin:for_md0
		if(n < iCLK_N)
			assign	m_clkout_d[n]	= m_pll_n[(n+1)*16-1:n*16];
		else
			assign	m_clkout_d[n]	= 128*8;
	end
	

	// These signals are used as direct connections between the MMCM_ADV and the
	// MMCM_DRP.
	(* mark_debug = "true" *)	wire	[15:0]	di;
	(* mark_debug = "true" *)	wire	[6:0]	daddr;
	(* mark_debug = "true" *)	wire	[15:0]	dout;
	(* mark_debug = "true" *)	wire			den;
	(* mark_debug = "true" *)	wire			dwe;
	wire			dclk;
	wire			rst_mmcm;
	wire			drdy;
	wire			locked;

	wire			clkfb_bufgout;
	wire			clkfb_bufgin;

	wire	[6:0]	clk_bufgin;

    assign          pll_locked  = locked;


	BUFR BUFG_FB
	(
		.O(clkfb_bufgout),
		.I(clkfb_bufgin)
	);
	for(n=0;n<iCLK_N;n=n+1)
	begin:for_bufg
		BUFG BUFG_CLK0
		(
			.O(clk_out[n]),
			.I(clk_bufgin[n])
		);
	end


	// MMCM_ADV that reconfiguration will take place on
	MMCME2_ADV	#
	(
		// "HIGH", "LOW" or "OPTIMIZED"
		.BANDWIDTH				(BANDWIDTH),
		.DIVCLK_DIVIDE			(iPLL_D),			// (1 to 106)
		.CLKFBOUT_MULT_F		(iPLL_M/8),			// (2 to 64)
		.CLKFBOUT_PHASE			(0.0),
		.CLKFBOUT_USE_FINE_PS	("FALSE"),
		// Set the clock period (ns) of input clocks
		.CLKIN1_PERIOD			(1000*1000*1000/iFREQ_IN),
		.REF_JITTER1			(0.0),
		.CLKIN2_PERIOD			(10.000),
		.REF_JITTER2			(0.0),

		// CLKOUT parameters:
		// DIVIDE: (1 to 128)
		// DUTY_CYCLE: (0.01 to 0.99) - This is dependent on the divide value.
		// PHASE: (0.0 to 360.0) - This is dependent on the divide value.
		// USE_FINE_PS: (TRUE or FALSE)

		.CLKOUT0_DIVIDE_F		(iPLL_N0),
		.CLKOUT0_DUTY_CYCLE		(0.5),
		.CLKOUT0_PHASE			(0.0),
		.CLKOUT0_USE_FINE_PS	("FALSE"),

		.CLKOUT1_DIVIDE			(iPLL_N1),
		.CLKOUT1_DUTY_CYCLE		(0.5),
		.CLKOUT1_PHASE			(0.0),
		.CLKOUT1_USE_FINE_PS	("FALSE"),

		.CLKOUT2_DIVIDE			(iPLL_N2),
		.CLKOUT2_DUTY_CYCLE		(0.5),
		.CLKOUT2_PHASE			(0.0),
		.CLKOUT2_USE_FINE_PS	("FALSE"),

		.CLKOUT3_DIVIDE			(iPLL_N3),
		.CLKOUT3_DUTY_CYCLE		(0.5),
		.CLKOUT3_PHASE			(0.0),
		.CLKOUT3_USE_FINE_PS	("FALSE"),

		.CLKOUT4_DIVIDE			(iPLL_N4),
		.CLKOUT4_DUTY_CYCLE		(0.5),
		.CLKOUT4_PHASE			(0.0),
		.CLKOUT4_USE_FINE_PS	("FALSE"),
		.CLKOUT4_CASCADE		("FALSE"),

		.CLKOUT5_DIVIDE			(iPLL_N5),
		.CLKOUT5_DUTY_CYCLE		(0.5),
		.CLKOUT5_PHASE			(0.0),
		.CLKOUT5_USE_FINE_PS	("FALSE"),

		.CLKOUT6_DIVIDE			(iPLL_N6),
		.CLKOUT6_DUTY_CYCLE		(0.5),
		.CLKOUT6_PHASE			(0.0),
		.CLKOUT6_USE_FINE_PS	("FALSE"),

		// Misc parameters
		.COMPENSATION			("ZHOLD"),
		.STARTUP_WAIT			("FALSE")
	)
	mmcme2_test_inst
	(
		.CLKFBOUT				(clkfb_bufgin),
		.CLKFBOUTB				(),

		.CLKFBSTOPPED			(),
		.CLKINSTOPPED			(),

		// Clock outputs
		.CLKOUT0				(clk_bufgin[0]),
		.CLKOUT0B				(),
		.CLKOUT1				(clk_bufgin[1]),
		.CLKOUT1B				(),
		.CLKOUT2				(clk_bufgin[2]),
		.CLKOUT2B				(),
		.CLKOUT3				(clk_bufgin[3]),
		.CLKOUT3B				(),
		.CLKOUT4				(clk_bufgin[4]),
		.CLKOUT5				(clk_bufgin[5]),
		.CLKOUT6				(clk_bufgin[6]),

		// DRP Ports
		.DO						(dout),				// (16-bits)
		.DRDY					(drdy),
		.DADDR					(daddr),			// 5 bits
		.DCLK					(dclk),
		.DEN					(den),
		.DI						(di),				// 16 bits
		.DWE					(dwe),

		.LOCKED					(locked),
		.CLKFBIN				(clkfb_bufgout),

		// Clock inputs
		.CLKIN1					(clk_in),
		.CLKIN2					(),
		.CLKINSEL				(1'b1),

		// Fine phase shifting
		.PSDONE					(),
		.PSCLK					(1'b0),
		.PSEN					(1'b0),
		.PSINCDEC				(1'b0),

		.PWRDWN					(1'b0),
		.RST					(rst_mmcm)
	);

	clock_mmcme2_drp	#
	(
		.S1_BANDWIDTH		(BANDWIDTH)	// "HIGH", "LOW" or "OPTIMIZED"	"LOW_SS"
	)
	mmcme2_drp_inst
	(
	
		.s1_clkfbout_mult	(m_pll_m),
		.s1_divclk_divide	(m_pll_d),
		.s1_clkout0_divide	(m_clkout_d[0]),
		.s1_clkout1_divide	(m_clkout_d[1]),
		.s1_clkout2_divide	(m_clkout_d[2]),
		.s1_clkout3_divide	(m_clkout_d[3]),
		.s1_clkout4_divide	(m_clkout_d[4]),
		.s1_clkout5_divide	(m_clkout_d[5]),
		.s1_clkout6_divide	(m_clkout_d[6]),
			
		.drp_den			(star_cp),
		.drp_sclk			(drp_clk),
		.drp_rst_n			(rst_n),
		.drp_srdy			(),
	//////////////////////////////////////////////////////////////////////////////////////////////////
		.DO					(dout),
		.DRDY				(drdy),
		.LOCKED				(locked),
		.DWE				(dwe),
		.DEN				(den),
		.DADDR				(daddr),
		.DI					(di),
		.DCLK				(dclk),
		.RST_MMCM			(rst_mmcm)
	);


endmodule

module clock_mmcme2_drp	#
(
		parameter S1_BANDWIDTH  			= "LOW"		// "HIGH", "LOW" or "OPTIMIZED"	"LOW_SS"

)
(
		input	[15:0]	s1_clkfbout_mult,
		input	[15:0]	s1_divclk_divide,
		input	[15:0]	s1_clkout0_divide,
		input	[15:0]	s1_clkout1_divide,
		input	[15:0]	s1_clkout2_divide,
		input	[15:0]	s1_clkout3_divide,
		input	[15:0]	s1_clkout4_divide,
		input	[15:0]	s1_clkout5_divide,
		input	[15:0]	s1_clkout6_divide,

		
		input			drp_den,
		input			drp_sclk,
		input			drp_rst_n,
		output	reg		drp_srdy,
//////////////////////////////////////////////////////////////////////////////////////////////////
		input	[15:0]			DO,
		input					DRDY,
		input					LOCKED,
		output	reg			DWE,
		output	reg			DEN,
		output	reg [6:0]		DADDR,
		output	reg [15:0]	DI,
		output					DCLK,
		output	reg			RST_MMCM
);

	function [31:0] mmcm_divider
	(
		input	[7:0]	divide		// Max divide is 128
	);

	reg	[6:0]	high_time;
	reg	[6:0]	low_time;
	reg			w_edge;
	reg			no_count;

	begin
		if(divide <= 1)
		begin
			high_time	= 1;
			w_edge		= 0;
			low_time	= 1;
			no_count	= 1;
		end
		else
		begin
			high_time	= divide >> 1;
			w_edge		= divide[0];
			low_time	= divide - high_time;
			no_count	= 0;
		end
		mmcm_divider[31:16] = {
    
    8'b0,w_edge,no_count,6'b0};
		mmcm_divider[15:00] = {
    
    4'b0,high_time[5:0],low_time[5:0]};
	end
	endfunction

	function [35:0] mmcm_divider_frac
	(
		input [9:0] mult	 // Max mult is 128
	);
		reg  [7:0]     lt_frac;
		reg  [7:0]     ht_frac;
		reg            wf_fall_frac;
		reg            wf_rise_frac;

		reg [15:0]     drp_reg1;
		reg [15:0]     drp_reg2;
		reg  [3:0]     drp_regshared;


	begin
		lt_frac = mult[9:4] - (mult[3:0] <= 9);
		ht_frac = mult[9:4] - (mult[3:0] <= 8);
		wf_fall_frac = ((mult[3:0] >=2) && (mult[3:0] <=9)) || (mult == 'h11);
		wf_rise_frac = (mult[3:0] >=1) && (mult[3:0] <=8);

		drp_regshared[3:0] = {
    
     mult[3:1], wf_fall_frac};
		drp_reg2[15:0] = {
    
     1'b0, mult[2:0], 1'b1, wf_rise_frac, 10'h0};
		drp_reg1[15:0] = {
    
     4'b0, ht_frac[5:0], lt_frac[5:0] };
		mmcm_divider_frac[35:0] =   {
    
    drp_regshared, drp_reg2, drp_reg1};
	end
	endfunction

	
	wire	[9:0]	lookup_low	[63:0];

	assign	lookup_low[00]	=	10'b0010_1111_00; // 1
	assign	lookup_low[01]	=	10'b0010_1111_00; // 2
	assign	lookup_low[02]	=	10'b0010_1111_00; // 3
	assign	lookup_low[03]	=	10'b0010_1111_00; // 4
	assign	lookup_low[04]	=	10'b0010_0111_00; // ....
	assign	lookup_low[05]	=	10'b0010_1011_00;
	assign	lookup_low[06]	=	10'b0010_1101_00;
	assign	lookup_low[07]	=	10'b0010_0011_00;
	assign	lookup_low[08]	=	10'b0010_0101_00;
	assign	lookup_low[09]	=	10'b0010_0101_00;
	assign	lookup_low[10]	=	10'b0010_1001_00;
	assign	lookup_low[11]	=	10'b0010_1110_00;
	assign	lookup_low[12]	=	10'b0010_1110_00;
	assign	lookup_low[13]	=	10'b0010_1110_00;
	assign	lookup_low[14]	=	10'b0010_1110_00;
	assign	lookup_low[15]	=	10'b0010_0001_00;
	assign	lookup_low[16]	=	10'b0010_0001_00;
	assign	lookup_low[17]	=	10'b0010_0001_00;
	assign	lookup_low[18]	=	10'b0010_0110_00;
	assign	lookup_low[19]	=	10'b0010_0110_00;
	assign	lookup_low[20]	=	10'b0010_0110_00;
	assign	lookup_low[21]	=	10'b0010_0110_00;
	assign	lookup_low[22]	=	10'b0010_0110_00;
	assign	lookup_low[23]	=	10'b0010_0110_00;
	assign	lookup_low[24]	=	10'b0010_0110_00;
	assign	lookup_low[25]	=	10'b0010_1010_00;
	assign	lookup_low[26]	=	10'b0010_1010_00;
	assign	lookup_low[27]	=	10'b0010_1010_00;
	assign	lookup_low[28]	=	10'b0010_1010_00;
	assign	lookup_low[29]	=	10'b0010_1010_00;
	assign	lookup_low[30]	=	10'b0010_1100_00;
	assign	lookup_low[31]	=	10'b0010_1100_00;
	assign	lookup_low[32]	=	10'b0010_1100_00;
	assign	lookup_low[33]	=	10'b0010_1100_00;
	assign	lookup_low[34]	=	10'b0010_1100_00;
	assign	lookup_low[35]	=	10'b0010_1100_00;
	assign	lookup_low[36]	=	10'b0010_1100_00;
	assign	lookup_low[37]	=	10'b0010_1100_00;
	assign	lookup_low[38]	=	10'b0010_1100_00;
	assign	lookup_low[39]	=	10'b0010_1100_00;
	assign	lookup_low[40]	=	10'b0010_1100_00;
	assign	lookup_low[41]	=	10'b0010_1100_00;
	assign	lookup_low[42]	=	10'b0010_1100_00;
	assign	lookup_low[43]	=	10'b0010_1100_00;
	assign	lookup_low[44]	=	10'b0010_1100_00;
	assign	lookup_low[45]	=	10'b0010_1100_00;
	assign	lookup_low[46]	=	10'b0010_1100_00;
	assign	lookup_low[47]	=	10'b0010_0010_00;
	assign	lookup_low[48]	=	10'b0010_0010_00;
	assign	lookup_low[49]	=	10'b0010_0010_00;
	assign	lookup_low[50]	=	10'b0010_0010_00;
	assign	lookup_low[51]	=	10'b0010_0010_00;
	assign	lookup_low[52]	=	10'b0010_0010_00;
	assign	lookup_low[53]	=	10'b0010_0010_00;
	assign	lookup_low[54]	=	10'b0010_0010_00;
	assign	lookup_low[55]	=	10'b0010_0010_00;
	assign	lookup_low[56]	=	10'b0010_0010_00;
	assign	lookup_low[57]	=	10'b0010_0010_00;
	assign	lookup_low[58]	=	10'b0010_0010_00;
	assign	lookup_low[59]	=	10'b0010_0010_00; // ....
	assign	lookup_low[60]	=	10'b0010_0010_00; // 61
	assign	lookup_low[61]	=	10'b0010_0010_00; // 62
	assign	lookup_low[62]	=	10'b0010_0010_00; // 63
	assign	lookup_low[63]	=	10'b0010_0010_00; // 64

	wire	[9:0]	lookup_low_ss	[63:0];

	assign	lookup_low_ss[00]	=	10'b0010_1111_11; // 1
	assign	lookup_low_ss[01]	=	10'b0010_1111_11; // 2
	assign	lookup_low_ss[02]	=	10'b0010_1111_11; // 3
	assign	lookup_low_ss[03]	=	10'b0010_1111_11; // 4
	assign	lookup_low_ss[04]	=	10'b0010_0111_11; // ....
	assign	lookup_low_ss[05]	=	10'b0010_1011_11;
	assign	lookup_low_ss[06]	=	10'b0010_1101_11;
	assign	lookup_low_ss[07]	=	10'b0010_0011_11;
	assign	lookup_low_ss[08]	=	10'b0010_0101_11;
	assign	lookup_low_ss[09]	=	10'b0010_0101_11;
	assign	lookup_low_ss[10]	=	10'b0010_1001_11;
	assign	lookup_low_ss[11]	=	10'b0010_1110_11;
	assign	lookup_low_ss[12]	=	10'b0010_1110_11;
	assign	lookup_low_ss[13]	=	10'b0010_1110_11;
	assign	lookup_low_ss[14]	=	10'b0010_1110_11;
	assign	lookup_low_ss[15]	=	10'b0010_0001_11;
	assign	lookup_low_ss[16]	=	10'b0010_0001_11;
	assign	lookup_low_ss[17]	=	10'b0010_0001_11;
	assign	lookup_low_ss[18]	=	10'b0010_0110_11;
	assign	lookup_low_ss[19]	=	10'b0010_0110_11;
	assign	lookup_low_ss[20]	=	10'b0010_0110_11;
	assign	lookup_low_ss[21]	=	10'b0010_0110_11;
	assign	lookup_low_ss[22]	=	10'b0010_0110_11;
	assign	lookup_low_ss[23]	=	10'b0010_0110_11;
	assign	lookup_low_ss[24]	=	10'b0010_0110_11;
	assign	lookup_low_ss[25]	=	10'b0010_1010_11;
	assign	lookup_low_ss[26]	=	10'b0010_1010_11;
	assign	lookup_low_ss[27]	=	10'b0010_1010_11;
	assign	lookup_low_ss[28]	=	10'b0010_1010_11;
	assign	lookup_low_ss[29]	=	10'b0010_1010_11;
	assign	lookup_low_ss[30]	=	10'b0010_1100_11;
	assign	lookup_low_ss[31]	=	10'b0010_1100_11;
	assign	lookup_low_ss[32]	=	10'b0010_1100_11;
	assign	lookup_low_ss[33]	=	10'b0010_1100_11;
	assign	lookup_low_ss[34]	=	10'b0010_1100_11;
	assign	lookup_low_ss[35]	=	10'b0010_1100_11;
	assign	lookup_low_ss[36]	=	10'b0010_1100_11;
	assign	lookup_low_ss[37]	=	10'b0010_1100_11;
	assign	lookup_low_ss[38]	=	10'b0010_1100_11;
	assign	lookup_low_ss[39]	=	10'b0010_1100_11;
	assign	lookup_low_ss[40]	=	10'b0010_1100_11;
	assign	lookup_low_ss[41]	=	10'b0010_1100_11;
	assign	lookup_low_ss[42]	=	10'b0010_1100_11;
	assign	lookup_low_ss[43]	=	10'b0010_1100_11;
	assign	lookup_low_ss[44]	=	10'b0010_1100_11;
	assign	lookup_low_ss[45]	=	10'b0010_1100_11;
	assign	lookup_low_ss[46]	=	10'b0010_1100_11;
	assign	lookup_low_ss[47]	=	10'b0010_0010_11;
	assign	lookup_low_ss[48]	=	10'b0010_0010_11;
	assign	lookup_low_ss[49]	=	10'b0010_0010_11;
	assign	lookup_low_ss[50]	=	10'b0010_0010_11;
	assign	lookup_low_ss[51]	=	10'b0010_0010_11;
	assign	lookup_low_ss[52]	=	10'b0010_0010_11;
	assign	lookup_low_ss[53]	=	10'b0010_0010_11;
	assign	lookup_low_ss[54]	=	10'b0010_0010_11;
	assign	lookup_low_ss[55]	=	10'b0010_0010_11;
	assign	lookup_low_ss[56]	=	10'b0010_0010_11;
	assign	lookup_low_ss[57]	=	10'b0010_0010_11;
	assign	lookup_low_ss[58]	=	10'b0010_0010_11;
	assign	lookup_low_ss[59]	=	10'b0010_0010_11; // ....
	assign	lookup_low_ss[60]	=	10'b0010_0010_11; // 61
	assign	lookup_low_ss[61]	=	10'b0010_0010_11; // 62
	assign	lookup_low_ss[62]	=	10'b0010_0010_11; // 63
	assign	lookup_low_ss[63]	=	10'b0010_0010_11; // 64

	wire	[9:0]	lookup_high	[63:0];

	assign	lookup_high[00]	=	10'b0010_1111_00; // 1
	assign	lookup_high[01]	=	10'b0100_1111_00; // 2
	assign	lookup_high[02]	=	10'b0101_1011_00; // 3
	assign	lookup_high[03]	=	10'b0111_0111_00; // 4
	assign	lookup_high[04]	=	10'b1101_0111_00; // ....
	assign	lookup_high[05]	=	10'b1110_1011_00;
	assign	lookup_high[06]	=	10'b1110_1101_00;
	assign	lookup_high[07]	=	10'b1111_0011_00;
	assign	lookup_high[08]	=	10'b1110_0101_00;
	assign	lookup_high[09]	=	10'b1111_0101_00;
	assign	lookup_high[10]	=	10'b1111_1001_00;
	assign	lookup_high[11]	=	10'b1101_0001_00;
	assign	lookup_high[12]	=	10'b1111_1001_00;
	assign	lookup_high[13]	=	10'b1111_1001_00;
	assign	lookup_high[14]	=	10'b1111_1001_00;
	assign	lookup_high[15]	=	10'b1111_1001_00;
	assign	lookup_high[16]	=	10'b1111_0101_00;
	assign	lookup_high[17]	=	10'b1111_0101_00;
	assign	lookup_high[18]	=	10'b1100_0001_00;
	assign	lookup_high[19]	=	10'b1100_0001_00;
	assign	lookup_high[20]	=	10'b1100_0001_00;
	assign	lookup_high[21]	=	10'b0101_1100_00;
	assign	lookup_high[22]	=	10'b0101_1100_00;
	assign	lookup_high[23]	=	10'b0101_1100_00;
	assign	lookup_high[24]	=	10'b0101_1100_00;
	assign	lookup_high[25]	=	10'b0011_0100_00;
	assign	lookup_high[26]	=	10'b0011_0100_00;
	assign	lookup_high[27]	=	10'b0011_0100_00;
	assign	lookup_high[28]	=	10'b0011_0100_00;
	assign	lookup_high[29]	=	10'b0011_0100_00;
	assign	lookup_high[30]	=	10'b0011_0100_00;
	assign	lookup_high[31]	=	10'b0011_0100_00;
	assign	lookup_high[32]	=	10'b0011_0100_00;
	assign	lookup_high[33]	=	10'b0011_0100_00;
	assign	lookup_high[34]	=	10'b0011_0100_00;
	assign	lookup_high[35]	=	10'b0011_0100_00;
	assign	lookup_high[36]	=	10'b0011_0100_00;
	assign	lookup_high[37]	=	10'b0011_0100_00;
	assign	lookup_high[38]	=	10'b0011_0100_00;
	assign	lookup_high[39]	=	10'b0011_0100_00;
	assign	lookup_high[40]	=	10'b0011_0100_00;
	assign	lookup_high[41]	=	10'b0010_1000_00;
	assign	lookup_high[42]	=	10'b0010_1000_00;
	assign	lookup_high[43]	=	10'b0010_1000_00;
	assign	lookup_high[44]	=	10'b0010_1000_00;
	assign	lookup_high[45]	=	10'b0010_1000_00;
	assign	lookup_high[46]	=	10'b0111_0001_00;
	assign	lookup_high[47]	=	10'b0111_0001_00;
	assign	lookup_high[48]	=	10'b0100_1100_00;
	assign	lookup_high[49]	=	10'b0100_1100_00;
	assign	lookup_high[50]	=	10'b0100_1100_00;
	assign	lookup_high[51]	=	10'b0100_1100_00;
	assign	lookup_high[52]	=	10'b0110_0001_00;
	assign	lookup_high[53]	=	10'b0110_0001_00;
	assign	lookup_high[54]	=	10'b0101_0110_00;
	assign	lookup_high[55]	=	10'b0101_0110_00;
	assign	lookup_high[56]	=	10'b0101_0110_00;
	assign	lookup_high[57]	=	10'b0010_0100_00;
	assign	lookup_high[58]	=	10'b0010_0100_00;
	assign	lookup_high[59]	=	10'b0010_0100_00; // ....
	assign	lookup_high[60]	=	10'b0010_0100_00; // 61
	assign	lookup_high[61]	=	10'b0100_1010_00; // 62
	assign	lookup_high[62]	=	10'b0011_1100_00; // 63
	assign	lookup_high[63]	=	10'b0011_1100_00; // 64


	wire	[9:0]	lookup_optimized	[63:0];
	
	assign	lookup_optimized[00]	=	10'b0010_1111_00; // 1
	assign	lookup_optimized[01]	=	10'b0100_1111_00; // 2
	assign	lookup_optimized[02]	=	10'b0101_1011_00; // 3
	assign	lookup_optimized[03]	=	10'b0111_0111_00; // 4
	assign	lookup_optimized[04]	=	10'b1101_0111_00; // ....
	assign	lookup_optimized[05]	=	10'b1110_1011_00;
	assign	lookup_optimized[06]	=	10'b1110_1101_00;
	assign	lookup_optimized[07]	=	10'b1111_0011_00;
	assign	lookup_optimized[08]	=	10'b1110_0101_00;
	assign	lookup_optimized[09]	=	10'b1111_0101_00;
	assign	lookup_optimized[10]	=	10'b1111_1001_00;
	assign	lookup_optimized[11]	=	10'b1101_0001_00;
	assign	lookup_optimized[12]	=	10'b1111_1001_00;
	assign	lookup_optimized[13]	=	10'b1111_1001_00;
	assign	lookup_optimized[14]	=	10'b1111_1001_00;
	assign	lookup_optimized[15]	=	10'b1111_1001_00;
	assign	lookup_optimized[16]	=	10'b1111_0101_00;
	assign	lookup_optimized[17]	=	10'b1111_0101_00;
	assign	lookup_optimized[18]	=	10'b1100_0001_00;
	assign	lookup_optimized[19]	=	10'b1100_0001_00;
	assign	lookup_optimized[20]	=	10'b1100_0001_00;
	assign	lookup_optimized[21]	=	10'b0101_1100_00;
	assign	lookup_optimized[22]	=	10'b0101_1100_00;
	assign	lookup_optimized[23]	=	10'b0101_1100_00;
	assign	lookup_optimized[24]	=	10'b0101_1100_00;
	assign	lookup_optimized[25]	=	10'b0011_0100_00;
	assign	lookup_optimized[26]	=	10'b0011_0100_00;
	assign	lookup_optimized[27]	=	10'b0011_0100_00;
	assign	lookup_optimized[28]	=	10'b0011_0100_00;
	assign	lookup_optimized[29]	=	10'b0011_0100_00;
	assign	lookup_optimized[30]	=	10'b0011_0100_00;
	assign	lookup_optimized[31]	=	10'b0011_0100_00;
	assign	lookup_optimized[32]	=	10'b0011_0100_00;
	assign	lookup_optimized[33]	=	10'b0011_0100_00;
	assign	lookup_optimized[34]	=	10'b0011_0100_00;
	assign	lookup_optimized[35]	=	10'b0011_0100_00;
	assign	lookup_optimized[36]	=	10'b0011_0100_00;
	assign	lookup_optimized[37]	=	10'b0011_0100_00;
	assign	lookup_optimized[38]	=	10'b0011_0100_00;
	assign	lookup_optimized[39]	=	10'b0011_0100_00;
	assign	lookup_optimized[40]	=	10'b0011_0100_00;
	assign	lookup_optimized[41]	=	10'b0010_1000_00;
	assign	lookup_optimized[42]	=	10'b0010_1000_00;
	assign	lookup_optimized[43]	=	10'b0010_1000_00;
	assign	lookup_optimized[44]	=	10'b0010_1000_00;
	assign	lookup_optimized[45]	=	10'b0010_1000_00;
	assign	lookup_optimized[46]	=	10'b0111_0001_00;
	assign	lookup_optimized[47]	=	10'b0111_0001_00;
	assign	lookup_optimized[48]	=	10'b0100_1100_00;
	assign	lookup_optimized[49]	=	10'b0100_1100_00;
	assign	lookup_optimized[50]	=	10'b0100_1100_00;
	assign	lookup_optimized[51]	=	10'b0100_1100_00;
	assign	lookup_optimized[52]	=	10'b0110_0001_00;
	assign	lookup_optimized[53]	=	10'b0110_0001_00;
	assign	lookup_optimized[54]	=	10'b0101_0110_00;
	assign	lookup_optimized[55]	=	10'b0101_0110_00;
	assign	lookup_optimized[56]	=	10'b0101_0110_00;
	assign	lookup_optimized[57]	=	10'b0010_0100_00;
	assign	lookup_optimized[58]	=	10'b0010_0100_00;
	assign	lookup_optimized[59]	=	10'b0010_0100_00; // ....
	assign	lookup_optimized[60]	=	10'b0010_0100_00; // 61
	assign	lookup_optimized[61]	=	10'b0100_1010_00; // 62
	assign	lookup_optimized[62]	=	10'b0011_1100_00; // 63
	assign	lookup_optimized[63]	=	10'b0011_1100_00; // 64
	
	wire	[39:0]	lookup	[63:0];

	assign	lookup[00]	=	40'b00110_00110_1111101000_1111101001_0000000001;
	assign	lookup[01]	=	40'b00110_00110_1111101000_1111101001_0000000001;
	assign	lookup[02]	=	40'b01000_01000_1111101000_1111101001_0000000001;
	assign	lookup[03]	=	40'b01011_01011_1111101000_1111101001_0000000001;
	assign	lookup[04]	=	40'b01110_01110_1111101000_1111101001_0000000001;
	assign	lookup[05]	=	40'b10001_10001_1111101000_1111101001_0000000001;
	assign	lookup[06]	=	40'b10011_10011_1111101000_1111101001_0000000001;
	assign	lookup[07]	=	40'b10110_10110_1111101000_1111101001_0000000001;
	assign	lookup[08]	=	40'b11001_11001_1111101000_1111101001_0000000001;
	assign	lookup[09]	=	40'b11100_11100_1111101000_1111101001_0000000001;
	assign	lookup[10]	=	40'b11111_11111_1110000100_1111101001_0000000001;
	assign	lookup[11]	=	40'b11111_11111_1100111001_1111101001_0000000001;
	assign	lookup[12]	=	40'b11111_11111_1011101110_1111101001_0000000001;
	assign	lookup[13]	=	40'b11111_11111_1010111100_1111101001_0000000001;
	assign	lookup[14]	=	40'b11111_11111_1010001010_1111101001_0000000001;
	assign	lookup[15]	=	40'b11111_11111_1001110001_1111101001_0000000001;
	assign	lookup[16]	=	40'b11111_11111_1000111111_1111101001_0000000001;
	assign	lookup[17]	=	40'b11111_11111_1000100110_1111101001_0000000001;
	assign	lookup[18]	=	40'b11111_11111_1000001101_1111101001_0000000001;
	assign	lookup[19]	=	40'b11111_11111_0111110100_1111101001_0000000001;
	assign	lookup[20]	=	40'b11111_11111_0111011011_1111101001_0000000001;
	assign	lookup[21]	=	40'b11111_11111_0111000010_1111101001_0000000001;
	assign	lookup[22]	=	40'b11111_11111_0110101001_1111101001_0000000001;
	assign	lookup[23]	=	40'b11111_11111_0110010000_1111101001_0000000001;
	assign	lookup[24]	=	40'b11111_11111_0110010000_1111101001_0000000001;
	assign	lookup[25]	=	40'b11111_11111_0101110111_1111101001_0000000001;
	assign	lookup[26]	=	40'b11111_11111_0101011110_1111101001_0000000001;
	assign	lookup[27]	=	40'b11111_11111_0101011110_1111101001_0000000001;
	assign	lookup[28]	=	40'b11111_11111_0101000101_1111101001_0000000001;
	assign	lookup[29]	=	40'b11111_11111_0101000101_1111101001_0000000001;
	assign	lookup[30]	=	40'b11111_11111_0100101100_1111101001_0000000001;
	assign	lookup[31]	=	40'b11111_11111_0100101100_1111101001_0000000001;
	assign	lookup[32]	=	40'b11111_11111_0100101100_1111101001_0000000001;
	assign	lookup[33]	=	40'b11111_11111_0100010011_1111101001_0000000001;
	assign	lookup[34]	=	40'b11111_11111_0100010011_1111101001_0000000001;
	assign	lookup[35]	=	40'b11111_11111_0100010011_1111101001_0000000001;
	assign	lookup[36]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[37]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[38]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[39]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[40]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[41]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[42]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[43]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[44]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[45]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[46]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[47]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[48]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[49]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[50]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[51]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[52]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[53]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[54]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[55]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[56]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[57]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[58]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[59]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[60]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[61]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[62]	=	40'b11111_11111_0011111010_1111101001_0000000001;
	assign	lookup[63]	=	40'b11111_11111_0011111010_1111101001_0000000001;

	
	// Make sure the memory is implemented as distributed
	(* ram_style = "distributed" *)
	reg	[38:0]  ram [23:0];
	reg	[7:0]   ram_addr;
	reg	[7:0]   ram_rd_addr	= 255;
	reg	[38:0]  ram_do;
	reg			drp_sen=0;
	reg			next_srdy;

	reg	[5:0]	next_ram_addr;
	reg	[6:0]	next_daddr;
	reg			next_dwe;
	reg			next_den;
	reg			next_rst_mmcm;
	reg	[15:0]	next_di;
	
	reg	[31:0]	s1_clkout0;
	reg	[31:0]	s1_clkout1;
	reg	[31:0]	s1_clkout2;
	reg	[31:0]	s1_clkout3;
	reg	[31:0]	s1_clkout4;
	reg	[31:0]	s1_clkout5;
	reg	[31:0]	s1_clkout5x;
	reg	[31:0]	s1_clkout6;
	reg	[31:0]	s1_clkout6x;
	reg	[31:0]	s1_clkfbout;
	reg	[31:0]	s1_divclk;
	wire	[7:0]	s1_clkfbout_mult_int;
	
	reg	[3:0]	frac_ck0;
	reg	[3:0]	frac_fb;
	
	reg	[39:0]	s1_lock;
	reg	[9:0]	s1_digital_filt;
	assign DCLK = drp_sclk;

	always @ (posedge drp_sclk)
	begin
		if(drp_rst_n == 0)
			ram_rd_addr	<= #1 255;
		else if(drp_den == 1)
			ram_rd_addr	<= #1 0;
		else if(ram_rd_addr != 255)
			ram_rd_addr	<= #1 ram_rd_addr + 1;
	end
   
	always @ (posedge drp_sclk)
	begin
		if(drp_rst_n == 0 || drp_den == 1)
			drp_sen	<= #1 0;
		else if(ram_rd_addr == 32)
			drp_sen	<= #1 1;
		else
			drp_sen	<= #1 0;
	end

	
	assign	s1_clkfbout_mult_int	= s1_clkfbout_mult/8;

	always @ (posedge drp_sclk)
	begin

		{
    
    frac_fb,s1_clkfbout}	<= #1 mmcm_divider_frac(s1_clkfbout_mult);

		s1_clkout1	<= #1 mmcm_divider(s1_clkout1_divide/8);
		s1_clkout2	<= #1 mmcm_divider(s1_clkout2_divide/8);
		s1_clkout3	<= #1 mmcm_divider(s1_clkout3_divide/8);
		s1_clkout4	<= #1 mmcm_divider(s1_clkout4_divide/8);
		s1_clkout5x	<= #1 mmcm_divider(s1_clkout5_divide/8);
		s1_clkout6x	<= #1 mmcm_divider(s1_clkout6_divide/8);
		s1_clkout6[15:00]		<= #1 s1_clkout6x[15:0];
		s1_clkout6[31:16]		<= #1 {
    
    s1_clkout6x[31:30], frac_fb[3:0],s1_clkout6x[25:16]};

		s1_clkout5[15:00]		<= #1 s1_clkout5x[15:0];
		if(s1_clkout0_divide[2:0] != 0)
		begin
			{
    
    frac_ck0,s1_clkout0}	<= #1 mmcm_divider_frac(s1_clkout0_divide);
			s1_clkout5[31:16]		<= #1 {
    
    s1_clkout5x[31:30], frac_ck0[3:0],s1_clkout5x[25:16]};
		end
		else
		begin
			s1_clkout0				<= #1 mmcm_divider(s1_clkout0_divide/8);
			s1_clkout5[31:16]		<= #1 s1_clkout5x[31:16];
		end

		s1_divclk	<= #1 mmcm_divider(s1_divclk_divide);

		if(s1_clkfbout_mult_int !=0)
		begin
			s1_digital_filt		<= #1	S1_BANDWIDTH == "LOW"		?	lookup_low		[s1_clkfbout_mult_int-1]:
										S1_BANDWIDTH == "LOW_SS"	?	lookup_low_ss	[s1_clkfbout_mult_int-1]:
										S1_BANDWIDTH == "HIGH"		?	lookup_high		[s1_clkfbout_mult_int-1]:
										S1_BANDWIDTH == "OPTIMIZED" ?	lookup_optimized[s1_clkfbout_mult_int-1]:0;

			s1_lock				<= #1	lookup[s1_clkfbout_mult_int-1];					
		end
		else
		begin
			s1_digital_filt		<= #1	S1_BANDWIDTH == "LOW"		?	lookup_low		[0]:
										S1_BANDWIDTH == "LOW_SS"	?	lookup_low_ss	[0]:
										S1_BANDWIDTH == "HIGH"		?	lookup_high		[0]:
										S1_BANDWIDTH == "OPTIMIZED" ?	lookup_optimized[0]:0;

			s1_lock				<= #1	lookup[0];					
		end
	end

	always @ (posedge drp_sclk)
	begin
		case(ram_rd_addr)
		0:	ram[0]	<= #1 {
    
    7'h28, 16'h0000, 16'hffff};				// store the power bits
		1:	ram[1]	<= #1 {
    
    7'h08, 16'h1000, s1_clkout0[15:0]};
		2:	ram[2]	<= #1 {
    
    7'h09, 16'h8000, s1_clkout0[31:16]};		// store clkout0
		
	  
		3:	ram[3]	<= #1 {
    
    7'h0a, 16'h1000, s1_clkout1[15:0]};		// store clkout1
		4:	ram[4]	<= #1 {
    
    7'h0b, 16'hfc00, s1_clkout1[31:16]};

		5:	ram[5]	<= #1 {
    
    7'h0c, 16'h1000, s1_clkout2[15:0]};		// store clkout2
		6:	ram[6]	<= #1 {
    
    7'h0d, 16'hfc00, s1_clkout2[31:16]};

		7:	ram[7]	<= #1 {
    
    7'h0e, 16'h1000, s1_clkout3[15:0]};		// store clkout3
		8:	ram[8]	<= #1 {
    
    7'h0f, 16'hfc00, s1_clkout3[31:16]};

		9:	ram[9]	<= #1 {
    
    7'h10, 16'h1000, s1_clkout4[15:0]};		// store clkout4
		10:	ram[10]	<= #1 {
    
    7'h11, 16'hfc00, s1_clkout4[31:16]};

		11:	ram[11]	<= #1 {
    
    7'h06, 16'h1000, s1_clkout5[15:0]};		// store clkout5
		12:	ram[12]	<= #1 {
    
    7'h07, 16'hc000, s1_clkout5[31:16]};

		13:	ram[13]	<= #1 {
    
    7'h12, 16'h1000, s1_clkout6[15:0]};		// store clkout6
		14:	ram[14]	<= #1 {
    
    7'h13, 16'hc000, s1_clkout6[31:16]};

		15:	ram[15]	<= #1 {
    
    7'h16, 16'hc000, {2'h0, s1_divclk[23:22], s1_divclk[11:0]} };		// store the input divider

		16:	ram[16]	<= #1 {
    
    7'h14, 16'h1000, s1_clkfbout[15:0]};		// store feedback
		17:	ram[17]	<= #1 {
    
    7'h15, 16'h8000, s1_clkfbout[31:16]};

		18:	ram[18]	<= #1 {
    
    7'h18, 16'hfc00, {6'h00, s1_lock[29:20]} };
		19:	ram[19]	<= #1 {
    
    7'h19, 16'h8000, {1'b0 , s1_lock[34:30], s1_lock[9:0]} };
		20:	ram[20]	<= #1 {
    
    7'h1a, 16'h8000, {1'b0 , s1_lock[39:35], s1_lock[19:10]} };
		
		21:	ram[21]	<= #1 {
    
    7'h4e, 16'h66ff,s1_digital_filt[9], 2'h0,s1_digital_filt[8:7], 2'h0,s1_digital_filt[6], 8'h00 };
		22:	ram[22] <= #1 {
    
    7'h4f, 16'h666f,s1_digital_filt[5], 2'h0,s1_digital_filt[4:3], 2'h0,s1_digital_filt[2:1], 2'h0, s1_digital_filt[0], 4'h0 };
		default:	ram[ram_rd_addr]	<= #1 0;
		endcase
	end

   // Output the initialized ram value based on ram_addr each clock cycle
	always @ (posedge drp_sclk)
	begin
		ram_do<= #1 ram[ram_addr];
	end

	//**************************************************************************
	// Everything below is associated whith the state machine that is used to
	// Read/Modify/Write to the MMCM.
	//**************************************************************************

	// State Definitions
	localparam	RESTART		= 4'h1;
	localparam	WAIT_LOCK	= 4'h2;
	localparam	WAIT_SEN	= 4'h3;
	localparam	ADDRESS		= 4'h4;
	localparam	WAIT_A_DRDY	= 4'h5;
	localparam	BITMASK		= 4'h6;
	localparam	BITSET		= 4'h7;
	localparam	WRITE		= 4'h8;
	localparam	WAIT_DRDY	= 4'h9;

	// State sync
	reg	[3:0]	current_state	= RESTART;
	reg	[3:0]	next_state		= RESTART;

	// These variables are used to keep track of the number of iterations that
	//    each state takes to reconfigure.
	// STATE_COUNT_CONST is used to reset the counters and should match the
	//    number of registers necessary to reconfigure each state.
	localparam		STATE_COUNT_CONST	= 23;
	reg	[4:0]	state_count			= STATE_COUNT_CONST;
	reg	[4:0]	next_state_count	= STATE_COUNT_CONST;

	// This block assigns the next register value from the state machine below
	always @ (posedge drp_sclk)
	begin
		DADDR		<= #1 next_daddr;
		DWE			<= #1 next_dwe;
		DEN			<= #1 next_den;
		RST_MMCM	<= #1 next_rst_mmcm;
		DI			<= #1 next_di;

		drp_srdy	<= #1 next_srdy;

		ram_addr	<= #1 next_ram_addr;
		state_count	<= #1 next_state_count;
	end

   // This block assigns the next state, reset is syncronous.
	always @(posedge drp_sclk)
	begin
		if(drp_rst_n == 0)
			current_state	<= #1 RESTART;
		else
			current_state	<= #1 next_state;
	end

	always @ (*)
	begin
		// Setup the default values
		next_srdy			= 1'b0;
		next_daddr			= DADDR;
		next_dwe			= 1'b0;
		next_den			= 1'b0;
		next_rst_mmcm		= RST_MMCM;
		next_di				= DI;
		next_ram_addr		= ram_addr;
		next_state_count	= state_count;

		case	(current_state)
		// If RST is asserted reset the machine
		RESTART:
		begin
			next_daddr			= 7'h00;
			next_di				= 16'h0000;
			next_ram_addr		= 6'h00;
			next_rst_mmcm		= 1'b1;
			next_state			= WAIT_LOCK;
		end

		 // Waits for the MMCM to assert LOCKED - once it does asserts SRDY
		WAIT_LOCK:
		begin
			// Make sure reset is de-asserted
			next_rst_mmcm		= 1'b0;
			// Reset the number of registers left to write for the next
			// reconfiguration event.
			next_state_count	= STATE_COUNT_CONST ;
			next_ram_addr		= 8'h00;

			if(LOCKED)
			begin
				// MMCM is locked, go on to wait for the SEN signal
				next_state		= WAIT_SEN;
				// Assert SRDY to indicate that the reconfiguration module is
				// ready
				next_srdy		= 1'b1;
			end
			else
			begin
				// Keep waiting, locked has not asserted yet
				next_state		= WAIT_LOCK;
			end
		end

		// Wait for the next SEN pulse and set the RAM addr appropriately
		//    based on SADDR
		WAIT_SEN:
		begin
			next_ram_addr		= 8'h00;
			if (drp_sen)
			begin
				next_ram_addr	= 8'h00;
				// Go on to address the MMCM
				next_state		= ADDRESS;
			end
			else
			begin
				// Keep waiting for SEN to be asserted
				next_state		= WAIT_SEN;
			end
		end

		// Set the address on the MMCM and assert DEN to read the value
		ADDRESS:
		begin
			// Reset the DCM through the reconfiguration
			next_rst_mmcm		= 1'b1;
			// Enable a read from the MMCM and set the MMCM address
			next_den			= 1'b1;
			next_daddr			= ram_do[38:32];

			// Wait for the data to be ready
			next_state			= WAIT_A_DRDY;
		end

		// Wait for DRDY to assert after addressing the MMCM
		WAIT_A_DRDY:
		begin
			if (DRDY)			// Data is ready, mask out the bits to save
				next_state		= BITMASK;
			else				// Keep waiting till data is ready
				next_state		= WAIT_A_DRDY;
		end

		// Zero out the bits that are not set in the mask stored in ram
		BITMASK:
		begin
			// Do the mask
			next_di				= ram_do[31:16] & DO;
			// Go on to set the bits
			next_state			= BITSET;
		end

		// After the input is masked, OR the bits with calculated value in ram
		BITSET:
		begin
			// Set the bits that need to be assigned
			next_di				= ram_do[15:0] | DI;
			// Set the next address to read from RAM
			next_ram_addr		= ram_addr + 1'b1;
			// Go on to write the data to the MMCM
			next_state			= WRITE;
		end

		// DI is setup so assert DWE, DEN, and RST_MMCM.  Subtract one from the
		//    state count and go to wait for DRDY.
		WRITE:
		begin
			// Set WE and EN on MMCM
			next_dwe			= 1'b1;
			next_den			= 1'b1;

			// Decrement the number of registers left to write
			next_state_count	= state_count - 1'b1;
			// Wait for the write to complete
			next_state			= WAIT_DRDY;
		end

		// Wait for DRDY to assert from the MMCM.  If the state count is not 0
		//    jump to ADDRESS (continue reconfiguration).  If state count is
		//    0 wait for lock.
		WAIT_DRDY:
		begin
			if(DRDY)
			begin
				// Write is complete
				if(state_count > 0)
				// If there are more registers to write keep going
					next_state	= ADDRESS;
				else
				// There are no more registers to write so wait for the MMCM
				// to lock
				next_state		= WAIT_LOCK;
			end
			else
			begin
				// Keep waiting for write to complete
				next_state		= WAIT_DRDY;
			end
		end

		// If in an unknown state reset the machine
		default:
		begin
			next_state = RESTART;
		end
		endcase
	end
endmodule


Verilog 仿真测试激励 pll_cfg_testbench.v


///////////////////////////////////////////////////////////////////////

`timescale 1ns / 100ps

module pll_cfg_testbench;

reg			rst_n;
reg			clk;
parameter CLK_PERIOD	= 20;		//50MHz

initial	begin
	rst_n 		= 0;
	#(10 * CLK_PERIOD)
	rst_n	= 1;
end

initial
	clk = 0;
always
begin
	clk = #(CLK_PERIOD/2.0) ~clk;
end
	
wire	pll_locked;
wire	pll_clk_out0;
wire	pll_clk_out1;

pll_demo	pll_ux1
(
	.sys_clk		(clk),
    .pll_locked		(pll_locked),
	.pll_clk_out0	(pll_clk_out0),
	.pll_clk_out1	(pll_clk_out1)
);
endmodule

Verilog 顶层文件 pll_demo.v


module	pll_demo	#
(
	parameter	iCLK_N		= 1	///< *输出数量 1-7
)
(
	input				sys_clk,
    output				pll_locked,
	output  			pll_clk_out0,
	output  			pll_clk_out1
);

parameter	FVCO_MIN	= 600*1000*1000;	//600MHz
parameter	FVCO_MAX	= 1440*1000*1000;	//-1 1200MHz,-2 1440MHz,-3 1600MHz
parameter	FIN			= 100*1000*1000;

//下面这组缺省的参数是输入100MHz,FVCO=iFREQ_IN/iPLL_D*iPLL_M/8=800MHz,输出=FVCO/iPLL_N=100MHz

wire					drp_clk;
wire					clk_100m;
wire					rst_n;
wire	[31:0]			divx;
wire	[iCLK_N-1:0]	clk_out;
reg		[2:0]			fcnt = 0;

wire	[31:0]			fout_freq[7:0];
assign	fout_freq[0] = 765.432*1000*1000;
assign	fout_freq[1] = 543.210*1000*1000;
assign	fout_freq[2] = 321.098*1000*1000;
assign	fout_freq[3] = 109.876*1000*1000;
assign	fout_freq[4] = 098.765*1000*1000;
assign	fout_freq[5] = 076.543*1000*1000;
assign	fout_freq[6] = 054.321*1000*1000;
assign	fout_freq[7] = 032.109*1000*1000;

assign	divx[7:0]	= 1;
//assign	divx[15:8]	= 1;

assign	pll_clk_out0	= clk_out[0];
assign	pll_clk_out1	= clk_out[1];

clk_wiz_pll_100m pll_u1
(
	.clk_out1	(clk_100m),
	.clk_out2	(drp_clk),
	.locked		(rst_n),
	.clk_in1	(sys_clk)
);
////////////////////////////////////////////////////////////////////////////////////////////////
//在 iCLK_N = 1 时,只有1路输出,计算精度比较高
//在 iCLK_N > 1 时,有多路输出,各路输出的频率必须有个公倍数
//比如输出多个频率,out1=100MHz,out2=300MHz,out3=50MHz,out4=25MHz,
//fout_freq=300_000_000	//300MHz,fout_freq必须选这组频率中的最大的值
//divx[07:00]	= fout_freq/out1=300/100=3;	//这必须能整除
//divx[15:08]	= fout_freq/out2=300/300=1;	//这必须能整除
//divx[23:16]	= fout_freq/out3=300/50 =6;	//这必须能整除
//divx[31:24]	= fout_freq/out4=300/25 =12;//这必须能整除
//divx 的取值范围在 1-128 ,并且 divx * pll_n 必须小于等于 128

pll	#
(
	.iCLK_N		(iCLK_N),	//时钟输出通道数量 1-7
	.FVCO_MIN	(FVCO_MIN),	//600MHz
	.FVCO_MAX	(FVCO_MAX),	//-1 1200MHz,-2 1440MHz,-3 1600MHz
	.FIN		(FIN)		//100MHz
)
pll_ux
(
	.drp_clk			(drp_clk),
	.rst_n				(rst_n),
	.fout_freq			(fout_freq[fcnt]),
	.divx				(divx),

	.clk_in				(clk_100m),
	.pll_locked			(pll_locked),
	.clk_out			(clk_out)
);

reg	[1:0]	locked_r = 0;

always@(posedge	drp_clk)
begin
	if(rst_n == 0)
		locked_r	<= 0;
	else
	begin
		locked_r[0]	<= pll_locked;
		locked_r[1]	<= locked_r[0];
	end
end

always@(posedge	drp_clk)
begin
	if(rst_n == 0)
		fcnt	<= 0;
	else if(locked_r == 2'b10)		//此 DEMO 利用 pll_locked 的下降沿来切换频率值并重新计算锁相环的值
									//在实际应用中,可以在任何时候改变频率的值。
		fcnt	<= fcnt + 1;	
end

endmodule


本文中的相关下载链接

猜你喜欢

转载自blog.csdn.net/qq_46621272/article/details/130484100
PLL