奇数分频器电路设计

  • Post author:
  • Post category:其他



目录


奇数分频器电路设计


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分频电路代码


`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/15 10:58:02
// Design Name: 
// Module Name: divider_7
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

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


module divider_7(
    input          sys_clk,      //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
    input          sys_rst_n,    //复位信号,低电平有效
    
    output         clk_7         //输出7分频信号
    );
    
parameter  N = 7;

//reg  define
reg [2:0]    cnt;       //最大值为6,所以需要3位位宽
reg          cnt_pos;   //上升沿敏感信号
reg          cnt_neg;   //下降沿敏感信号

assign clk_7 = cnt_pos && cnt_neg;   //组合逻辑与

//计数模块,从0计数到6共计7个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt <= 3'd0;         //复位清零
    else if(cnt == 3'd6)     //计满7个时钟周期,从0开始计数,所以需要-1
        cnt <= 3'd0;         //计满则清零
    else
        cnt <= cnt + 3'd1;    //没计满就一直计数
end

//cnt_pos:上升沿触发
//低电平维持三个基准时钟周期,高电平维持4个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_pos <= 1'b0;    //复位清零
    else if(cnt==3'd2)      //计满三个时钟周期,
        cnt_pos <= 1'b1;    //前三个时钟周期输出为0,满足条件则输出1
    else if(cnt==3'd6)      //计满7个时钟周期
        cnt_pos <= 1'b0;    //后四个时钟周期输出为1,满足条件则输出0
    else
        cnt_pos <= cnt_pos; //不满足条件就保持原来状态
end

//cnt_neg:下降沿触发
//低电平维持三个基准时钟周期,高电平维持四个时钟周期
always @(negedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_neg <= 1'b0;     //复位清零
    else if(cnt==3'd2)       //计满三个时钟周期
        cnt_neg <= 1'b1;     //前三个时钟周期输出为0,满足条件则输出为1.
    else if(cnt==3'd6)       //计满7个时钟周期
        cnt_neg <= 1'b0;     //后四个时钟周期输出为1,满足条件则输出0
    else
        cnt_neg <= cnt_neg;   //不满足条件就保持原来状态
end

endmodule

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



3.2、仿真验证






3.2.1、编写 TB 文件


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

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/15 13:49:48
// Design Name: 
// Module Name: tb_divider_7
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module tb_divider_7();  //仿真模块

//输入 reg 定义
reg         sys_clk;
reg         sys_rst_n;

//输出 wire 定义
wire        clk_7;

//设置初始化条件
initial begin
    sys_clk = 1'b0;     //初始化时钟为0
    sys_rst_n <= 1'b0;  //初始复位
    #10                 //10个时间单位后
    sys_rst_n <= 1'b1;  //拉高复位
end

//always代表重复进行,#10代表每10个时间单位
//每10个时间单位反转时钟,即时钟周期为20个时间单位(20ns)
always #10 sys_clk = ~sys_clk;

//例化被测试模块
divider_7 divider_7_inst
(
    .sys_clk           (sys_clk       ),
    .sys_rst_n         (sys_rst_n     ),
    
    .clk_7             (clk_7         )
);

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、代码如下:


`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/20 16:16:39
// Design Name: 
// Module Name: divider7_fsm
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


//以一个 7 分频为例
module divider7_fsm(
     //系统时钟与复位
     input              sys_clk,
     input              sys_rst_n,
     
     //输出时钟
     output  reg       clk_divide_7
    );
//在编写状态机代码时首先要定义状态变量(代码中的参数 S0~S6)与状态寄存器(curr_st、next_st)
//parameter define
parameter S0 = 7'b0000001;    //独热码定义方式
parameter S1 = 7'b0000010;
parameter S2 = 7'b0000100;
parameter S3 = 7'b0001000;
parameter S4 = 7'b0010000;
parameter S5 = 7'b0100000;
parameter S6 = 7'b1000000;

//reg define
reg  [6:0]     curr_st;     //当前状态
reg  [6:0]     next_st;     //下一个状态

//*********************************************************
//**           main  code
//**********************************************************

//状态机的第一段采用同步时序描述状态转移
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
         curr_st <= S0;
    else
         curr_st <= next_st;
end

//状态机的第二段采用逻辑组合判断状态转移条件
always @(*) begin
    case (curr_st)
        S0:next_st = S1;
        S1:next_st = S2;
        S2:next_st = S3;
        S3:next_st = S4;
        S4:next_st = S5;
        S5:next_st = S6;
        S6:next_st = S0;
        default:next_st = S0;
    endcase
end

//状态机的第三段描述状态输出(这里采用时序电路输出)
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        clk_divide_7 <= 1'b0;
    else if ((curr_st == S0) | (curr_st == S1) | (curr_st == S2) | (curr_st == S3))
        clk_divide_7 <= 1'b0;
    else if ((curr_st == S4) | (curr_st == S5) | (curr_st == S6))
        clk_divide_7 <= 1'b1;
    else
        ;
end
  
endmodule

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



4.2、使用状态机的好处




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



版权声明:本文为yishuihanq原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。