解题思路:

    题目要求实现任意奇数倍数的分频,实现分频的基本方法是采用计数器,使用输入时钟信号驱动计数器。例如实现偶数倍的n分频时,每当计数器从0计数到n/2 - 1时,输出时钟信号跳变,同时计数器归零从新开始计数。

题目要求的是奇数倍分频,且要求占空比为50%,则需要稍加调整:以五分频为例,需要输出时钟信号保持2.5个输入时钟的1,然后保持2.5个时钟的0。比如在第一个时钟的上升沿跳变为1,则在第三个时钟的下降沿跳变为0,在第六个时钟的上升沿再一次跳变为1。即需要对上升/下降两个时钟沿的进行计数。

由此得出实现奇数倍数分频的方法:加入两个中间信号flag_1,flag_2,分别使用输入时钟信号的上升/下降沿驱动。以五分频为例,flag_1的变化由上升沿驱动,flag_2的变化由下降沿驱动。当计数器计数到(n-1)/2=2和(n-1)=4时,flag_1、flag_2发生跳变(即取反)。再将flag_1,flag_2相或即可得到分频之后的时钟信号。

如下图所示。红色框分别为flag_1,flag_2,持续两个时钟,相或得到蓝色部分刚好占2.5个时钟,实现五分频。

 

解题过程:

    在模块声明时定义分频倍数dividor为参数,以便在不同场景下例化,可以修改分频数。

定义参数的符号为‘#”:

module clk_divider

#(parameter dividor = 5)

{     input clk_in,

    input rst_n,

    output clk_out

};

 

    然后声明一个计数器变量,用来指示flag_1,flag_2的跳变。计数器变量的位宽,依据分频数确定,可以使用$clog2()实现,$clog2()为取对数操作,在编译过程中执行完成。因此在模块运行过程中CNT_WIDTH是一个确定的数值。

    parameter CNT_WIDTH = $clog2(dividor-1);    

reg [CNT_WIDTH :0] cnt

    根据上文的分析,当计数器计数到n-1时,计数器归零。

always @(posedge clk_in or negedge rst_n)

    if (!rst_n)

        cnt <= 0;

    else (cnt == divior-1)

        cnt <= 0;

    else cnt <= cnt + 1'd1;

 

    然后声明两个标志信号,flag_1,flag_2。分别由clk_in的上升沿和下降沿驱动。分别在(divior-1)/2和divior-1时跳变

    reg flag_1;

reg flag_2;

always @(posedge clk_in or negedge rst_n)

    if (!rst_n)

        flag_1 <= 0;

    else if(cnt == (dividor-1>>1))

        flag_1 <= ~flag_1;

    else if(cnt == dividor-1)

        flag_1 <= ~flag_1;        

    else flag_1 <= flag_1;

    

always @(negedge clk_in or negedge rst_n)

    if (!rst_n)

        flag_2 <= 0;

    else if(cnt == (dividor-1 >>1))

        flag_2 <= ~flag_2;

    else if(cnt == dividor-1)

        flag_2 <= ~flag_2;

    else flag_2 <= flag_2;

最后将flag_1,flag_2取或,赋值给clk_out。

    assign clk_out = flag_1 || flag_2;

 

仿真结果:

五分频结果:

 

九分频结果:

 

可以看出在不同奇数分频下,都可以正常实现功能。

 参考答案
`timescale 1ns/1ns

module clk_divider
    #(parameter dividor = 5)
( 	input clk_in,
	input rst_n,
	output clk_out
);
//定义计数器的位宽,$clog2()为取对数操作,在编译过程中执行完成。因此在模块运行过程中CNT_WIDTH是一个确定的数值。
parameter CNT_WIDTH = $clog2(dividor-1);	

reg flag_1;
reg flag_2;
reg [CNT_WIDTH :0] cnt;

always @(posedge clk_in or negedge rst_n)
	if (!rst_n)
		cnt <= 0;
	else if(cnt == dividor-1)
		cnt <= 0;
	else cnt <= cnt + 1'd1;
	
always @(posedge clk_in or negedge rst_n)
	if (!rst_n)
		flag_1 <= 0;
	else if(cnt == (dividor-1>>1))
		flag_1 <= ~flag_1;
	else if(cnt == dividor-1)
		flag_1 <= ~flag_1;		
	else flag_1 <= flag_1;
	
always @(negedge clk_in or negedge rst_n)
	if (!rst_n)
		flag_2 <= 0;
	else if(cnt == (dividor-1 >>1))
		flag_2 <= ~flag_2;
	else if(cnt == dividor-1)
		flag_2 <= ~flag_2;
	else flag_2 <= flag_2;	
	
	assign clk_out = flag_1 || flag_2;
endmodule



如果关于此次题单有问题或者反馈意见,欢迎加入牛客用户反馈群沟通~