序章
最近、筆記試験の面接をいくつか準備しましたが、クロックの奇数と偶数の周波数分割を再編成したいと考えています。
以前、任意の周波数/デューティ サイクルのクロックを生成する PWM 生成モジュールを作成したことがあります。以下を参照できます。
偶数周波数分割
周波数を 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
シミュレーション結果: