偶数分频最容易实现,可以用计数器实现。计数值小的时候也可以使用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
不要求占空比为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
无法单纯使用计数方式来实现,可以利用时钟双边沿特性产生两个占空比不为50%的时钟,然后将这两个时钟信号相与或者相或产生。
比如产生占空比为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
对应 牛客 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
半整数分频实际上就是特殊一点的小数分频,可以按照小数分频的方法完成,也可以利用时钟的双边沿特性完成。
假设要进行5.5倍分频,那么让分频后的时钟信号2.5个原周期为高电平,3个原时钟周期为低电平。这样的信号要由两个沿不同时钟沿变化的信号产生:
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
假设要进行5.5倍分频,相当于在11个原时钟周期里进行了两次5.5分频。采用5分频和6分频来实现5.5分频。那么
5N + 6M = 11
N + M = 2
=>
N = 1, M = 1
也就是说在11个原时钟周期里进行了一次5分频和一次6分频。