将原始时钟分为2分频、4分频、8分频、3分频,以及2hz分频。即将原始时钟周期分别2倍、4倍、8倍、3倍与2hz分频,本次例程尝试了例化调用,将各种分频单独写成小模块。
代码设计
1、顶层代码
module Clk_Divider# //模块名 Clk_Divider,加#是可以提供给外部调用的参数
(
parameter DEBUG_ENABLE = 1'b1-1'b0
)
(
input clk_p,
input clk_n,
input rst_n_i,
output div2_o,
output div3_o,
output div4_o,
output div8_o,
output div2hz_o
);
wire clk_i;
#对差分时钟采用IBUFGDS IP核去转换
IBUFGDS CLK_U(
.I(clk_p),
.IB(clk_n),
.O(clk_i)
);
#///调用2分频模块
div_2 u_div_2(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.div2_o_r(div2_o_r)
);
#///调用4分频模块
div_4 u_div_4(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.div4_o_r(div4_o_r)
);
#///调用3分频模块
div_3 u_div_3(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.div3_o_r(div3_o_r)
);
#///调用8分频模块
div_8 u_div_8(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.div8_o_r(div8_o_r)
);
#///调用2hz分频模块
div_2hz u_div_2hz(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.div2hz_o_r(div2hz_o_r)
);
assign div2_o = div2_o_r;
assign div3_o = div3_o_r;
assign div4_o = div4_o_r;
assign div8_o = div8_o_r;
assign div2hz_o = div2hz_o_r;
#///调用ila IP核,在线调试
generate if(DEBUG_ENABLE == 1'b1) begin : debugcore
//添加ila IP ,Chipscope观察信号
ila_0 ila_0_0 (
.clk(clk_i), // input wire clk
.probe0(div2hz_o), // input wire [0:0] probe0
.probe1({div2_o,div3_o,div4_o,div8_o}) // input wire [3:0] probe1
);
end
endgenerate
endmodule
2、2分频模块
module div_2(
input clk_i,
input rst_n_i,
output div2_o_r
);
reg div2_o_r;
//2分频代码:只要基于源时钟每个时钟的上升沿对div2_o_r寄存器取反
always@(posedge clk_i)begin //时钟上升沿进行
if(!rst_n_i) //复位低电平时
div2_o_r <= 1'b0; //div2_o_r赋值为0
else
div2_o_r <= ~div2_o_r; //复位高电平时,逢上升沿取反操作,将周期二倍化
end
endmodule
3、4/8分频模块
module div_4(
input clk_i,
input rst_n_i,
output div4_o_r
);
reg [1:0] div_cnt1; //设置计数器div_cnt1
//4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'b10的时候对div4_o_r寄存器取反;
always@(posedge clk_i)begin //时钟上升沿进行
if(!rst_n_i) //复位低电平时
div_cnt1 <= 2'b00; //div_cnt1赋值为0
else
div_cnt1 <= div_cnt1+1'b1; //复位高电平时,逢上升沿div_cnt1+1操作
end
reg div4_o_r;
always@(posedge clk_i)begin //时钟上升沿进行
if(!rst_n_i) //复位低电平时
div4_o_r <= 1'b0; //div4_o_r赋值为0
else if(div_cnt1==2'b00 || div_cnt1==2'b10) //div_cnt1为b00或b10时
div4_o_r <= ~div4_o_r; //div4_o_r取反
else
div4_o_r <= div4_o_r;
end
endmodule
逻辑分析:div_cnt1值循环取00,01,10,11~00,01,10,11,由于div_cnt1变化一次,时钟走两个周期,变化两次走四个周期,即div_cnt1变化两次可实现时钟周期四倍化,取b00或b10时令div4_o_r取反一次。
八分频同理,div_cnt1变化四次,令div8_o_r取反一次,取div_cnt1每次为b00时取反。
4、3分频模块
3分频的本质是我们需要在每次1.5倍的时钟周期的时候实现3分频寄存器的翻转(实现3倍周期),但是我们无法直接实现1.5倍的分频。因此采取分别采取2个计数器pos_cnt和neg_cnt,分别对上升沿和下降沿计数。计数周期是0-1-2,共计3个时钟周期。
module div_3(
input clk_i,
input rst_n_i,
output div3_o_r
);
reg [1:0] pos_cnt;
reg [1:0] neg_cnt;
always@(posedge clk_i)begin //时钟上升沿进行
if(!rst_n_i) //复位低电平时
pos_cnt <= 2'b00; //pos_cnt赋值为0
else if(pos_cnt == 2'd2) //复位高电平,且pos_cnt为2时
pos_cnt <= 2'b00; //pos_cnt重新赋值为0
else
pos_cnt <= pos_cnt + 1'b1; //复位高电平,且pos_cnt小于2时,实现pos_cnt+1操作
end
always@(negedge clk_i)begin
if(!rst_n_i)
neg_cnt <= 2'b00;
else if(neg_cnt == 2'd2)
neg_cnt <= 2'b00;
else
neg_cnt <= neg_cnt + 1'b1;
end //逻辑同pos_cnt,时钟下降沿触发
reg div3_o_r0;
reg div3_o_r1;
always@(posedge clk_i)begin
if(!rst_n_i)
div3_o_r0 <= 1'b0;
else if(pos_cnt < 2'd1)
div3_o_r0 <= 1'b1;
else
div3_o_r0 <= 1'b0;
end //pos_cnt == 2'd0的时候div3_o_r0输出高电平
always@(negedge clk_i)begin
if(!rst_n_i)
div3_o_r1 <= 1'b0;
else if(neg_cnt < 2'd1)
div3_o_r1 <= 1'b1;
else
div3_o_r1 <= 1'b0;
end //neg_cnt == 2'd0的时候div3_o_r1输出高电平
assign div3_o_r= div3_o_r0 | div3_o_r1; //在div3_o_r0与div3_o_r1相位相差180°条件下,两个1周期高电平相或即得1.5倍,即3倍周期
endmodule
5、2hz分频模块
module div_2hz(
input clk_i,
input rst_n_i,
output div_2hz_o_r
);
reg div_2hz_o_r;
reg [25:0] div_2hz_cnt; //2hz计数值为0~25_000_000变化
wire ms250_en = (div_2hz_cnt == 32'd25_000_000 - 1'b1); //2hz计数值为25_000_000时令使能为1
always@(posedge clk_i)
begin
if(!rst_n_i)
div_2hz_cnt <= 0;
else if(div_2hz_cnt < 32'd25_000_000 - 1'b1)
div_2hz_cnt <= div_2hz_cnt + 1'b1;
else
div_2hz_cnt <= 0;
end //2hz计数值在0~25_000_000之间变化
always@(posedge clk_i)
begin
if(!rst_n_i)
div_2hz_o_r <= 0;
else if(ms250_en)
div_2hz_o_r <= ~div_2hz_o_r;
else
div_2hz_o_r <= div_2hz_o_r;
end //2hz计数值每到25_000_000时,div_2hz_o_r输出翻转一次
endmodule
关于计数值25_000_000的来源:频率2hz即周期为0.5s,即0.25s时钟翻转一次,开发版时钟为100Mhz,即时钟周期为10ns,2hz计数值每上升沿+1,故有0.25s/10ns为25_000_000。
仿真设计
仿真代码
module Clk_Divider_tb();
// Inputs
reg clk_i;
reg rst_n_i;
// Outputs
wire div2_o;
wire div3_o;
wire div4_o;
wire div8_o;
wire div2hz_o;
// Instantiate the Unit Under Test (UUT)
Clk_Divider#(
.DEBUG_ENABLE(1'b0),
.REF_CLK(100000000)
)
Clk_Divider
(
.clk_p(clk_i),
.clk_n(~clk_i),
.rst_n_i(rst_n_i),
.div2_o(div2_o),
.div3_o(div3_o),
.div4_o(div4_o),
.div8_o(div8_o),
.div2hz_o(div2hz_o)
);
initial begin
clk_i = 0;
rst_n_i = 0;
#100; // Wait 100ns
rst_n_i=1;
end
always #5 clk_i=~clk_i;
endmodule
仿真结果
解释三分频仿真结果,上升沿计数和下降沿计数显示高位,即为1时实际计数值为2,紧接着是0和1,上升沿对着计数0,此时div3_o_r0拉高,稍有延时;同理下降沿对计数0时,div3_o_r1拉高。
实现2hz分频,LED等0.5s闪烁。
2hz分频