本节目录
一、ddr3控制器的时钟架构
1、PLL输入时钟——系统时钟system_clk
2、PLL输出时钟——sync_pulse、mem_refclk、freq_refclk、MMCM1的输入时钟
3、MMCM1的输入时钟和输出时钟
4、MMCM2的输入时钟和输出时钟
一、ddr3控制器的时钟架构
对于FPGA开发来说,调用IP或者移植功能模块时,首先了解的模块的时钟架构。
如何设计时钟架构至关重要,其次对于一些优秀的设计思路可以借鉴参考。
根据ug586_7Series_MIS,我们可以了解下大体上所有的时钟架构,手册提供了具体的时钟设计方式。之后,会对每个时钟进行分析,用于了解对应时钟的功能,以及用途。
从时钟图中可以看出来,PHY设计使用了一个PLL和两个MMCM(其中对应的MMCM1,需要与PLL在同一个Bank,用于补偿插入延迟)。
通过位置约束将PLL与MMCM1固定在同一个Bank,此种方法可用于解决时序优化,通过固定位置约束,通过布线保证两路时钟shew几乎一致。
1、PLL输入时钟——系统时钟system_clk
IP生成界面中对应位置,共有三种选择差分Differential,单端Single-Ended,以及No buffer。
选择差分和单端,都会消耗bufg,如果在顶层输入的时钟,已经经过bufg,此处使用No buffer,可以节省一个bufg资源。
此处需要注意的是:选择输入时钟频率需要与硬件设计一致,本系列课程使用的是单端时钟200M,配置如下,
还有一个小bug,是vivado的显示bug,并不会保存我们设置的Input Clock period。
对于移植别人工程时,需要留意这一点,用两种方式,第一种可以与硬件确认提供的时钟,但是一般这种都是fpga开发者在顶层通过PLL生成的200m时钟 ,另一种方式可以通过旧工程的MIG IP的xdc约束文件去确认下。
具体看看代码生成模块中,我们看看是怎么对应的?
在mig_7series_v4_2_clk_ibuf.v模块中,通过参数化SYSCKL_TYPE和generate if功能块实现对不同配置的设置。
这种方式值得借鉴,用于工程中可以兼容不同的设计。从下面的代码中,只有no buffer不会占用时钟资源。
2、PLL输出时钟——sync_pulse、mem_refclk、freq_refclk、MMCM1的输入时钟
PLL输出用途主要有四个:生成用于内部(FPGA)逻辑的时钟、生成用于写路径(输出)I/O逻辑的时钟、生成用于读取路径(输入)和延迟I/O逻辑的时钟、生成用于IDELAY参考时钟的时钟。
注意:参数最终生效的最顶层的,通过一层一层传递的至底层模块。
//***************************************************************************
// The following parameters are multiplier and divisor factors for PLLE2.
// Based on the selected design frequency these parameters vary.
//***************************************************************************
parameter CLKIN_PERIOD = 5000, // Input Clock Period
parameter CLKFBOUT_MULT = 8, // write PLL VCO multiplier
parameter DIVCLK_DIVIDE = 1, // write PLL VCO divisor
parameter CLKOUT0_PHASE = 337.5, // Phase for PLL output clock (CLKOUT0)
parameter CLKOUT0_DIVIDE = 2, // VCO output divisor for PLL output clock (CLKOUT0)
parameter CLKOUT1_DIVIDE = 2, // VCO output divisor for PLL output clock (CLKOUT1)
parameter CLKOUT2_DIVIDE = 32, // VCO output divisor for PLL output clock (CLKOUT2)
parameter CLKOUT3_DIVIDE = 8, // VCO output divisor for PLL output clock (CLKOUT3)
parameter MMCM_VCO = 800, // Max Freq (MHz) of MMCM VCO
parameter MMCM_MULT_F = 4, // write MMCM VCO multiplier
parameter MMCM_DIVCLK_DIVIDE = 1, // write MMCM VCO divisor
(1)sync_pulse
手册要求:sync_pulse必须为mem_refclk频率的1/16,且占空比必须为1/16或6.25%。
PLLE2_ADV的CLK_OUT2为同步脉冲,输入时钟为5000ps=5ns,即200M
localparam real CLKIN1_PERIOD_NS = CLKIN_PERIOD / 1000.0;
localparam CLKOUT4_DIVIDE = 2 * CLKOUT1_DIVIDE;
localparam integer VCO_PERIOD
= (CLKIN1_PERIOD_NS * DIVCLK_DIVIDE * 1000) / CLKFBOUT_MULT;
localparam CLKOUT0_PERIOD = VCO_PERIOD * CLKOUT0_DIVIDE;
localparam CLKOUT1_PERIOD = VCO_PERIOD * CLKOUT1_DIVIDE;
localparam CLKOUT2_PERIOD = VCO_PERIOD * CLKOUT2_DIVIDE;
localparam CLKOUT3_PERIOD = VCO_PERIOD * CLKOUT3_DIVIDE;
localparam CLKOUT4_PERIOD = VCO_PERIOD * CLKOUT4_DIVIDE;
localparam CLKOUT4_PHASE = (SIMULATION == "TRUE") ? 22.5 : 168.75;
localparam real CLKOUT3_PERIOD_NS = CLKOUT3_PERIOD / 1000.0;
localparam real CLKOUT4_PERIOD_NS = CLKOUT4_PERIOD / 1000.0;
VCO_PERIOD=(CLKIN1_PERIOD_NS * DIVCLK_DIVIDE * 1000) / CLKFBOUT_MULT
=(5000/100011000)/8=625ps
sync_pulse的时钟周期:CLKOUT2_PERIOD = VCO_PERIOD * CLKOUT2_DIVIDE=62532=20000ps=20ns
mem_refclk的时钟周期:CLKOUT1_PERIOD = VCO_PERIOD * CLKOUT1_DIVIDE=6252=1250ps
sync_pulse的时钟频率是mem_refclk的1/16,仿真图结果观察下,确实如此,总周期为1250ps+18750ps=20ns,占空比为1250ps/20000ps=1/16。
(2)mem_refclk的时钟周期为1250ps,时钟频率为800M
此处对应800M的是Clock Period
(3)freq_refclk的时钟周期为79ps+1171ps=1250ps,时钟频率为800M
(4)pll_clk3_out的时钟周期为1250ps+3671ps+79ps=5000ps,时钟频率为200M,主要是用于MMCM1的输入。
3、MMCM1的输入时钟和输出时钟
(1)MMCM1的输入时钟来自于pll_clk3,即200M时钟
(2)用于FPGA内部的输出时钟为clk,即图上PHY_Clk,来自于MMCM1的CLKFBOUT。
(3)MMCM Phase shift clok为mmcm_ps_clk=1250ps+3671ps+79ps+1171ps+3829ps=10ns
4、MMCM2的输入时钟和输出时钟
MMCM2的输入时钟,也可通过UI界面进行配置。
同样此处也会出现bug,选择设置使用系统时钟。
代码功能实现在mig_7series_v4_2_iodelay_ctrl.v模块中,主要涉及到IDELAYCTRL,因此需要引入参考时钟。
手册中介绍:对于DDR3的设计,IDELAY参考时钟的生成需要一个MMCM2。如果设计频率为> 667 MHz,则IDELAY参考时钟为300 MHz或400MHz(取决于FPGA速度等级)。MIG实例化了一个MMCM2,用于300 MHz和400 MHz的时钟生成。
## FPGA Family: ZYNQ
## FPGA Part: XC7Z100-FFG900
## Speedgrade: -2
## Design Entry: VERILOG
## Frequency: 800 MHz
## Time Period: 1250 ps
parameter STARVE_LIMIT = 2, // # = 2,3,4.
parameter REF_CLK_MMCM_IODELAY_CTRL = "TRUE",
// reference clock 300MHz and 400MHz generation with MMCM
generate
if (REF_CLK_MMCM_IODELAY_CTRL == "TRUE") begin: clk_ref_mmcm_gen
MMCME2_ADV
#(.BANDWIDTH ("HIGH"),
.CLKOUT4_CASCADE ("FALSE"),
.COMPENSATION ("INTERNAL"),
.STARTUP_WAIT ("FALSE"),
.DIVCLK_DIVIDE (1),
.CLKFBOUT_MULT_F (6),
.CLKFBOUT_PHASE (0.000),
.CLKFBOUT_USE_FINE_PS ("FALSE"),
.CLKOUT0_DIVIDE_F (4),
.CLKOUT0_PHASE (0.000),
.CLKOUT0_DUTY_CYCLE (0.500),
.CLKOUT0_USE_FINE_PS ("FALSE"),
.CLKOUT1_DIVIDE (3),
.CLKOUT1_PHASE (0.000),
.CLKOUT1_DUTY_CYCLE (0.500),
.CLKOUT1_USE_FINE_PS ("FALSE"),
.CLKIN1_PERIOD (5),
.REF_JITTER1 (0.000))
mmcm_i
// Output clocks
(.CLKFBOUT (mmcm_clkfbout),
.CLKFBOUTB (),
.CLKOUT0 (clk_ref_mmcm_300),
.CLKOUT0B (),
.CLKOUT1 (clk_ref_mmcm_400),
.CLKOUT1B (),
.CLKOUT2 (),
.CLKOUT2B (),
.CLKOUT3 (),
.CLKOUT3B (),
.CLKOUT4 (),
.CLKOUT5 (),
.CLKOUT6 (),
// Input clock control
.CLKFBIN (mmcm_clkfbout),
.CLKIN1 (clk_ref_ibufg),
.CLKIN2 (1'b0),
// Tied to always select the primary input clock
.CLKINSEL (1'b1),
// Ports for dynamic reconfiguration
.DADDR (7'h0),
.DCLK (1'b0),
.DEN (1'b0),
.DI (16'h0),
.DO (),
.DRDY (),
.DWE (1'b0),
// Ports for dynamic phase shift
.PSCLK (1'b0),
.PSEN (1'b0),
.PSINCDEC (1'b0),
.PSDONE (),
// Other control and status signals
.LOCKED (mmcm_Locked),
.CLKINSTOPPED (),
.CLKFBSTOPPED (),
.PWRDWN (1'b0),
.RST (sys_rst_act_hi));
end
endgenerate
generate
if (REF_CLK_MMCM_IODELAY_CTRL == "TRUE") begin : clk_ref_300_400_en
if(FPGA_SPEED_GRADE == 1) begin: clk_ref_300
BUFG u_bufg_clk_ref_300
(
.O (clk_ref[1]),
.I (clk_ref_mmcm_300)
);
end else if (FPGA_SPEED_GRADE == 2 || FPGA_SPEED_GRADE == 3) begin: clk_ref_400
BUFG u_bufg_clk_ref_400
(
.O (clk_ref[1]),
.I (clk_ref_mmcm_400)
);
end
end
endgenerate
generate
if ((REFCLK_TYPE == "DIFFERENTIAL") ||
(REFCLK_TYPE == "SINGLE_ENDED") ||
(REFCLK_TYPE == "USE_SYSTEM_CLOCK" && SYSCLK_TYPE != "NO_BUFFER")) begin: clk_ref_200
BUFG u_bufg_clk_ref
(
.O (clk_ref[0]),
.I (clk_ref_ibufg)
);
end else begin: clk_ref_200_no_buffer
assign clk_ref[0] = clk_ref_i;
end
endgenerate
//*****************************************************************
// IDELAYCTRL reset
// This assumes an external clock signal driving the IDELAYCTRL
// blocks. Otherwise, if a PLL drives IDELAYCTRL, then the PLL
// lock signal will need to be incorporated in this.
//*****************************************************************
// Add PLL lock if PLL drives IDELAYCTRL in user design
assign rst_tmp_idelay = sys_rst_act_hi;
generate
if (REF_CLK_MMCM_IODELAY_CTRL == "TRUE") begin: rst_ref_gen_1
always @(posedge clk_ref[1] or posedge rst_tmp_idelay)
if (rst_tmp_idelay)
rst_ref_sync_r[1] <= #TCQ {RST_SYNC_NUM{1'b1}};
else
rst_ref_sync_r[1] <= #TCQ rst_ref_sync_r[1] << 1;
assign rst_ref[1] = rst_ref_sync_r[1][RST_SYNC_NUM-1];
end
endgenerate
always @(posedge clk_ref[0] or posedge rst_tmp_idelay)
if (rst_tmp_idelay)
rst_ref_sync_r[0] <= #TCQ {RST_SYNC_NUM{1'b1}};
else
rst_ref_sync_r[0] <= #TCQ rst_ref_sync_r[0] << 1;
assign rst_ref[0] = rst_ref_sync_r[0][RST_SYNC_NUM-1];
//*****************************************************************
generate
if (REF_CLK_MMCM_IODELAY_CTRL == "TRUE") begin: idelayctrl_gen_1
(* IODELAY_GROUP = IODELAY_GRP1 *) IDELAYCTRL u_idelayctrl_300_400
(
.RDY (iodelay_ctrl_rdy[1]),
.REFCLK (clk_ref[1]),
.RST (rst_ref[1])
);
end
endgenerate
(* IODELAY_GROUP = IODELAY_GRP0 *) IDELAYCTRL u_idelayctrl_200
(
.RDY (iodelay_ctrl_rdy[0]),
.REFCLK (clk_ref[0]),
.RST (rst_ref[0])
);
芯片型号为:XC7Z100-FFG900-2,速度等级是2,此处使用的参考时钟为400MHz。
输出的时钟,通过仿真图观察下:
输入clk_ref_i=3333ps+1667ps=5000ps,时钟频率为200M
输出clk_ref_mmcm_300=3333ps,时钟频率为300M