FPGA入门程序(2)——多路分频器


将原始时钟分为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分频

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页