Verilog 编程——奇偶分频(50%占空)



引言

最近准备一些笔试面试,想再把时钟奇偶分频的再整理一下。

我之前写过一个PWM产生的模块,里面有任意频率/占空比的时钟生成。可以参考:

基于FPGA的PWM发生器设计icon-default.png?t=N3I4https://blog.csdn.net/qq_43045275/article/details/128365705?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168362558616800227492685%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168362558616800227492685&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-128365705-null-null.blog_rank_default&utm_term=PWM&spm=1018.2226.3001.4450


偶数分频

以4分频为例,时序图:

假设时钟分频系数:P_DIV_EVEN

那么偶数分频是在,计数器的计数值 = P_DIV_EVEN / 2 - 1 时,输出时钟翻转即可


// ========================================================================
// 功能描述:-1- 偶数倍时钟分频,50%占空比
// 作者:Xu Y. B.
// 时间:2023-05-09
// ========================================================================

`timescale 1ns / 1ps
module clk_div_even #(
parameter P_DIV_EVEN = 4
)(
input				 clk,										
input				 rstn,										
output 		reg		 clk_div										
);

localparam N = (P_DIV_EVEN)/2;

reg [$clog2(P_DIV_EVEN)-1:0] r_cnt;

always @ (posedge clk)
begin
	if(~rstn)
	begin
		r_cnt <= 0;
		clk_div <= 0;
	end
	else if(r_cnt == (N-1))
	begin
		r_cnt <= 0;
		clk_div <= ~clk_div;
	end
	else
	begin
		r_cnt <= r_cnt + 1;
	end
end

endmodule

奇数分频

以5分频为例,时序图:

假设时钟分频系数:P_DIV_ODD

参数 N = (P_DIV_ODD - 1)/2

其中clk_div为最终输出时钟,clk1和clk2为辅助时钟生成。

计数器counter由0计数至(M-1)。

  • clk1在clk_in的上升延跳变,条件是r_cnt==(N-1)或(P_DIV_ODD-1)。

  • clk2在clk_in的下降延跳变,条件是r_cnt==(N-1)或(P_DIV_ODD-1)。

  • 之后 clk_div = clk1 & clk2 即可得到对应奇数分频的时钟。


// ========================================================================
// 功能描述:-1- 奇数倍时钟分频,50%占空比
// 作者:Xu Y. B.
// 时间:2023-05-09
// ========================================================================

`timescale 1ns / 1ps
module clk_div_odd #(
parameter P_DIV_ODD = 3
)(
input				 clk,										
input				 rstn,										
output 				 clk_div										
);

localparam N = (P_DIV_ODD-1)/2;

reg [$clog2(P_DIV_ODD)-1:0] r_cnt;

reg 						clk1;
reg 						clk2;

// 原始时钟计数
always @ (posedge clk)
begin
	if(~rstn)
	begin
		r_cnt <= 0;
	end
	else if(r_cnt == (P_DIV_ODD-1))
	begin
		r_cnt <= 0;
	end
	else
	begin
		r_cnt <= r_cnt + 1;
	end
end

// 上升沿触发
always @ (posedge clk)
begin
	if(~rstn)
	begin
		clk1 <= 0;
	end
	else if(r_cnt == (N-1))
	begin
		clk1 <= ~clk1;
	end
	else if(r_cnt == (P_DIV_ODD-1))
	begin
		clk1 <= ~clk1;
	end
end

// 下降沿触发
always @ (negedge clk)
begin
	if(~rstn)
	begin
		clk2 <= 0;
	end
	else if(r_cnt == (N-1))
	begin
		clk2 <= ~clk2;
	end
	else if(r_cnt == (P_DIV_ODD-1))
	begin
		clk2 <= ~clk2;
	end
end

assign clk_div = clk1&clk2;

endmodule

仿真代码


// ========================================================================
// 功能描述:-1- 仿真测试模块 clk_div_odd , clk_div_even 功能
// 作者:Xu Y. B.
// 时间:2023-05-09
// ========================================================================

`timescale 1ns / 1ps
module tb_clk_div();
parameter P_DIV_EVEN = 4;
parameter P_DIV_ODD = 5;
reg					 clk;										
reg					 rstn;										
wire 				 o_clk_div_odd;
wire 				 o_clk_div_even;

initial clk = 0;
always #5 clk = ~clk;

initial
begin
	rstn = 0;
	#1034;
	@(posedge clk)
	rstn <= 1;
	#2987;
	$stop;
end

clk_div_odd  #(.P_DIV_ODD(P_DIV_ODD)) INST_clk_div_odd (.clk(clk), .rstn(rstn), .clk_div(o_clk_div_odd));
clk_div_even #(.P_DIV_EVEN(P_DIV_EVEN)) INST_clk_div_even (.clk(clk), .rstn(rstn), .clk_div(o_clk_div_even));

endmodule

仿真结果:

猜你喜欢

转载自blog.csdn.net/qq_43045275/article/details/130585199
今日推荐