目录
在数字电路中,使用 Verilog 生成不同频率的时钟和占空比是较为常见的一种设计,主要分为偶数分频,奇数分频,也可以任意进行分频和占空比的配置;
1、偶分频
偶分频电路指的是分频系数为 2、4、6、8 ... 等偶数整数的分频电路,这种是比较简单的分频方式
例如下面 divider.v 中,对输入时钟进行6分频,即假设clk 为 50MHz ,分频后的时钟频率为 (50/6) MHz。程序如下:
//rtl
module clk_even_div(
input clk,
input rst_n,
output reg clk_div
);
parameter NUM_DIV = 6;
reg [3:0]cnt;
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
cnt <= 4'd0;
clk_div <= 1'b0;
end
else if(cnt < NUM_DIV / 2 - 1) begin
cnt <= cnt + 1'b1;
clk_div <= clk_div;
end
else begin
cnt <= 4'd0;
clk_div <= ~clk_div;
end
endmodule
testbench为:
//testbench
`timescale 1ns / 1ps
module testb_div_even;
// Inputs
reg clk;
reg rst_n;
// Outputs
wire clk_div;
// Instantiate the Unit Under Test (UUT)
clk_even_div uut (
.clk(clk),
.rst_n(rst_n),
.clk_div(clk_div)
);
always #10 clk = ~clk;
initial begin
// Initialize Inputs
clk = 0;
rst_n = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
rst_n = 1;
end
endmodule
2、奇分频
由于奇分频需要保持分频后的时钟占空比为 50% ,所以不能像偶分频那样直接在分频系数的一半时使时钟信号翻转(高电平一半,低电平一半)。
接下来我们设计一个 5 分频的模块,设计思路如下:
采用计数器 cnt1 进行计数,在时钟上升沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div1。
采用计数器 cnt2 进行计数,在时钟下降沿进行加 1 操作,计数器的值为 0、1 时,输出时钟信号 clk_div 为高电平;计数器的值为2、3、4 时,输出时钟信号 clk_div 为低电平,计数到 5 时清零,从头开始计数。我们可以得到占空比为 40% 的波形 clk_div2。
clk_div1 和clk_div2 的上升沿到来时间相差半个输入周期,所以将这两个信号进行或操作,即可得到占空比为 50% 的5分频时钟。程序如下:
设计代码:
module clk_odd_div(
input clk,
input rst_n,
output clk_div
);
parameter NUM_DIV = 5;
reg[2:0] cnt1;
reg[2:0] cnt2;
reg clk_div1, clk_div2;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt1 <= 0;
else if(cnt1 < NUM_DIV - 1)
cnt1 <= cnt1 + 1'b1;
else
cnt1 <= 0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_div1 <= 1'b1;
else if(cnt1 < NUM_DIV / 2)
clk_div1 <= 1'b1;
else
clk_div1 <= 1'b0;
always @(negedge clk or negedge rst_n)
if(!rst_n)
cnt2 <= 0;
else if(cnt2 < NUM_DIV - 1)
cnt2 <= cnt2 + 1'b1;
else
cnt2 <= 0;
always @(negedge clk or negedge rst_n)
if(!rst_n)
clk_div2 <= 1'b1;
else if(cnt2 < NUM_DIV / 2)
clk_div2 <= 1'b1;
else
clk_div2 <= 1'b0;
assign clk_div = clk_div1 | clk_div2;
endmodule
testbench 为;
//testbench
`timescale 1ns / 1ps
module testb_div_odd;
// Inputs
reg clk;
reg rst_n;
// Outputs
wire clk_div;
// Instantiate the Unit Under Test (UUT)
clk_odd_div uut (
.clk(clk),
.rst_n(rst_n),
.clk_div(clk_div)
);
always #10 clk = ~clk;
initial begin
// Initialize Inputs
clk = 0;
rst_n = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
rst_n = 1;
end
endmodule
绿色部分是输入的时钟,红色的部分是输出的 5 分频的时钟;
黄色和白色部分是 clk_div1 和 clk_div2
3、任意分频和占空比
获取的任意占空比和分频系数的方式是通过直接进行输入 clk 的值进行计数,在计数器到达某时刻,直接进行输出时钟的 0/1 翻转控制;
示例代码为:
module clk_div(
input clk,
input rst_n,
output reg clk_div
);
reg[5:0] cnt;
parameter MAX_CNT = 8;
parameter TOG_CNT = 2;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else if(cnt < MAX_CNT)
cnt <= cnt + 1'b1;
else
cnt <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_div <= 1'b0;
else if(cnt < TOG_CNT)
clk_div <= 1'b1;
else
clk_div <= 1'b0;
testbench 为:
module clk_div;
// Inputs
reg clk;
reg rst_n;
// Outputs
wire clk_div;
// Instantiate the Unit Under Test (UUT)
clk_div uut (
.clk(clk),
.rst_n(rst_n),
.clk_div(clk_div)
);
always #10 clk = ~clk;
initial begin
// Initialize Inputs
clk = 0;
rst_n = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
rst_n = 1;
end
endmodule
黄色部分为分频出来的时钟;
参考: