时钟分频
前言,本专题属于verilog手撕专题中的一节,思维导图如下,其他专题请见导航
2^n时钟分频
module div_4
(
input clk,
input rst_n,
output reg clk_out
);
reg clk_div2;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_div2 <= 1'b0;
else
clk_div2 <= ~clk_div2;
end
always @(posedge clk_div2 or negedge rst_n)begin
if(!rst_n)
clk_out <= 1'b0;
else
clk_out <= ~clk_out;
end
endmodule
偶数时钟分频
module div_clk #(parameter DIV = 10)
(
input clk,
input rst_n,
output reg clk_out
);
reg [$clog2(DIV)-1:0] cnt;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 1'b0;
end
else if (cnt == DIV/2 - 1)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_out <= 1'b0;
else if (cnt == DIV/2 - 1)
clk_out <= ~clk_out;
end
endmodule
// ----------------------- testbench -------------------------
`timescale 1ns/1ps
module tb;
reg clk, rst_n;
initial begin
clk = 1'b0;
#5 clk = 1'b1;
forever #5 clk = ~clk;
end
initial begin
rst_n = 1'b0;
#9 rst_n = 1'b1;
#600
$finish;
end
wire clkout;
div_clk #(.DIV(8)) dut (clk, rst_n, clkout);
initial begin
$dumpfile("showmebug.vcd");
$dumpvars(0, tb);
end
endmodule
奇数时钟分频
module div_clk #(
parameter DIV = 7
)(
input clk,
input rst_n,
output clkout
);
reg [$clog2(DIV)-1:0] cnt;
reg clk_p, clk_n;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 1'b0;
else if (cnt == DIV - 1)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_p <= 1'b0;
else if (cnt == DIV - 1)
clk_p <= 1'b0;
else if (cnt == ((DIV >> 1) - 1))
clk_p <= 1'b1;
end
always @(negedge clk or rst_n)begin
if(!rst_n)
clk_n <= 1'b0;
else if (cnt == (DIV - 1))
clk_n <= 1'b0;
else if (cnt == ((DIV >> 1) - 1))
clk_n <= 1'b1;
end
assign clkout = clk_n | clk_p;
endmodule
// ----------------------- testbench -------------------------
`timescale 1ns/1ps
module tb;
reg clk, rst_n;
initial begin
clk = 1'b0;
#5 clk = 1'b1;
forever #5 clk = ~clk;
end
initial begin
rst_n = 1'b0;
#9 rst_n = 1'b1;
#600
$finish;
end
wire clkout;
div_clk #(.DIV(7)) dut (clk, rst_n, clkout);
initial begin
$dumpfile("showmebug.vcd");
$dumpvars(0, tb);
end
endmodule
0.5小数时钟分频
module half_divisor(
input rstn ,
input clk,
output clk_div3p5
);
//计数器
parameter MUL2_DIV_CLK = 7 ;
reg [3:0] cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (cnt == MUL2_DIV_CLK-1) begin //计数2倍分频比
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
reg clk_ave_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clk_ave_r <= 1'b0 ;
end
//first cycle: 4 source clk cycle
else if (cnt == 0) begin
clk_ave_r <= 1 ;
end
//2nd cycle: 3 source clk cycle
else if (cnt == (MUL2_DIV_CLK/2)+1) begin
clk_ave_r <= 1 ;
end
else begin
clk_ave_r <= 0 ;
end
end
//adjust
reg clk_adjust_r ;
always @(negedge clk or negedge rstn) begin
if (!rstn) begin
clk_adjust_r <= 1'b0 ;
end
//本次时钟只为调整一致的占空比
else if (cnt == 1) begin
clk_adjust_r <= 1 ;
end
//本次时钟只为调整一致的精确分频比
else if (cnt == (MUL2_DIV_CLK/2)+1 ) begin
clk_adjust_r <= 1 ;
end
else begin
clk_adjust_r <= 0 ;
end
end
assign clk_div3p5 = clk_adjust_r | clk_ave_r ;
endmodule
任意小数分频
module frac_divisor
#(
parameter SOURCE_NUM = 76 , //cycles in source clock
parameter DEST_NUM = 10 //cycles in destination clock
)
(
input rstn ,
input clk,
output clk_frac
);
//7分频参数、8分频参数、次数差值
parameter SOURCE_DIV = SOURCE_NUM/DEST_NUM ;
parameter DEST_DIV = SOURCE_DIV + 1;
parameter DIFF_ACC = SOURCE_NUM - SOURCE_DIV*DEST_NUM ;
reg [3:0] cnt_end_r ; //可变分频周期
reg [3:0] main_cnt ; //主计数器
reg clk_frac_r ; //时钟输出,高电平周期数为1
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
main_cnt <= 'b0 ;
clk_frac_r <= 1'b0 ;
end
else if (main_cnt == cnt_end_r) begin
main_cnt <= 'b0 ;
clk_frac_r <= 1'b1 ;
end
else begin
main_cnt <= main_cnt + 1'b1 ;
clk_frac_r <= 1'b0 ;
end
end
//输出时钟
assign clk_frac = clk_frac_r ;
//差值累加器使能控制
wire diff_cnt_en = main_cnt == cnt_end_r ;
//差值累加器逻辑
reg [4:0] diff_cnt_r ;
wire [4:0] diff_cnt = diff_cnt_r >= DEST_NUM ?
diff_cnt_r -10 + DIFF_ACC :
diff_cnt_r + DIFF_ACC ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
diff_cnt_r <= 0 ;
end
else if (diff_cnt_en) begin
diff_cnt_r <= diff_cnt ;
end
end
//分频周期变量的控制逻辑
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt_end_r <= SOURCE_DIV-1 ;
end
//差值累加器溢出时,修改分频周期
else if (diff_cnt >= 10) begin
cnt_end_r <= DEST_DIV-1 ;
end
else begin
cnt_end_r <= SOURCE_DIV-1 ;
end
end
endmodule
标签:分频,begin,clk,cnt,verilog,input,reg,时钟
From: https://www.cnblogs.com/pu1se/p/16583686.html