解题思路:
题目要求实现任意奇数倍数的分频,实现分频的基本方法是采用计数器,使用输入时钟信号驱动计数器。例如实现偶数倍的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
如果关于此次题单有问题或者反馈意见,欢迎加入牛客用户反馈群沟通~