计数器奇数分频的问题
markdown
i_clk= x HZ
o_clk_div 是 i_clk 的 n 分频
n个i_clk周期 对应 o_clk_div的一个周期
n=2k 时 ,o_clk_div 有k个高,k个低
n=2k+1 时,o_clk_div 有 k个高 或 k+1个高
简单计数分频仅适用偶分频 clock_div.v
verilog
`timescale 1ns / 1ps
module clock_div#(
parameter P_CLK_DIV_CNT = 2 //MAX = 65535
)(
input i_clk ,
input i_rst_n ,
output o_clk_div
);
reg ro_clk_div ;
reg [15:0] r_cnt ;
assign o_clk_div = ro_clk_div;
localparam L_COMPARE_CNT = P_CLK_DIV_CNT/2 - 1;
always @(posedge i_clk or negedge i_rst_n)begin
if(!i_rst_n)
r_cnt <= 'd0;
else if(r_cnt == L_COMPARE_CNT)
r_cnt <= 'd0;
else
r_cnt <= r_cnt + 1;
end
always @(posedge i_clk or negedge i_rst_n)begin
if(!i_rst_n)
ro_clk_div <= 'd0;
else if(r_cnt == L_COMPARE_CNT)
ro_clk_div <= ~ro_clk_div;
else
ro_clk_div <= ro_clk_div;
end
endmodule
双边补充 clk_div_50duty.v
verilog
module clk_div_50duty #(
parameter integer P_CLK_DIV_CNT = 3 // N ≥ 2
)(
input wire i_clk, // 输入时钟
input wire i_rst_n, // 同步复位,高有效
output wire o_clk_div // 输出分频时钟,占空比约 50%
);
// ----------------------------------------
// 判定奇偶
// ----------------------------------------
localparam L_IS_ODD = P_CLK_DIV_CNT[0]; // 奇数为 1,偶数为 0
localparam L_HALF_CNT = (P_CLK_DIV_CNT >> 1); // N/2,用于偶数情况
// ----------------------------------------
// 偶数分频逻辑
// ----------------------------------------
reg [$clog2(P_CLK_DIV_CNT):0] r_cnt_even = 0;
reg r_clk_even = 0;
always @(posedge i_clk) begin
if (!i_rst_n) begin
r_cnt_even <= 0;
r_clk_even <= 0;
end else if (!L_IS_ODD) begin
if (r_cnt_even == L_HALF_CNT - 1) begin
r_cnt_even <= 0;
r_clk_even <= ~r_clk_even;
end else begin
r_cnt_even <= r_cnt_even + 1;
end
end
end
// ----------------------------------------
// 奇数分频逻辑(双沿互补法)
// ----------------------------------------
reg [$clog2(P_CLK_DIV_CNT):0] r_cnt_odd = 0;
reg r_clk1 = 1, r_clk2 = 1;
// 上升沿:clk1
always @(posedge i_clk) begin
if (!i_rst_n) begin
r_cnt_odd <= 0;
r_clk1 <= 1;
end else if (L_IS_ODD) begin
if (r_cnt_odd == P_CLK_DIV_CNT - 1)
r_cnt_odd <= 0;
else
r_cnt_odd <= r_cnt_odd + 1;
if (r_cnt_odd == (P_CLK_DIV_CNT >> 1))
r_clk1 <= 0;
else if (r_cnt_odd == P_CLK_DIV_CNT - 1)
r_clk1 <= 1;
end
end
// 下降沿:clk2
always @(negedge i_clk) begin
if (!i_rst_n)
r_clk2 <= 1;
else if (L_IS_ODD) begin
if (r_cnt_odd == (P_CLK_DIV_CNT >> 1))
r_clk2 <= 0;
else if (r_cnt_odd == P_CLK_DIV_CNT - 1)
r_clk2 <= 1;
end
end
// ----------------------------------------
// 输出选择
// ----------------------------------------
assign o_clk_div = (L_IS_ODD) ? (r_clk1 & r_clk2) : r_clk_even;
endmodule
tb.v
verilog
`timescale 1ns / 1ps
module tb;
// ---------------------------------
// 参数:分频系数
// 可尝试 3(奇数),4(偶数),5(奇数)等
// ---------------------------------
parameter P_CLK_DIV_CNT = 2;
// ---------------------------------
// 信号声明
// ---------------------------------
reg i_clk;
reg i_rstn;
wire o_clk_div;
// ---------------------------------
// 实例化被测模块
// ---------------------------------
clk_div_50duty #(
.P_CLK_DIV_CNT(P_CLK_DIV_CNT)
) dut (
.i_clk (!i_clk),
.i_rst_n (i_rstn),
.o_clk_div (o_clk_div)
);
// ---------------------------------
// 生成时钟:50MHz (周期 = 20ns)
// ---------------------------------
initial i_clk = 0;
always #10 i_clk = ~i_clk;
// ---------------------------------
// 复位过程
// ---------------------------------
initial begin
i_rst_n = 0;
#100; // 保持复位 100ns
i_rst_n = 1;
end
// ---------------------------------
// 仿真时间控制
// ---------------------------------
initial begin
$display("===== Start clk_div_50duty simulation (N = %0d) =====", P_CLK_DIV_CNT);
#2000; // 仿真 2000ns
$display("===== End clk_div_50duty simulation =====");
$stop;
end
endmodule