Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)

Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)

  • 偶数分频
  • 奇数分频
    • 不要求占空比为50%的奇数分频
    • 要求占空比为50%奇数分频
  • 小数分频
  • 半整数分频
    • 利用双边沿特性
    • 利用小数分频的思路

偶数分频

偶数分频最容易实现,可以用计数器实现。计数值小的时候也可以使用DFF直接完成。这里使用计数器实现,计数达到分频系数一半的时候进行翻转(占空比为50%)。对应: 牛客 VL37 时钟分频(偶数)

/**
	使用计数方式实现了8分频
*/
module even_div(
    input     wire  rstn,
    input     wire  clk,
    output    reg   clk_out
);

reg [1:0] count;
/**
    count operation
*/
always @(posedge clk or negedge rstn) begin
    if(~rstn) begin
        count <= 2'b0;
    end
    else begin
        count <= count + 1'b1;
    end
end


always @(posedge clk or negedge rstn) begin
    if(~rstn) begin
        clk_out <= 1'b0;
    end
    else if(count == 2'b00) begin
        clk_out <= ~clk_out;
    end
end

endmodule

仿真结果如下
Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)_第1张图片

奇数分频

不要求占空比为50%的奇数分频

不要求占空比为50%,可以与偶数分频一样,根据计数值进行波形翻转。
对应: 牛客 VL42 无占空比要去的奇数分频

/**
	不要求占空比的奇数分频
	5分频 3低电平2高电平
	占空比 2/5
*/
module odd_div (    
    input     wire rstn,
    input     wire clk,
    output    reg clko
);

parameter N = 5;

reg [2:0] count;
always @(posedge clk or negedge rstn) begin
    if(~rstn) begin
        count <= 3'd0;
    end
    else if(count == N - 1) begin
        count <= 3'd0;
    end
    else begin
        count <= count + 1'b1;
    end 
end

always @(posedge clk or negedge rstn) begin
    if(~rstn) begin
        clko <= 1'b0;
    end
    else if(count == N - 1 || count == (N >> 1)) begin
        clko <= ~clko;
    end
    else begin
        clko <= clko;
    end
end

endmodule

仿真结果如下
Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)_第2张图片

要求占空比为50%奇数分频

无法单纯使用计数方式来实现,可以利用时钟双边沿特性产生两个占空比不为50%的时钟,然后将这两个时钟信号相与或者相或产生。

比如产生占空比为50%的5分频时钟,可以通过:

  1. 沿源时钟上升沿变化的,2cycle高电平,3cycle低电平,周期为5的时钟信号。
  2. 沿源时钟下降沿变化的,2cycle高电平,3cycle低电平,周期为5的时钟信号。
  3. 将上述两时钟信号相或得到占空比为50%的5分频时钟。

或者

  1. 沿源时钟上升沿变化的,3cycle高电平,2cycle低电平,周期为5的时钟信号。
  2. 沿源时钟下降沿变化的,3cycle高电平,2cycle低电平,周期为5的时钟信号。
  3. 将上述两时钟信号相与得到占空比为50%的5分频时钟。
/**
	产生	50%占空比 5分频的时钟
	1. 沿源时钟上升沿变化的,3cycle高电平,2cycle低电平,周期为5的时钟信号。
	2. 沿源时钟下降沿变化的,3cycle高电平,2cycle低电平,周期为5的时钟信号。
	3. 将上述两时钟信号相与得到占空比为50%的5分频时钟。
*/
module odd_div1(
    input       clk,
    input       rstn,
    output      clko
);

    parameter N = 5;

    reg [2:0] count;
    // 沿时钟上升沿变化的时钟信号 2低3高
    reg clkp;
    // 沿时钟下降沿变化的时钟信号
    reg clkn;
    assign clko = clkp & clkn;

    always @(posedge clk or negedge rstn) begin
        if(~rstn) begin
            count <= 3'b0;
        end
        else if(count == N - 1) begin
            count <= 3'b0;
        end
        else begin
            count <= count + 1'b1;
        end
    end

    
    always @(posedge clk or negedge rstn) begin
        if(~rstn) begin
            clkp <= 1'b0;
        end
        else if(count == 3'b1 || count == N - 1) begin
            clkp = ~clkp;
        end
        else begin
            clkp <= clkp;
        end
    end

    always @(negedge clk or negedge rstn) begin
        if(~rstn) begin
            clkn <= 1'b0;
        end
        else if(count == 3'b1 || count == N - 1) begin
            clkn = ~clkn;
        end
        else begin
            clkn <= clkn;
        end
    end

endmodule

仿真波形如下
Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)_第3张图片

小数分频

对应 牛客 VL41 任意小数分频

无法做到占空比为50%的小数分频,也无法做到分频后的每个时钟周期都是原时钟周期的小数倍(当然如果是x.5是可以做到的)。
因此转换思路,进行7.6倍分频可以理解成分频后的时钟周期 To = 7.6T ,那么 10To = 76T,也就是说只要满足76个原时钟周期等于10个分频后的时钟周期即可。
这时候我们使用7分频和8分频的时钟来实现7.6倍分频的时钟,假设 10个分频后的时钟 由N个7分频的时钟和M个8分频的时钟组成,那么可以得到

	M + N = 10
	N7T + M8T = 76T
	⇒ 
	M + N = 10
	7N + 8M = 76

可以得到N = 4, M = 6。
就是由4个7分频的时钟和6个8分频的时钟组成7.6倍分频的时钟。
那么可以有四种方法将这两种时钟进行混合:

1. 先4次7分频再进行6次8分频
2. 先6次8分频再进行4次7分频
3. 将6次8分频插入4次7分频中
4. 将4次7分频插入6次8分频中
/**
	实现8.7倍分频
	由3个8分频和7个9分频时钟组成10个分频后的时钟
	=》
	24个原时钟周期用于8分频
	63个原时钟周期用于9分频
	
	先进行 3次8分频 再进行 7次9分频
*/

module div_M_N(
   input  wire clk_in,
   input  wire rst,
   output wire clk_out
);

/*
   8N + 9M = 87
   N + M = 10
   M = 7, N = 3
   因此3个8分频时钟 和 7个9分频时钟构成 8.7分频时钟
*/    

/*
   3*8 = 24 
   87个原时钟周期里前24个周期 用来8分频
   7*9 = 63
   后63个时钟周期 用来9分频

   相当于10个8.7分频的时钟,前3个分频后的时钟周期由8分频组成,后7个由9分频组成
*/
parameter M_N = 8'd87; 
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期

reg clk_div;
assign clk_out = clk_div;

// 计数87
reg [6:0] cnt87;
// 计数8
reg [2:0] cnt8;
// 计数9
reg [3:0] cnt9;

always @(posedge clk_in or negedge rst) begin
   if(~rst) begin
      cnt87 <= 7'b0;
   end
   else if(cnt87 == M_N - 1'b1) begin
      cnt87 <= 7'b0;
   end
   else begin
      cnt87 <= cnt87 + 1'b1;
   end   
end

/*
   8/9分频时钟切换
*/
always @(posedge clk_in or negedge rst) begin
   if(~rst) begin
      cnt8 <= 3'b0;
      cnt9 <= 4'b0;
   end
   // 8分频
   else if(cnt87 < c89) begin
      cnt9 <= 4'b0;
      cnt8 <= cnt8 + 1'b1;
   end
   // 9分频
   else begin
      cnt8 <= 3'b0;
      if(cnt9 == div_o - 1) begin
         cnt9 <= 4'b0;
      end
      else begin
         cnt9 <= cnt9 + 1'b1;
      end
   end
end

always @(posedge clk_in or negedge rst) begin
   if(~rst) begin
      clk_div <= 1'b0;
   end
   // 8分频
   else if(cnt87 < c89) begin
      if (cnt8 == 3'b0 || cnt8 == 3'b100) begin
         clk_div <= ~clk_div;
      end   
      else begin
         clk_div <= clk_div;
      end
   end
   // 9分频
   else begin
      // 4高5低
      if (cnt9 == 3'b0 || cnt9 == 3'b100) begin
         clk_div <= ~clk_div;
      end   
      else begin
         clk_div <= clk_div;
      end
   end
end

endmodule

仿真波形如下
Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)_第4张图片

半整数分频

半整数分频实际上就是特殊一点的小数分频,可以按照小数分频的方法完成,也可以利用时钟的双边沿特性完成。

利用双边沿特性

假设要进行5.5倍分频,那么让分频后的时钟信号2.5个原周期为高电平,3个原时钟周期为低电平。这样的信号要由两个沿不同时钟沿变化的信号产生:

  1. 由5时钟周期(沿上升沿变化,2周期高电平,3周期低电平)和6时钟周期的信号(沿上升沿变化,2周期高电平,4周期低电平)组成。
  2. 由5时钟周期(沿下降沿变化,2周期高电平,3周期低电平)和6时钟周期的信号(沿下降沿变化,2周期高电平,4周期低电平)组成。
  3. 将信号1和信号2相或得到 5.5倍分频的信号。
module half_int_div(
    input  wire clk,
    input  wire rstn,
    output wire clko
);

    reg clkp;
    reg clkn;
    assign clko = clkp | clkn;

    reg [3:0] cnt;
    always @(posedge clk or negedge rstn) begin
        if(~rstn) begin
            cnt <= 4'b0;
        end
        else if(cnt == 4'b1010) begin
            cnt <= 4'b0;
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end

    always @(posedge clk or negedge rstn) begin
        if(~rstn) begin
            clkp <= 1'b0;
        end
        else if(cnt == 4'd0 || cnt == 4'd1 || cnt ==  4'd6 || cnt ==  4'd7) begin
            clkp <= 1'b1;
        end
        else begin
            clkp <= 1'b0;
        end
    end

    always @(negedge clk or negedge rstn) begin
        if(~rstn) begin
            clkn <= 1'b0;
        end
        else if(cnt == 4'd1 || cnt == 4'd2 || cnt ==  4'd6 || cnt ==  4'd7) begin
            clkn <= 1'b1;
        end
        else begin
            clkn <= 1'b0;
        end
    end

endmodule

仿真波形如下
Verilog时钟分频(偶数分频、奇数分频、小数分频、半整数分频)_第5张图片

利用小数分频的思路

假设要进行5.5倍分频,相当于在11个原时钟周期里进行了两次5.5分频。采用5分频和6分频来实现5.5分频。那么

	5N + 6M = 11
	N + M = 2
	=>
	N = 1, M = 1

也就是说在11个原时钟周期里进行了一次5分频和一次6分频。

你可能感兴趣的:(FPGA学习,fpga开发)