[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA基础篇连载-08 FPGA多路分频器实验

文档创建者:FPGA课程
浏览次数:383
最后更新:2024-08-22
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 1-FPGA基础入门实验
本帖最后由 FPGA课程 于 2024-8-22 19:53 编辑

​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!




1概述
在FPGA中,时钟分频是经常用到的。本节课讲解2分频、3分频、4分频和8分频的Verilog实现并且学习generate语法功能的应用,本文的重点是培养读者对于verilog基础编程的基本功掌握。
2 硬件电路分析
硬件接口和子卡模块请阅读“附录 1”
配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。
3程序设计思路
1、整数倍分频为 2、4、8,这种2^n次方倍数关系的分频最容易实现,所以我们可以把这3种分频方式归为一类。
2、3分频是奇数倍分频,这种分频比较麻烦,对于初学者肯定得思考一番。
3、2HZ和前文中流水灯的延迟控制方法一样,只要实现每过500ms对寄存器取反操作
对于这类基础简单的方案,笔者认为,大家学习主要缺少的是思路,所以我们直接拿程序来分析。
Clk_Divider.v文件
  1. `timescale 1ns / 1ps
  2. module Clk_Divider#
  3. (
  4. parameter DEBUG_ENABLE = 1'b1,
  5. parameter REF_CLK      = 32'd100_000_000
  6. )
  7. (
  8. input I_sysclk_p,
  9. input I_sysclk_n,
  10. input  I_rst_n,
  11. output O_div2,
  12. output O_div3,
  13. output O_div4,
  14. output O_div8,
  15. output O_div2hz
  16.     );

  17. wire I_clk;
  18. IBUFGDS CLK_U(
  19. .I(I_sysclk_p),
  20. .IB(I_sysclk_n),
  21. .O(I_clk)
  22. );


  23. //2分频代码:只要基于源时钟每个时钟的上升沿对O_div2_r寄存器取反   
  24. reg O_div2_r;
  25. always@(posedge I_clk)begin
  26.         if(!I_rst_n)
  27.                 O_div2_r <= 1'b0;
  28.         else
  29.                 O_div2_r <= ~O_div2_r;
  30. end

  31. //4分频和8分频代码:共同使用了div_cnt1计数器
  32. //4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'b10的时候对O_div4_r寄存器取反;
  33. //而8分频是对div_cnt1==2'b00的时候对O_div8_r取反
  34. reg [1:0] div_cnt1;
  35. always@(posedge I_clk)begin
  36.         if(!I_rst_n)
  37.                 div_cnt1 <= 2'b00;
  38.         else
  39.                 div_cnt1 <= div_cnt1+1'b1;
  40. end

  41. reg O_div4_r;
  42. reg O_div8_r;
  43. always@(posedge I_clk)begin
  44.         if(!I_rst_n)
  45.                 O_div4_r <= 1'b0;
  46.         else if(div_cnt1==2'b00 || div_cnt1==2'b10)
  47.                 O_div4_r <= ~O_div4_r;
  48.         else
  49.                 O_div4_r <= O_div4_r;
  50. end

  51. always@(posedge I_clk)begin
  52.         if(!I_rst_n)
  53.                 O_div8_r <= 1'b0;
  54.         else if(div_cnt1==2'b00)
  55.                 O_div8_r <= ~O_div8_r;
  56.         else
  57.                 O_div8_r <= O_div8_r;
  58. end
  59. /*
  60. 3分频的本质是我们需要在每次1.5倍的时钟周期的时候实现3分频寄存器的翻转,但是我们无法直接实现1.5倍的分频。
  61. 因此采取分别采取2个计数器pos_cnt和neg_cnt,分别对上升沿和下降沿计数。计数周期是0-1-2,共计3个时钟周期。
  62. 我们取pos_cnt == 2'd1的时候O_div3_r0输出高电平,neg_cnt == 2'd1的时候O_div3_r1输出高电平。
  63. 由于O_div3_r0和O_div3_r1输出1个时钟的高电平,但是相位相差180°,因此只要执行O_div3 = O_div3_r0 | O_div3_r1运算,
  64. 就能实现1.5倍周期的输出高电平,那么剩余的1.5倍源时钟周期就是输出低电平了。
  65. */
  66. reg [1:0] pos_cnt;
  67. reg [1:0] neg_cnt;
  68. always@(posedge I_clk)begin
  69.         if(!I_rst_n)
  70.                 pos_cnt <= 2'b00;
  71.         else if(pos_cnt == 2'd2)
  72.                 pos_cnt <= 2'b00;
  73.         else
  74.                 pos_cnt <= pos_cnt + 1'b1;
  75. end

  76. always@(negedge I_clk)begin
  77.         if(!I_rst_n)       
  78.                 neg_cnt <= 2'b00;
  79.         else if(neg_cnt == 2'd2)
  80.                 neg_cnt <= 2'b00;
  81.         else
  82.                 neg_cnt <= neg_cnt + 1'b1;
  83. end

  84. reg O_div3_r0;
  85. reg O_div3_r1;
  86. always@(posedge I_clk)begin
  87.         if(!I_rst_n)
  88.                 O_div3_r0 <= 1'b0;
  89.         else if(pos_cnt < 2'd1)
  90.                 O_div3_r0 <= 1'b1;
  91.         else
  92.                 O_div3_r0 <= 1'b0;
  93. end

  94. always@(negedge I_clk)begin
  95.         if(!I_rst_n)
  96.                 O_div3_r1 <= 1'b0;
  97.         else if(neg_cnt < 2'd1)       
  98.                 O_div3_r1 <= 1'b1;
  99.         else
  100.                 O_div3_r1 <= 1'b0;
  101. end

  102. reg O_div2hz_r;
  103. reg [25:0] div2hz_cnt;

  104. wire ms250_en = (div2hz_cnt == REF_CLK/4 - 1'b1);
  105. always@(posedge I_clk)
  106. begin
  107.         if(!I_rst_n)
  108.                 div2hz_cnt <= 0;
  109.         else if(div2hz_cnt < REF_CLK/4 - 1'b1)
  110.                 div2hz_cnt <= div2hz_cnt + 1'b1;
  111.         else
  112.                 div2hz_cnt <= 0;
  113. end

  114. always@(posedge I_clk)
  115. begin
  116.         if(!I_rst_n)
  117.                 O_div2hz_r <= 0;
  118.         else if(ms250_en)
  119.                 O_div2hz_r <= ~O_div2hz_r;
  120.         else
  121.                 O_div2hz_r <= O_div2hz_r;
  122. end

  123. assign O_div2 = O_div2_r;
  124. assign O_div3 = O_div3_r0 | O_div3_r1;
  125. assign O_div4 = O_div4_r;
  126. assign O_div8 = O_div8_r;
  127. assign O_div2hz = O_div2hz_r;

  128. generate  if(DEBUG_ENABLE == 1'b1) begin : debugcore
  129. //添加ila IP ,Chipscope观察信号
  130. ila_0 ila_0 (
  131.         .clk(I_clk), // input wire clk
  132.         .probe0(O_div2hz), // input wire [0:0]  probe0  
  133.         .probe1({O_div2,O_div3,O_div4,O_div8}) // input wire [3:0]  probe1
  134. );
  135. end
  136. endgenerate

  137. endmodule       
复制代码

代码解释:
2分频代码:
只要基于源时钟每个时钟的上升沿对div2_o_r寄存器取反
4分频和8分频代码
共同使用了div_cnt1计数器,4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'b10的时候对div4_o_r寄存器取反;而8分频是对div_cnt1==2'b00的时候对div8_o_r取反
3分频代码:
3分频的本质是我们需要在每次1.5倍的时钟周期的时候实现3分频寄存器的翻转,但是我们无法直接实现1.5倍的分频。因此分别采取2个计数器pos_cnt和neg_cnt,分别对上升沿和下降沿计数。计数周期是0-1-2,共计3个时钟周期。我们取pos_cnt == 2’d1的时候div3_o_r0输出高电平,neg_cnt == 2’d1的时候div3_o_r1输出高电平。由于div3_o_r0和div3_o_r1输出1个时钟的高电平,但是相位相差180°,因此只要执行div3_o = div3_o_r0 | div3_o_r1运算,就能实现1.5倍周期的输出高电平,那么剩余的1.5倍源时钟周期就是输出低电平了。
2HZ分频代码:
和前面文章中的流水灯延迟控制的分频方法一样。
4 RTL仿真4.1添加仿真测试源码
仿真测试文件存放在工程目录uisrc\02_sim中,源码如下:
  1. `timescale 1ns / 1ps

  2. module Clk_Divider_Tb();
  3. // Inputs
  4. reg I_sysclk_p;
  5. reg I_sysclk_n;
  6. reg I_rst_n;
  7. // Outputs
  8. wire O_div2;
  9. //wire O_div3;
  10. wire O_div4;
  11. wire O_div8;
  12. wire O_div2hz;

  13. // Instantiate the Unit Under Test (UUT)
  14. Clk_Divider#(
  15. .DEBUG_ENABLE(1'b0),
  16. .REF_CLK(100000000)
  17. )
  18. Clk_Divider_inst
  19. (
  20. .I_sysclk_p(I_sysclk_p),
  21. .I_sysclk_n(I_sysclk_n),
  22. .I_rst_n(I_rst_n),
  23. .O_div2(O_div2),
  24. .O_div4(O_div4),
  25. .O_div8(O_div8),
  26. .O_div2hz(O_div2hz)
  27. );

  28. initial begin
  29. // Initialize Inputs4
  30.     I_sysclk_p= 0;
  31.     I_sysclk_n= 1;
  32.     I_rst_n = 0;
  33. // Wait 100 ns for global reset to finish
  34.     #100;
  35.     I_rst_n=1;
  36. end

  37. always #5 I_sysclk_p =~I_sysclk_p;
  38. always #5 I_sysclk_n =~I_sysclk_n;

  39. endmodule
复制代码

4.2仿真结果
3分频、2分频、4分频、8分频
9d56f4f919444d75a24d9e9593f0419d.jpg
2HZ分频
c09955e403504114bbc672a57e4a6247.jpg
5测试结果5.1 在线逻辑分析仪
通过ila在线逻辑分析仪我们也能看出我们的2分频、4分频、8分频信号都分频成功
7385a2f47b23435bb365a8e720e75166.jpg
5.2开发板现象
(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)
请确保下载器和开发板已经正确连接,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

如下图所示LED闪烁,闪烁频率2Hz
4fbba35784b44ecab2ef0d1f26c3fb9f.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则