2023.4.8
module even_div
(
input wire rst ,
input wire clk_in,
output wire clk_out2,
output wire clk_out4,
output wire clk_out8
);
reg clk_out2_r, clk_out4_r, clk_out8_r;
always@(posedge clk_in or negedge rst) begin //二分频是最简单的,直接把~Q接到D端就可以了
if(~rst)
clk_out2_r <= 0;
else
clk_out2_r <= ~clk_out2_r;
end
//四分频是以二分频为时钟,就可以在上升沿的时候四分频也是从0跳转到1
//使得二四八分频同时为高电平
always@(posedge clk_out2 or negedge rst) begin
if(~rst)
clk_out4_r <= 0;
else
clk_out4_r <= ~clk_out4_r;
end
always@(posedge clk_out4 or negedge rst) begin
if(~rst)
clk_out8_r <= 0;
else
clk_out8_r <= ~clk_out8_r;
end
assign clk_out2 = clk_out2_r;
assign clk_out4 = clk_out4_r;
assign clk_out8 = clk_out8_r;
endmodule
reg q;
always@(posedge clk or posedge rst)begin
if(rst)
q <= 0;
else if(set)
q <= 1;
else
q <= ~q;
end
分频系数:10,偶数分频,用计数器来实现
reg [3:0] cnt;
parameter CNT_DIV = 5, CNT_MAX = 10;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else
cnt <= (cnt==CNT_MAX-1) ? 0 : cnt+1; //注意计数器最大值为9,不能为10,为10的话下个周期会有问题
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
q <= 0;
else if(cnt<CNT_DIV)
q <= 1;
else
q <= 0;
end
原理和偶数分频一样,只不过由于是奇数个周期,可以决定高低电平的占比。例如三分频,占空比可以为1/3或者2/3
//下面是5分频
reg [2:0]cnt;
reg clk_out5_r;
always@(posedge clk_in or negedge rst)begin
if(!rst)
clk_out5_r<=0;
else if(cnt==0||cnt==2)
clk_out5_r<=~clk_out5_r;
else
clk_out5_r<=clk_out5_r;
end
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt<=0;
else
cnt<=(cnt==4)?0:cnt+1;
end
这种写法应该要避免,无法综合,前面学习verilog语法的时候说过,混合使用上升沿和下降沿触发的触发器。
parameter MAX = 7;
always@(posedge clk_in or negedge clk_in)begin
if(!rst)begin
cnt<=0;
clk_out7_r<=0;
end
else if(cnt==MAX-1)begin
cnt<=0;
clk_out7_r<=~clk_out7_r;
end
else
cnt<=cnt+1;
end
如果奇数分频输出时钟的高低电平只差一个cycle ,则可以利用源时钟双边沿特性并采用"与操作
"或"或操作
"的方式将分频时钟占空比调整到50%。
如果是高电平少,就是相或;如果高电平多,就是相与。
module oushu_div(
input clk_in,
input rst_n,
output clk_out //这里输出默认是wire类型
);
parameter MAX = 7;
reg [2:0] cnt1;
reg clk_div1, clk_div2;
always@(posedge clk_in or negedge rst_n)begin //用一个计数器计数
if(!rst_n)
cnt <= 0;
else
cnt <= (cnt==MAX-1) ? 0 : cnt+1;
end
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div1 <= 0;
else if(cnt<(MAX-1)/2)
clk_div1 <= 1;
else
clk_div1 <= 0;
end
always@(negedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div2 <= 0;
else if(cnt<(MAX-1)/2)
clk_div2 <= 1;
else
clk_div2 <= 0;
end
assign clk_out = clk_div1 | clk_div2 ;
endmodule
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else
cnt <= (cnt==MAX-1) ? 0 : cnt+1;
end
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div1 <= 0;
else if(cnt==(MAX-1)/2) //这样写就使得0-3为高电平,4-8为低电平
clk_div1 <= 0;
else if(cnt==(MAX-1))
clk_div1 <= 1;
end
always@(negedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div2 <= 0;
else if(cnt==(MAX-1)/2)
clk_div2 <= 0;
else if(cnt==(MAX-1))
clk_div2 <= 1;
end
assign clk_out = clk_div1 & clk_div2 ;
endmodule
利用时钟的双边沿逻辑,可以对时钟进行半整数的分频。半整数分频的占空比不可能是50%。
例如5.5分频,2.75个周期为高电平,2.75个周期为低电平,不可能实现占空比为50%。
module banzhengshushu_div(
input clk_in,
input rst_n,
output clk_out
);
parameter MAX = 11;
reg [3:0] cnt;
reg clk_div1, clk_div2;
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else
cnt <= (cnt==MAX-1) ? 0 : cnt+1;
end
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div1 <= 0;
else if(cnt==0)
clk_div1 <= 1;
else if(cnt==2)
clk_div1 <= 0;
else if(cnt==6)
clk_div1 <= 1;
else if(cnt==8)
clk_div1 <= 0;
end
always@(negedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div2 <= 0;
else if(cnt==1)
clk_div2 <= 1;
else if(cnt==3)
clk_div2 <= 0;
else if(cnt==6)
clk_div2 <= 1;
else if(cnt==8)
clk_div2 <= 0;
end
assign clk_out = clk_div1 | clk_div2 ;
endmodule
画波形图然后写的代码,可以实现接近50%占空比的5.5分频(11个半周期,5个高电平,6个低电平)
先画出目标波形,再调整clk1和clk2。(从计数值1开始画波形比较方便写代码)
下面代码这样写,占空比就不是接近50%,但是是5.5分频
always@(posedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div1 <= 0;
else if(cnt==0)
clk_div1 <= 1;
else if(cnt==(MAX+1)/2)
clk_div1 <= 1;
else
clk_div1 <= 0;
end
always@(negedge clk_in or negedge rst_n)begin
if(!rst_n)
clk_div2 <= 0;
else if(cnt==1)
clk_div2 <= 1;
else if(cnt==(MAX+1)/2)
clk_div2 <= 1;
else
clk_div2 <= 0;
end
8.7分频 :1个目标周期 = 8.7个原周期
相当于 :87个原周期 = 10个目标周期,去计算除有多少个8分频和9分频
module div_M_N(
input wire clk_in,
input wire rst,
output wire clk_out
);
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
reg [6:0]cnt0; //总计数器0-86
reg [3:0]cnt1; //分频计数器
reg flag; //时钟分频切换标志
reg clk_out_r;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt0<=0;
else
cnt0<=(cnt0==M_N-1)? 0 : cnt0+1;
end
always@(posedge clk_in or negedge rst)begin
if(!rst)
flag<=0;
else
flag<=(cnt0==c89-1||cnt0==M_N-1)? ~flag : flag;
end
//flag=0,八分频,flag=1,九分频
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt1<=0;
else if(!flag)
cnt1<=(cnt1==div_e-1) ? 0 : cnt1+1;
else
cnt1<=(cnt1==div_o-1) ? 0 : cnt1+1;
end
always@(posedge clk_in or negedge rst)begin
if(!rst)
clk_out_r<=0;
else if(!flag)
clk_out_r<=(cnt1<4) ? 1 : 0;
else
clk_out_r<=(cnt1<4) ? 1 : 0;
end
assign clk_out=clk_out_r;
endmodule
对于绝大多数的触发器,其实只需要用到时钟的上升沿触发,很少用到下降沿。在这种情况下,只要上升沿和时钟频率有关系,什么时候来下降沿不重要。所以50%的占空比不是必须的。
因此在小数分频器中关注的是得到一个尽量均匀的分频信号,而不是得到一个绝对50%占空比的分频信号。
module jishu_div_testbench ();
reg clk_in;
reg rst_n;
wire clk_out;
jishu_div jishu_div_i(
.clk_in(clk_in),
.rst_n(rst_n),
.clk_out(clk_out)
);
initial begin
clk_in = 0;
rst_n = 0;
//q = 0;
#10;
rst_n = 1;
end
always #5 clk_in = ~clk_in;
endmodule