首页 > 其他分享 >[verilog手撕专题]时钟分频

[verilog手撕专题]时钟分频

时间:2022-08-13 18:14:15浏览次数:70  
标签:分频 begin clk cnt verilog input reg 时钟

时钟分频

前言,本专题属于verilog手撕专题中的一节,思维导图如下,其他专题请见导航

2^n时钟分频

module div_4
(
    input clk,
    input rst_n,

    output reg clk_out
);
    reg clk_div2;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            clk_div2 <= 1'b0;
        else
            clk_div2 <= ~clk_div2;
    end

    always @(posedge clk_div2 or negedge rst_n)begin
        if(!rst_n)
            clk_out <= 1'b0;
        else
            clk_out <= ~clk_out;
    end

endmodule

偶数时钟分频

module div_clk #(parameter DIV = 10)
(
	input clk,
  input rst_n,
  
  output reg clk_out
);
  reg [$clog2(DIV)-1:0] cnt;
  
  always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
      cnt <= 1'b0;
    end
    else if (cnt == DIV/2 - 1)
      cnt <= 1'b0;
    else 
      cnt <= cnt + 1'b1;
  end
  
  always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
      clk_out <= 1'b0;
    else if (cnt == DIV/2 - 1)
      clk_out <= ~clk_out;
  end
endmodule

// ----------------------- testbench -------------------------
`timescale 1ns/1ps
module tb;
  
  reg clk, rst_n;
  
  initial begin
    clk = 1'b0;
    #5 clk = 1'b1;
    forever #5 clk = ~clk;
  end
  
  initial begin
    rst_n = 1'b0;
    #9 rst_n = 1'b1;
    #600 
    $finish;
  end
  
  wire clkout;
  div_clk #(.DIV(8)) dut (clk, rst_n, clkout);
  
  initial begin
    $dumpfile("showmebug.vcd");
    $dumpvars(0, tb);
  end
endmodule

奇数时钟分频

module div_clk #(
	parameter DIV = 7
)(
	input clk,
  input rst_n,
  output clkout
);
  
  reg [$clog2(DIV)-1:0] cnt;
  reg clk_p, clk_n;
  always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
      cnt <= 1'b0;
    else if (cnt == DIV - 1)
      cnt <= 1'b0;
    else
      cnt <= cnt + 1'b1;
  end
  
  always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
      clk_p <= 1'b0;
    else if (cnt == DIV - 1)
      clk_p <= 1'b0;
    else if (cnt == ((DIV >> 1) - 1))
      clk_p <= 1'b1;
  end
  
  always @(negedge clk or rst_n)begin
    if(!rst_n)
      clk_n <= 1'b0;
    else if (cnt == (DIV - 1))
      clk_n <= 1'b0;
    else if (cnt == ((DIV >> 1) - 1))
      clk_n <= 1'b1;
  end
  
  assign clkout = clk_n | clk_p;
endmodule

// ----------------------- testbench -------------------------
`timescale 1ns/1ps
module tb;
  
  reg clk, rst_n;
  
  initial begin
    clk = 1'b0;
    #5 clk = 1'b1;
    forever #5 clk = ~clk;
  end
  
  initial begin
    rst_n = 1'b0;
    #9 rst_n = 1'b1;
    #600 
    $finish;
  end
  
  wire clkout;
  div_clk #(.DIV(7)) dut (clk, rst_n, clkout);
  
  initial begin
    $dumpfile("showmebug.vcd");
    $dumpvars(0, tb);
  end
endmodule

0.5小数时钟分频

module half_divisor(
    input               rstn ,
    input               clk,
    output              clk_div3p5
    );

   //计数器
   parameter            MUL2_DIV_CLK = 7 ;
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == MUL2_DIV_CLK-1) begin //计数2倍分频比
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   reg                  clk_ave_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_ave_r <= 1'b0 ;
      end
      //first cycle: 4 source clk cycle
      else if (cnt == 0) begin
         clk_ave_r <= 1 ;
      end
      //2nd cycle: 3 source clk cycle
      else if (cnt == (MUL2_DIV_CLK/2)+1) begin
         clk_ave_r <= 1 ;
      end
      else begin
         clk_ave_r <= 0 ;
      end
   end

   //adjust
   reg                  clk_adjust_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_adjust_r <= 1'b0 ;
      end
      //本次时钟只为调整一致的占空比
      else if (cnt == 1) begin
         clk_adjust_r <= 1 ;
      end
      //本次时钟只为调整一致的精确分频比
      else if (cnt == (MUL2_DIV_CLK/2)+1 ) begin
         clk_adjust_r <= 1 ;
      end
      else begin
         clk_adjust_r <= 0 ;
      end
   end

   assign clk_div3p5 = clk_adjust_r | clk_ave_r ;

endmodule

任意小数分频

module frac_divisor
  #(
   parameter            SOURCE_NUM = 76 , //cycles in source clock
   parameter            DEST_NUM   = 10  //cycles in destination clock
   )
   (
    input               rstn ,
    input               clk,
    output              clk_frac
    );
 
   //7分频参数、8分频参数、次数差值
   parameter    SOURCE_DIV = SOURCE_NUM/DEST_NUM ;
   parameter    DEST_DIV   = SOURCE_DIV + 1;
   parameter    DIFF_ACC   = SOURCE_NUM - SOURCE_DIV*DEST_NUM ;


   reg [3:0]            cnt_end_r ;  //可变分频周期
   reg [3:0]            main_cnt ;   //主计数器
   reg                  clk_frac_r ; //时钟输出,高电平周期数为1
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b0 ;
      end
      else if (main_cnt == cnt_end_r) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b1 ;
      end
      else begin
         main_cnt    <= main_cnt + 1'b1 ;
         clk_frac_r  <= 1'b0 ;
      end
   end
   //输出时钟
   assign       clk_frac        = clk_frac_r ;
   //差值累加器使能控制
   wire         diff_cnt_en     = main_cnt == cnt_end_r ;

   //差值累加器逻辑
   reg [4:0]            diff_cnt_r ;
   wire [4:0]           diff_cnt = diff_cnt_r >= DEST_NUM ?
                                   diff_cnt_r -10 + DIFF_ACC :
                                   diff_cnt_r + DIFF_ACC ;                                
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         diff_cnt_r <= 0 ;
      end
      else if (diff_cnt_en) begin
         diff_cnt_r <= diff_cnt ;
      end
   end

   //分频周期变量的控制逻辑
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
      //差值累加器溢出时,修改分频周期
      else if (diff_cnt >= 10) begin
         cnt_end_r      <= DEST_DIV-1 ;
      end
      else begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
   end

endmodule

标签:分频,begin,clk,cnt,verilog,input,reg,时钟
From: https://www.cnblogs.com/pu1se/p/16583686.html

相关文章