xilinx/MMCM/DRP/使用

老师,您好,可以给我讲讲xilinx mmcm 这个框图嘛,原理,我作为一名fpga工程师如何使用drp功能;文件如上,然后有一个问题, top_mmcme2.v文件中.S1_DIVCLK_DIVIDE(2),是啥意思,谢谢您


你好!很高兴为你解答。作为FPGA工程师,理解MMCM(Mixed-Mode Clock Manager)和DRP(Dynamic Reconfiguration Port)是进阶的必备技能。我们分三部分来回答:框图原理 、代码中的参数含义 、以及如何使用DRP功能。


一、 Xilinx MMCM 框图原理详解

你提供的图片是Xilinx 7系列FPGA中MMCM的经典结构图。我们可以把它看作一个**"频率合成工厂"**,流程如下:

  1. 输入与预分频(D):

    • 左侧 CLKIN1/2 是外部晶振或时钟输入。

    • 进入 D 触发器(实际上是分频器),对应参数 DIVCLK_DIVIDE。

    • 作用:如果输入时钟频率太高,先除以D,降低频率,以便后续电路处理。

    • 公式:FPFD=FCLKIN/DFPFD​=FCLKIN​/D

  2. 鉴频鉴相器(PFD)与电荷泵(CP)、环路滤波器(LF):

    • PFD 比较 CLKFB(反馈时钟)和经过D分频后的时钟的相位差。

    • 输出误差信号,控制VCO(压控振荡器)的频率。

  3. 压控振荡器(VCO)与倍频(M):

    • 这是核心。VCO输出高频时钟。

    • 反馈路径上的 M(CLKFBOUT_MULT_F)是倍频系数。

    • 核心公式:FVCO=FCLKIN×MDFVCO​=FCLKIN​×DM​

    • 注意:VCO的频率必须在芯片规定的范围内(例如 600MHz - 1200MHz),否则MMCM无法锁定(Lock)。

  4. 输出分频(O0 - O6):

    • VCO产生的高频信号,经过右侧的 O0 到 O6 分频器输出。

    • 每个输出通道都可以独立设置分频系数(CLKOUTx_DIVIDE)和相位(Phase)。

    • 输出公式:FOUTx=FVCOOxFOUTx​=Ox​FVCO​​

总结:MMCM就像一个齿轮箱,输入频率先被D除,再被M乘,最后被O除,得到你想要的频率。


二、 关于 .S1_DIVCLK_DIVIDE(2) 的问题

在 top_mmcme2.v 文件中,这行代码的含义是:

这是配置"状态1(State 1)"下的输入时钟分频系数(D)= 2。

具体解释:

  • S1:代表 State 1。你的代码支持动态重配置(DRP),可以在不同状态间切换。这里定义的是第一种配置方案。

  • DIVCLK_DIVIDE :对应框图中最左边的 D。

  • 值为 2:意味着输入的时钟频率会被除以 2。

结合你的代码看:

  • 输入时钟 CLKIN_PERIOD 设为 10.000 ns,即 100 MHz。

  • 在 mmcme2_drp 模块中,S1_CLKFBOUT_MULT 设为 2(倍频 M=2)。

  • 那么 VCO 的频率计算为:FVCO=100MHz×22=100MHzFVCO​=100MHz×22​=100MHz。

  • 如果 S1_DIVCLK_DIVIDE(2) 改成 1,那么 FVCO=100MHz×21=200MHzFVCO​=100MHz×12​=200MHz。

为什么设为2?

通常是为了满足VCO的工作频率范围,或者为了得到特定的输出频率。如果你的输入时钟很高(比如 500MHz),D必须设大一点,否则VCO会超频。


三、 FPGA工程师如何使用 DRP 功能

DRP(Dynamic Reconfiguration Port)允许你在FPGA运行过程中,动态修改MMCM的配置参数(如倍频M、分频D、输出分频O、相位等),而不需要重新烧录比特流。

1. 核心文件解析

你提供的代码包含三个关键部分:

  • mmcme2_drp.v:DRP状态机。它负责读取ROM中的配置数据,并通过DRP接口写入MMCM。

  • mmcme2_drp_func.h:计算函数。它把用户设置的参数(如M=2, D=2)转换成MMCM寄存器能识别的二进制值(Bitmask)。

  • top_mmcme2.v:顶层模块。它实例化了MMCME2_ADV(硬核)和mmcme2_drp(控制器),并连接了DRP信号。

2. 使用步骤(基于你的代码)

你的代码设计了一个多状态重配置的机制。

  • Step 1: 定义状态参数

    在 mmcme2_drp 实例化时,你会看到 S1_, S2_, S3_ 开头的参数。

    • S1_...:第一种配置(例如:100MHz输入,输出50MHz)。

    • S2_...:第二种配置(例如:100MHz输入,输出200MHz)。

    • S3_...:第三种配置。

  • Step 2: 触发重配置

    在 top_mmcme2.v 的端口有 input SSTEP 和 input [1:0] STATE。

    • STATE:选择你要切换到哪一组参数(0对应S1,1对应S2,2对应S3)。

    • SSTEP:给一个脉冲(上升沿),启动重配置过程。

  • Step 3: 内部工作流程

    1. 你改变 STATE 引脚的值。

    2. 给 SSTEP 一个脉冲。

    3. mmcme2_drp 模块检测到 SSTEP,开始工作。

    4. 它根据 STATE 选择对应的 ROM 地址段(比如 S2 的地址)。

    5. 它通过 DADDR, DI, DEN, DWE 信号,将新配置写入 MMCME2_ADV。

    6. 写入完成后,MMCM会暂时失锁(LOST LOCK),然后重新锁定(LOCKED)。

    7. SRDY 信号拉高,表示重配置完成。

3. 注意事项(避坑指南)
  • 时钟切换风险 :在重配置过程中,输出时钟会不稳定甚至停止。如果你的系统依赖这个时钟运行逻辑,必须 设计好时钟切换电路(BUFGMUX),在DRP操作期间切换到备用时钟(如晶振直连),等待 LOCKED 信号再次变高后,再切回来。

  • VCO频率范围:无论你怎么重配置,计算出的 FVCOFVCO​ 必须满足芯片手册要求(7系列通常是 600MHz - 1200MHz)。否则MMCM无法锁定。

  • 复位 :重配置期间,RST_MMCM 信号会被拉高,这是正常的,为了重置MMCM内部状态。

总结 :你图中的红框 .S1_DIVCLK_DIVIDE(2) 只是定义了第一套配置方案中的输入分频系数。要使用DRP,你只需要在外部改变 STATE 并触发 SSTEP,剩下的寄存器计算和写入由 mmcme2_drp.v 自动完成。

top_mmcme2.v

复制代码
//------------------------------------------------------------------------------------------
//   ____  ____
//  /   /\/   /
// /___/  \  /
// \   \   \/    锟? Copyright 2019 Xilinx, Inc. All rights reserved.
//  \   \        This file contains confidential and proprietary information of Xilinx, Inc.
//  /   /        and is protected under U.S. and international copyright and other
// /___/   /\    intellectual property laws.
// \   \  /  \
//  \___\/\___\
//
//-------------------------------------------------------------------------------------------
// Device:              7-Series
// Author:              Tatsukawa, Kruger, Defossez
// Entity Name:         top_mmcme2
// Purpose:             This is a basic demonstration of the MMCM_DRP
//                      connectivity to the MMCM_ADV.
// Tools:               Vivado_2019.1 or newer
// Limitations:
//
// Vendor:              Xilinx Inc.
// Version:             1.40
// Filename:            top_mmcme2.v
// Date Created:        30-Jul-2014
// Date Last Modified:  25-Jun-2019
//-------------------------------------------------------------------------------------------
// Disclaimer:
//		This disclaimer is not a license and does not grant any rights to the materials
//		distributed herewith. Except as otherwise provided in a valid license issued to you
//		by Xilinx, and to the maximum extent permitted by applicable law: (1) THESE MATERIALS
//		ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL
//		WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED
//		TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR
//		PURPOSE; and (2) Xilinx shall not be liable (whether in contract or tort, including
//		negligence, or under any other theory of liability) for any loss or damage of any
//		kind or nature related to, arising under or in connection with these materials,
//		including for any direct, or any indirect, special, incidental, or consequential
//		loss or damage (including loss of data, profits, goodwill, or any type of loss or
//		damage suffered as a result of any action brought by a third party) even if such
//		damage or loss was reasonably foreseeable or Xilinx had been advised of the
//		possibility of the same.
//
// CRITICAL APPLICATIONS
//		Xilinx products are not designed or intended to be fail-safe, or for use in any
//		application requiring fail-safe performance, such as life-support or safety devices
//		or systems, Class III medical devices, nuclear facilities, applications related to
//		the deployment of airbags, or any other applications that could lead to death,
//		personal injury, or severe property or environmental damage (individually and
//		collectively, "Critical Applications"). Customer assumes the sole risk and
//		liability of any use of Xilinx products in Critical Applications, subject only to
//		applicable laws and regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES.
//
// Contact:    e-mail  hotline@xilinx.com        phone   + 1 800 255 7778
//-------------------------------------------------------------------------------------------
// Revision History:
//  Rev: 30-Apr-2014 - Tatsukawa
//      Initial code release
//  Rev: 25-Jun-2019 - Defossez
//      Add possibility to register the LOCKED signal.
//-------------------------------------------------------------------------------------------
//
`timescale 1ps/1ps
//
//-------------------------------------------------------------------------------------------
// Entity pin description
//-------------------------------------------------------------------------------------------
// Inputs
//      SSTEP:      Start a reconfiguration. It should only be pulsed for one clock cycle.
//      STATE:      Determines which state the MMCM_ADV will be reconfigured to. A value
//                  of 0 correlates to state 1, and a value of 1 correlates to state 2.
//      RST:        RST will reset the entire reference design including the MMCM_ADV.
//      CLKIN:      Clock for the MMCM_ADV CLKIN as well as the clock for the MMCM_DRP module
//      SRDY:       Pulses for one clock cycle after the MMCM_ADV is locked and the
//                  MMCM_DRP module is ready to start another re-configuration.
// Outputs
//      LOCKED_OUT: MMCM is locked after configuration or reconfiguration.
//      CLK0OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK1OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK2OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK3OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK4OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK5OUT:    These are the clock outputs from the MMCM_ADV.
//      CLK6OUT:    These are the clock outputs from the MMCM_ADV.
//-------------------------------------------------------------------------------------------
module top_mmcme2
    (
        input    SSTEP,
        input [1:0]   STATE,
        input    RST,
        input    CLKIN,
        output   SRDY,
 		output 	 LOCKED_OUT,
        output   CLK0OUT,
        output   CLK1OUT,
        output   CLK2OUT,
        output   CLK3OUT,
        output   CLK4OUT,
        output   CLK5OUT,
        output   CLK6OUT
    );
//-------------------------------------------------------------------------------------------
// 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;
reg				current_state;
reg [7:0]		sstep_int ;
reg				init_drp_state = 1;
// These signals are used for the BUFG's necessary for the design.
wire            CLKIN_ibuf;
wire            clkin_bufgout;
wire            clkfb_bufgout;
wire            clkfb_bufgin;
wire            clk0_bufgin;
wire            clk0_bufgout;
wire            clk1_bufgin;
wire            clk1_bufgout;
wire            clk2_bufgin;
wire            clk2_bufgout;
wire            clk3_bufgin;
wire            clk3_bufgout;
wire            clk4_bufgin;
wire            clk4_bufgout;
wire            clk5_bufgin;
wire            clk5_bufgout;
wire            clk6_bufgin;
wire            clk6_bufgout;
wire            LOCKED;
//-------------------------------------------------------------------------------------------
assign CLKIN_ibuf = CLKIN;
//
BUFG BUFG_IN    (.O (clkin_bufgout),    .I (CLKIN_ibuf));
BUFG BUFG_FB    (.O (clkfb_bufgout),    .I (clkfb_bufgin));
BUFG BUFG_CLK0  (.O (CLK0OUT),     .I (clk0_bufgin));
BUFG BUFG_CLK1  (.O (clk1_bufgout),     .I (clk1_bufgin));
BUFG BUFG_CLK2  (.O (clk2_bufgout),     .I (clk2_bufgin));
BUFG BUFG_CLK3  (.O (clk3_bufgout),     .I (clk3_bufgin));
BUFG BUFG_CLK4  (.O (clk4_bufgout),     .I (clk4_bufgin));
BUFG BUFG_CLK5  (.O (clk5_bufgout),     .I (clk5_bufgin));
BUFG BUFG_CLK6  (.O (clk6_bufgout),     .I (clk6_bufgin));
//
// ODDR registers used to output clocks
// ODDR ODDR_CLK0 (.Q(CLK0OUT), .C(clk0_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK1 (.Q(CLK1OUT), .C(clk1_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK2 (.Q(CLK2OUT), .C(clk2_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK3 (.Q(CLK3OUT), .C(clk3_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK4 (.Q(CLK4OUT), .C(clk4_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK5 (.Q(CLK5OUT), .C(clk5_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
ODDR ODDR_CLK6 (.Q(CLK6OUT), .C(clk6_bufgout), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(RST), .S(1'b0));
//
// MMCM_ADV that reconfiguration will take place on
//
//  BANDWIDTH:              : "HIGH", "LOW" or "OPTIMIZED"
//  DIVCLK_DIVIDE           : Value from 1 to 106
//  CLKFBOUT_MULT_F         : Value from 2 to 64
//  CLKFBOUT_PHASE          :
//  CLKFBOUT_USE_FINE_PS    : "TRUE" or "FALSE",
//  CLKIN1_PERIOD           : Value from 0.968 to 100.000. Set the period (ns) of input clocks
//  REF_JITTER1             :
//  CLKIN2_PERIOD           :
//  REF_JITTER2             :
//  CLKOUT parameters:
//  DIVIDE                  : Value from 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
//  Misc parameters
//  COMPENSATION
//  STARTUP_WAIT
//  -------------------Fclkin 输入时钟频率
//  -------------------Fvco = (Fclkin * M) /D  ---- Fvco电压控制振荡器内部频率
//  -------------------Fout(x) = Fvco/O(x)     ---- O(x)--CLKOUTx 的分频系数
MMCME2_ADV #(
   .BANDWIDTH           ("OPTIMIZED"),
   .DIVCLK_DIVIDE       (1),  //D分频参数  输入时钟分频器(DIVCLK_DIVIDE)
   .CLKFBOUT_MULT_F     (6),//(3),  //M倍频参数  倍频因子(CLKFBOUT_MULT_F)
   .CLKFBOUT_PHASE      (0.0),
   .CLKFBOUT_USE_FINE_PS("FALSE"),
   .CLKIN1_PERIOD       (10.000),//(5.000), 
   .REF_JITTER1         (0.010),
   .CLKIN2_PERIOD       (10.000),
   .REF_JITTER2         (0.010),
   .CLKOUT0_DIVIDE_F    (24),//(24),//CLKOUTx 的分频系数
   .CLKOUT0_DUTY_CYCLE  (0.5),
   .CLKOUT0_PHASE       (0.0),
   .CLKOUT0_USE_FINE_PS ("FALSE"),
   .CLKOUT1_DIVIDE      (6), //
   .CLKOUT1_DUTY_CYCLE  (0.5),
   .CLKOUT1_PHASE       (0.0),
   .CLKOUT1_USE_FINE_PS ("FALSE"),
   .CLKOUT2_DIVIDE      (6),
   .CLKOUT2_DUTY_CYCLE  (0.5),
   .CLKOUT2_PHASE       (0.0),
   .CLKOUT2_USE_FINE_PS ("FALSE"),
   .CLKOUT3_DIVIDE      (6),
   .CLKOUT3_DUTY_CYCLE  (0.5),
   .CLKOUT3_PHASE       (0.0),
   .CLKOUT3_USE_FINE_PS ("FALSE"),
   .CLKOUT4_DIVIDE      (6),
   .CLKOUT4_DUTY_CYCLE  (0.5),
   .CLKOUT4_PHASE       (0.0),
   .CLKOUT4_USE_FINE_PS ("FALSE"),
   .CLKOUT4_CASCADE     ("FALSE"),
   .CLKOUT5_DIVIDE      (6),
   .CLKOUT5_DUTY_CYCLE  (0.5),
   .CLKOUT5_PHASE       (0.0),
   .CLKOUT5_USE_FINE_PS ("FALSE"),
   .CLKOUT6_DIVIDE      (6),
   .CLKOUT6_DUTY_CYCLE  (0.5),
   .CLKOUT6_PHASE       (0.0),
   .CLKOUT6_USE_FINE_PS ("FALSE"),
   .COMPENSATION        ("ZHOLD"),
   .STARTUP_WAIT        ("FALSE")
) mmcme2_test_inst (
   .CLKFBOUT            (clkfb_bufgin),
   .CLKFBOUTB           (),
   .CLKFBSTOPPED        (),
   .CLKINSTOPPED        (),
   .CLKOUT0             (clk0_bufgin),
   .CLKOUT0B            (),
   .CLKOUT1             (clk1_bufgin),
   .CLKOUT1B            (),
   .CLKOUT2             (clk2_bufgin),
   .CLKOUT2B            (),
   .CLKOUT3             (clk3_bufgin),
   .CLKOUT3B            (),
   .CLKOUT4             (clk4_bufgin),
   .CLKOUT5             (clk5_bufgin),
   .CLKOUT6             (clk6_bufgin),
   .DO                  (dout),
   .DRDY                (drdy),
   .DADDR               (daddr),
   .DCLK                (dclk),
   .DEN                 (den),
   .DI                  (di),
   .DWE                 (dwe),
   .LOCKED              (LOCKED),
   .CLKFBIN             (clkfb_bufgout),
   .CLKIN1              (clkin_bufgout),
   .CLKIN2              (),
   .CLKINSEL            (1'b1),
   .PSDONE              (),
   .PSCLK               (1'b0),
   .PSEN                (1'b0),
   .PSINCDEC            (1'b0),
   .PWRDWN              (1'b0),
   .RST                 (rst_mmcm)
);
// MMCM_DRP instance that will perform the reconfiguration operations
mmcme2_drp #(
    // Register the LOCKED signal with teh MMCME3_ADV input clock.
    // The LOCKED_IN (LOCKED from the MMCME3_ADV) is fed into a register and then
    // passed the LOCKED_OUT when REGISTER_LOCKED is set to "Reg" or when set to
    // "NoReg" LOCKED_IN is just passed on to LOCKED_OUT without being registered.
    .REGISTER_LOCKED       ("Reg"),
    // Use the registered LOCKED signal from the MMCME3 also for the DRP state machine.
    .USE_REG_LOCKED        ("No"),
    // Possible combination of above two parameters:
    // | REGISTER_LOCKED | USE_REG_LOCKED |                                            |
    // |-----------------|----------------|--------------------------------------------|
    // |      "NoReg"    |     "No"       | LOCKED is just passed through mmcme3_drp   |
    // |                 |                | and is used as is with the state machine   |
    // |      "NoReg"    |     "Yes"      | NOT ALLOWED                                |
    // |       "Reg"     |     "No"       | LOCKED is registered but the unregistered  |
    // |                 |                | version is used for the state machine.     |
    // |       "Reg"     |     "Yes"      | LOCKED is registered and the registered    |
    // |                 |                | version is also used by the state machine. |
    //
    //***********************************************************************
    // State 1 Parameters - These are for the first reconfiguration state.
    //***********************************************************************
    // Set the multiply to 6.0 with 0 deg phase offset, optimized bandwidth, input divide of 1
    .S1_CLKFBOUT_MULT(2),
    .S1_CLKFBOUT_PHASE(000_000),
    .S1_CLKFBOUT_FRAC(000),
    .S1_CLKFBOUT_FRAC_EN(0),
    .S1_BANDWIDTH("OPTIMIZED"),
    .S1_DIVCLK_DIVIDE(2),
    // Set clockout0 to a divide of 6.0 (unity gain), 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT0_DIVIDE(4),//(8),
    .S1_CLKOUT0_PHASE(000_000),
    .S1_CLKOUT0_DUTY(50000),
    .S1_CLKOUT0_FRAC(000),
    .S1_CLKOUT0_FRAC_EN(0),
    // Set clockout 1 to a divide of 1, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT1_DIVIDE(1),
    .S1_CLKOUT1_PHASE(000_000),
    .S1_CLKOUT1_DUTY(50000),
    // Set clockout 2 to a divide of 2, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT2_DIVIDE(2),
    .S1_CLKOUT2_PHASE(000_000),
    .S1_CLKOUT2_DUTY(50000),
    // Set clockout 3 to a divide of 3, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT3_DIVIDE(3),
    .S1_CLKOUT3_PHASE(000_000),
    .S1_CLKOUT3_DUTY(50000),
    // Set clockout 4 to a divide of 4, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT4_DIVIDE(4),
    .S1_CLKOUT4_PHASE(000_000),
    .S1_CLKOUT4_DUTY(50000),
    // Set clockout 5 to a divide of 5, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT5_DIVIDE(5),
    .S1_CLKOUT5_PHASE(000_000),
    .S1_CLKOUT5_DUTY(50000),
    // Set clockout 6 to a divide of 10, 0 deg phase offset, 50/50 duty cycle
    .S1_CLKOUT6_DIVIDE(10),
    .S1_CLKOUT6_PHASE(000_000),
    .S1_CLKOUT6_DUTY(50000),
    //***********************************************************************
    // State 2 Parameters - These are for the second reconfiguration state.
    //***********************************************************************
    .S2_CLKFBOUT_MULT(2),
    .S2_CLKFBOUT_PHASE(000_000),
    .S2_CLKFBOUT_FRAC(000),
    .S2_CLKFBOUT_FRAC_EN(0),
    .S2_BANDWIDTH("OPTIMIZED"),
    .S2_DIVCLK_DIVIDE(2),
    // Set clockout 0 to a divide of 4.750, 0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT0_DIVIDE(2),//(4),
    .S2_CLKOUT0_PHASE(000_000),
    .S2_CLKOUT0_DUTY(50000),
    .S2_CLKOUT0_FRAC(000),
    .S2_CLKOUT0_FRAC_EN(0),
    // Set clockout 1 to a divide of 1, 45.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT1_DIVIDE(1),
    .S2_CLKOUT1_PHASE(045_000),
    .S2_CLKOUT1_DUTY(50000),
    // Set clock out 0 to a divide of 1, 90.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT2_DIVIDE(1),
    .S2_CLKOUT2_PHASE(090_000),
    .S2_CLKOUT2_DUTY(90000),
    // Set clockout3 to a divide of 1, 135.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT3_DIVIDE(1),
    .S2_CLKOUT3_PHASE(135_000),
    .S2_CLKOUT3_DUTY(50000),
    // Set clockout4 to a divide of 1, 180.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT4_DIVIDE(1),
    .S2_CLKOUT4_PHASE(180_000),
    .S2_CLKOUT4_DUTY(50000),
    // Set clockout5 to a divide of 1, 225.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT5_DIVIDE(1),
    .S2_CLKOUT5_PHASE(225_000),
    .S2_CLKOUT5_DUTY(50000),
    // Set clockout6 to a divide of 1, 270.0 deg phase offset, 50/50 duty cycle
    .S2_CLKOUT6_DIVIDE(1),
    .S2_CLKOUT6_PHASE(270_000),
    .S2_CLKOUT6_DUTY(50000),
    //***********************************************************************
    // State 2 Parameters - These are for the second reconfiguration state.
    //***********************************************************************
    .S3_CLKFBOUT_MULT(2),
    .S3_CLKFBOUT_PHASE(000_000),
    .S3_CLKFBOUT_FRAC(000),
    .S3_CLKFBOUT_FRAC_EN(0),
    .S3_BANDWIDTH("OPTIMIZED"),
    .S3_DIVCLK_DIVIDE(2),
    // Set clockout 0 to a divide of 4.750, 0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT0_DIVIDE(1),//(2),
    .S3_CLKOUT0_PHASE(000_000),
    .S3_CLKOUT0_DUTY(50000),
    .S3_CLKOUT0_FRAC(000),
    .S3_CLKOUT0_FRAC_EN(0),
    // Set clockout 1 to a divide of 1, 45.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT1_DIVIDE(1),
    .S3_CLKOUT1_PHASE(045_000),
    .S3_CLKOUT1_DUTY(50000),
    // Set clock out 0 to a divide of 1, 90.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT2_DIVIDE(1),
    .S3_CLKOUT2_PHASE(090_000),
    .S3_CLKOUT2_DUTY(90000),
    // Set clockout3 to a divide of 1, 135.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT3_DIVIDE(1),
    .S3_CLKOUT3_PHASE(135_000),
    .S3_CLKOUT3_DUTY(50000),
    // Set clockout4 to a divide of 1, 180.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT4_DIVIDE(1),
    .S3_CLKOUT4_PHASE(180_000),
    .S3_CLKOUT4_DUTY(50000),
    // Set clockout5 to a divide of 1, 225.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT5_DIVIDE(1),
    .S3_CLKOUT5_PHASE(225_000),
    .S3_CLKOUT5_DUTY(50000),
    // Set clockout6 to a divide of 1, 270.0 deg phase offset, 50/50 duty cycle
    .S3_CLKOUT6_DIVIDE(1),
    .S3_CLKOUT6_PHASE(270_000),
    .S3_CLKOUT6_DUTY(50000)
) mmcme2_drp_inst (
    .SADDR              (STATE),
    .SEN                (sstep_int[0]),
    .RST                (RST),
    .SRDY               (SRDY),
    .SCLK               (clkin_bufgout),
    .DO                 (dout),
    .DRDY               (drdy),
    .LOCK_REG_CLK_IN    (clkin_bufgout),
    .LOCKED_IN          (LOCKED),
    .DWE                (dwe),
    .DEN                (den),
    .DADDR              (daddr),
    .DI                 (di),
    .DCLK               (dclk),
    .RST_MMCM           (rst_mmcm),
    .LOCKED_OUT         (LOCKED_OUT)
);
   //***********************************************************************
   // Additional STATE and SSTEP logic for push buttons and switches
   //***********************************************************************
// The following logic is not required but is being used to allow the DRP
// circuitry work more effectively with boards that use toggle switches or
// buttons that may not adhere to the single clock requirement.
//
// Only start DRP after initial lock and when STATE has changed
always @ (posedge clkin_bufgout or posedge SSTEP)
    if (SSTEP) sstep_int <=  8'h80;
    else sstep_int <= {1'b0, sstep_int[7:1]};
//
//-------------------------------------------------------------------------------------------
endmodule

mmcme2_drp.v

复制代码
//-------------------------------------------------------------------------------------------
//   ____  ____
//  /   /\/   /
// /___/  \  /
// \   \   \/    锟? Copyright 2019 Xilinx, Inc. All rights reserved.
//  \   \        This file contains confidential and proprietary information of Xilinx, Inc.
//  /   /        and is protected under U.S. and international copyright and other
// /___/   /\    intellectual property laws.
// \   \  /  \
//  \___\/\___\
//
//-------------------------------------------------------------------------------------------
// Device:              7_Series
// Author:              Tatsukawa, Kruger, Ribbing, Defossez
// Entity Name:         mmcme2_drp
// Purpose:             This calls the DRP register calculation functions and
//                      provides a state machine to perform MMCM reconfiguration
//                      based on the calculated values stored in a initialized
//                      ROM.
//                      7-Series MMCM is called:            MMCME2
//                          Ultrascale MMCM is called:      MMCME3
//                          UltrascalePlus MMCM is called:  MMCME4
//                      MMCME3 attributes
//                          CLKINx_PERIOD:      0.968 to 100.000 (x = 1 or 2)
//                          REF_JITTERx:        0.001 to 0.999 (x = 1 or 2)
//                          BANDWIDTH:          LOW, HIGH, OPTIMIZED and POSTCRC
//                          COMPENSATION:       AUTO, ZHOLD, EXTERNAL, INTERNAL and BUF_IN
//                          DIVCLK_DIVIDE:      1 to 106
//                          CLKFBOUT_MULT_F:    2 to 64
//                          CLKFBOUT_PHASE:     -360 to 360
//                          CLKOUTn_DIVIDE:     1 to 128 (n = 0 to 6)
//                          CLKOUTn_PHASE:      -360 to 360 (n = 0 to 6)
//                          CLKOUTn_DUTY_CYCLE: 0.01 to 0.99 (n = 0 to 6)
//
// Tools:               Vivado_2019.1 or newer
// Limitations:         None
//
// Vendor:              Xilinx Inc.
// Version:             1.40
// Filename:            mmcme3_drp.v
// Date Created:        22-Oct-2014
// Date Last Modified:  25-Jun-2019
//-------------------------------------------------------------------------------------------
// Disclaimer:
//        This disclaimer is not a license and does not grant any rights to the materials
//        distributed herewith. Except as otherwise provided in a valid license issued to you
//        by Xilinx, and to the maximum extent permitted by applicable law: (1) THESE MATERIALS
//        ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL
//        WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED
//        TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR
//        PURPOSE; and (2) Xilinx shall not be liable (whether in contract or tort, including
//        negligence, or under any other theory of liability) for any loss or damage of any
//        kind or nature related to, arising under or in connection with these materials,
//        including for any direct, or any indirect, special, incidental, or consequential
//        loss or damage (including loss of data, profits, goodwill, or any type of loss or
//        damage suffered as a result of any action brought by a third party) even if such
//        damage or loss was reasonably foreseeable or Xilinx had been advised of the
//        possibility of the same.
//
// CRITICAL APPLICATIONS
//        Xilinx products are not designed or intended to be fail-safe, or for use in any
//        application requiring fail-safe performance, such as life-support or safety devices
//        or systems, Class III medical devices, nuclear facilities, applications related to
//        the deployment of airbags, or any other applications that could lead to death,
//        personal injury, or severe property or environmental damage (individually and
//        collectively, "Critical Applications"). Customer assumes the sole risk and
//        liability of any use of Xilinx products in Critical Applications, subject only to
//        applicable laws and regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES.
//
// Contact:    e-mail  hotline@xilinx.com        phone   + 1 800 255 7778
//-------------------------------------------------------------------------------------------
// Revision History:
//  Rev: 13-Jan-2011 - Tatsukawa
//      Updated ROM[18,41] LOCKED bitmask to 16'HFC00
//  Rev: 30-May-2013 - Tatsukawa
//      Adding Fractional support for CLKFBOUT_MULT_F, CLKOUT0_DIVIDE_F
//  Rev: 30-Apr-2014 - Tatsukawa
//      For fractional multiply changed order to enable fractional
//      before the multiply is applied to prevent false VCO DRCs
//      (e.g. DADDR 7'h15 must be set before updating 7'h14)
//  Rev: 24-Oct-2014 - Ribbing
//      Parameters have been added to clarify Reg1/Reg2/Shared registers
//  Rev: 08-Jun-2015 - Kruger
//      WAIT_LOCK update
//  Rev: 02-May-2016 - Kruger
//      Reordering FRAC_EN bits DADDR(7'h09, 7'h15)
//      Registers before frac settings (7'h08, 7'h14)
//  Rev: 19-Sep-2018 - Defossez
//      Updated comments of BANDWIDTH.
//      Corrected some typos.
//  Rev: 25-Jun-2019 - Defossez
//      Adding registering possibility for LOCKE signal.
//-------------------------------------------------------------------------------------------
//
`timescale 1ps/1ps
//
module mmcme2_drp
    #(
        // Register the LOCKED signal with teh MMCME3_ADV input clock.
        // The LOCKED_IN (LOCKED from the MMCME3_ADV) is fed into a register and then
        // passed the LOCKED_OUT when REGISTER_LOCKED is set to "Reg" or when set to
        // "NoReg" LOCKED_IN is just passed on to LOCKED_OUT without being registered.
        parameter REGISTER_LOCKED       = "Reg",
        // Use the registered LOCKED signal from the MMCME3 also for the DRP state machine.
        parameter USE_REG_LOCKED        = "No",
        // Possible/allowed combinations of above two parameters:
        // | REGISTER_LOCKED | USE_REG_LOCKED |                                            |
        // |-----------------|----------------|--------------------------------------------|
        // |      "NoReg"    |     "No"       | LOCKED is just passed through mmcme3_drp   |
        // |                 |                | and is used as is with the state machine   |
        // |      "NoReg"    |     "Yes"      | NOT ALLOWED                                |
        // |       "Reg"     |     "No"       | LOCKED is registered but the unregistered  |
        // |                 |                | version is used for the state machine.     |
        // |       "Reg"     |     "Yes"      | LOCKED is registered and the registered    |
        // |                 |                | version is also used by the state machine. |
        //
        //***********************************************************************
        // State 1 Parameters - These are for the first reconfiguration state.
        //***********************************************************************
        //
        // These parameters have an effect on the feedback path. A change on
        // these parameters will effect all of the clock outputs.
        //
        // The parameters are composed of:
        //    _MULT: This can be from 2 to 64. It has an effect on the VCO
        //          frequency which consequently, effects all of the clock
        //          outputs.
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567. The range for the phase is from -360000 to 360000.
        //    _FRAC: This can be from 0 to 875. This represents the fractional
        //          divide multiplied by 1000.
        //          M = _MULT + _FRAC / 1000
        //          e.g. M=8.125
        //               _MULT = 8
        //               _FRAC = 125
        //    _FRAC_EN: This indicates fractional divide has been enabled. If 1
        //          then the fractional divide algorithm will be used to calculate
        //          register settings. If 0 then default calculation to be used.
        parameter S1_CLKFBOUT_MULT          = 5,
        parameter S1_CLKFBOUT_PHASE         = 0,
        parameter S1_CLKFBOUT_FRAC          = 125,
        parameter S1_CLKFBOUT_FRAC_EN       = 1,
        //
        // The bandwidth parameter effects the phase error and the jitter filter
        // capability of the MMCM. For more information on this parameter see the
        // Device user guide.
        // Possible values are: "LOW", "LOW_SS", "HIGH" and "OPTIMIZED"
        parameter S1_BANDWIDTH              = "LOW",
        //
        // The divclk parameter allows the input clock to be divided before it
        // reaches the phase and frequency comparator. This can be set between
        // 1 and 128.
        parameter S1_DIVCLK_DIVIDE          = 1,

        // The following parameters describe the configuration that each clock
        // output should have once the reconfiguration for state one has
        // completed.
        //
        // The parameters are composed of:
        //    _DIVIDE: This can be from 1 to 128
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567. The range for the phase is from -360000 to 360000.
        //    _DUTY: This is the duty cycle multiplied by 100,000.  For example if
        //          a duty cycle of .24567 was desired the input would be
        //          24567.
        //
        parameter S1_CLKOUT0_DIVIDE         = 1,
        parameter S1_CLKOUT0_PHASE          = 0,
        parameter S1_CLKOUT0_DUTY           = 50000,
        parameter S1_CLKOUT0_FRAC          = 125,
        parameter S1_CLKOUT0_FRAC_EN       = 1,
        //
        parameter S1_CLKOUT1_DIVIDE         = 1,
        parameter S1_CLKOUT1_PHASE          = 0,
        parameter S1_CLKOUT1_DUTY           = 50000,
        //
        parameter S1_CLKOUT2_DIVIDE         = 1,
        parameter S1_CLKOUT2_PHASE          = 0,
        parameter S1_CLKOUT2_DUTY           = 50000,
        //
        parameter S1_CLKOUT3_DIVIDE         = 1,
        parameter S1_CLKOUT3_PHASE          = 0,
        parameter S1_CLKOUT3_DUTY           = 50000,
        //
        parameter S1_CLKOUT4_DIVIDE         = 1,
        parameter S1_CLKOUT4_PHASE          = 0,
        parameter S1_CLKOUT4_DUTY           = 50000,
        //
        parameter S1_CLKOUT5_DIVIDE         = 1,
        parameter S1_CLKOUT5_PHASE          = 0,
        parameter S1_CLKOUT5_DUTY           = 50000,
        //
        parameter S1_CLKOUT6_DIVIDE         = 1,
        parameter S1_CLKOUT6_PHASE          = 0,
        parameter S1_CLKOUT6_DUTY           = 50000,
        //
        //***********************************************************************
        // State 2 Parameters - These are for the second reconfiguration state.
        //***********************************************************************
        //
        // These parameters have an effect on the feedback path. A change on
        // these parameters will effect all of the clock outputs.
        //
        // The parameters are composed of:
        //    _MULT: This can be from 2 to 64. It has an effect on the VCO
        //          frequency which consequently, effects all of the clock
        //          outputs.
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567.  The range for the phase is from -360000 to 360000.
        //    _FRAC: This can be from 0 to 875. This represents the fractional
        //          divide multiplied by 1000.
        //          M = _MULT + _FRAC / 1000
        //          e.g. M=8.125
        //               _MULT = 8
        //               _FRAC = 125
        //    _FRAC_EN: This indicates fractional divide has been enabled. If 1
        //          then the fractional divide algorithm will be used to calculate
        //          register settings. If 0 then default calculation to be used.
        parameter S2_CLKFBOUT_MULT          = 1,
        parameter S2_CLKFBOUT_PHASE         = 0,
        parameter S2_CLKFBOUT_FRAC          = 125,
        parameter S2_CLKFBOUT_FRAC_EN       = 1,
        //
        // The bandwidth parameter effects the phase error and the jitter filter
        // capability of the MMCM. For more information on this parameter see the
        // Device user guide.
        // Possible values are: "LOW", "LOW_SS", "HIGH" and "OPTIMIZED"
        parameter S2_BANDWIDTH              = "LOW",
        //
        // The divclk parameter allows the input clock to be divided before it
        // reaches the phase and frequency comparator. This can be set between
        // 1 and 128.
        parameter S2_DIVCLK_DIVIDE          = 1,
        //
        // The following parameters describe the configuration that each clock
        // output should have once the reconfiguration for state one has
        // completed.
        //
        // The parameters are composed of:
        //    _DIVIDE: This can be from 1 to 128
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567. The range for the phase is from -360000 to 360000
        //    _DUTY: This is the duty cycle multiplied by 100,000. For example if
        //          a duty cycle of .24567 was desired the input would be
        //          24567.
        //
        parameter S2_CLKOUT0_DIVIDE         = 1,
        parameter S2_CLKOUT0_PHASE          = 0,
        parameter S2_CLKOUT0_DUTY           = 50000,
        parameter S2_CLKOUT0_FRAC          = 125,
        parameter S2_CLKOUT0_FRAC_EN       = 1,
        //
        parameter S2_CLKOUT1_DIVIDE         = 2,
        parameter S2_CLKOUT1_PHASE          = 0,
        parameter S2_CLKOUT1_DUTY           = 50000,
        //
        parameter S2_CLKOUT2_DIVIDE         = 3,
        parameter S2_CLKOUT2_PHASE          = 0,
        parameter S2_CLKOUT2_DUTY           = 50000,
        //
        parameter S2_CLKOUT3_DIVIDE         = 4,
        parameter S2_CLKOUT3_PHASE          = 0,
        parameter S2_CLKOUT3_DUTY           = 50000,
        //
        parameter S2_CLKOUT4_DIVIDE         = 5,
        parameter S2_CLKOUT4_PHASE          = 0,
        parameter S2_CLKOUT4_DUTY           = 50000,
        //
        parameter S2_CLKOUT5_DIVIDE         = 5,
        parameter S2_CLKOUT5_PHASE          = 0,
        parameter S2_CLKOUT5_DUTY           = 50000,
        //
        parameter S2_CLKOUT6_DIVIDE         = 5,
        parameter S2_CLKOUT6_PHASE          = -90,
        parameter S2_CLKOUT6_DUTY           = 50000,
        //
        //***********************************************************************
        // State 2 Parameters - These are for the second reconfiguration state.
        //***********************************************************************
        //
        // These parameters have an effect on the feedback path. A change on
        // these parameters will effect all of the clock outputs.
        //
        // The parameters are composed of:
        //    _MULT: This can be from 2 to 64. It has an effect on the VCO
        //          frequency which consequently, effects all of the clock
        //          outputs.
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567.  The range for the phase is from -360000 to 360000.
        //    _FRAC: This can be from 0 to 875. This represents the fractional
        //          divide multiplied by 1000.
        //          M = _MULT + _FRAC / 1000
        //          e.g. M=8.125
        //               _MULT = 8
        //               _FRAC = 125
        //    _FRAC_EN: This indicates fractional divide has been enabled. If 1
        //          then the fractional divide algorithm will be used to calculate
        //          register settings. If 0 then default calculation to be used.
        parameter S3_CLKFBOUT_MULT          = 1,
        parameter S3_CLKFBOUT_PHASE         = 0,
        parameter S3_CLKFBOUT_FRAC          = 125,
        parameter S3_CLKFBOUT_FRAC_EN       = 1,
        //
        // The bandwidth parameter effects the phase error and the jitter filter
        // capability of the MMCM. For more information on this parameter see the
        // Device user guide.
        // Possible values are: "LOW", "LOW_SS", "HIGH" and "OPTIMIZED"
        parameter S3_BANDWIDTH              = "LOW",
        //
        // The divclk parameter allows the input clock to be divided before it
        // reaches the phase and frequency comparator. This can be set between
        // 1 and 128.
        parameter S3_DIVCLK_DIVIDE          = 1,
        //
        // The following parameters describe the configuration that each clock
        // output should have once the reconfiguration for state one has
        // completed.
        //
        // The parameters are composed of:
        //    _DIVIDE: This can be from 1 to 128
        //    _PHASE: This is the phase multiplied by 1000. For example if
        //          a phase of 24.567 deg was desired the input value would be
        //          24567. The range for the phase is from -360000 to 360000
        //    _DUTY: This is the duty cycle multiplied by 100,000. For example if
        //          a duty cycle of .24567 was desired the input would be
        //          24567.
        //
        parameter S3_CLKOUT0_DIVIDE         = 1,
        parameter S3_CLKOUT0_PHASE          = 0,
        parameter S3_CLKOUT0_DUTY           = 50000,
        parameter S3_CLKOUT0_FRAC          = 125,
        parameter S3_CLKOUT0_FRAC_EN       = 1,
        //
        parameter S3_CLKOUT1_DIVIDE         = 2,
        parameter S3_CLKOUT1_PHASE          = 0,
        parameter S3_CLKOUT1_DUTY           = 50000,
        //
        parameter S3_CLKOUT2_DIVIDE         = 3,
        parameter S3_CLKOUT2_PHASE          = 0,
        parameter S3_CLKOUT2_DUTY           = 50000,
        //
        parameter S3_CLKOUT3_DIVIDE         = 4,
        parameter S3_CLKOUT3_PHASE          = 0,
        parameter S3_CLKOUT3_DUTY           = 50000,
        //
        parameter S3_CLKOUT4_DIVIDE         = 5,
        parameter S3_CLKOUT4_PHASE          = 0,
        parameter S3_CLKOUT4_DUTY           = 50000,
        //
        parameter S3_CLKOUT5_DIVIDE         = 5,
        parameter S3_CLKOUT5_PHASE          = 0,
        parameter S3_CLKOUT5_DUTY           = 50000,
        //
        parameter S3_CLKOUT6_DIVIDE         = 5,
        parameter S3_CLKOUT6_PHASE          = -90,
        parameter S3_CLKOUT6_DUTY           = 50000
    ) (
        // These signals are controlled by user logic interface and are covered
        // in more detail within the XAPP.
        input       [1:0] SADDR,
        input             SEN,
        input             SCLK,
        input             RST,
        output reg        SRDY,
        //
        // These signals are to be connected to the MMCM_ADV by port name.
        // Their use matches the MMCM port description in the Device User Guide.
        input      [15:0] DO,
        input             DRDY,
        input             LOCK_REG_CLK_IN,
        input             LOCKED_IN,
        output reg        DWE,
        output reg        DEN,
        output reg [6:0]  DADDR,
        output reg [15:0] DI,
        output            DCLK,
        output reg        RST_MMCM,
        output            LOCKED_OUT
    );
//----------------------------------------------------------------------------------------
    //
    wire        IntLocked;
    wire        IntRstMmcm;
    //
    // 100 ps delay for behavioral simulations
    localparam  TCQ = 100;

    // Make sure the memory is implemented as distributed
    (* rom_style = "distributed" *)
    //
    // ROM of:  39 bit word 64 words deep
    reg [38:0]  rom [63:0];
    reg [5:0]   rom_addr;
    reg [38:0]  rom_do;
    reg         next_srdy;
    reg [5:0]   next_rom_addr;
    reg [6:0]   next_daddr;
    reg         next_dwe;
    reg         next_den;
    reg         next_rst_mmcm;
    reg [15:0]  next_di;
    //
    // Insert a register in LOCKED or not depending on the value given to the parameters
    // REGISTER_LOCKED. When REGISTER_LOCKED is set to "Reg" insert a register, when set
    // to "NoReg" don't insert a register but just pass the LOCKED signal from input to
    // output.
    // Use or not, under USE_REG_LOCKED parameter control, the registered version of the
    // LOCKED signal for the DRP state machine.
    // Possible/allowed combinations of the two LOCKED related parameters:
    //
    // | REGISTER_LOCKED | USE_REG_LOCKED |                                            |
    // |-----------------|----------------|--------------------------------------------|
    // |      "NoReg"    |     "No"       | LOCKED is just passed through mmcme3_drp   |
    // |                 |                | and is used as is with the state machine   |
    // |      "NoReg"    |     "Yes"      | NOT ALLOWED                                |
    // |       "Reg"     |     "No"       | LOCKED is registered but the unregistered  |
    // |                 |                | version is used for the state machine.     |
    // |       "Reg"     |     "Yes"      | LOCKED is registered and the registered    |
    // |                 |                | version is also used by the state machine. |
    //
    generate
        if (REGISTER_LOCKED == "NoReg" && USE_REG_LOCKED == "No") begin
            assign LOCKED_OUT = LOCKED_IN;
            assign IntLocked = LOCKED_IN;
        end else if (REGISTER_LOCKED == "Reg" && USE_REG_LOCKED == "No") begin
            FDRE #(
                .INIT           (0),
                .IS_C_INVERTED  (0),
                .IS_D_INVERTED  (0),
                .IS_R_INVERTED  (0)
            ) mmcme3_drp_I_Fdrp (
                .D      (LOCKED_IN),
                .CE     (1'b1),
                .R      (IntRstMmcm),
                .C      (LOCK_REG_CLK_IN),
                .Q      (LOCKED_OUT)
            );
            //
            assign IntLocked = LOCKED_IN;
        end else if (REGISTER_LOCKED == "Reg" && USE_REG_LOCKED == "Yes") begin
            FDRE #(
                .INIT           (0),
                .IS_C_INVERTED  (0),
                .IS_D_INVERTED  (0),
                .IS_R_INVERTED  (0)
            ) mmcme3_drp_I_Fdrp (
                .D  (LOCKED_IN),
                .CE (1'b1),
                .R  (IntRstMmcm),
                .C  (LOCK_REG_CLK_IN),
                .Q  (LOCKED_OUT)
            );
            //
            assign IntLocked = LOCKED_OUT;
        end
    endgenerate

    // Integer used to initialize remainder of unused ROM
    integer     ii;

    // Pass SCLK to DCLK for the MMCM
    assign DCLK = SCLK;
     assign IntRstMmcm = RST_MMCM;

    // Include the MMCM reconfiguration functions.  This contains the constant
    // functions that are used in the calculations below.  This file is
    // required.
    `include "mmcme2_drp_func.h"

    //**************************************************************************
    // State 1 Calculations
    //**************************************************************************
    // Please see header for information.
    localparam [37:0] S1_CLKFBOUT       =
       mmcm_count_calc(S1_CLKFBOUT_MULT, S1_CLKFBOUT_PHASE, 50000);

    localparam [37:0] S1_CLKFBOUT_FRAC_CALC       =
       mmcm_frac_count_calc(S1_CLKFBOUT_MULT, S1_CLKFBOUT_PHASE, 50000, S1_CLKFBOUT_FRAC);

    localparam [9:0]  S1_DIGITAL_FILT   =
       mmcm_filter_lookup(S1_CLKFBOUT_MULT, S1_BANDWIDTH);

    localparam [39:0] S1_LOCK           =
       mmcm_lock_lookup(S1_CLKFBOUT_MULT);

    localparam [37:0] S1_DIVCLK         =
       mmcm_count_calc(S1_DIVCLK_DIVIDE, 0, 50000);

    localparam [37:0] S1_CLKOUT0        =
       mmcm_count_calc(S1_CLKOUT0_DIVIDE, S1_CLKOUT0_PHASE, S1_CLKOUT0_DUTY);
       localparam [15:0] S1_CLKOUT0_REG1        = S1_CLKOUT0[15:0]; //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT0_REG2        = S1_CLKOUT0[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT0_FRAC_CALC        =
       mmcm_frac_count_calc(S1_CLKOUT0_DIVIDE, S1_CLKOUT0_PHASE, 50000, S1_CLKOUT0_FRAC);
        localparam [15:0] S1_CLKOUT0_FRAC_REG1        = S1_CLKOUT0_FRAC_CALC[15:0];  //See log file for 16 bit reporting of the register
        localparam [15:0] S1_CLKOUT0_FRAC_REG2        = S1_CLKOUT0_FRAC_CALC[31:16];  //See log file for 16 bit reporting of the register
        localparam [5:0] S1_CLKOUT0_FRAC_REGSHARED        = S1_CLKOUT0_FRAC_CALC[37:32];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT1        =
       mmcm_count_calc(S1_CLKOUT1_DIVIDE, S1_CLKOUT1_PHASE, S1_CLKOUT1_DUTY);
        localparam [15:0] S1_CLKOUT1_REG1        = S1_CLKOUT1[15:0];  //See log file for 16 bit reporting of the register
        localparam [15:0] S1_CLKOUT1_REG2        = S1_CLKOUT1[31:16];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT2        =
       mmcm_count_calc(S1_CLKOUT2_DIVIDE, S1_CLKOUT2_PHASE, S1_CLKOUT2_DUTY);
       localparam [15:0] S1_CLKOUT2_REG1        = S1_CLKOUT2[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT2_REG2        = S1_CLKOUT2[31:16];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT3        =
       mmcm_count_calc(S1_CLKOUT3_DIVIDE, S1_CLKOUT3_PHASE, S1_CLKOUT3_DUTY);
       localparam [15:0] S1_CLKOUT3_REG1        = S1_CLKOUT3[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT3_REG2        = S1_CLKOUT3[31:16];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT4        =
       mmcm_count_calc(S1_CLKOUT4_DIVIDE, S1_CLKOUT4_PHASE, S1_CLKOUT4_DUTY);
       localparam [15:0] S1_CLKOUT4_REG1        = S1_CLKOUT4[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT4_REG2        = S1_CLKOUT4[31:16];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT5        =
       mmcm_count_calc(S1_CLKOUT5_DIVIDE, S1_CLKOUT5_PHASE, S1_CLKOUT5_DUTY);
       localparam [15:0] S1_CLKOUT5_REG1        = S1_CLKOUT5[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT5_REG2        = S1_CLKOUT5[31:16];  //See log file for 16 bit reporting of the register

    localparam [37:0] S1_CLKOUT6        =
       mmcm_count_calc(S1_CLKOUT6_DIVIDE, S1_CLKOUT6_PHASE, S1_CLKOUT6_DUTY);
       localparam [15:0] S1_CLKOUT6_REG1        = S1_CLKOUT6[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S1_CLKOUT6_REG2        = S1_CLKOUT6[31:16]; //See log file for 16 bit reporting of the register

    //**************************************************************************
    // State 2 Calculations
    //**************************************************************************
    localparam [37:0] S2_CLKFBOUT       =
       mmcm_count_calc(S2_CLKFBOUT_MULT, S2_CLKFBOUT_PHASE, 50000);

    localparam [37:0] S2_CLKFBOUT_FRAC_CALC       =
       mmcm_frac_count_calc(S2_CLKFBOUT_MULT, S2_CLKFBOUT_PHASE, 50000, S2_CLKFBOUT_FRAC);

    localparam [9:0] S2_DIGITAL_FILT    =
       mmcm_filter_lookup(S2_CLKFBOUT_MULT, S2_BANDWIDTH);

    localparam [39:0] S2_LOCK           =
       mmcm_lock_lookup(S2_CLKFBOUT_MULT);

    localparam [37:0] S2_DIVCLK         =
       mmcm_count_calc(S2_DIVCLK_DIVIDE, 0, 50000);

    localparam [37:0] S2_CLKOUT0        =
       mmcm_count_calc(S2_CLKOUT0_DIVIDE, S2_CLKOUT0_PHASE, S2_CLKOUT0_DUTY);
       localparam [15:0] S2_CLKOUT0_REG1        = S2_CLKOUT0[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT0_REG2        = S2_CLKOUT0[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT0_FRAC_CALC        =
       mmcm_frac_count_calc(S2_CLKOUT0_DIVIDE, S2_CLKOUT0_PHASE, 50000, S2_CLKOUT0_FRAC);
       localparam [15:0] S2_CLKOUT0_FRAC_CALC_REG1        = S2_CLKOUT0_FRAC_CALC[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT0_FRAC_CALC_REG2        = S2_CLKOUT0_FRAC_CALC[31:16]; //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT0_FRAC_CALC_REGSHARED        = S2_CLKOUT0_FRAC_CALC[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT1        =
       mmcm_count_calc(S2_CLKOUT1_DIVIDE, S2_CLKOUT1_PHASE, S2_CLKOUT1_DUTY);
       localparam [15:0] S2_CLKOUT1_REG1        = S2_CLKOUT1[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT1_REG2        = S2_CLKOUT1[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT2        =
       mmcm_count_calc(S2_CLKOUT2_DIVIDE, S2_CLKOUT2_PHASE, S2_CLKOUT2_DUTY);
       localparam [15:0] S2_CLKOUT2_REG1        = S2_CLKOUT2[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT2_REG2        = S2_CLKOUT2[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT3        =
       mmcm_count_calc(S2_CLKOUT3_DIVIDE, S2_CLKOUT3_PHASE, S2_CLKOUT3_DUTY);
       localparam [15:0] S2_CLKOUT3_REG1        = S2_CLKOUT3[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT3_REG2        = S2_CLKOUT3[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT4        =
       mmcm_count_calc(S2_CLKOUT4_DIVIDE, S2_CLKOUT4_PHASE, S2_CLKOUT4_DUTY);
       localparam [15:0] S2_CLKOUT4_REG1        = S2_CLKOUT4[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT4_REG2        = S2_CLKOUT4[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT5        =
       mmcm_count_calc(S2_CLKOUT5_DIVIDE, S2_CLKOUT5_PHASE, S2_CLKOUT5_DUTY);
       localparam [15:0] S2_CLKOUT5_REG1        = S2_CLKOUT5[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT5_REG2        = S2_CLKOUT5[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S2_CLKOUT6        =
       mmcm_count_calc(S2_CLKOUT6_DIVIDE, S2_CLKOUT6_PHASE, S2_CLKOUT6_DUTY);
       localparam [15:0] S2_CLKOUT6_REG1        = S2_CLKOUT6[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S2_CLKOUT6_REG2        = S2_CLKOUT6[31:16]; //See log file for 16 bit reporting of the register

    //**************************************************************************
    // State 2 Calculations
    //**************************************************************************
    localparam [37:0] S3_CLKFBOUT       =
       mmcm_count_calc(S3_CLKFBOUT_MULT, S3_CLKFBOUT_PHASE, 50000);

    localparam [37:0] S3_CLKFBOUT_FRAC_CALC       =
       mmcm_frac_count_calc(S3_CLKFBOUT_MULT, S3_CLKFBOUT_PHASE, 50000, S3_CLKFBOUT_FRAC);

    localparam [9:0] S3_DIGITAL_FILT    =
       mmcm_filter_lookup(S3_CLKFBOUT_MULT, S3_BANDWIDTH);

    localparam [39:0] S3_LOCK           =
       mmcm_lock_lookup(S3_CLKFBOUT_MULT);

    localparam [37:0] S3_DIVCLK         =
       mmcm_count_calc(S3_DIVCLK_DIVIDE, 0, 50000);

    localparam [37:0] S3_CLKOUT0        =
       mmcm_count_calc(S3_CLKOUT0_DIVIDE, S3_CLKOUT0_PHASE, S3_CLKOUT0_DUTY);
       localparam [15:0] S3_CLKOUT0_REG1        = S3_CLKOUT0[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT0_REG2        = S3_CLKOUT0[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT0_FRAC_CALC        =
       mmcm_frac_count_calc(S3_CLKOUT0_DIVIDE, S3_CLKOUT0_PHASE, 50000, S3_CLKOUT0_FRAC);
       localparam [15:0] S3_CLKOUT0_FRAC_CALC_REG1        = S3_CLKOUT0_FRAC_CALC[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT0_FRAC_CALC_REG2        = S3_CLKOUT0_FRAC_CALC[31:16]; //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT0_FRAC_CALC_REGSHARED        = S3_CLKOUT0_FRAC_CALC[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT1        =
       mmcm_count_calc(S3_CLKOUT1_DIVIDE, S3_CLKOUT1_PHASE, S3_CLKOUT1_DUTY);
       localparam [15:0] S3_CLKOUT1_REG1        = S3_CLKOUT1[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT1_REG2        = S3_CLKOUT1[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT2        =
       mmcm_count_calc(S3_CLKOUT2_DIVIDE, S3_CLKOUT2_PHASE, S3_CLKOUT2_DUTY);
       localparam [15:0] S3_CLKOUT2_REG1        = S3_CLKOUT2[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT2_REG2        = S3_CLKOUT2[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT3        =
       mmcm_count_calc(S3_CLKOUT3_DIVIDE, S3_CLKOUT3_PHASE, S3_CLKOUT3_DUTY);
       localparam [15:0] S3_CLKOUT3_REG1        = S3_CLKOUT3[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT3_REG2        = S3_CLKOUT3[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT4        =
       mmcm_count_calc(S3_CLKOUT4_DIVIDE, S3_CLKOUT4_PHASE, S3_CLKOUT4_DUTY);
       localparam [15:0] S3_CLKOUT4_REG1        = S3_CLKOUT4[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT4_REG2        = S3_CLKOUT4[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT5        =
       mmcm_count_calc(S3_CLKOUT5_DIVIDE, S3_CLKOUT5_PHASE, S3_CLKOUT5_DUTY);
       localparam [15:0] S3_CLKOUT5_REG1        = S3_CLKOUT5[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT5_REG2        = S3_CLKOUT5[31:16]; //See log file for 16 bit reporting of the register

    localparam [37:0] S3_CLKOUT6        =
       mmcm_count_calc(S3_CLKOUT6_DIVIDE, S3_CLKOUT6_PHASE, S3_CLKOUT6_DUTY);
       localparam [15:0] S3_CLKOUT6_REG1        = S3_CLKOUT6[15:0];  //See log file for 16 bit reporting of the register
       localparam [15:0] S3_CLKOUT6_REG2        = S3_CLKOUT6[31:16]; //See log file for 16 bit reporting of the register

    initial begin
       // rom entries contain (in order) the address, a bitmask, and a bitset
       //***********************************************************************
       // State 1 Initialization
       //***********************************************************************

       // Store the power bits
       rom[0] = {7'h28, 16'h0000, 16'hFFFF};

       // Store CLKOUT0 divide and phase
       rom[1]  = (S1_CLKOUT0_FRAC_EN == 0) ?
                         {7'h09, 16'h8000, S1_CLKOUT0[31:16]}:
                         {7'h09, 16'h8000, S1_CLKOUT0_FRAC_CALC[31:16]};
       rom[2]  = (S1_CLKOUT0_FRAC_EN == 0) ?
                         {7'h08, 16'h1000, S1_CLKOUT0[15:0]}:
                         {7'h08, 16'h1000, S1_CLKOUT0_FRAC_CALC[15:0]};

       // Store the input divider
       rom[3] = {7'h16, 16'hC000, {2'h0, S1_DIVCLK[23:22], S1_DIVCLK[11:0]} };

       // Store the feedback divide and phase
       rom[4] = (S1_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h14, 16'h1000, S1_CLKFBOUT[15:0]}:
                 {7'h14, 16'h1000, S1_CLKFBOUT_FRAC_CALC[15:0]};
       rom[5] = (S1_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h15, 16'h8000, S1_CLKFBOUT[31:16]}:
                 {7'h15, 16'h8000, S1_CLKFBOUT_FRAC_CALC[31:16]};

       // Store the lock settings
       rom[6] = {7'h18, 16'hFC00, {6'h00, S1_LOCK[29:20]} };
       rom[7] = {7'h19, 16'h8000, {1'b0 , S1_LOCK[34:30], S1_LOCK[9:0]} };
       rom[8] = {7'h1A, 16'h8000, {1'b0 , S1_LOCK[39:35], S1_LOCK[19:10]} };

       // Store the filter settings
       rom[9] = {7'h4E, 16'h66FF,
                 S1_DIGITAL_FILT[9], 2'h0, S1_DIGITAL_FILT[8:7], 2'h0,
                 S1_DIGITAL_FILT[6], 8'h00 };
       rom[10] = {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 };

       //***********************************************************************
       // State 2 Initialization
       //***********************************************************************

       // Store the power bits
       rom[11] = {7'h28, 16'h0000, 16'hFFFF};

       // Store CLKOUT0 divide and phase
       rom[12] = (S2_CLKOUT0_FRAC_EN == 0) ?
                 {7'h09, 16'h8000, S2_CLKOUT0[31:16]}:
                 {7'h09, 16'h8000, S2_CLKOUT0_FRAC_CALC[31:16]};
       rom[13] = (S2_CLKOUT0_FRAC_EN == 0) ?
                 {7'h08, 16'h1000, S2_CLKOUT0[15:0]}:
                 {7'h08, 16'h1000, S2_CLKOUT0_FRAC_CALC[15:0]};

       // Store the input divider
       rom[14] = {7'h16, 16'hC000, {2'h0, S2_DIVCLK[23:22], S2_DIVCLK[11:0]} };

       // Store the feedback divide and phase
       rom[15] = (S2_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h14, 16'h1000, S2_CLKFBOUT[15:0]}:
                 {7'h14, 16'h1000, S2_CLKFBOUT_FRAC_CALC[15:0]};
       rom[16] = (S2_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h15, 16'h8000, S2_CLKFBOUT[31:16]}:
                 {7'h15, 16'h8000, S2_CLKFBOUT_FRAC_CALC[31:16]};

       // Store the lock settings
       rom[17] = {7'h18, 16'hFC00, {6'h00, S2_LOCK[29:20]} };
       rom[18] = {7'h19, 16'h8000, {1'b0 , S2_LOCK[34:30], S2_LOCK[9:0]} };
       rom[19] = {7'h1A, 16'h8000, {1'b0 , S2_LOCK[39:35], S2_LOCK[19:10]} };

       // Store the filter settings
       rom[20] = {7'h4E, 16'h66FF,
                 S2_DIGITAL_FILT[9], 2'h0, S2_DIGITAL_FILT[8:7], 2'h0,
                 S2_DIGITAL_FILT[6], 8'h00 };
       rom[21] = {7'h4F, 16'h666F,
                 S2_DIGITAL_FILT[5], 2'h0, S2_DIGITAL_FILT[4:3], 2'h0,
                 S2_DIGITAL_FILT[2:1], 2'h0, S2_DIGITAL_FILT[0], 4'h0 };

       //***********************************************************************
       // State 2 Initialization
       //***********************************************************************

       // Store the power bits
       rom[22] = {7'h28, 16'h0000, 16'hFFFF};

       // Store CLKOUT0 divide and phase
       rom[23] = (S3_CLKOUT0_FRAC_EN == 0) ?
                 {7'h09, 16'h8000, S3_CLKOUT0[31:16]}:
                 {7'h09, 16'h8000, S3_CLKOUT0_FRAC_CALC[31:16]};
       rom[24] = (S3_CLKOUT0_FRAC_EN == 0) ?
                 {7'h08, 16'h1000, S3_CLKOUT0[15:0]}:
                 {7'h08, 16'h1000, S3_CLKOUT0_FRAC_CALC[15:0]};

       // Store the input divider
       rom[25] = {7'h16, 16'hC000, {2'h0, S3_DIVCLK[23:22], S3_DIVCLK[11:0]} };

       // Store the feedback divide and phase
       rom[26] = (S3_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h14, 16'h1000, S3_CLKFBOUT[15:0]}:
                 {7'h14, 16'h1000, S3_CLKFBOUT_FRAC_CALC[15:0]};
       rom[27] = (S3_CLKFBOUT_FRAC_EN == 0) ?
                 {7'h15, 16'h8000, S3_CLKFBOUT[31:16]}:
                 {7'h15, 16'h8000, S3_CLKFBOUT_FRAC_CALC[31:16]};

       // Store the lock settings
       rom[28] = {7'h18, 16'hFC00, {6'h00, S3_LOCK[29:20]} };
       rom[29] = {7'h19, 16'h8000, {1'b0 , S3_LOCK[34:30], S3_LOCK[9:0]} };
       rom[30] = {7'h1A, 16'h8000, {1'b0 , S3_LOCK[39:35], S3_LOCK[19:10]} };

       // Store the filter settings
       rom[31] = {7'h4E, 16'h66FF,
                 S3_DIGITAL_FILT[9], 2'h0, S3_DIGITAL_FILT[8:7], 2'h0,
                 S3_DIGITAL_FILT[6], 8'h00 };
       rom[32] = {7'h4F, 16'h666F,
                 S3_DIGITAL_FILT[5], 2'h0, S3_DIGITAL_FILT[4:3], 2'h0,
                 S3_DIGITAL_FILT[2:1], 2'h0, S3_DIGITAL_FILT[0], 4'h0 };

       // Initialize the rest of the ROM
       rom[33] = {7'h28,32'h0000_0000};
       for(ii = 34; ii < 64; ii = ii +1) begin
          rom[ii] = 0;
       end
    end

    // Output the initialized rom value based on rom_addr each clock cycle
    always @(posedge SCLK) begin
       rom_do<= #TCQ rom[rom_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  = 11;
    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 SCLK) begin
       DADDR       <= #TCQ next_daddr;
       DWE         <= #TCQ next_dwe;
       DEN         <= #TCQ next_den;
       RST_MMCM    <= #TCQ next_rst_mmcm;
       DI          <= #TCQ next_di;

       SRDY        <= #TCQ next_srdy;

       rom_addr    <= #TCQ next_rom_addr;
       state_count <= #TCQ next_state_count;
    end

    // This block assigns the next state, reset is syncronous.
    always @(posedge SCLK) begin
       if(RST) begin
          current_state <= #TCQ RESTART;
       end else begin
          current_state <= #TCQ next_state;
       end
    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_rom_addr     = rom_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_rom_addr  = 6'h00;
             next_rst_mmcm  = 1'b1;
             next_state     = WAIT_LOCK;
          end

          // Waits for the MMCM to assert IntLocked - 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_rom_addr = SADDR * STATE_COUNT_CONST;

             if(IntLocked) begin
                // MMCM is IntLocked, 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, IntLocked has not asserted yet
                next_state  = WAIT_LOCK;
             end
          end

          // Wait for the next SEN pulse and set the ROM addr appropriately
          //    based on SADDR
          WAIT_SEN: begin
             next_rom_addr = SADDR * STATE_COUNT_CONST;
             if (SEN) begin
                next_rom_addr = SADDR * STATE_COUNT_CONST;
                // 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     = rom_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) begin
                // Data is ready, mask out the bits to save
                next_state = BITMASK;
             end else begin
                // Keep waiting till data is ready
                next_state = WAIT_A_DRDY;
             end
          end

          // Zero out the bits that are not set in the mask stored in rom
          BITMASK: begin
             // Do the mask
             next_di     = rom_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 rom
          BITSET: begin
             // Set the bits that need to be assigned
             next_di           = rom_do[15:0] | DI;
             // Set the next address to read from ROM
             next_rom_addr     = rom_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) begin
                   // If there are more registers to write keep going
                   next_state  = ADDRESS;
                end else begin
                   // There are no more registers to write so wait for the MMCM
                   // to lock
                   next_state  = WAIT_LOCK;
                end
             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

mmcme2_drp_func.h

复制代码
///////////////////////////////////////////////////////////////////////////////
//
//    Company:          Xilinx
//    Engineer:         Jim Tatsukawa, Karl Kurbjun and Carl Ribbing
//                      Updated by Marc Defossez
//    Date:             19 Sep 2018
//    Design Name:      MMCME2 DRP
//    Module Name:      mmcme2_drp_func.h
//    Version:          1.31
//    Target Devices:   7 Series
//    Tool versions:    2014.3 or later
//    Description:      This header provides the functions necessary to
//                      calculate the DRP register values for the V6 MMCM.
//
//	Revision Notes:
//      3/12       - Updating lookup_low/lookup_high (CR)
//			4/13       - Fractional divide function in mmcm_frac_count_calc function. CRS610807
//			10/24      - Adjusting settings for clarity
//      19 Sep 18  - Update of CP_RES_LFHF tables -- CR1010263
//
//    Disclaimer:  XILINX IS PROVIDING THIS DESIGN, CODE, OR
//                 INFORMATION "AS IS" SOLELY FOR USE IN DEVELOPING
//                 PROGRAMS AND SOLUTIONS FOR XILINX DEVICES.  BY
//                 PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
//                 ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
//                 APPLICATION OR STANDARD, XILINX IS MAKING NO
//                 REPRESENTATION THAT THIS IMPLEMENTATION IS FREE
//                 FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE
//                 RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY
//                 REQUIRE FOR YOUR IMPLEMENTATION.  XILINX
//                 EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH
//                 RESPECT TO THE ADEQUACY OF THE IMPLEMENTATION,
//                 INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
//                 REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
//                 FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES
//                 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//                 PURPOSE.
//
//                 (c) Copyright 2009-2010 Xilinx, Inc.
//                 All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////

// These are user functions that should not be modified.  Changes to the defines
// or code within the functions may alter the accuracy of the calculations.

// Define debug to provide extra messages durring elaboration
`define DEBUG 1

// FRAC_PRECISION describes the width of the fractional portion of the fixed
//    point numbers.  These should not be modified, they are for development
//    only
`define FRAC_PRECISION  10
// FIXED_WIDTH describes the total size for fixed point calculations(int+frac).
// Warning: L.50 and below will not calculate properly with FIXED_WIDTHs
//    greater than 32
`define FIXED_WIDTH     32

// This function takes a fixed point number and rounds it to the nearest
//    fractional precision bit.
function [`FIXED_WIDTH:1] round_frac
   (
      // Input is (FIXED_WIDTH-FRAC_PRECISION).FRAC_PRECISION fixed point number
      input [`FIXED_WIDTH:1] decimal,

      // This describes the precision of the fraction, for example a value
      //    of 1 would modify the fractional so that instead of being a .16
      //    fractional, it would be a .1 (rounded to the nearest 0.5 in turn)
      input [`FIXED_WIDTH:1] precision
   );

   begin

   `ifdef DEBUG
      $display("round_frac - decimal: %h, precision: %h", decimal, precision);
   `endif
      // If the fractional precision bit is high then round up
      if( decimal[(`FRAC_PRECISION-precision)] == 1'b1) begin
         round_frac = decimal + (1'b1 << (`FRAC_PRECISION-precision));
      end else begin
         round_frac = decimal;
      end
   `ifdef DEBUG
      $display("round_frac: %h", round_frac);
   `endif
   end
endfunction

// This function calculates high_time, low_time, w_edge, and no_count
//    of a non-fractional counter based on the divide and duty cycle
//
// NOTE: high_time and low_time are returned as integers between 0 and 63
//    inclusive.  64 should equal 6'b000000 (in other words it is okay to
//    ignore the overflow)
function [13:0] mmcm_divider
   (
      input [7:0] divide,        // Max divide is 128
      input [31:0] duty_cycle    // Duty cycle is multiplied by 100,000
   );

   reg [`FIXED_WIDTH:1]    duty_cycle_fix;
      // min/max allowed duty cycle range calc for divide => 64
   reg [`FIXED_WIDTH:1]    duty_cycle_min;
   reg [`FIXED_WIDTH:1]    duty_cycle_max;


   // High/Low time is initially calculated with a wider integer to prevent a
   // calculation error when it overflows to 64.
   reg [6:0]               high_time;
   reg [6:0]               low_time;
   reg                     w_edge;
   reg                     no_count;

   reg [`FIXED_WIDTH:1]    temp;

   begin
      // Duty Cycle must be between 0 and 1,000
      if(duty_cycle <=0 || duty_cycle >= 100000) begin
         $display("ERROR: duty_cycle: %d is invalid", duty_cycle);
         $finish;
      end
      if (divide >= 64) begin     // DCD and frequency generation fix if O divide => 64
           duty_cycle_min = ((divide - 64) * 100_000) / divide;
           duty_cycle_max = (64.5 / divide) * 100_000;
           if (duty_cycle > duty_cycle_max)  duty_cycle = duty_cycle_max;
           if (duty_cycle < duty_cycle_min)  duty_cycle = duty_cycle_min;
       end
      // Convert to FIXED_WIDTH-FRAC_PRECISION.FRAC_PRECISION fixed point
      duty_cycle_fix = (duty_cycle << `FRAC_PRECISION) / 100_000;

   `ifdef DEBUG
      $display("duty_cycle_fix: %h", duty_cycle_fix);
   `endif

      // If the divide is 1 nothing needs to be set except the no_count bit.
      //    Other values are dummies
      if(divide == 7'h01) begin
         high_time   = 7'h01;
         w_edge      = 1'b0;
         low_time    = 7'h01;
         no_count    = 1'b1;
      end else begin
         temp = round_frac(duty_cycle_fix*divide, 1);

         // comes from above round_frac
         high_time   = temp[`FRAC_PRECISION+7:`FRAC_PRECISION+1];
         // If the duty cycle * divide rounded is .5 or greater then this bit
         //    is set.
         w_edge      = temp[`FRAC_PRECISION]; // comes from round_frac

         // If the high time comes out to 0, it needs to be set to at least 1
         // and w_edge set to 0
         if(high_time == 7'h00) begin
            high_time   = 7'h01;
            w_edge      = 1'b0;
         end

         if(high_time == divide) begin
            high_time   = divide - 1;
            w_edge      = 1'b1;
         end

         // Calculate low_time based on the divide setting and set no_count to
         //    0 as it is only used when divide is 1.
         low_time    = divide - high_time;
         no_count    = 1'b0;
      end

      // Set the return value.
      mmcm_divider = {w_edge,no_count,high_time[5:0],low_time[5:0]};
   end
endfunction

// This function calculates mx, delay_time, and phase_mux
//  of a non-fractional counter based on the divide and phase
//
// NOTE: The only valid value for the MX bits is 2'b00 to ensure the coarse mux
//    is used.
function [10:0] mmcm_phase
   (
      // divide must be an integer (use fractional if not)
      //  assumed that divide already checked to be valid
      input [7:0] divide, // Max divide is 128

      // Phase is given in degrees (-360,000 to 360,000)
      input signed [31:0] phase
   );

   reg [`FIXED_WIDTH:1] phase_in_cycles;
   reg [`FIXED_WIDTH:1] phase_fixed;
   reg [1:0]            mx;
   reg [5:0]            delay_time;
   reg [2:0]            phase_mux;

   reg [`FIXED_WIDTH:1] temp;

   begin
`ifdef DEBUG
      $display("mmcm_phase-divide:%d,phase:%d",
         divide, phase);
`endif

      if ((phase < -360000) || (phase > 360000)) begin
         $display("ERROR: phase of $phase is not between -360000 and 360000");
         $finish;
      end

      // If phase is less than 0, convert it to a positive phase shift
      // Convert to (FIXED_WIDTH-FRAC_PRECISION).FRAC_PRECISION fixed point
      if(phase < 0) begin
         phase_fixed = ( (phase + 360000) << `FRAC_PRECISION ) / 1000;
      end else begin
         phase_fixed = ( phase << `FRAC_PRECISION ) / 1000;
      end

      // Put phase in terms of decimal number of vco clock cycles
      phase_in_cycles = ( phase_fixed * divide ) / 360;

`ifdef DEBUG
      $display("phase_in_cycles: %h", phase_in_cycles);
`endif


	 temp  =  round_frac(phase_in_cycles, 3);

	 // set mx to 2'b00 that the phase mux from the VCO is enabled
	 mx    			=  2'b00;
	 phase_mux      =  temp[`FRAC_PRECISION:`FRAC_PRECISION-2];
	 delay_time     =  temp[`FRAC_PRECISION+6:`FRAC_PRECISION+1];

   `ifdef DEBUG
      $display("temp: %h", temp);
   `endif

      // Setup the return value
      mmcm_phase={mx, phase_mux, delay_time};
   end
endfunction

// This function takes the divide value and outputs the necessary lock values
function [39:0] mmcm_lock_lookup
   (
      input [6:0] divide // Max divide is 64
   );

   reg [2559:0]   lookup;

   begin
      lookup = {
         // This table is composed of:
         // LockRefDly_LockFBDly_LockCnt_LockSatHigh_UnlockCnt
         40'b00110_00110_1111101000_1111101001_0000000001,
         40'b00110_00110_1111101000_1111101001_0000000001,
         40'b01000_01000_1111101000_1111101001_0000000001,
         40'b01011_01011_1111101000_1111101001_0000000001,
         40'b01110_01110_1111101000_1111101001_0000000001,
         40'b10001_10001_1111101000_1111101001_0000000001,
         40'b10011_10011_1111101000_1111101001_0000000001,
         40'b10110_10110_1111101000_1111101001_0000000001,
         40'b11001_11001_1111101000_1111101001_0000000001,
         40'b11100_11100_1111101000_1111101001_0000000001,
         40'b11111_11111_1110000100_1111101001_0000000001,
         40'b11111_11111_1100111001_1111101001_0000000001,
         40'b11111_11111_1011101110_1111101001_0000000001,
         40'b11111_11111_1010111100_1111101001_0000000001,
         40'b11111_11111_1010001010_1111101001_0000000001,
         40'b11111_11111_1001110001_1111101001_0000000001,
         40'b11111_11111_1000111111_1111101001_0000000001,
         40'b11111_11111_1000100110_1111101001_0000000001,
         40'b11111_11111_1000001101_1111101001_0000000001,
         40'b11111_11111_0111110100_1111101001_0000000001,
         40'b11111_11111_0111011011_1111101001_0000000001,
         40'b11111_11111_0111000010_1111101001_0000000001,
         40'b11111_11111_0110101001_1111101001_0000000001,
         40'b11111_11111_0110010000_1111101001_0000000001,
         40'b11111_11111_0110010000_1111101001_0000000001,
         40'b11111_11111_0101110111_1111101001_0000000001,
         40'b11111_11111_0101011110_1111101001_0000000001,
         40'b11111_11111_0101011110_1111101001_0000000001,
         40'b11111_11111_0101000101_1111101001_0000000001,
         40'b11111_11111_0101000101_1111101001_0000000001,
         40'b11111_11111_0100101100_1111101001_0000000001,
         40'b11111_11111_0100101100_1111101001_0000000001,
         40'b11111_11111_0100101100_1111101001_0000000001,
         40'b11111_11111_0100010011_1111101001_0000000001,
         40'b11111_11111_0100010011_1111101001_0000000001,
         40'b11111_11111_0100010011_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001,
         40'b11111_11111_0011111010_1111101001_0000000001
      };

      // Set lookup_entry with the explicit bits from lookup with a part select
      mmcm_lock_lookup = lookup[ ((64-divide)*40) +: 40];
   `ifdef DEBUG
      $display("lock_lookup: %b", mmcm_lock_lookup);
   `endif
   end
endfunction

// This function takes the divide value and the bandwidth setting of the MMCM
//  and outputs the digital filter settings necessary.
function [9:0] mmcm_filter_lookup
  (
     input [6:0] divide, // Max divide is 64
     input [8*9:0] BANDWIDTH
  );

  reg [639:0] lookup_low;
  reg [639:0] lookup_low_ss;
  reg [639:0] lookup_high;
  reg [639:0] lookup_optimized;

  reg [9:0] lookup_entry;

  begin
    lookup_low = {
      // CP_RES_LFHF
      10'b0010_1111_00, // 1
      10'b0010_1111_00, // 2
      10'b0010_1111_00, // 3
      10'b0010_1111_00, // 4
      10'b0010_0111_00, // ....
      10'b0010_1011_00,
      10'b0010_1101_00,
      10'b0010_0011_00,
      10'b0010_0101_00,
      10'b0010_0101_00,
      10'b0010_1001_00,
      10'b0010_1110_00,
      10'b0010_1110_00,
      10'b0010_1110_00,
      10'b0010_1110_00,
      10'b0010_0001_00,
      10'b0010_0001_00,
      10'b0010_0001_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_0110_00,
      10'b0010_1010_00,
      10'b0010_1010_00,
      10'b0010_1010_00,
      10'b0010_1010_00,
      10'b0010_1010_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_1100_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00,
      10'b0010_0010_00, // ....
      10'b0010_0010_00, // 61
      10'b0010_0010_00, // 62
      10'b0010_0010_00, // 63
      10'b0010_0010_00  // 64
    };

    lookup_low_ss = {
      // CP_RES_LFHF
      10'b0010_1111_11, // 1
      10'b0010_1111_11, // 2
      10'b0010_1111_11, // 3
      10'b0010_1111_11, // 4
      10'b0010_0111_11, // ....
      10'b0010_1011_11,
      10'b0010_1101_11,
      10'b0010_0011_11,
      10'b0010_0101_11,
      10'b0010_0101_11,
      10'b0010_1001_11,
      10'b0010_1110_11,
      10'b0010_1110_11,
      10'b0010_1110_11,
      10'b0010_1110_11,
      10'b0010_0001_11,
      10'b0010_0001_11,
      10'b0010_0001_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_0110_11,
      10'b0010_1010_11,
      10'b0010_1010_11,
      10'b0010_1010_11,
      10'b0010_1010_11,
      10'b0010_1010_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_1100_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11,
      10'b0010_0010_11, // ....
      10'b0010_0010_11, // 61
      10'b0010_0010_11, // 62
      10'b0010_0010_11, // 63
      10'b0010_0010_11  // 64
    };

    lookup_high = {
      // CP_RES_LFHF
      10'b0010_1111_00, // 1
      10'b0100_1111_00, // 2
      10'b0101_1011_00, // 3
      10'b0111_0111_00, // 4
      10'b1101_0111_00, // ....
      10'b1110_1011_00,
      10'b1110_1101_00,
      10'b1111_0011_00,
      10'b1110_0101_00,
      10'b1111_0101_00,
      10'b1111_1001_00,
      10'b1101_0001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_0101_00,
      10'b1111_0101_00,
      10'b1100_0001_00,
      10'b1100_0001_00,
      10'b1100_0001_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0111_0001_00,
      10'b0111_0001_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0110_0001_00,
      10'b0110_0001_00,
      10'b0101_0110_00,
      10'b0101_0110_00,
      10'b0101_0110_00,
      10'b0010_0100_00,
      10'b0010_0100_00,
      10'b0010_0100_00, // ....
      10'b0010_0100_00, // 61
      10'b0100_1010_00, // 62
      10'b0011_1100_00, // 63
      10'b0011_1100_00  // 64
    };

    lookup_optimized = {
      // CP_RES_LFHF
      10'b0010_1111_00, // 1
      10'b0100_1111_00, // 2
      10'b0101_1011_00, // 3
      10'b0111_0111_00, // 4
      10'b1101_0111_00, // ....
      10'b1110_1011_00,
      10'b1110_1101_00,
      10'b1111_0011_00,
      10'b1110_0101_00,
      10'b1111_0101_00,
      10'b1111_1001_00,
      10'b1101_0001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_1001_00,
      10'b1111_0101_00,
      10'b1111_0101_00,
      10'b1100_0001_00,
      10'b1100_0001_00,
      10'b1100_0001_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0101_1100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0011_0100_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0010_1000_00,
      10'b0111_0001_00,
      10'b0111_0001_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0100_1100_00,
      10'b0110_0001_00,
      10'b0110_0001_00,
      10'b0101_0110_00,
      10'b0101_0110_00,
      10'b0101_0110_00,
      10'b0010_0100_00,
      10'b0010_0100_00,
      10'b0010_0100_00, // ....
      10'b0010_0100_00, // 61
      10'b0100_1010_00, // 62
      10'b0011_1100_00, // 63
      10'b0011_1100_00  // 64
    };

    // Set lookup_entry with the explicit bits from lookup with a part select
    if(BANDWIDTH == "LOW") begin
      // Low Bandwidth
      mmcm_filter_lookup = lookup_low[((64-divide)*10) +: 10];
    end
    else if (BANDWIDTH == "LOW_SS") begin
      // low Spread spectrum bandwidth
      mmcm_filter_lookup = lookup_low_ss[((64-divide)*10) +: 10];
    end
    else if (BANDWIDTH == "HIGH") begin
      // High bandwidth
      mmcm_filter_lookup = lookup_high[((64-divide)*10) +: 10];
    end
    else if (BANDWIDTH == "OPTIMIZED") begin
      // Optimized bandwidth
      mmcm_filter_lookup = lookup_optimized[((64-divide)*10) +: 10];
    end

    `ifdef DEBUG
        $display("filter_lookup: %b", mmcm_filter_lookup);
    `endif
  end
endfunction

// This function takes in the divide, phase, and duty cycle
// setting to calculate the upper and lower counter registers.
function [37:0] mmcm_count_calc
   (
      input [7:0] divide, // Max divide is 128
      input signed [31:0] phase,
      input [31:0] duty_cycle // Multiplied by 100,000
   );

   reg [13:0] div_calc;
   reg [16:0] phase_calc;

   begin
   `ifdef DEBUG
      $display("mmcm_count_calc- divide:%h, phase:%d, duty_cycle:%d",
         divide, phase, duty_cycle);
   `endif

      // w_edge[13], no_count[12], high_time[11:6], low_time[5:0]
      div_calc = mmcm_divider(divide, duty_cycle);
      // mx[10:9], pm[8:6], dt[5:0]
      phase_calc = mmcm_phase(divide, phase);

      // Return value is the upper and lower address of counter
      //    Upper address is:
      //       RESERVED    [31:26]
      //       MX          [25:24]
      //       EDGE        [23]
      //       NOCOUNT     [22]
      //       DELAY_TIME  [21:16]
      //    Lower Address is:
      //       PHASE_MUX   [15:13]
      //       RESERVED    [12]
      //       HIGH_TIME   [11:6]
      //       LOW_TIME    [5:0]

   `ifdef DEBUG
      $display("div:%d dc:%d phase:%d ht:%d lt:%d ed:%d nc:%d mx:%d dt:%d pm:%d",
         divide, duty_cycle, phase, div_calc[11:6], div_calc[5:0],
         div_calc[13], div_calc[12],
         phase_calc[16:15], phase_calc[5:0], phase_calc[14:12]);
   `endif

      mmcm_count_calc =
         {
            // Upper Address
            6'h00, phase_calc[10:9], div_calc[13:12], phase_calc[5:0],
            // Lower Address
            phase_calc[8:6], 1'b0, div_calc[11:0]
         };
   end
endfunction


// This function takes in the divide, phase, and duty cycle
// setting to calculate the upper and lower counter registers.
// for fractional multiply/divide functions.
//
//
function [37:0] mmcm_frac_count_calc
   (
      input [7:0] divide, // Max divide is 128
      input signed [31:0] phase,
      input [31:0] duty_cycle, // Multiplied by 1,000
      input [9:0] frac // Multiplied by 1000
   );

	//Required for fractional divide calculations
			  reg  [7:0]     lt_frac;
			  reg  [7:0]     ht_frac;

			  reg            wf_fall_frac;
			  reg            wf_rise_frac;

			  reg [31:0]     a;
			  reg  [7:0]     pm_rise_frac_filtered ;
			  reg  [7:0]     pm_fall_frac_filtered ;
			  reg  [7:0]     clkout0_divide_int;
			  reg  [2:0]     clkout0_divide_frac;
			  reg  [7:0]     even_part_high;
			  reg  [7:0]     even_part_low;
			  reg [15:0]     drp_reg1;
			  reg [15:0]     drp_reg2;
			  reg  [5:0]     drp_regshared;

			  reg  [7:0]     odd;
			  reg  [7:0]     odd_and_frac;

			  reg  [7:0]     pm_fall;
			  reg  [7:0]     pm_rise;
			  reg  [7:0]     dt;
			  reg  [7:0]     dt_int;
			  reg [63:0]     dt_calc;

			  reg  [7:0]     pm_rise_frac;
			  reg  [7:0]     pm_fall_frac;

			  reg [31:0]     a_per_in_octets;
			  reg [31:0]     a_phase_in_cycles;

			                 parameter precision = 0.125;
			  reg [31:0]     phase_fixed; // changed to 31:0 from 32:1 jt 5/2/11
			  reg [31:0]     phase_pos;
			  reg [31:0]     phase_vco;
			  reg [31:0]     temp;// changed to 31:0 from 32:1 jt 5/2/11
			  reg [13:0]     div_calc;
			  reg [16:0]     phase_calc;

   begin
	`ifdef DEBUG
			$display("mmcm_frac_count_calc- divide:%h, phase:%d, duty_cycle:%d",
				divide, phase, duty_cycle);
	`endif

   //convert phase to fixed
   if ((phase < -360000) || (phase > 360000)) begin
      $display("ERROR: phase of $phase is not between -360000 and 360000");
      $finish;
   end


      // Return value is
      //    Shared data
      //       RESERVED     [37:36]
      //       FRAC_TIME    [35:33]
      //       FRAC_WF_FALL [32]
      //    Register 2 - Upper address is:
      //       RESERVED     [31:26]
      //       MX           [25:24]
      //       EDGE         [23]
      //       NOCOUNT      [22]
      //       DELAY_TIME   [21:16]
      //    Register 1 - Lower Address is:
      //       PHASE_MUX    [15:13]
      //       RESERVED     [12]
      //       HIGH_TIME    [11:6]
      //       LOW_TIME     [5:0]



	clkout0_divide_frac = frac / 125;
	clkout0_divide_int = divide;

	even_part_high = clkout0_divide_int >> 1;//$rtoi(clkout0_divide_int / 2);
	even_part_low = even_part_high;

	odd = clkout0_divide_int - even_part_high - even_part_low;
	odd_and_frac = (8*odd) + clkout0_divide_frac;

	lt_frac = even_part_high - (odd_and_frac <= 9);//IF(odd_and_frac>9,even_part_high, even_part_high - 1)
	ht_frac = even_part_low  - (odd_and_frac <= 8);//IF(odd_and_frac>8,even_part_low, even_part_low- 1)

	pm_fall =  {odd[6:0],2'b00} + {6'h00, clkout0_divide_frac[2:1]}; // using >> instead of clkout0_divide_frac / 2
	pm_rise = 0; //0

	wf_fall_frac = ((odd_and_frac >=2) && (odd_and_frac <=9)) || ((clkout0_divide_frac == 1) && (clkout0_divide_int == 2));//CRS610807
	wf_rise_frac = (odd_and_frac >=1) && (odd_and_frac <=8);//IF(odd_and_frac>=1,IF(odd_and_frac <= 8,1,0),0)



	//Calculate phase in fractional cycles
	a_per_in_octets		= (8 * divide) + (frac / 125) ;
	a_phase_in_cycles	= (phase+10) * a_per_in_octets / 360000 ;//Adding 1 due to rounding errors
	pm_rise_frac		= (a_phase_in_cycles[7:0] ==8'h00)?8'h00:a_phase_in_cycles[7:0] - {a_phase_in_cycles[7:3],3'b000};

	dt_calc 	= ((phase+10) * a_per_in_octets / 8 )/360000 ;//TRUNC(phase* divide / 360); //or_simply (a_per_in_octets / 8)
	dt 	= dt_calc[7:0];

	pm_rise_frac_filtered = (pm_rise_frac >=8) ? (pm_rise_frac ) - 8: pm_rise_frac ;				//((phase_fixed * (divide + frac / 1000)) / 360) - {pm_rise_frac[7:3],3'b000};//$rtoi(clkout0_phase * clkout0_divide / 45);//a;

	dt_int			= dt + (& pm_rise_frac[7:4]); //IF(pm_rise_overwriting>7,dt+1,dt)
	pm_fall_frac		= pm_fall + pm_rise_frac;
	pm_fall_frac_filtered	= pm_fall + pm_rise_frac - {pm_fall_frac[7:3], 3'b000};

	div_calc	= mmcm_divider(divide, duty_cycle); //Use to determine edge[7], no count[6]
	phase_calc	= mmcm_phase(divide, phase);// returns{mx[1:0], phase_mux[2:0], delay_time[5:0]}



      drp_regshared[5:0] = { 2'b11, pm_fall_frac_filtered[2:0], wf_fall_frac};
      drp_reg2[15:0] = { 1'b0, clkout0_divide_frac[2:0], 1'b1, wf_rise_frac, 4'h0, dt[5:0] };
      drp_reg1[15:0] = { pm_rise_frac_filtered[2], pm_rise_frac_filtered[1], pm_rise_frac_filtered[0], 1'b0, ht_frac[5:0], lt_frac[5:0] };
      mmcm_frac_count_calc[37:0] =   {drp_regshared, drp_reg2, drp_reg1} ;

   `ifdef DEBUG
      $display("DADDR Reg1 %h", drp_reg1);
      $display("DADDR Reg2 %h", drp_reg2);
      $display("DADDR Reg Shared %h", drp_regshared);
      $display("-%d.%d p%d>>  :DADDR_9_15 frac30to28.frac_en.wf_r_frac.dt:%b%d%d_%b:DADDR_7_13 pm_f_frac_filtered_29to27.wf_f_frac_26:%b%d:DADDR_8_14.pm_r_frac_filt_15to13.ht_frac.lt_frac:%b%b%b:", divide, frac, phase, clkout0_divide_frac, 1, wf_rise_frac, dt, pm_fall_frac_filtered, wf_fall_frac, pm_rise_frac_filtered, ht_frac, lt_frac);
   `endif

   end
endfunction
相关推荐
北城笑笑1 天前
FPGA 55 ,FPGA 行业 2026 发展分析与报告
fpga开发·fpga
FPGA小徐6 天前
通信系统核心技术概念解析
fpga
神电测控8 天前
神电新型PG-RFSOC-27DR/47DR/67DR sbRIO板卡(内置8路上GS/s AD/DA模块,兼容LabVIEW FPGA开发)
labview·fpga·usrp·sdr·神电测控·王电令·rfsoc
Eloudy19 天前
开源 gpunetio examples 01 解析 gpunetio_verbs_put_bw
gpu·fpga·rdma·roce·doca
博览鸿蒙19 天前
FPGA开发使用的语言
fpga开发·fpga
明德扬19 天前
机器人多摄像头如何做FPGA多路视频聚合?
学习·fpga开发·fpga
Eloudy20 天前
GPUNetIO开源实现与FPGA的 RoCEv2 通信延迟实验
gpu·fpga·rdma·roce·doca
unicrom_深圳市由你创科技20 天前
FPGA仿真正常,上板为何出错?
fpga
Eloudy21 天前
fpga-latency 指标分析与触发机制
fpga