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%2 522%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_ran k_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 が生成されます。

カウンタは 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