版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46621272/article/details/130484100
Verilog 锁相环参数动态自动生成,Xilinx MMCM 和 PLL 动态配置频率
文章目录
- 前言
- 简介
- Verilog 代码 pll_cfg_x1.v 自动计算生成 PLL_M、PLL_D、PLL_N
- Verilog 代码 pll.v
- Verilog 代码 pll_set.v
- Verilog 仿真测试激励 pll_cfg_testbench.v
- Verilog 顶层文件 pll_demo.v
- 本文中的相关下载链接
前言
- 本文介绍了Xilinx MMCM 和 PLL 的动态配置的方法
- Verilog 代码计算生成 PLL_M、PLL_D、PLL_N
- 动态输出一个自定义频率的时钟
- 提供 VIVADO 仿真工程下载
简介
- 本文直接采用 Xilinx MMCM/PLL 原语,没有采用 Clock Wizard IP
- 模块综合后的电路图
- 例化代码
- 动态配置的代码参考了官方的 xapp888 的例程,在文章后面有相关下载的链接
- verilog 根据频率值(精确到HZ)自动计算 M/N/D 等锁相环参数
Verilog 代码 pll_cfg_x1.v 自动计算生成 PLL_M、PLL_D、PLL_N
- 本代码经过了一些优化,能在 133MHz 下以较小的时间穷举计算出锁相环的各参数值。
- 优化后的计算代码可能比较难理解,可以下载本文的VIVADO 仿真工程,在压缩包中有个未优化的原始计算代码(只能跑25MHz)
- 计算中的约束条件如下
XILINX 7系列PLL时钟的条件约束
FIN 最小 10MHz 最大 800MHz,这个频率越大,最后计算出的频率越精准
FVCO_MIN 最小600MHz
FVCO_MAX 最大1200MHz - 1600MHz FPGA -1,-2,-3 芯片不同的后缀频率不同
PLL_D 输入分频系数,整数,1-106, 输入的分频频率 FIN/PLL_D , (FIN/PLL_D) >= 10MHz
PLL_D 的取值范围整数1-106
FVCO 为锁相环频率,FVCO >= FVCO_MIN && FVCO <= FVCO_MAX
FVCO = (FIN/PLL_D)*PLL_M
PLL_M 是个10bit的变量,是个定点小数,最小分辨率是 0.125
例如 PLL_M = 10’b01_1101_0011 就是 0x1d3/8=467/8=58.375
PLL_M 的取值范围小数 2.0-64.0, 10’b00_0001_0000-10’b10_0000_0000
clk_out 是 PLL 的输出频率,clk_out <= 800MHz
clk_out= FVCO/PLL_N=(FIN/PLL_D)*PLL_M/PLL_N
PLL_N 是输出分频系数
PLL_N 的取值范围整数1.0-128.0,最小分辨率是 0.125
例如 PLL_N = 10’b01_1101_0011 就是 0x1d3/8=467/8=58.375
多个 clk_out 输出是对应有多个 PLL_N,这几个输出共用这同一组 FIN,PLL_M,PLL_D,FVCO 系数
- pll_cfg_x1.v
// XILINX 7系列PLL时钟的条件约束
// FIN 最小 10MHz 最大 800MHz,这个频率越大,最后计算出的频率越精准
// FVCO_MIN 最小600MHz
// FVCO_MAX 最大1200MHz - 1600MHz FPGA -1,-2,-3 芯片不同的后缀频率不同
// PLL_D 输入分频系数,整数,1-106, 输入的分频频率 FIN/PLL_D , (FIN/PLL_D) >= 10MHz
// PLL_D 的取值范围整数1-106
// FVCO 为锁相环频率,FVCO >= FVCO_MIN && FVCO <= FVCO_MAX
// FVCO = (FIN/PLL_D)*PLL_M/8
// PLL_M 是个10bit的变量,是个定点小数,最小分辨率是 0.125
// 例如 PLL_M = 10'b01_1101_0011 就是 0x1d3/8=467/8=58.375
// PLL_M 的取值范围小数 2.0-64.0, 10'b00_0001_0000-10'b10_0000_0000
// clk_out 是 PLL 的输出频率,clk_out <= 800MHz
// clk_out= FVCO/PLL_N=(FIN/PLL_D)*PLL_M/PLL_N/8
// PLL_N 是输出分频系数
// PLL_N 的取值范围整数1-128
// 多个 clk_out 输出是对应有多个 PLL_N,这几个输出共用这同一组 FIN,PLL_M,PLL_D,FVCO 系数
//在计算频率时,一些 0 误差的计算,结果很快就能输出(不到10ms)。
//一些频率无法做到 0 误差,只能尽可能的精确。在最糟糕的时候可能会有 600-700ms 的计算时间(100MHz)
module pll_cfg_x1 #
(
parameter iCLK_N = 1, ///< 输出数量 1-7
parameter FVCO_MIN = 600*1000*1000, ///< XILINX 7 系FPGA FVCO 最小 600MHz
parameter FVCO_MAX = 1440*1000*1000, ///< XILINX 7 系FPGA FVCO 最大频率-1 1200MHz,-2 1440MHz,-3 1600MHz
parameter FIN = 100*1000*1000 ///< 100MHz 输入频率
)
(
input clk, //这个时钟是 DRP 参考时钟,和PLL 实际的输入时钟可以不同
//这个代码中有些乘除法计算,建议用频率较低的时钟
input rst_n,
input star_cp, //计算开始,这是个脉冲,高电平有效
input [31:0] fout_freq, //输出频率的频率值,单位HZ,如果有多个输出,选频率最高的值
input [iCLK_N*8-1:0] divx, //各路输出的分频比//这是个二维数组
output [15:0] pll_m, //计算输出结果,倍频值
output [15:0] pll_d, //计算输出结果,输入分频值
output [iCLK_N*16-1:0] pll_n, //计算输出结果,输出分频值//这是个二维数组
output pll_err,
output pll_done
);
//比如输出多个频率,out1=100MHz,out2=300MHz,out3=50MHz,out4=25MHz,
//fout_freq=300_000_000 //300MHz
//divx[07:00] = fout_freq/out1=300/100=3; //这必须能整除
//divx[15:08] = fout_freq/out2=300/300=1; //这必须能整除
//divx[23:16] = fout_freq/out3=300/50 =6; //这必须能整除
//divx[31:24] = fout_freq/out3=300/25 =12; //这必须能整除
//divx 的取值范围在 1-128 ,并且 divx * pll_n 必须小于等于 128
//pll_m 输出是个定点小数,二进制保留 3 位。最小颗粒是0.125
//比如 pll_m 输出 197,这个值实际是 197/8=24.625
//D = 1~80;
//M = 2.0~64.0; //步长0.125
//N = 1.0~128.0; //定点小数 iCLK_N==1 时步长为0.125,iCLK_N > 1 时步长为1,
//( Fin / D ) >= 10*10^6; //10000000(10MHz)
//Fvco = ( Fin / D ) * M;
//fout_freq = Fvco/N;
genvar n;
localparam PLL_IDLE = 0;
localparam PLL_MAX = 1;
localparam PLL_START = 2;
localparam PLL_OK = 3;
localparam PLL_ERROR = 4;
localparam F_MIN = FVCO_MIN;
localparam F_MAX = FVCO_MAX;
localparam PLL_N_STEP = iCLK_N == 1 ? 1:8;
reg [15:0] p_m = 16;
reg [15:0] p_n = 1;
reg [15:0] p_d = 1;
reg [15:0] s_m;
reg [15:0] s_n;
reg [15:0] s_d;
reg p_err = 0;
reg [15:0] s_m_r;
reg [15:0] s_n_r[iCLK_N-1:0];
reg [15:0] s_d_r;
wire [7:0] divx_i[iCLK_N-1:0];
reg p_done = 0;
reg [3:0] st= 0;
reg [7:0] i= 0;
wire [31:0] fin_d;
reg [37:0] f_vfo_m;
reg [37:0] f_vfo_n;
wire [31:0] differ_y;
reg [31:0] differ_x = -1;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
wire [15:0] f_d_max;
wire [15:0] f_m_max;
reg [15:0] f_n_max;
reg [31:0] fout_mult;
wire [31:0] fd_rom [255:0]; //建立一个表,用查表法来计算除法
for(n=1;n<256;n=n+1)
begin:for_rom_01
assign fd_rom[n] = FIN/n; //用查表法计算FIN/p_d
end
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
assign pll_done = p_done;
assign pll_err = p_err;
for(n=0;n<iCLK_N;n=n+1)
begin:for_xx1
assign divx_i[n] = divx[(n+1)*8-1:n*8];
assign pll_n[(n+1)*16-1:n*16] = s_n_r[n] > 128*8 ? 128*8:s_n_r[n];
end
assign pll_m = s_m_r;
assign pll_d = s_d_r;
// assign f_vfo_n = fout_freq/8*p_n;
assign fin_d = fd_rom[p_d]; //用查表法计算//FIN/p_d;
// assign f_vfo_m = fd_rom[p_d]/8*p_m; //用查表法计算//FIN/p_d/8*p_m;
assign differ_y = f_vfo_m > f_vfo_n ? (f_vfo_m-f_vfo_n):(f_vfo_n-f_vfo_m);
assign f_d_max = FIN/10_000_000;
assign f_m_max = FVCO_MAX/FIN*p_d*8;
always @(posedge clk)
begin
if(rst_n == 0)
begin
st <= PLL_IDLE;
p_err <= 0;
p_done <= 0;
end
else
case(st)
PLL_IDLE:
begin
f_vfo_n <= fout_freq;//fout_freq*p_n
f_vfo_m <= fin_d/8*16;
p_d <= 1;
p_m <= 16;
p_n <= 8;
p_done <= 0;
differ_x <= -1;
f_n_max <= 8;
fout_mult <= fout_freq;
if(star_cp == 1)
st <= PLL_MAX;
end
PLL_MAX:
begin
if(f_n_max < 128*8 && fout_mult < F_MAX)
begin
f_n_max <= f_n_max + PLL_N_STEP;
fout_mult <= fout_mult + fout_freq/8*PLL_N_STEP;
end
else
st <= PLL_START;
end
PLL_START:
begin
p_err <= 0;
p_done <= 0;
if(p_d > 106 || p_d > f_d_max)
st <= PLL_ERROR;
else if(fin_d >= 10*1000*1000 && f_vfo_m >= F_MIN && f_vfo_m <= F_MAX && f_vfo_n >= F_MIN && f_vfo_n <= F_MAX && p_n != 9)
begin
if(differ_y < differ_x)
begin
s_d <= p_d;
s_m <= p_m;
s_n <= p_n;
differ_x <= differ_y;
end
if(differ_y == 0)
st <= PLL_OK;
end
if(p_m < 512 && p_m <= f_m_max)
begin
p_m <= p_m + 1;
f_vfo_m<= f_vfo_m+fin_d/8;
end
else
begin
p_m <= 16;
if(p_n < 128*8 && p_n <= f_n_max)
begin
p_n <= p_n + PLL_N_STEP;
f_vfo_n <= f_vfo_n + fout_freq/8*PLL_N_STEP; //fout_freq*p_n
f_vfo_m <= fin_d/8*16;
end
else
begin
p_n <= 8;
f_vfo_n <= fout_freq;
f_vfo_m <= fd_rom[p_d+1]/8*16;
if(p_d <= 106 && p_d <= f_d_max)
p_d <= p_d + 1;
end
end
end
PLL_OK:
begin
st <= PLL_IDLE;
p_done <= 1;
s_d_r <= s_d;
s_m_r <= s_m;
for(i=0;i<iCLK_N;i=i+1)
s_n_r[i] <= s_n * divx_i[i];
end
PLL_ERROR:
begin
if(differ_x == 32'hffff_ffff)
begin
p_err <= 1;
st <= PLL_IDLE;
end
else
st <= PLL_OK;
end
default:
begin
st <= PLL_IDLE;
p_done <= 0;
end
endcase
end
endmodule
Verilog 代码 pll.v
- 这代码是例化锁相环参数计算和例化 MMCM 动态加载的代码
- 当输入频率 fout_freq 的值发生变化时,自动启动锁相环参数的计算和加载
module pll #
(
parameter iCLK_N = 2, ///< *输出数量 1-7
parameter FVCO_MIN = 600*1000*1000, //600MHz
parameter FVCO_MAX = 1440*1000*1000, //-1 1200MHz,-2 1440MHz,-3 1600MHz
parameter FIN = 100*1000*1000 //输入频率100MHz
)
(
input drp_clk,
input rst_n,
input [31:0] fout_freq,
input [iCLK_N*8-1:0] divx, //各路输出的分频比列
input clk_in,
output pll_locked,
output [iCLK_N-1:0] clk_out
);
wire [15:0] pll_m;
wire [15:0] pll_d;
wire [iCLK_N*16-1:0] pll_n;
wire pll_done;
reg [31:0] ss_r1=0;
reg [31:0] ss_r2=0;
reg star_cp=0;
////////////////////////////////////////////////////////////////////////////////////////////////
//这段代码,是监测 fout_freq 是否发生变化,变化后产生 star_cp 脉冲,启动 PLL 设置
////////////////////////////////////////////////////////////////////////////////////////////////
always @(posedge drp_clk)
begin
if(rst_n == 0)
begin
ss_r1 <= 0;
ss_r2 <= 0;
end
else if(pll_locked == 1)
begin
ss_r1 <= fout_freq;
ss_r2 <= ss_r1;
end
end
always @(posedge drp_clk)
begin
if(rst_n == 0)
star_cp <= 0;
else if(ss_r1 != ss_r2) //监测 fout_freq 发生了变化
star_cp <= 1; //产生 star_cp 脉冲,启动 PLL 设置
else
star_cp <= 0;
end
////////////////////////////////////////////////////////////////////////////////////////////////
pll_cfg_x1 #
(
.iCLK_N (iCLK_N),
.FVCO_MIN (FVCO_MIN),
.FVCO_MAX (FVCO_MAX),
.FIN (FIN)
)
pll_cfg_ux
(
.clk (drp_clk),
.rst_n (rst_n),
.star_cp (star_cp),
.fout_freq (fout_freq),
.divx (divx),
.pll_m (pll_m),
.pll_d (pll_d),
.pll_n (pll_n),
.pll_err (),
.pll_done (pll_done)
);
pll_set #
(
.iCLK_N (iCLK_N),
.iFREQ_IN (FIN), ///< *输入时钟频率
.iPLL_M (297.0), ///< *倍频系数
.iPLL_D (5), ///< *输入分频系数
.iPLL_N0 (5), ///< *输出分频系数
.iPLL_N1 (5) ///< *输出分频系数
)
pll_set_ux
(
.star_cp (pll_done),
.drp_clk (drp_clk),
.clk_in (clk_in),
.rst_n (rst_n),
.m_pll_n (pll_n),
.m_pll_m (pll_m),
.m_pll_d (pll_d),
.pll_locked (pll_locked),
.clk_out (clk_out)
);
endmodule
Verilog 代码 pll_set.v
- MMCM 动态加载的代码,主要参考了官方的 xapp888 中的代码
//下面这组缺省的参数是输入100MHz,FVCO=iFREQ_IN/iPLL_D*iPLL_M/8=1250MHz,输出=FVCO/iPLL_N=125MHz
module pll_set #
(
parameter iFREQ_IN = 100*1000*1000, ///< *输入时钟频率
parameter iPLL_M = 400.0, ///< *倍频系数
parameter iPLL_D = 4, ///< *输入分频系数
parameter iPLL_N0 = 10, ///< *输出分频系数
parameter iPLL_N1 = 64, ///< *输出分频系数
parameter iPLL_N2 = 64, ///< *输出分频系数
parameter iPLL_N3 = 64, ///< *输出分频系数
parameter iPLL_N4 = 64, ///< *输出分频系数
parameter iPLL_N5 = 64, ///< *输出分频系数
parameter iPLL_N6 = 64, ///< *输出分频系数
parameter iCLK_N = 1 ///< *输出数量
)
(
input star_cp,
input drp_clk,
input clk_in,
input rst_n,
input [iCLK_N*16-1:0] m_pll_n,
input [15:0] m_pll_m,
input [15:0] m_pll_d,
output pll_locked,
output [iCLK_N-1:0] clk_out
);
parameter BANDWIDTH = "OPTIMIZED"; //// "HIGH", "LOW" or "OPTIMIZED" "LOW_SS"
genvar n;
wire [15:0] m_clkout_d[6:0];
for(n=0;n<7;n=n+1)
begin:for_md0
if(n < iCLK_N)
assign m_clkout_d[n] = m_pll_n[(n+1)*16-1:n*16];
else
assign m_clkout_d[n] = 128*8;
end
// These signals are used as direct connections between the MMCM_ADV and the
// MMCM_DRP.
(* mark_debug = "true" *) wire [15:0] di;
(* mark_debug = "true" *) wire [6:0] daddr;
(* mark_debug = "true" *) wire [15:0] dout;
(* mark_debug = "true" *) wire den;
(* mark_debug = "true" *) wire dwe;
wire dclk;
wire rst_mmcm;
wire drdy;
wire locked;
wire clkfb_bufgout;
wire clkfb_bufgin;
wire [6:0] clk_bufgin;
assign pll_locked = locked;
BUFR BUFG_FB
(
.O(clkfb_bufgout),
.I(clkfb_bufgin)
);
for(n=0;n<iCLK_N;n=n+1)
begin:for_bufg
BUFG BUFG_CLK0
(
.O(clk_out[n]),
.I(clk_bufgin[n])
);
end
// MMCM_ADV that reconfiguration will take place on
MMCME2_ADV #
(
// "HIGH", "LOW" or "OPTIMIZED"
.BANDWIDTH (BANDWIDTH),
.DIVCLK_DIVIDE (iPLL_D), // (1 to 106)
.CLKFBOUT_MULT_F (iPLL_M/8), // (2 to 64)
.CLKFBOUT_PHASE (0.0),
.CLKFBOUT_USE_FINE_PS ("FALSE"),
// Set the clock period (ns) of input clocks
.CLKIN1_PERIOD (1000*1000*1000/iFREQ_IN),
.REF_JITTER1 (0.0),
.CLKIN2_PERIOD (10.000),
.REF_JITTER2 (0.0),
// CLKOUT parameters:
// DIVIDE: (1 to 128)
// DUTY_CYCLE: (0.01 to 0.99) - This is dependent on the divide value.
// PHASE: (0.0 to 360.0) - This is dependent on the divide value.
// USE_FINE_PS: (TRUE or FALSE)
.CLKOUT0_DIVIDE_F (iPLL_N0),
.CLKOUT0_DUTY_CYCLE (0.5),
.CLKOUT0_PHASE (0.0),
.CLKOUT0_USE_FINE_PS ("FALSE"),
.CLKOUT1_DIVIDE (iPLL_N1),
.CLKOUT1_DUTY_CYCLE (0.5),
.CLKOUT1_PHASE (0.0),
.CLKOUT1_USE_FINE_PS ("FALSE"),
.CLKOUT2_DIVIDE (iPLL_N2),
.CLKOUT2_DUTY_CYCLE (0.5),
.CLKOUT2_PHASE (0.0),
.CLKOUT2_USE_FINE_PS ("FALSE"),
.CLKOUT3_DIVIDE (iPLL_N3),
.CLKOUT3_DUTY_CYCLE (0.5),
.CLKOUT3_PHASE (0.0),
.CLKOUT3_USE_FINE_PS ("FALSE"),
.CLKOUT4_DIVIDE (iPLL_N4),
.CLKOUT4_DUTY_CYCLE (0.5),
.CLKOUT4_PHASE (0.0),
.CLKOUT4_USE_FINE_PS ("FALSE"),
.CLKOUT4_CASCADE ("FALSE"),
.CLKOUT5_DIVIDE (iPLL_N5),
.CLKOUT5_DUTY_CYCLE (0.5),
.CLKOUT5_PHASE (0.0),
.CLKOUT5_USE_FINE_PS ("FALSE"),
.CLKOUT6_DIVIDE (iPLL_N6),
.CLKOUT6_DUTY_CYCLE (0.5),
.CLKOUT6_PHASE (0.0),
.CLKOUT6_USE_FINE_PS ("FALSE"),
// Misc parameters
.COMPENSATION ("ZHOLD"),
.STARTUP_WAIT ("FALSE")
)
mmcme2_test_inst
(
.CLKFBOUT (clkfb_bufgin),
.CLKFBOUTB (),
.CLKFBSTOPPED (),
.CLKINSTOPPED (),
// Clock outputs
.CLKOUT0 (clk_bufgin[0]),
.CLKOUT0B (),
.CLKOUT1 (clk_bufgin[1]),
.CLKOUT1B (),
.CLKOUT2 (clk_bufgin[2]),
.CLKOUT2B (),
.CLKOUT3 (clk_bufgin[3]),
.CLKOUT3B (),
.CLKOUT4 (clk_bufgin[4]),
.CLKOUT5 (clk_bufgin[5]),
.CLKOUT6 (clk_bufgin[6]),
// DRP Ports
.DO (dout), // (16-bits)
.DRDY (drdy),
.DADDR (daddr), // 5 bits
.DCLK (dclk),
.DEN (den),
.DI (di), // 16 bits
.DWE (dwe),
.LOCKED (locked),
.CLKFBIN (clkfb_bufgout),
// Clock inputs
.CLKIN1 (clk_in),
.CLKIN2 (),
.CLKINSEL (1'b1),
// Fine phase shifting
.PSDONE (),
.PSCLK (1'b0),
.PSEN (1'b0),
.PSINCDEC (1'b0),
.PWRDWN (1'b0),
.RST (rst_mmcm)
);
clock_mmcme2_drp #
(
.S1_BANDWIDTH (BANDWIDTH) // "HIGH", "LOW" or "OPTIMIZED" "LOW_SS"
)
mmcme2_drp_inst
(
.s1_clkfbout_mult (m_pll_m),
.s1_divclk_divide (m_pll_d),
.s1_clkout0_divide (m_clkout_d[0]),
.s1_clkout1_divide (m_clkout_d[1]),
.s1_clkout2_divide (m_clkout_d[2]),
.s1_clkout3_divide (m_clkout_d[3]),
.s1_clkout4_divide (m_clkout_d[4]),
.s1_clkout5_divide (m_clkout_d[5]),
.s1_clkout6_divide (m_clkout_d[6]),
.drp_den (star_cp),
.drp_sclk (drp_clk),
.drp_rst_n (rst_n),
.drp_srdy (),
//////////////////////////////////////////////////////////////////////////////////////////////////
.DO (dout),
.DRDY (drdy),
.LOCKED (locked),
.DWE (dwe),
.DEN (den),
.DADDR (daddr),
.DI (di),
.DCLK (dclk),
.RST_MMCM (rst_mmcm)
);
endmodule
module clock_mmcme2_drp #
(
parameter S1_BANDWIDTH = "LOW" // "HIGH", "LOW" or "OPTIMIZED" "LOW_SS"
)
(
input [15:0] s1_clkfbout_mult,
input [15:0] s1_divclk_divide,
input [15:0] s1_clkout0_divide,
input [15:0] s1_clkout1_divide,
input [15:0] s1_clkout2_divide,
input [15:0] s1_clkout3_divide,
input [15:0] s1_clkout4_divide,
input [15:0] s1_clkout5_divide,
input [15:0] s1_clkout6_divide,
input drp_den,
input drp_sclk,
input drp_rst_n,
output reg drp_srdy,
//////////////////////////////////////////////////////////////////////////////////////////////////
input [15:0] DO,
input DRDY,
input LOCKED,
output reg DWE,
output reg DEN,
output reg [6:0] DADDR,
output reg [15:0] DI,
output DCLK,
output reg RST_MMCM
);
function [31:0] mmcm_divider
(
input [7:0] divide // Max divide is 128
);
reg [6:0] high_time;
reg [6:0] low_time;
reg w_edge;
reg no_count;
begin
if(divide <= 1)
begin
high_time = 1;
w_edge = 0;
low_time = 1;
no_count = 1;
end
else
begin
high_time = divide >> 1;
w_edge = divide[0];
low_time = divide - high_time;
no_count = 0;
end
mmcm_divider[31:16] = {8'b0,w_edge,no_count,6'b0};
mmcm_divider[15:00] = {4'b0,high_time[5:0],low_time[5:0]};
end
endfunction
function [35:0] mmcm_divider_frac
(
input [9:0] mult // Max mult is 128
);
reg [7:0] lt_frac;
reg [7:0] ht_frac;
reg wf_fall_frac;
reg wf_rise_frac;
reg [15:0] drp_reg1;
reg [15:0] drp_reg2;
reg [3:0] drp_regshared;
begin
lt_frac = mult[9:4] - (mult[3:0] <= 9);
ht_frac = mult[9:4] - (mult[3:0] <= 8);
wf_fall_frac = ((mult[3:0] >=2) && (mult[3:0] <=9)) || (mult == 'h11);
wf_rise_frac = (mult[3:0] >=1) && (mult[3:0] <=8);
drp_regshared[3:0] = { mult[3:1], wf_fall_frac};
drp_reg2[15:0] = { 1'b0, mult[2:0], 1'b1, wf_rise_frac, 10'h0};
drp_reg1[15:0] = { 4'b0, ht_frac[5:0], lt_frac[5:0] };
mmcm_divider_frac[35:0] = {drp_regshared, drp_reg2, drp_reg1};
end
endfunction
wire [9:0] lookup_low [63:0];
assign lookup_low[00] = 10'b0010_1111_00; // 1
assign lookup_low[01] = 10'b0010_1111_00; // 2
assign lookup_low[02] = 10'b0010_1111_00; // 3
assign lookup_low[03] = 10'b0010_1111_00; // 4
assign lookup_low[04] = 10'b0010_0111_00; // ....
assign lookup_low[05] = 10'b0010_1011_00;
assign lookup_low[06] = 10'b0010_1101_00;
assign lookup_low[07] = 10'b0010_0011_00;
assign lookup_low[08] = 10'b0010_0101_00;
assign lookup_low[09] = 10'b0010_0101_00;
assign lookup_low[10] = 10'b0010_1001_00;
assign lookup_low[11] = 10'b0010_1110_00;
assign lookup_low[12] = 10'b0010_1110_00;
assign lookup_low[13] = 10'b0010_1110_00;
assign lookup_low[14] = 10'b0010_1110_00;
assign lookup_low[15] = 10'b0010_0001_00;
assign lookup_low[16] = 10'b0010_0001_00;
assign lookup_low[17] = 10'b0010_0001_00;
assign lookup_low[18] = 10'b0010_0110_00;
assign lookup_low[19] = 10'b0010_0110_00;
assign lookup_low[20] = 10'b0010_0110_00;
assign lookup_low[21] = 10'b0010_0110_00;
assign lookup_low[22] = 10'b0010_0110_00;
assign lookup_low[23] = 10'b0010_0110_00;
assign lookup_low[24] = 10'b0010_0110_00;
assign lookup_low[25] = 10'b0010_1010_00;
assign lookup_low[26] = 10'b0010_1010_00;
assign lookup_low[27] = 10'b0010_1010_00;
assign lookup_low[28] = 10'b0010_1010_00;
assign lookup_low[29] = 10'b0010_1010_00;
assign lookup_low[30] = 10'b0010_1100_00;
assign lookup_low[31] = 10'b0010_1100_00;
assign lookup_low[32] = 10'b0010_1100_00;
assign lookup_low[33] = 10'b0010_1100_00;
assign lookup_low[34] = 10'b0010_1100_00;
assign lookup_low[35] = 10'b0010_1100_00;
assign lookup_low[36] = 10'b0010_1100_00;
assign lookup_low[37] = 10'b0010_1100_00;
assign lookup_low[38] = 10'b0010_1100_00;
assign lookup_low[39] = 10'b0010_1100_00;
assign lookup_low[40] = 10'b0010_1100_00;
assign lookup_low[41] = 10'b0010_1100_00;
assign lookup_low[42] = 10'b0010_1100_00;
assign lookup_low[43] = 10'b0010_1100_00;
assign lookup_low[44] = 10'b0010_1100_00;
assign lookup_low[45] = 10'b0010_1100_00;
assign lookup_low[46] = 10'b0010_1100_00;
assign lookup_low[47] = 10'b0010_0010_00;
assign lookup_low[48] = 10'b0010_0010_00;
assign lookup_low[49] = 10'b0010_0010_00;
assign lookup_low[50] = 10'b0010_0010_00;
assign lookup_low[51] = 10'b0010_0010_00;
assign lookup_low[52] = 10'b0010_0010_00;
assign lookup_low[53] = 10'b0010_0010_00;
assign lookup_low[54] = 10'b0010_0010_00;
assign lookup_low[55] = 10'b0010_0010_00;
assign lookup_low[56] = 10'b0010_0010_00;
assign lookup_low[57] = 10'b0010_0010_00;
assign lookup_low[58] = 10'b0010_0010_00;
assign lookup_low[59] = 10'b0010_0010_00; // ....
assign lookup_low[60] = 10'b0010_0010_00; // 61
assign lookup_low[61] = 10'b0010_0010_00; // 62
assign lookup_low[62] = 10'b0010_0010_00; // 63
assign lookup_low[63] = 10'b0010_0010_00; // 64
wire [9:0] lookup_low_ss [63:0];
assign lookup_low_ss[00] = 10'b0010_1111_11; // 1
assign lookup_low_ss[01] = 10'b0010_1111_11; // 2
assign lookup_low_ss[02] = 10'b0010_1111_11; // 3
assign lookup_low_ss[03] = 10'b0010_1111_11; // 4
assign lookup_low_ss[04] = 10'b0010_0111_11; // ....
assign lookup_low_ss[05] = 10'b0010_1011_11;
assign lookup_low_ss[06] = 10'b0010_1101_11;
assign lookup_low_ss[07] = 10'b0010_0011_11;
assign lookup_low_ss[08] = 10'b0010_0101_11;
assign lookup_low_ss[09] = 10'b0010_0101_11;
assign lookup_low_ss[10] = 10'b0010_1001_11;
assign lookup_low_ss[11] = 10'b0010_1110_11;
assign lookup_low_ss[12] = 10'b0010_1110_11;
assign lookup_low_ss[13] = 10'b0010_1110_11;
assign lookup_low_ss[14] = 10'b0010_1110_11;
assign lookup_low_ss[15] = 10'b0010_0001_11;
assign lookup_low_ss[16] = 10'b0010_0001_11;
assign lookup_low_ss[17] = 10'b0010_0001_11;
assign lookup_low_ss[18] = 10'b0010_0110_11;
assign lookup_low_ss[19] = 10'b0010_0110_11;
assign lookup_low_ss[20] = 10'b0010_0110_11;
assign lookup_low_ss[21] = 10'b0010_0110_11;
assign lookup_low_ss[22] = 10'b0010_0110_11;
assign lookup_low_ss[23] = 10'b0010_0110_11;
assign lookup_low_ss[24] = 10'b0010_0110_11;
assign lookup_low_ss[25] = 10'b0010_1010_11;
assign lookup_low_ss[26] = 10'b0010_1010_11;
assign lookup_low_ss[27] = 10'b0010_1010_11;
assign lookup_low_ss[28] = 10'b0010_1010_11;
assign lookup_low_ss[29] = 10'b0010_1010_11;
assign lookup_low_ss[30] = 10'b0010_1100_11;
assign lookup_low_ss[31] = 10'b0010_1100_11;
assign lookup_low_ss[32] = 10'b0010_1100_11;
assign lookup_low_ss[33] = 10'b0010_1100_11;
assign lookup_low_ss[34] = 10'b0010_1100_11;
assign lookup_low_ss[35] = 10'b0010_1100_11;
assign lookup_low_ss[36] = 10'b0010_1100_11;
assign lookup_low_ss[37] = 10'b0010_1100_11;
assign lookup_low_ss[38] = 10'b0010_1100_11;
assign lookup_low_ss[39] = 10'b0010_1100_11;
assign lookup_low_ss[40] = 10'b0010_1100_11;
assign lookup_low_ss[41] = 10'b0010_1100_11;
assign lookup_low_ss[42] = 10'b0010_1100_11;
assign lookup_low_ss[43] = 10'b0010_1100_11;
assign lookup_low_ss[44] = 10'b0010_1100_11;
assign lookup_low_ss[45] = 10'b0010_1100_11;
assign lookup_low_ss[46] = 10'b0010_1100_11;
assign lookup_low_ss[47] = 10'b0010_0010_11;
assign lookup_low_ss[48] = 10'b0010_0010_11;
assign lookup_low_ss[49] = 10'b0010_0010_11;
assign lookup_low_ss[50] = 10'b0010_0010_11;
assign lookup_low_ss[51] = 10'b0010_0010_11;
assign lookup_low_ss[52] = 10'b0010_0010_11;
assign lookup_low_ss[53] = 10'b0010_0010_11;
assign lookup_low_ss[54] = 10'b0010_0010_11;
assign lookup_low_ss[55] = 10'b0010_0010_11;
assign lookup_low_ss[56] = 10'b0010_0010_11;
assign lookup_low_ss[57] = 10'b0010_0010_11;
assign lookup_low_ss[58] = 10'b0010_0010_11;
assign lookup_low_ss[59] = 10'b0010_0010_11; // ....
assign lookup_low_ss[60] = 10'b0010_0010_11; // 61
assign lookup_low_ss[61] = 10'b0010_0010_11; // 62
assign lookup_low_ss[62] = 10'b0010_0010_11; // 63
assign lookup_low_ss[63] = 10'b0010_0010_11; // 64
wire [9:0] lookup_high [63:0];
assign lookup_high[00] = 10'b0010_1111_00; // 1
assign lookup_high[01] = 10'b0100_1111_00; // 2
assign lookup_high[02] = 10'b0101_1011_00; // 3
assign lookup_high[03] = 10'b0111_0111_00; // 4
assign lookup_high[04] = 10'b1101_0111_00; // ....
assign lookup_high[05] = 10'b1110_1011_00;
assign lookup_high[06] = 10'b1110_1101_00;
assign lookup_high[07] = 10'b1111_0011_00;
assign lookup_high[08] = 10'b1110_0101_00;
assign lookup_high[09] = 10'b1111_0101_00;
assign lookup_high[10] = 10'b1111_1001_00;
assign lookup_high[11] = 10'b1101_0001_00;
assign lookup_high[12] = 10'b1111_1001_00;
assign lookup_high[13] = 10'b1111_1001_00;
assign lookup_high[14] = 10'b1111_1001_00;
assign lookup_high[15] = 10'b1111_1001_00;
assign lookup_high[16] = 10'b1111_0101_00;
assign lookup_high[17] = 10'b1111_0101_00;
assign lookup_high[18] = 10'b1100_0001_00;
assign lookup_high[19] = 10'b1100_0001_00;
assign lookup_high[20] = 10'b1100_0001_00;
assign lookup_high[21] = 10'b0101_1100_00;
assign lookup_high[22] = 10'b0101_1100_00;
assign lookup_high[23] = 10'b0101_1100_00;
assign lookup_high[24] = 10'b0101_1100_00;
assign lookup_high[25] = 10'b0011_0100_00;
assign lookup_high[26] = 10'b0011_0100_00;
assign lookup_high[27] = 10'b0011_0100_00;
assign lookup_high[28] = 10'b0011_0100_00;
assign lookup_high[29] = 10'b0011_0100_00;
assign lookup_high[30] = 10'b0011_0100_00;
assign lookup_high[31] = 10'b0011_0100_00;
assign lookup_high[32] = 10'b0011_0100_00;
assign lookup_high[33] = 10'b0011_0100_00;
assign lookup_high[34] = 10'b0011_0100_00;
assign lookup_high[35] = 10'b0011_0100_00;
assign lookup_high[36] = 10'b0011_0100_00;
assign lookup_high[37] = 10'b0011_0100_00;
assign lookup_high[38] = 10'b0011_0100_00;
assign lookup_high[39] = 10'b0011_0100_00;
assign lookup_high[40] = 10'b0011_0100_00;
assign lookup_high[41] = 10'b0010_1000_00;
assign lookup_high[42] = 10'b0010_1000_00;
assign lookup_high[43] = 10'b0010_1000_00;
assign lookup_high[44] = 10'b0010_1000_00;
assign lookup_high[45] = 10'b0010_1000_00;
assign lookup_high[46] = 10'b0111_0001_00;
assign lookup_high[47] = 10'b0111_0001_00;
assign lookup_high[48] = 10'b0100_1100_00;
assign lookup_high[49] = 10'b0100_1100_00;
assign lookup_high[50] = 10'b0100_1100_00;
assign lookup_high[51] = 10'b0100_1100_00;
assign lookup_high[52] = 10'b0110_0001_00;
assign lookup_high[53] = 10'b0110_0001_00;
assign lookup_high[54] = 10'b0101_0110_00;
assign lookup_high[55] = 10'b0101_0110_00;
assign lookup_high[56] = 10'b0101_0110_00;
assign lookup_high[57] = 10'b0010_0100_00;
assign lookup_high[58] = 10'b0010_0100_00;
assign lookup_high[59] = 10'b0010_0100_00; // ....
assign lookup_high[60] = 10'b0010_0100_00; // 61
assign lookup_high[61] = 10'b0100_1010_00; // 62
assign lookup_high[62] = 10'b0011_1100_00; // 63
assign lookup_high[63] = 10'b0011_1100_00; // 64
wire [9:0] lookup_optimized [63:0];
assign lookup_optimized[00] = 10'b0010_1111_00; // 1
assign lookup_optimized[01] = 10'b0100_1111_00; // 2
assign lookup_optimized[02] = 10'b0101_1011_00; // 3
assign lookup_optimized[03] = 10'b0111_0111_00; // 4
assign lookup_optimized[04] = 10'b1101_0111_00; // ....
assign lookup_optimized[05] = 10'b1110_1011_00;
assign lookup_optimized[06] = 10'b1110_1101_00;
assign lookup_optimized[07] = 10'b1111_0011_00;
assign lookup_optimized[08] = 10'b1110_0101_00;
assign lookup_optimized[09] = 10'b1111_0101_00;
assign lookup_optimized[10] = 10'b1111_1001_00;
assign lookup_optimized[11] = 10'b1101_0001_00;
assign lookup_optimized[12] = 10'b1111_1001_00;
assign lookup_optimized[13] = 10'b1111_1001_00;
assign lookup_optimized[14] = 10'b1111_1001_00;
assign lookup_optimized[15] = 10'b1111_1001_00;
assign lookup_optimized[16] = 10'b1111_0101_00;
assign lookup_optimized[17] = 10'b1111_0101_00;
assign lookup_optimized[18] = 10'b1100_0001_00;
assign lookup_optimized[19] = 10'b1100_0001_00;
assign lookup_optimized[20] = 10'b1100_0001_00;
assign lookup_optimized[21] = 10'b0101_1100_00;
assign lookup_optimized[22] = 10'b0101_1100_00;
assign lookup_optimized[23] = 10'b0101_1100_00;
assign lookup_optimized[24] = 10'b0101_1100_00;
assign lookup_optimized[25] = 10'b0011_0100_00;
assign lookup_optimized[26] = 10'b0011_0100_00;
assign lookup_optimized[27] = 10'b0011_0100_00;
assign lookup_optimized[28] = 10'b0011_0100_00;
assign lookup_optimized[29] = 10'b0011_0100_00;
assign lookup_optimized[30] = 10'b0011_0100_00;
assign lookup_optimized[31] = 10'b0011_0100_00;
assign lookup_optimized[32] = 10'b0011_0100_00;
assign lookup_optimized[33] = 10'b0011_0100_00;
assign lookup_optimized[34] = 10'b0011_0100_00;
assign lookup_optimized[35] = 10'b0011_0100_00;
assign lookup_optimized[36] = 10'b0011_0100_00;
assign lookup_optimized[37] = 10'b0011_0100_00;
assign lookup_optimized[38] = 10'b0011_0100_00;
assign lookup_optimized[39] = 10'b0011_0100_00;
assign lookup_optimized[40] = 10'b0011_0100_00;
assign lookup_optimized[41] = 10'b0010_1000_00;
assign lookup_optimized[42] = 10'b0010_1000_00;
assign lookup_optimized[43] = 10'b0010_1000_00;
assign lookup_optimized[44] = 10'b0010_1000_00;
assign lookup_optimized[45] = 10'b0010_1000_00;
assign lookup_optimized[46] = 10'b0111_0001_00;
assign lookup_optimized[47] = 10'b0111_0001_00;
assign lookup_optimized[48] = 10'b0100_1100_00;
assign lookup_optimized[49] = 10'b0100_1100_00;
assign lookup_optimized[50] = 10'b0100_1100_00;
assign lookup_optimized[51] = 10'b0100_1100_00;
assign lookup_optimized[52] = 10'b0110_0001_00;
assign lookup_optimized[53] = 10'b0110_0001_00;
assign lookup_optimized[54] = 10'b0101_0110_00;
assign lookup_optimized[55] = 10'b0101_0110_00;
assign lookup_optimized[56] = 10'b0101_0110_00;
assign lookup_optimized[57] = 10'b0010_0100_00;
assign lookup_optimized[58] = 10'b0010_0100_00;
assign lookup_optimized[59] = 10'b0010_0100_00; // ....
assign lookup_optimized[60] = 10'b0010_0100_00; // 61
assign lookup_optimized[61] = 10'b0100_1010_00; // 62
assign lookup_optimized[62] = 10'b0011_1100_00; // 63
assign lookup_optimized[63] = 10'b0011_1100_00; // 64
wire [39:0] lookup [63:0];
assign lookup[00] = 40'b00110_00110_1111101000_1111101001_0000000001;
assign lookup[01] = 40'b00110_00110_1111101000_1111101001_0000000001;
assign lookup[02] = 40'b01000_01000_1111101000_1111101001_0000000001;
assign lookup[03] = 40'b01011_01011_1111101000_1111101001_0000000001;
assign lookup[04] = 40'b01110_01110_1111101000_1111101001_0000000001;
assign lookup[05] = 40'b10001_10001_1111101000_1111101001_0000000001;
assign lookup[06] = 40'b10011_10011_1111101000_1111101001_0000000001;
assign lookup[07] = 40'b10110_10110_1111101000_1111101001_0000000001;
assign lookup[08] = 40'b11001_11001_1111101000_1111101001_0000000001;
assign lookup[09] = 40'b11100_11100_1111101000_1111101001_0000000001;
assign lookup[10] = 40'b11111_11111_1110000100_1111101001_0000000001;
assign lookup[11] = 40'b11111_11111_1100111001_1111101001_0000000001;
assign lookup[12] = 40'b11111_11111_1011101110_1111101001_0000000001;
assign lookup[13] = 40'b11111_11111_1010111100_1111101001_0000000001;
assign lookup[14] = 40'b11111_11111_1010001010_1111101001_0000000001;
assign lookup[15] = 40'b11111_11111_1001110001_1111101001_0000000001;
assign lookup[16] = 40'b11111_11111_1000111111_1111101001_0000000001;
assign lookup[17] = 40'b11111_11111_1000100110_1111101001_0000000001;
assign lookup[18] = 40'b11111_11111_1000001101_1111101001_0000000001;
assign lookup[19] = 40'b11111_11111_0111110100_1111101001_0000000001;
assign lookup[20] = 40'b11111_11111_0111011011_1111101001_0000000001;
assign lookup[21] = 40'b11111_11111_0111000010_1111101001_0000000001;
assign lookup[22] = 40'b11111_11111_0110101001_1111101001_0000000001;
assign lookup[23] = 40'b11111_11111_0110010000_1111101001_0000000001;
assign lookup[24] = 40'b11111_11111_0110010000_1111101001_0000000001;
assign lookup[25] = 40'b11111_11111_0101110111_1111101001_0000000001;
assign lookup[26] = 40'b11111_11111_0101011110_1111101001_0000000001;
assign lookup[27] = 40'b11111_11111_0101011110_1111101001_0000000001;
assign lookup[28] = 40'b11111_11111_0101000101_1111101001_0000000001;
assign lookup[29] = 40'b11111_11111_0101000101_1111101001_0000000001;
assign lookup[30] = 40'b11111_11111_0100101100_1111101001_0000000001;
assign lookup[31] = 40'b11111_11111_0100101100_1111101001_0000000001;
assign lookup[32] = 40'b11111_11111_0100101100_1111101001_0000000001;
assign lookup[33] = 40'b11111_11111_0100010011_1111101001_0000000001;
assign lookup[34] = 40'b11111_11111_0100010011_1111101001_0000000001;
assign lookup[35] = 40'b11111_11111_0100010011_1111101001_0000000001;
assign lookup[36] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[37] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[38] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[39] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[40] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[41] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[42] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[43] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[44] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[45] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[46] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[47] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[48] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[49] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[50] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[51] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[52] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[53] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[54] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[55] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[56] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[57] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[58] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[59] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[60] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[61] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[62] = 40'b11111_11111_0011111010_1111101001_0000000001;
assign lookup[63] = 40'b11111_11111_0011111010_1111101001_0000000001;
// Make sure the memory is implemented as distributed
(* ram_style = "distributed" *)
reg [38:0] ram [23:0];
reg [7:0] ram_addr;
reg [7:0] ram_rd_addr = 255;
reg [38:0] ram_do;
reg drp_sen=0;
reg next_srdy;
reg [5:0] next_ram_addr;
reg [6:0] next_daddr;
reg next_dwe;
reg next_den;
reg next_rst_mmcm;
reg [15:0] next_di;
reg [31:0] s1_clkout0;
reg [31:0] s1_clkout1;
reg [31:0] s1_clkout2;
reg [31:0] s1_clkout3;
reg [31:0] s1_clkout4;
reg [31:0] s1_clkout5;
reg [31:0] s1_clkout5x;
reg [31:0] s1_clkout6;
reg [31:0] s1_clkout6x;
reg [31:0] s1_clkfbout;
reg [31:0] s1_divclk;
wire [7:0] s1_clkfbout_mult_int;
reg [3:0] frac_ck0;
reg [3:0] frac_fb;
reg [39:0] s1_lock;
reg [9:0] s1_digital_filt;
assign DCLK = drp_sclk;
always @ (posedge drp_sclk)
begin
if(drp_rst_n == 0)
ram_rd_addr <= #1 255;
else if(drp_den == 1)
ram_rd_addr <= #1 0;
else if(ram_rd_addr != 255)
ram_rd_addr <= #1 ram_rd_addr + 1;
end
always @ (posedge drp_sclk)
begin
if(drp_rst_n == 0 || drp_den == 1)
drp_sen <= #1 0;
else if(ram_rd_addr == 32)
drp_sen <= #1 1;
else
drp_sen <= #1 0;
end
assign s1_clkfbout_mult_int = s1_clkfbout_mult/8;
always @ (posedge drp_sclk)
begin
{frac_fb,s1_clkfbout} <= #1 mmcm_divider_frac(s1_clkfbout_mult);
s1_clkout1 <= #1 mmcm_divider(s1_clkout1_divide/8);
s1_clkout2 <= #1 mmcm_divider(s1_clkout2_divide/8);
s1_clkout3 <= #1 mmcm_divider(s1_clkout3_divide/8);
s1_clkout4 <= #1 mmcm_divider(s1_clkout4_divide/8);
s1_clkout5x <= #1 mmcm_divider(s1_clkout5_divide/8);
s1_clkout6x <= #1 mmcm_divider(s1_clkout6_divide/8);
s1_clkout6[15:00] <= #1 s1_clkout6x[15:0];
s1_clkout6[31:16] <= #1 {s1_clkout6x[31:30], frac_fb[3:0],s1_clkout6x[25:16]};
s1_clkout5[15:00] <= #1 s1_clkout5x[15:0];
if(s1_clkout0_divide[2:0] != 0)
begin
{frac_ck0,s1_clkout0} <= #1 mmcm_divider_frac(s1_clkout0_divide);
s1_clkout5[31:16] <= #1 {s1_clkout5x[31:30], frac_ck0[3:0],s1_clkout5x[25:16]};
end
else
begin
s1_clkout0 <= #1 mmcm_divider(s1_clkout0_divide/8);
s1_clkout5[31:16] <= #1 s1_clkout5x[31:16];
end
s1_divclk <= #1 mmcm_divider(s1_divclk_divide);
if(s1_clkfbout_mult_int !=0)
begin
s1_digital_filt <= #1 S1_BANDWIDTH == "LOW" ? lookup_low [s1_clkfbout_mult_int-1]:
S1_BANDWIDTH == "LOW_SS" ? lookup_low_ss [s1_clkfbout_mult_int-1]:
S1_BANDWIDTH == "HIGH" ? lookup_high [s1_clkfbout_mult_int-1]:
S1_BANDWIDTH == "OPTIMIZED" ? lookup_optimized[s1_clkfbout_mult_int-1]:0;
s1_lock <= #1 lookup[s1_clkfbout_mult_int-1];
end
else
begin
s1_digital_filt <= #1 S1_BANDWIDTH == "LOW" ? lookup_low [0]:
S1_BANDWIDTH == "LOW_SS" ? lookup_low_ss [0]:
S1_BANDWIDTH == "HIGH" ? lookup_high [0]:
S1_BANDWIDTH == "OPTIMIZED" ? lookup_optimized[0]:0;
s1_lock <= #1 lookup[0];
end
end
always @ (posedge drp_sclk)
begin
case(ram_rd_addr)
0: ram[0] <= #1 {7'h28, 16'h0000, 16'hffff}; // store the power bits
1: ram[1] <= #1 {7'h08, 16'h1000, s1_clkout0[15:0]};
2: ram[2] <= #1 {7'h09, 16'h8000, s1_clkout0[31:16]}; // store clkout0
3: ram[3] <= #1 {7'h0a, 16'h1000, s1_clkout1[15:0]}; // store clkout1
4: ram[4] <= #1 {7'h0b, 16'hfc00, s1_clkout1[31:16]};
5: ram[5] <= #1 {7'h0c, 16'h1000, s1_clkout2[15:0]}; // store clkout2
6: ram[6] <= #1 {7'h0d, 16'hfc00, s1_clkout2[31:16]};
7: ram[7] <= #1 {7'h0e, 16'h1000, s1_clkout3[15:0]}; // store clkout3
8: ram[8] <= #1 {7'h0f, 16'hfc00, s1_clkout3[31:16]};
9: ram[9] <= #1 {7'h10, 16'h1000, s1_clkout4[15:0]}; // store clkout4
10: ram[10] <= #1 {7'h11, 16'hfc00, s1_clkout4[31:16]};
11: ram[11] <= #1 {7'h06, 16'h1000, s1_clkout5[15:0]}; // store clkout5
12: ram[12] <= #1 {7'h07, 16'hc000, s1_clkout5[31:16]};
13: ram[13] <= #1 {7'h12, 16'h1000, s1_clkout6[15:0]}; // store clkout6
14: ram[14] <= #1 {7'h13, 16'hc000, s1_clkout6[31:16]};
15: ram[15] <= #1 {7'h16, 16'hc000, {2'h0, s1_divclk[23:22], s1_divclk[11:0]} }; // store the input divider
16: ram[16] <= #1 {7'h14, 16'h1000, s1_clkfbout[15:0]}; // store feedback
17: ram[17] <= #1 {7'h15, 16'h8000, s1_clkfbout[31:16]};
18: ram[18] <= #1 {7'h18, 16'hfc00, {6'h00, s1_lock[29:20]} };
19: ram[19] <= #1 {7'h19, 16'h8000, {1'b0 , s1_lock[34:30], s1_lock[9:0]} };
20: ram[20] <= #1 {7'h1a, 16'h8000, {1'b0 , s1_lock[39:35], s1_lock[19:10]} };
21: ram[21] <= #1 {7'h4e, 16'h66ff,s1_digital_filt[9], 2'h0,s1_digital_filt[8:7], 2'h0,s1_digital_filt[6], 8'h00 };
22: ram[22] <= #1 {7'h4f, 16'h666f,s1_digital_filt[5], 2'h0,s1_digital_filt[4:3], 2'h0,s1_digital_filt[2:1], 2'h0, s1_digital_filt[0], 4'h0 };
default: ram[ram_rd_addr] <= #1 0;
endcase
end
// Output the initialized ram value based on ram_addr each clock cycle
always @ (posedge drp_sclk)
begin
ram_do<= #1 ram[ram_addr];
end
//**************************************************************************
// Everything below is associated whith the state machine that is used to
// Read/Modify/Write to the MMCM.
//**************************************************************************
// State Definitions
localparam RESTART = 4'h1;
localparam WAIT_LOCK = 4'h2;
localparam WAIT_SEN = 4'h3;
localparam ADDRESS = 4'h4;
localparam WAIT_A_DRDY = 4'h5;
localparam BITMASK = 4'h6;
localparam BITSET = 4'h7;
localparam WRITE = 4'h8;
localparam WAIT_DRDY = 4'h9;
// State sync
reg [3:0] current_state = RESTART;
reg [3:0] next_state = RESTART;
// These variables are used to keep track of the number of iterations that
// each state takes to reconfigure.
// STATE_COUNT_CONST is used to reset the counters and should match the
// number of registers necessary to reconfigure each state.
localparam STATE_COUNT_CONST = 23;
reg [4:0] state_count = STATE_COUNT_CONST;
reg [4:0] next_state_count = STATE_COUNT_CONST;
// This block assigns the next register value from the state machine below
always @ (posedge drp_sclk)
begin
DADDR <= #1 next_daddr;
DWE <= #1 next_dwe;
DEN <= #1 next_den;
RST_MMCM <= #1 next_rst_mmcm;
DI <= #1 next_di;
drp_srdy <= #1 next_srdy;
ram_addr <= #1 next_ram_addr;
state_count <= #1 next_state_count;
end
// This block assigns the next state, reset is syncronous.
always @(posedge drp_sclk)
begin
if(drp_rst_n == 0)
current_state <= #1 RESTART;
else
current_state <= #1 next_state;
end
always @ (*)
begin
// Setup the default values
next_srdy = 1'b0;
next_daddr = DADDR;
next_dwe = 1'b0;
next_den = 1'b0;
next_rst_mmcm = RST_MMCM;
next_di = DI;
next_ram_addr = ram_addr;
next_state_count = state_count;
case (current_state)
// If RST is asserted reset the machine
RESTART:
begin
next_daddr = 7'h00;
next_di = 16'h0000;
next_ram_addr = 6'h00;
next_rst_mmcm = 1'b1;
next_state = WAIT_LOCK;
end
// Waits for the MMCM to assert LOCKED - once it does asserts SRDY
WAIT_LOCK:
begin
// Make sure reset is de-asserted
next_rst_mmcm = 1'b0;
// Reset the number of registers left to write for the next
// reconfiguration event.
next_state_count = STATE_COUNT_CONST ;
next_ram_addr = 8'h00;
if(LOCKED)
begin
// MMCM is locked, go on to wait for the SEN signal
next_state = WAIT_SEN;
// Assert SRDY to indicate that the reconfiguration module is
// ready
next_srdy = 1'b1;
end
else
begin
// Keep waiting, locked has not asserted yet
next_state = WAIT_LOCK;
end
end
// Wait for the next SEN pulse and set the RAM addr appropriately
// based on SADDR
WAIT_SEN:
begin
next_ram_addr = 8'h00;
if (drp_sen)
begin
next_ram_addr = 8'h00;
// Go on to address the MMCM
next_state = ADDRESS;
end
else
begin
// Keep waiting for SEN to be asserted
next_state = WAIT_SEN;
end
end
// Set the address on the MMCM and assert DEN to read the value
ADDRESS:
begin
// Reset the DCM through the reconfiguration
next_rst_mmcm = 1'b1;
// Enable a read from the MMCM and set the MMCM address
next_den = 1'b1;
next_daddr = ram_do[38:32];
// Wait for the data to be ready
next_state = WAIT_A_DRDY;
end
// Wait for DRDY to assert after addressing the MMCM
WAIT_A_DRDY:
begin
if (DRDY) // Data is ready, mask out the bits to save
next_state = BITMASK;
else // Keep waiting till data is ready
next_state = WAIT_A_DRDY;
end
// Zero out the bits that are not set in the mask stored in ram
BITMASK:
begin
// Do the mask
next_di = ram_do[31:16] & DO;
// Go on to set the bits
next_state = BITSET;
end
// After the input is masked, OR the bits with calculated value in ram
BITSET:
begin
// Set the bits that need to be assigned
next_di = ram_do[15:0] | DI;
// Set the next address to read from RAM
next_ram_addr = ram_addr + 1'b1;
// Go on to write the data to the MMCM
next_state = WRITE;
end
// DI is setup so assert DWE, DEN, and RST_MMCM. Subtract one from the
// state count and go to wait for DRDY.
WRITE:
begin
// Set WE and EN on MMCM
next_dwe = 1'b1;
next_den = 1'b1;
// Decrement the number of registers left to write
next_state_count = state_count - 1'b1;
// Wait for the write to complete
next_state = WAIT_DRDY;
end
// Wait for DRDY to assert from the MMCM. If the state count is not 0
// jump to ADDRESS (continue reconfiguration). If state count is
// 0 wait for lock.
WAIT_DRDY:
begin
if(DRDY)
begin
// Write is complete
if(state_count > 0)
// If there are more registers to write keep going
next_state = ADDRESS;
else
// There are no more registers to write so wait for the MMCM
// to lock
next_state = WAIT_LOCK;
end
else
begin
// Keep waiting for write to complete
next_state = WAIT_DRDY;
end
end
// If in an unknown state reset the machine
default:
begin
next_state = RESTART;
end
endcase
end
endmodule
Verilog 仿真测试激励 pll_cfg_testbench.v
///////////////////////////////////////////////////////////////////////
`timescale 1ns / 100ps
module pll_cfg_testbench;
reg rst_n;
reg clk;
parameter CLK_PERIOD = 20; //50MHz
initial begin
rst_n = 0;
#(10 * CLK_PERIOD)
rst_n = 1;
end
initial
clk = 0;
always
begin
clk = #(CLK_PERIOD/2.0) ~clk;
end
wire pll_locked;
wire pll_clk_out0;
wire pll_clk_out1;
pll_demo pll_ux1
(
.sys_clk (clk),
.pll_locked (pll_locked),
.pll_clk_out0 (pll_clk_out0),
.pll_clk_out1 (pll_clk_out1)
);
endmodule
Verilog 顶层文件 pll_demo.v
module pll_demo #
(
parameter iCLK_N = 1 ///< *输出数量 1-7
)
(
input sys_clk,
output pll_locked,
output pll_clk_out0,
output pll_clk_out1
);
parameter FVCO_MIN = 600*1000*1000; //600MHz
parameter FVCO_MAX = 1440*1000*1000; //-1 1200MHz,-2 1440MHz,-3 1600MHz
parameter FIN = 100*1000*1000;
//下面这组缺省的参数是输入100MHz,FVCO=iFREQ_IN/iPLL_D*iPLL_M/8=800MHz,输出=FVCO/iPLL_N=100MHz
wire drp_clk;
wire clk_100m;
wire rst_n;
wire [31:0] divx;
wire [iCLK_N-1:0] clk_out;
reg [2:0] fcnt = 0;
wire [31:0] fout_freq[7:0];
assign fout_freq[0] = 765.432*1000*1000;
assign fout_freq[1] = 543.210*1000*1000;
assign fout_freq[2] = 321.098*1000*1000;
assign fout_freq[3] = 109.876*1000*1000;
assign fout_freq[4] = 098.765*1000*1000;
assign fout_freq[5] = 076.543*1000*1000;
assign fout_freq[6] = 054.321*1000*1000;
assign fout_freq[7] = 032.109*1000*1000;
assign divx[7:0] = 1;
//assign divx[15:8] = 1;
assign pll_clk_out0 = clk_out[0];
assign pll_clk_out1 = clk_out[1];
clk_wiz_pll_100m pll_u1
(
.clk_out1 (clk_100m),
.clk_out2 (drp_clk),
.locked (rst_n),
.clk_in1 (sys_clk)
);
////////////////////////////////////////////////////////////////////////////////////////////////
//在 iCLK_N = 1 时,只有1路输出,计算精度比较高
//在 iCLK_N > 1 时,有多路输出,各路输出的频率必须有个公倍数
//比如输出多个频率,out1=100MHz,out2=300MHz,out3=50MHz,out4=25MHz,
//fout_freq=300_000_000 //300MHz,fout_freq必须选这组频率中的最大的值
//divx[07:00] = fout_freq/out1=300/100=3; //这必须能整除
//divx[15:08] = fout_freq/out2=300/300=1; //这必须能整除
//divx[23:16] = fout_freq/out3=300/50 =6; //这必须能整除
//divx[31:24] = fout_freq/out4=300/25 =12;//这必须能整除
//divx 的取值范围在 1-128 ,并且 divx * pll_n 必须小于等于 128
pll #
(
.iCLK_N (iCLK_N), //时钟输出通道数量 1-7
.FVCO_MIN (FVCO_MIN), //600MHz
.FVCO_MAX (FVCO_MAX), //-1 1200MHz,-2 1440MHz,-3 1600MHz
.FIN (FIN) //100MHz
)
pll_ux
(
.drp_clk (drp_clk),
.rst_n (rst_n),
.fout_freq (fout_freq[fcnt]),
.divx (divx),
.clk_in (clk_100m),
.pll_locked (pll_locked),
.clk_out (clk_out)
);
reg [1:0] locked_r = 0;
always@(posedge drp_clk)
begin
if(rst_n == 0)
locked_r <= 0;
else
begin
locked_r[0] <= pll_locked;
locked_r[1] <= locked_r[0];
end
end
always@(posedge drp_clk)
begin
if(rst_n == 0)
fcnt <= 0;
else if(locked_r == 2'b10) //此 DEMO 利用 pll_locked 的下降沿来切换频率值并重新计算锁相环的值
//在实际应用中,可以在任何时候改变频率的值。
fcnt <= fcnt + 1;
end
endmodule
本文中的相关下载链接
- XAPP888 例程、文档 Xilinx FPGA 动态重新配置频率
- Verilog 锁相环参数动态自动生成,Xilinx MMCM 和 PLL 动态配置频率 vivado 仿真工程