• [文档].艾米电子 分频器,Verilog


    对读者的假设

    已经掌握:

    内容

    1 从计数器到分频器

    此处所说的分频器,即把输入时钟的频率降低后再输出时钟的模块。今天我们只讨论等占空比的偶数分频和奇数分频,关于小数分频以及倍频将不做介绍,有兴趣的朋友可以自行研究。在之之前我们先看下前面讲的模-m计数器。

    代码1.1 模-m计数器(缺省为模-10计数器)

    module mod_m_bin_counter
    #(parameter M=10) // mod-M
    (
      // global clock and asyn reset
      input clk,
      input rst_n,
      // counter interface
      output max_tick,
      output min_tick,
      output [N-1:0] q
    );
    
    // signal declaration
    localparam N = log2(M); // number of bits in counter
    reg [N-1:0] r_reg;
    wire [N-1:0] r_next;
    
    // body
    // register
    always@(posedge clk, negedge rst_n)
      if(!rst_n)
        r_reg <= 0;
      else
        r_reg <= r_next;
       
    // next-state logic
    assign r_next = (r_reg == (M-1)) ? 0 : r_reg + 1'b1;
    //output logic
    assign q = r_reg;
    assign max_tick = (r_reg == (M-1)) ? 1'b1 : 1'b0;
    
    
    // log2 constant function
    function integer log2(input integer n);
      integer i;
    begin
      log2 = 1;
      for(i=0; 2**i<n; i = i + 1)
        log2 = i + 1;
    end
    endfunction                
    endmodule

    根据这个模-m计数器,我们再写一个testbench。

    代码1.2 模m-计数器的testbench(重新配置为模-10计数器)

    `timescale 1ns/1ns
    module f_div_tb;
    localparam T=20; // clock period
    localparam M = 10;
    localparam N = log2(M);
    // global clock and asyn reset
    reg clk, rst_n;
    wire o_clk;
    // counter interface
    wire max_tick, min_tick;
    wire [N-1:0] q;
    
    
    // log2 constant function
    function integer log2(input integer n);
      integer i;
    begin
      log2 = 1;
      for(i=0; 2**i<n; i = i + 1)
        log2 = i + 1;
    end
    endfunction
    
    
    // clcok
    always
    begin
      clk = 1'b0; 
      #(T/2);
      clk = 1'b1;
      #(T/2);
    end
    
    
    // reset
    initial
    begin
      rst_n = 1'b0;
      #(T/2)
      rst_n = 1'b1;
    end
    
    
    // inst
    mod_m_bin_counter #(.M(M)) m_inst
    (
      .clk(clk),
      .rst_n(rst_n),
      .max_tick(max_tick),
      .min_tick(min_tick),
      .q(q)
    );
    
    
    // stimulus body
    initial 
    begin
      // initial input 
      @(posedge rst_n); // wait to deassert rst_n
      @(negedge clk); // wait for a clock
      // run 1024 cock cycle
      repeat(1024) @(negedge clk); // last 1024 clock cycle
      $stop;
    end
    endmodule

    但是我在使用Quartus II + Modelsim_Altera仿真的时候出现了下面的错误提示。

    image

    代码2的第10行,变量N未定义,然后第11行用N来索引的q数组也没有定义。奇怪了,我在Quartus II都能综合通过,而且RTL视图也是正确的,为什么Modelsim_Altera就不可以这样定义呢?咳,把常量(localparam)换成参数(parameter)即可。

    代码1.3 改进后的模-m计数器(重新配置为模-10计数器)

    module mod_m_bin_counter
    #(parameter M=10, N = log2(M)) // mod-M
    (
      // global clock and asyn reset
      input clk,
      input rst_n,
      // counter interface
      output max_tick,
      output min_tick,
      output [N-1:0] q
    );
    
    // signal declaration
    //localparam N = log2(M); // number of bits in counter
    reg [N-1:0] r_reg;
    wire [N-1:0] r_next;
    
    // body
    // register
    always@(posedge clk, negedge rst_n)
      if(!rst_n)
        r_reg <= 0;
      else
        r_reg <= r_next;
       
    // next-state logic
    assign r_next = (r_reg == (M-1)) ? 0 : r_reg + 1'b1;
    //output logic
    assign q = r_reg;
    assign min_tick = (r_reg == 0) ? 1'b1 : 1'b0;
    assign max_tick = (r_reg == (M-1)) ? 1'b1 : 1'b0;
    
    
    // log2 constant function
    function integer log2(input integer n);
      integer i;
    begin
      log2 = 1;
      for(i=0; 2**i<n; i = i + 1)
        log2 = i + 1;
    end
    endfunction                
    endmodule

    修改完,在Quartus II里再综合一下。接下来就是按照[文档].艾米电子 - 使用Verilog设计的Quartus II入门指南再做一次仿真。告诉大家一个技巧,如果执行完一次RTL级或门级的仿真,那么Quartus II会为我们的主模块及其testbench生成do文件的。这样假如有错误,就无需重新启动Modelsim_Altera,只需重新load一下do文件即可。

    image (路径格式:Quarter II工程文件夹\simulation\modelsim\mod_m_bin_counter_run_msim_rtl_verilog.do)

    下面看下RTL级仿真波形。

     image

    下面讨论一下如何测量波形的长度,我们注意两个工具。在Modelsim>Windows处,打开image ,对应的工具为image 。下面在波形上加(image )几个Cursor,使用鼠标调整其位置。

    image

    注意红色区域及标识,我们可以清楚地看到两个Cursor之间的距离为20, 000ps,亦即20ns,也就是模-10计数器的周期。那么怎么让它显示20ns呢?在坐标轴区域(image ),右键选择image ,设置所需时间单元。

    image

    修改为ns,顺便测一下max_tick和min_tick脉冲的持续时间,显示如下:

    image 

    测量完,可通过image ,把不需要的Cursor去掉。

    换一种视角看波形。

    image

    假设我们以max_tick作为使能信号,来翻转某个寄存器,那么这个变量就会输出方波,且其周期为模-m周期的2倍。

    image

    这就是偶数分频器的原理。

    假如我们分别使用全局时钟的上升沿和下降沿触发计数器,然后根据两个计数器的值来生成时钟,再拿这两个时钟运算,得到所需分频的时钟。下面以模-3计数器为例来说明如何3分频。

    方法1

    image

    黄色区域分别为分别使用全局时钟的上升沿和下降沿触发计数器;绿色区域为根据两个计数器的输出q生成的两个脉冲信号;青色区域为所需分频时钟。

    绿色区域是0 ~ 3>>1输出低电平,3>> ~ 3-1输出高电平;对应的绿色区域做按位或运算。

    方法2

    image

    绿色区域是0 ~ 3>>1输出高电平,3>> ~ 3-1输出低电平;对应的绿色区域做按位与运算。

    以上就是技术分频的原理。

    2 偶数分频器

    根据第1节的解析,下面我们写一个偶数分频的案例。

    代码 2.1 偶数分频(缺省分频比为50, 000, 000)

    module f_div
    #(parameter RATIO=50_000_000)
    (
      // global clock and asyn reset
      input clk,
      input rst_n,
      //output clock
      output reg o_clk
    );
    
    wire max_tick;
    
    mod_m_bin_counter #(.M(RATIO/2)) mod_m_inst
    (
      .clk(clk),
      .rst_n(rst_n),
      .max_tick(max_tick),
      .min_tick(),
      .q()
    );
    
    always@(posedge clk, negedge rst_n)
      if(!rst_n)
        o_clk <= 1'b0;
      else if(max_tick)
        o_clk <= ~o_clk;
    
    endmodule
    

    第22~26行,我们使用模-m计数器的max_tick来作为时钟使能,驱动o_clk翻转。注意第13行的模-m例化参数中,M的值为分频比RATIO的一半。

    下面给出testbench。

    代码2.2 偶数分频的testbench(重新配置为6分频)

    `timescale 1ns/1ns
    module f_div_tb;
    localparam RATIO = 6;
    localparam T=20; // clock period
    // global clock and asyn reset
    reg clk, rst_n;
    wire o_clk;
    
    
    // clcok
    always
    begin
      clk = 1'b0; 
      #(T/2);
      clk = 1'b1;
      #(T/2);
    end
    
    
    // reset
    initial
    begin
      rst_n = 1'b0;
      #(T/2)
      rst_n = 1'b1;
    end
    
    
    // inst
    f_div #(.RATIO(RATIO)) f_div_inst
    (
      .clk(clk),
      .rst_n(rst_n),
      .o_clk(o_clk)
    );
    
    
    // stimulus body
    initial 
    begin
      // initial input 
      @(posedge rst_n); // wait to deassert rst_n
      @(negedge clk); // wait for a clock
      // run 1024 cock cycle
      repeat(1024) @(negedge clk); // last 1024 clock cycle
      $stop;
    end
    endmodule

    RTL级仿真波形如下所示。观察3个Cursor的间距,分别为120ns、20ns,即实现了6分频动作。

    image 

    3 奇数分频器

    讲完了偶数分频,下面再写个奇数分频。

    代码3.1 奇数分频(缺省为25分频)

    module f_div
    #(parameter RATIO=25)
    (
      // global clock and asyn reset
      input clk,
      input rst_n,
      //output clock
      output o_clk_p, o_clk_n, o_clk
    );
    
    // log2 constant function
    function integer log2(input integer n);
      integer i;
    begin
      log2 = 1;
      for(i=0; 2**i<n; i = i + 1)
        log2 = i + 1;
    end
    endfunction
    
    localparam N = log2(RATIO);
    wire [N-1:0] q_pos, q_neg;
    
    mod_m_bin_counter #(.M(RATIO)) mod_m_inst1
    (
      .clk(clk),
      .rst_n(rst_n),
      .max_tick(),
      .min_tick(),
      .q(q_pos)
    );
    
    mod_m_bin_counter #(.M(RATIO)) mod_m_inst2
    (
      .clk(~clk),
      .rst_n(rst_n),
      .max_tick(),
      .min_tick(),
      .q(q_neg)
    ); 
    
    assign o_clk_p = (q_pos <= RATIO>>1) ? 0 : 1; 
    assign o_clk_n = (q_neg <= RATIO>>1) ? 0 : 1;
    assign o_clk = o_clk_n | o_clk_p;
      
    endmodule
    

    第24~31行和第33~40行,我们分别例化了两个相同参数的模-m计数器,不同点在于第26行和第35行的区别,即上升沿触发和下降沿触发两种。

    下面给出testbench。

    代码3.2 奇数分频的testbench(重新配置为5分频)

    `timescale 1ns/1ns
    module f_div_tb;
    localparam RATIO = 5;
    localparam T=20; // clock period
    // global clock and asyn reset
    reg clk, rst_n;
    wire o_clk_p, o_clk_n, o_clk;
    
    
    // clcok
    always
    begin
      clk = 1'b0; 
      #(T/2);
      clk = 1'b1;
      #(T/2);
    end
    
    
    // reset
    initial
    begin
      rst_n = 1'b0;
      #(T/2)
      rst_n = 1'b1;
    end
    
    
    // inst
    f_div #(.RATIO(RATIO)) f_div_inst
    (
      .clk(clk),
      .rst_n(rst_n),
      .o_clk_p(o_clk_p),
      .o_clk_n(o_clk_n),
      .o_clk(o_clk)
    );
    
    
    // stimulus body
    initial 
    begin
      // initial input 
      @(posedge rst_n); // wait to deassert rst_n
      @(negedge clk); // wait for a clock
      // run 1024 cock cycle
      repeat(1024) @(negedge clk); // last 1024 clock cycle
      $stop;
    end
    endmodule 

    下面给出RTL级别仿真波形。红圈内的Cursor测距显示,5分频OK。

    image 

    好了,今天就啰嗦到这里,大家有什么疑问可以留言讨论。

    参考

    [笔记].等占空比分频器的几种写法.[Verilog]

    另见

    [与艾米一起学FPGA/SOPC].[逻辑实验文档连载计划]

  • 相关阅读:
    HDU 3874 Necklace 区间查询的离线操作
    POJ 1651 Multiplication Puzzle (区间dp)
    POJ 2528 Mayor's posters(离散+线段树)
    POJ 2886 Who Gets the Most Candies?
    webgl教程
    GL_ARRAY_BUFFER 和 GL_ELEMENT_ARRAY_BUFFER
    几个不错的webgl教程网
    svg图标库
    sublime text nodejs set
    图形学着色器学习
  • 原文地址:https://www.cnblogs.com/yuphone/p/1917395.html
Copyright © 2020-2023  润新知