搜索
bottom↓
回复: 0

【正点原子FPGA连载】第十六章IP核之PLL实验

[复制链接]

出0入234汤圆

发表于 2021-1-27 10:56:29 | 显示全部楼层 |阅读模式
1)实验平台:正点原子超越者FPGA开发板
2)  章节摘自【正点原子】超越者之FPGA开发指南
3)购买链接:https://item.taobao.com/item.htm?&id=631660290421
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-chaoyuezhe.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流群:905624739
QQ群头像.png

100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第十六章IP核之PLL实验


Spartan-6 FPGA芯片提供了非常丰富的时钟资源,它由四类时钟资源互连线,两种时钟资源网络组成。这四类连接线分别是全局时钟输入(GCLK)、全局时钟多路复用器(BUFG、BUFPLL)、I/O时钟缓冲器(BUFIO2、BUFIO2_2CLK、BUFPLL)、水平时钟路由缓冲器(BUFH);两种时钟网络分别是全局时钟网络和I/O区域时钟网络。这些互联线具有高速、低抖动的特点,非常适合于传输高频信号、最大量减小时钟抖动并且可以和DCM、PLL等实现连接。DCM和PLL是我们最常用的时钟资源管理器,通过对他们的调用可以实现对时钟的分频、倍频功能、消抖、抗扭斜等功能,得到相对平稳的高质量时钟信号。
本章我们将详细的讲解如何使用Xilinx Spartan-6的PLL IP核。本章包括以下几个部分:
1616.1  简介
16.2  实验任务
16.3  硬件设计
16.4  程序设计
16.5  下载验证


16.1简介
在Spartan-6中时钟资源模块称之为CMT,它是由两个DCM和一个PLL组成的。
DCM即数字时钟管理器(DCMs),它为Spartan-6提供了先进的时钟功能。并且DCM是将时钟功能直接集成到全局时钟网络中去的。因此在高性能、高频等应用中,DCM解决了以下常见的时钟问题:
1. 消除设备内部或外部组件的时钟偏差,以改善整体系统性能并消除时钟分配延迟;
2. 通过改变时钟周期的固定分量或增量对时钟信号进行相移;
3. 对输入时钟进行分频或倍频来产生全新的时钟,全新的时钟频率是基于输入时钟频率的静态或动态参数的乘法和除法混合而成;
4. 确保输出时钟标准和稳定,可以调整占空比(常用50%)、镜像、转发或重新缓冲时钟信号等等,可以将输入时钟去歪斜并转换为其他I / O标准。
例如,使用转发和转换功能,可以将LVTTL时钟输入到LVDS;时钟输入抖动滤波;自由运行振荡器;扩频时钟产生等等功能。
PLL即锁相环,它的主要用途是作为广泛频率范围的频率合成器,以及作为与DCMs结合的外部或内部时钟的抖动滤波器。如下图所示:
16868.png

图 16.1.1 PLL结构图

从上图中我们可以看到:首先是参考时钟和反馈时钟输入(Clock Pin通常反馈时钟都是使用PLL或者DCM自己产生,进行自反馈),这两个时钟可以来自IBUFG、 BUFG、 IBUF、PLL或者DCM,进来的时钟都会有一个可编程计数器D。然后时钟经过相位频率检测器(PFD),这一步主要用来比较参考时钟和反馈时钟的频率和相位,生成一个比例信号。该信号驱动电荷泵(CP)和环路滤波器(LF)产生一个电压控制振荡器(VCO)的参考电压。PFD产生上升或下降信号到电荷泵和环路滤波器以确定VCO是否应以更高或更低的频率运行。当VCO的频率过高时,PFD激活一个下降信号,导致控制电压降低,降低VCO工作频率,当VCO的频率太低时,信号会增加电压。 VCO产生八个输出相位,每个输出阶段可以选择作为输出计数器的参考时钟。如下图所示:
161292.png

图 16.1.2 PLL工作原理图1

161356.png

图 16.1.3 PLL工作原理图

在开发设计过程中会经常用到PLL,甚至可以说只要是稍微复杂点的工程都一定有它。PLL的主要用途是用于时钟网络去歪斜、当作合成器、抖动滤波器和产生一个零延迟缓冲时钟。这里需要注意的是当你想产生一个零延迟输出时钟时就需要用到ODDR2(ODDR2是IO资源,可以去参考官方文档UG831)如下图所示:
161568.png

图 16.1.4 产生单端零延迟输出时钟

上图是产生单端时钟信号,如果想产生差分时钟信号则如下图所示:
161665.png

图 16.1.5产生差分零延迟缓冲时钟

可能刚看到这两张图有点懵,其实仔细看上文对PLL的描述就会发现这两张图其实不难理解,我们先分析一下单端时钟信号怎么产生的如图 16.1.4所示:首先参考时钟(CLKIN)和反馈时钟(CLKFB_IN)进入PLL(在进入PLL之前参考时钟先进入IBUFG缓冲,反馈时钟进入IBUF缓冲),到达PLL后经过处理(处理主要指分频、倍频、消抖、滤波等),处理完成后输出(CLKFBOUT),这个时候我们想零延迟通过IO输出,就要调用IO的ODDR资源。CLKFBOUT输出的信号先进入BUFG,之后进入ODDR2,再由OBUF输出,这样我们在FPGA对应信号引脚上就可以用示波器测到时钟信号了。差分信号其实是类似的,唯一的区别就是单端时钟的OBUF缓冲器变成了差分时钟的OBUFDS缓冲器,其他的过程都一样。
看到这读者可能就会问了,那我怎们调用这些资源呢?比如外部时钟进来后我们怎么把它接入IBUFG,又怎么让它进入PLL,然后再怎么把处理好的时钟信号放入ODDR2呢?其实最简单的就是用原语,用原语定义(差分的原语和单端不同,本章节只讲解单端,差分的原语请参考官方文档UG832)IBUFG、ODDR2,但是PLL就不用原语了,我们会创建一个IP核,然后例化IP核。最后的OBUF是不需要定义的,IO资源已经自动帮我们接上了。那原语长什么样呢?不着急,软件设计章节会跟大家讲解,我们继续往下看。
在上文提到过PLL是可以与DCMs相结合的,那他们是如何结合的呢?Xilinx提供了三种使用方案:
第一种:DCM Driving PLL        
162421.png

图 16.1.6 DCM Driving PLL

从上图我们可以看到参考时钟首先是进入DCM然后由CLK90输出用来作为PLL的参考时钟,这种方式就叫做DCM驱动PLL模式。
第二种:PLL Driving DCM
162574.png

图 16.1.7 PLL Driving DCM

从上图我们可以看到参考时钟首先是进入PLL然后由CLKOUT0输出用来作为DCM的参考时钟,这种方式就叫做PLL驱动DCM模式。
第三种:PLL to PLL Connection
162735.png

图 16.1.8 PLL to PLL Connection

从上图中我们可以看到是两个PLL级联,参考时钟进入第一个PLL在通过BUFG路由到第二个PLL,这里需要注意的是两个PLL级联的时钟信号范围是共用的,不能无限倍频也不能无限分频。
这里或许有人要问了,为什么要提供这三种方案呢,其实和DCM与PLL本身的特点有关,其中DCM主要的特点是相位调整,PLL的特点是消抖去扭斜。这就造成了为什么出现这样三种组合,比如说我需要的时钟对相位没什么太大的要求,我们就选第一种方案,如果对相位有要求就选第二种方案,还有的可能对输出时钟的抖动要求非常高,那就选第三种,最大限度的消除抖动和扭斜。所以根据不同的设计需求我们选择不同的设计方案。
16.2实验任务
本章的实验任务是通过调用PLL IP核,对输入时钟(晶振时钟)进行分频、倍频并输出出去。
16.3硬件设计
本章实验将PLL IP核产生的4个时钟100MHz、100MHz_180deg 、50MHz和25MHz,连接到开发板的J2扩展口IO上,分别是第B0_L37_N、B0_L37_P、B0_L48_P和B0_L50_P号引脚,扩展口原理图如下图所示:
163278.png

图 16.3.1 扩展口引脚图

本实验中,各端口信号的管脚分配如下表所示:
表 16.3.1 IP核之PLL实验管脚分配
1631.png

对应的引脚约束语句如下:
  1. NET sys_clk                       TNM_NET = sys_clk_pin;
  2. TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz;

  3. NET sys_clk                       LOC = N8  | IOSTANDARD = "LVCMOS33";
  4. NET sys_rst_n                     LOC = G16 | IOSTANDARD = "LVCMOS33";

  5. NET CLK_OUT1                      LOC = H16 | IOSTANDARD = "LVCMOS33";
  6. NET CLK_OUT2                      LOC = H15 | IOSTANDARD = "LVCMOS33";
  7. NET CLK_OUT3                      LOC = P15 | IOSTANDARD = "LVCMOS33";
  8. NET CLK_OUT4                      LOC = R14 | IOSTANDARD = "LVCMOS33";
复制代码

16.4程序设计
我们先创建好一个空的工程,工程命名为ip_pll,然后我们在这个空的的工程中添加PLL IP核,如下图所示:
164312.png

图 16.4.1 添加IP核

我们将添加的IP核命名为pll然后设置好路径,点击“Next>”进入IP核选择界面,如下图所示:
164421.png

图 16.4.2 创建PLL IP核

直接在Search IP Catalog搜索栏中输入Clocking就可以直接找到PLL IP核,选中IP核点“Next>”进入Summary界面,如下图所示:
164566.png

图 16.4.3 Summary界面

在Summary界面我们可以看到路径信息等,如果没有问题就直接点击“Finish”进入IP核参数配置界面如下图所示:
164689.png

图 16.4.4 IP核参数配置

在参数配置第一页我们主要是设置输入时钟频率(参考时钟),我们这里采用晶振时钟作为输入时钟,所以设置为50MHZ,后面的Source框就是设置我们的Buffer缓冲,这里选择No buffer,因为在上文已经跟大家说了我们要用原语来调用IBUFG,所以这边就没必要再设置了。之后我们点击“Next>”进入参数配置第二页,如下图所示:

164918.png

图 16.4.5 IP核参数配置

参数配置第二页主要是配置输出时钟的参数,我们需要输出四路时钟(一个PLL最多输出六路)所以在第一个方框中再勾选三路时钟输出,第二个方框是设置时钟频率,第三个方框是设置相位差,这里我们把CLK_OUT2设置180度相位差,第四个方框是设置占空比(我们默认50%占空比),第五个方框是BUFG设置我们保持默认(在上文中我提到过PLL处理过时钟信号后在进入ODDR2之前会有个BUFG如图 16.1.4所示,这里就是把这个BUFG勾选上)。设置好输出时钟参数后我们点击“Next>”进入参数配置第三页,如下图所示:
165259.png

图 16.4.6 IP核参数配置

这一页主要是设置复位引脚(RESET)和时钟锁存信号(LOCKED),其中时钟锁存信号的作用是指示PLL输出的时钟信号是否稳定,当LOCKED拉高代表PLL输出时钟稳定,反之则表示输出不稳定或者错误。设置好后点击“Next>”进入参数配置第四页,如下图所示:
165451.png

图 16.4.7 IP核参数配置

参数配置第四页不需要作任何修改保持默认即可,直接点击“Next>”进入参数配置第五页,如下图所示:
165563.png

图 16.4.8 IP核参数配置

参数配置第五页,主要是对IP引脚名称的修改,这里可以不作修改保持默认名称即可,点击“Next>”进入参数配置第六页,如下图所示:

165691.png

图 16.4.9 IP核参数配置

参数配置第六页是Summary界面,显示将要生成哪些文件,直接点击“Generate”生成PLL IP核。生成成功后会在工程中显示我们刚刚创建的PLL IP核,如下图所示:
165840.png

图 16.4.10 PLL IP核创建成功

现在我们就可以创建一个设计输入源文件来例化刚刚创建的PLL IP核了,先创建一个空白源文件,如下图所示:
165960.png

图 16.4.11创建源文件

如上图所示将创建的源文件命名为ip_pll,路径放在工程的rtl文件夹下,然后一路点击“Next>”,这样空白源文件就创建好了,如下图所示:
166091.png

图 16.4.12 空白源文件

如上图所示,因为我的ISE和Notepad++建立了关联,所以创建好源文件后会,自动进入Notepad++界面(如何关联请参考软件篇),现在我们来例化PLL IP核,如下图所示:

166244.png


图 16.4.13 生成例化模板

如上图所示,先选中IP核,然后双击View HDL Instantiation Template,ISE会自动帮你生成例化模板,如下图所示:
166377.png

图 16.4.14 例化模板

如上图所示将红色方框中的例化模板复制到源文件中去,如下图所示:
166469.png

图 16.4.15 例化PLL

如上图所示:将模板中的instance_name改成你想要的名字(建议改成u_+模块名),接下来我们就来调用原语把上文中提到的IBUFG和ODDR2调用上,下面是完整的代码:
  1. 1   module ip_pll(
  2. 2       input  sys_clk   ,
  3. 3       input  sys_rst_n ,  
  4. 4      
  5. 5       output CLK_OUT1  ,
  6. 6       output CLK_OUT2  ,
  7. 7       output CLK_OUT3  ,
  8. 8       output CLK_OUT4  ,
  9. 9       output LOCKED
  10. 10      
  11. 11      );
  12. 12  
  13. 13  wire  sys_clk_pll  ;
  14. 14  
  15. 15  wire  CLK_OUT1_pll ;
  16. 16  wire  CLK_OUT2_pll ;
  17. 17  wire  CLK_OUT3_pll ;
  18. 18  wire  CLK_OUT4_pll ;
  19. 19  
  20. 20  pll u_pll
  21. 21     (// Clock in ports
  22. 22      .CLK_IN1(sys_clk_pll),      // IN
  23. 23      // Clock out ports
  24. 24      .CLK_OUT1(CLK_OUT1_pll),     // OUT
  25. 25      .CLK_OUT2(CLK_OUT2_pll),     // OUT
  26. 26      .CLK_OUT3(CLK_OUT3_pll),     // OUT
  27. 27      .CLK_OUT4(CLK_OUT4_pll),     // OUT
  28. 28      // Status and control signals
  29. 29      .RESET(~sys_rst_n),// IN
  30. 30      .LOCKED(LOCKED));      // OUT   
  31. 31      
  32. 32      ODDR2 #(
  33. 33        .DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"
  34. 34        .INIT(1'b0),    // Sets initial state of the Q output to 1'b0 or 1'b1
  35. 35        .SRTYPE("SYNC") // Specifies "SYNC" or "ASYNC" set/reset
  36. 36     ) ODDR2_inst_1 (
  37. 37        .Q(CLK_OUT1),   // 1-bit DDR output data
  38. 38        .C0(CLK_OUT1_pll),   // 1-bit clock input
  39. 39        .C1(~CLK_OUT1_pll),   // 1-bit clock input
  40. 40        .CE(1'b1), // 1-bit clock enable input
  41. 41        .D0(1'b1), // 1-bit data input (associated with C0)
  42. 42        .D1(1'b0), // 1-bit data input (associated with C1)
  43. 43        .R(1'b0),   // 1-bit reset input
  44. 44        .S(1'b0)    // 1-bit set input
  45. 45     );   
  46. 46     
  47. 47    ODDR2 #(
  48. 48        .DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"
  49. 49        .INIT(1'b0),    // Sets initial state of the Q output to 1'b0 or 1'b1
  50. 50        .SRTYPE("SYNC") // Specifies "SYNC" or "ASYNC" set/reset
  51. 51     ) ODDR2_inst_2 (
  52. 52        .Q(CLK_OUT2),   // 1-bit DDR output data
  53. 53        .C0(CLK_OUT2_pll),   // 1-bit clock input
  54. 54        .C1(~CLK_OUT2_pll),   // 1-bit clock input
  55. 55        .CE(1'b1), // 1-bit clock enable input
  56. 56        .D0(1'b1), // 1-bit data input (associated with C0)
  57. 57        .D1(1'b0), // 1-bit data input (associated with C1)
  58. 58        .R(1'b0),   // 1-bit reset input
  59. 59        .S(1'b0)    // 1-bit set input
  60. 60     );   
  61. 61  
  62. 62  ODDR2 #(
  63. 63        .DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"
  64. 64        .INIT(1'b0),    // Sets initial state of the Q output to 1'b0 or 1'b1
  65. 65        .SRTYPE("SYNC") // Specifies "SYNC" or "ASYNC" set/reset
  66. 66     ) ODDR2_inst_3 (
  67. 67        .Q(CLK_OUT3),   // 1-bit DDR output data
  68. 68        .C0(CLK_OUT3_pll),   // 1-bit clock input
  69. 69        .C1(~CLK_OUT3_pll),   // 1-bit clock input
  70. 70        .CE(1'b1), // 1-bit clock enable input
  71. 71        .D0(1'b1), // 1-bit data input (associated with C0)
  72. 72        .D1(1'b0), // 1-bit data input (associated with C1)
  73. 73        .R(1'b0),   // 1-bit reset input
  74. 74        .S(1'b0)    // 1-bit set input
  75. 75     );   
  76. 76  
  77. 77  ODDR2 #(
  78. 78        .DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"
  79. 79        .INIT(1'b0),    // Sets initial state of the Q output to 1'b0 or 1'b1
  80. 80        .SRTYPE("SYNC") // Specifies "SYNC" or "ASYNC" set/reset
  81. 81     ) ODDR2_inst_4 (
  82. 82        .Q(CLK_OUT4),   // 1-bit DDR output data
  83. 83        .C0(CLK_OUT4_pll),   // 1-bit clock input
  84. 84        .C1(~CLK_OUT4_pll),   // 1-bit clock input
  85. 85        .CE(1'b1), // 1-bit clock enable input
  86. 86        .D0(1'b1), // 1-bit data input (associated with C0)
  87. 87        .D1(1'b0), // 1-bit data input (associated with C1)
  88. 88        .R(1'b0),   // 1-bit reset input
  89. 89        .S(1'b0)    // 1-bit set input
  90. 90     );     
  91. 91   
  92. 92    IBUFG #(
  93. 93        .IOSTANDARD("DEFAULT")
  94. 94     ) IBUFG_inst (
  95. 95        .O(sys_clk_pll), // Clock buffer output
  96. 96        .I(sys_clk)  // Clock buffer input (connect directly to top-level port)
  97. 97     );
  98. 98     
  99. 99   endmodule  
复制代码

上面的代码可以看到除了例化一个PLL IP核之外我调用了一个IBUFG原语,四个ODDR2原语分别对应图 16.1.4中的IBUFG和ODDR2,至于原语为什么是这么使用的请参考官方文档UG381,这篇文档有详细的介绍,我在这里就不在花费大量篇幅介绍了。
代码写好并且综合通过后我们就可以使用Modelsim对代码进行仿真了。首先我们来编写一个仿真文件(Testbench文件),如下所示:
  1. 1 `timescale 1ns / 1ps
  2. 2
  3. 3 module pll_tb;
  4. 4
  5. 5   // Inputs
  6. 6   reg sys_clk;
  7. 7   reg sys_rst_n;
  8. 8
  9. 9   // Outputs
  10. 10  wire CLK_OUT1;
  11. 11  wire CLK_OUT2;
  12. 12  wire CLK_OUT3;
  13. 13  wire CLK_OUT4;
  14. 14    wire LOCKED;
  15. 15  // Instantiate the Unit Under Test (UUT)
  16. 16  ip_pll uut (
  17. 17      .sys_clk(sys_clk),
  18. 18      .sys_rst_n(sys_rst_n),
  19. 19      .CLK_OUT1(CLK_OUT1),
  20. 20      .CLK_OUT2(CLK_OUT2),
  21. 21      .CLK_OUT3(CLK_OUT3),
  22. 22      .CLK_OUT4(CLK_OUT4),
  23. 23      .LOCKED  (LOCKED  )
  24. 24  );
  25. 25
  26. 26  initial begin
  27. 27      // Initialize Inputs
  28. 28      sys_clk = 0;
  29. 29      sys_rst_n = 0;
  30. 30
  31. 31      // Wait 100 ns for global reset to finish
  32. 32      #100;
  33. 33        sys_rst_n = 1;
  34. 34      // Add stimulus here
  35. 35
  36. 36  end
  37. 37   always #10 sys_clk=~sys_clk;
  38. 38
  39. 39   
  40. 40 endmodule
复制代码

写好仿真文件我们回到ISE点击联合仿真按钮,如下图所示:
1611477.png

图 16.4.16 使用Modelsim仿真

仿真波形如下图所示:
1611556.png

图 16.4.17 仿真波形

上图的波形中我们的参考时钟是sys_clk,它是50MHZ的标准时钟,我们以它为参考,可以看到CLK_OUT1是100MHZ时钟,CLK_OUT2跟CLK_OUT1相差180度相位,CLK_OUT3是50MHZ时钟,CLK_OUT4是25MHZ时钟,跟我们预期的目标是完全一致的,说明我们仿真时完全没有问题的。
仿真没问题后就可以添加引脚约束文件了,新建一个时序约束文件(UCF文件),如下图所示:
1611816.png

图 16.4.18 新建引脚约束文件

如上图所示给引脚约束文件命名为pll,并将路径放在prj文件夹下,然后一路点击“Next>”就可以创建一个新的UCF约束文件(不会创建UCF文件的请参考软件篇),在这个空白文件中输入约束语句,如下图所示:
1611983.png

图 16.4.19 引脚约束

写好约束语句后点击保存就可以生成bit流文件进行下板验证了。
16.5下载验证
首先我们将下载器与超越者开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后连接开发板的电源,并打开电源开关。
回到ISE界面,我们将生成好的bit流文件下载到开发板中去,然后用示波器去抓取一路信号看看是否符合预期目标,我们将示波器探针接到R14引脚,示波器测量到的波形如下图所示:
1612225.png

图 16.5.1 25MHZ测量波形

可以看到示波器显示抓取到的信号为25MHZ,再测另外三路信号,发现都是符合预期的,这就说明本次PLL IP核实验成功了。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 16:05

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表