当前位置:   article > 正文

奇数分频器电路设计_奇数分频电路

奇数分频电路

目录

奇数分频器电路设计

1、奇数分频器电路简介

2、实验任务

3、程序设计

3.1、7分频电路代码

3.2、仿真验证

3.2.1、编写 TB 文件

3.2.2、仿真验证

4、用状态机实现7分频电路设计

4.1、代码如下:

4.2、使用状态机的好处


奇数分频器电路设计

       前面一节我们学习了偶数分频器的设计方法,本节我们来学习下奇数分频器的设计方法。实现偶数分频可通过一个简单计数器实现,而如果需要三分频,五分频,七分频等奇数分频,一个计数器是不够的。奇数分频器的设计相对偶数分频器设计要复杂一点,我们来看下奇数分频设计方法。

1、奇数分频器电路简介

       在《偶数分频器电路设计》章节提到实现分频一般有两个方法,一个方法是直接使用 PLL 进行分频,比如 FPGA 或者 ASIC 设计中,都可以直接使用 PLL 进行分频。还有一种实现方法就是直接使用逻辑实现,即使用代码实现分频设计。我们本节介绍的是使用代码进行设计奇数分频器。本节我们先看下奇数分频设计。

  • 奇数分频设计的一般方法:
  • 假设为 N分频,需从0计数到 N-1(一共N/2个基准时钟),一直循环。
  • 设计一个对基准时钟上升沿敏感的信号,每当计数到(N-1)/2-1时,时钟翻转;计数到计数器最大值时再反转。
  • 设计一个对基准时钟下降沿敏感的信号,每当计数到(N-1)/2-1时,时钟翻转;计数到计数器最大值时再反转。
  • 将 上升沿敏感的信号和 下降沿敏感的信号相与(&&)即N分频电路。

2、实验任务

使用 Verilog 语言设计一个任意奇数分频电路,默认进行 7 分频

3、程序设计

3.1、7分频电路代码

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/05/15 10:58:02
  7. // Design Name:
  8. // Module Name: divider_7
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. //奇数分频设计的一般方法:
  22. //假设为 N分频,需从0计数到 N-1(一共N/2个基准时钟),一直循环.
  23. //设计一个对基准时钟上升沿敏感的信号,每当计数到(N-1/2-1时,时钟翻转;计数到计数器最大值时再反转.
  24. //设计一个对基准时钟下降沿敏感的信号,每当计数到(N-1/2-1时,时钟翻转;计数到计数器最大值时再反转.
  25. //将 上升沿敏感的信号和 下降沿敏感的信号相与(&&)即N分频电路.
  26. module divider_7(
  27. input sys_clk, //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
  28. input sys_rst_n, //复位信号,低电平有效
  29. output clk_7 //输出7分频信号
  30. );
  31. parameter N = 7;
  32. //reg define
  33. reg [2:0] cnt; //最大值为6,所以需要3位位宽
  34. reg cnt_pos; //上升沿敏感信号
  35. reg cnt_neg; //下降沿敏感信号
  36. assign clk_7 = cnt_pos && cnt_neg; //组合逻辑与
  37. //计数模块,从0计数到6共计7个时钟周期
  38. always @(posedge sys_clk or negedge sys_rst_n) begin
  39. if(!sys_rst_n)
  40. cnt <= 3'd0; //复位清零
  41. else if(cnt == 3'd6) //计满7个时钟周期,从0开始计数,所以需要-1
  42. cnt <= 3'd0; //计满则清零
  43. else
  44. cnt <= cnt + 3'd1; //没计满就一直计数
  45. end
  46. //cnt_pos:上升沿触发
  47. //低电平维持三个基准时钟周期,高电平维持4个时钟周期
  48. always @(posedge sys_clk or negedge sys_rst_n) begin
  49. if(!sys_rst_n)
  50. cnt_pos <= 1'b0; //复位清零
  51. else if(cnt==3'd2) //计满三个时钟周期,
  52. cnt_pos <= 1'b1; //前三个时钟周期输出为0,满足条件则输出1
  53. else if(cnt==3'd6) //计满7个时钟周期
  54. cnt_pos <= 1'b0; //后四个时钟周期输出为1,满足条件则输出0
  55. else
  56. cnt_pos <= cnt_pos; //不满足条件就保持原来状态
  57. end
  58. //cnt_neg:下降沿触发
  59. //低电平维持三个基准时钟周期,高电平维持四个时钟周期
  60. always @(negedge sys_clk or negedge sys_rst_n) begin
  61. if(!sys_rst_n)
  62. cnt_neg <= 1'b0; //复位清零
  63. else if(cnt==3'd2) //计满三个时钟周期
  64. cnt_neg <= 1'b1; //前三个时钟周期输出为0,满足条件则输出为1.
  65. else if(cnt==3'd6) //计满7个时钟周期
  66. cnt_neg <= 1'b0; //后四个时钟周期输出为1,满足条件则输出0
  67. else
  68. cnt_neg <= cnt_neg; //不满足条件就保持原来状态
  69. end
  70. endmodule

接下来我们使用 Vivado 的 RTL  ANALYSIS,来看一下我们编写代码的 RTL 视图。

3.2、仿真验证

3.2.1、编写 TB 文件

      只需要对时钟以及复位信号进行激励,代码编写如下:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/05/15 13:49:48
  7. // Design Name:
  8. // Module Name: tb_divider_7
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module tb_divider_7(); //仿真模块
  22. //输入 reg 定义
  23. reg sys_clk;
  24. reg sys_rst_n;
  25. //输出 wire 定义
  26. wire clk_7;
  27. //设置初始化条件
  28. initial begin
  29. sys_clk = 1'b0; //初始化时钟为0
  30. sys_rst_n <= 1'b0; //初始复位
  31. #10 //10个时间单位后
  32. sys_rst_n <= 1'b1; //拉高复位
  33. end
  34. //always代表重复进行,#10代表每10个时间单位
  35. //每10个时间单位反转时钟,即时钟周期为20个时间单位(20ns)
  36. always #10 sys_clk = ~sys_clk;
  37. //例化被测试模块
  38. divider_7 divider_7_inst
  39. (
  40. .sys_clk (sys_clk ),
  41. .sys_rst_n (sys_rst_n ),
  42. .clk_7 (clk_7 )
  43. );
  44. endmodule

3.2.2、仿真验证

       测试程序在 Xilinx 的 Vivado 软件 或者其他仿真工具运行后的波形如下显示:

       从波形图可以看到:10ns后停止复位,计数器cnt一直在从0计数到6;每当cnt计数到3的上升沿(L2),cnt_pos信号输出翻转,每当cnt计数清零(L4),cnt_pos信号输出翻转;每当cnt计数到3的下降沿(L1),cnt_neg信号输出翻转,每当cnt计数清零(L3),cnt_neg信号输出翻转;从L2到L3为7分频信号的半个时钟周期(高电平),从L2到L3为8分频信号的半个时钟周期(低电平);从L2到L6为7分频信号的1个完整的时钟周期,同时也是7个基准时钟周期。

4、用状态机实现7分频电路设计

三段式状态机的基本格式是:

  • 第一个 always 语句实现同步状态跳转;
  • 第二个 always 语句采用组合逻辑判断状态转移条件; 
  • 第三个 always 语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。

在开始编写状态机代码之前,一般先画出状态跳转图,这样在编写代码时思路会比较清晰。

4.1、代码如下:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/06/20 16:16:39
  7. // Design Name:
  8. // Module Name: divider7_fsm
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. //以一个 7 分频为例
  22. module divider7_fsm(
  23. //系统时钟与复位
  24. input sys_clk,
  25. input sys_rst_n,
  26. //输出时钟
  27. output reg clk_divide_7
  28. );
  29. //在编写状态机代码时首先要定义状态变量(代码中的参数 S0~S6)与状态寄存器(curr_st、next_st)
  30. //parameter define
  31. parameter S0 = 7'b0000001; //独热码定义方式
  32. parameter S1 = 7'b0000010;
  33. parameter S2 = 7'b0000100;
  34. parameter S3 = 7'b0001000;
  35. parameter S4 = 7'b0010000;
  36. parameter S5 = 7'b0100000;
  37. parameter S6 = 7'b1000000;
  38. //reg define
  39. reg [6:0] curr_st; //当前状态
  40. reg [6:0] next_st; //下一个状态
  41. //*********************************************************
  42. //** main code
  43. //**********************************************************
  44. //状态机的第一段采用同步时序描述状态转移
  45. always @(posedge sys_clk or negedge sys_rst_n) begin
  46. if(!sys_rst_n)
  47. curr_st <= S0;
  48. else
  49. curr_st <= next_st;
  50. end
  51. //状态机的第二段采用逻辑组合判断状态转移条件
  52. always @(*) begin
  53. case (curr_st)
  54. S0:next_st = S1;
  55. S1:next_st = S2;
  56. S2:next_st = S3;
  57. S3:next_st = S4;
  58. S4:next_st = S5;
  59. S5:next_st = S6;
  60. S6:next_st = S0;
  61. default:next_st = S0;
  62. endcase
  63. end
  64. //状态机的第三段描述状态输出(这里采用时序电路输出)
  65. always @(posedge sys_clk or negedge sys_rst_n) begin
  66. if (!sys_rst_n)
  67. clk_divide_7 <= 1'b0;
  68. else if ((curr_st == S0) | (curr_st == S1) | (curr_st == S2) | (curr_st == S3))
  69. clk_divide_7 <= 1'b0;
  70. else if ((curr_st == S4) | (curr_st == S5) | (curr_st == S6))
  71. clk_divide_7 <= 1'b1;
  72. else
  73. ;
  74. end
  75. endmodule
  76. //采用这种描述方法虽然代码结构复杂了一些,
  77. //但是这样做的好处是可以有效地滤去组合逻辑输出的毛刺,
  78. //同时也可以更好的进行时序计算与约束,另外对于总线形式的输出信号来说,
  79. //容易使总线数据对齐,减小总线数据间的偏移,从而降低接收端数据采样出错的频率。

4.2、使用状态机的好处

       采用这种描述方法虽然代码结构复杂了一些,但是这样做的好处是可以有效地滤去组合逻辑输出的毛刺,同时也可以更好的进行时序计算与约束,另外对于总线形式的输出信号来说,容易使总线数据对齐,减小总线数据间的偏移,从而降低接收端数据采样出错的频率。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/519103
推荐阅读
相关标签
  

闽ICP备14008679号