在fpga 设计中, 我们有时需要对输入的时钟进行分频。 例如:1/2,1/3,1/4,1/5 等等。在偶数分频时,分频逻辑比较容易实现,在奇数分频时,fpga 设计相对麻烦一些。
偶数分频逻辑设计:
even_div.v
module even_div #
(
parameter [31:0] N = 2
)
(
input clk,
output clk_out,
input reset
);
reg [31:0] cnt;
always @(posedge clk or posedge reset)
begin
if(reset)
cnt <= 0;
else if(cnt == N - 1)
cnt <= 0;
else cnt <= cnt + 1;
end
reg clk_reg;
always @(posedge clk or posedge reset)
begin
if(reset)
clk_reg <= 0;
else if(cnt == N/2 - 1)
clk_reg <= ~clk_reg;
else if(cnt == N - 1)
clk_reg <= ~clk_reg;
end
assign clk_out = clk_reg;
endmodule
偶数分频相对比较简单, 程序中使用cnt 作为计数器, 当计数器计数到分频值的一半时(N/2), clk 反转; 当计数器计数到分频值(N)时, clk 再次反转。
odd_div.v
module odd_div #
(
parameter [31:0] N = 3
)
(
input clk,
output clk_out,
input reset
);
reg [31:0] cnt_p , cnt_n;
reg clk_p , clk_n;
localparam BAUD_HALF = (N-1)/2 - 1;
localparam BAUD_TICK = N - 1;
always @(posedge clk or posedge reset)
begin
if(reset)
cnt_p <= 4'd0;
else if(cnt_p == BAUD_TICK)
cnt_p <= 0;
else
cnt_p <= cnt_p + 1'b1;
end
always @(negedge clk or posedge reset)
begin
if(reset)
cnt_n <= 4'd0;
else if(cnt_n == BAUD_TICK)
cnt_n <= 0;
else
cnt_n <= cnt_n + 1'b1;
end
//clk_p
always @(posedge clk or posedge reset)
begin
if(reset)
clk_p <= 1;
else if(cnt_p == BAUD_HALF)
clk_p <= ~clk_p;
else if(cnt_p == BAUD_TICK)
clk_p <= ~clk_p;
end
//clk_n
always @(negedge clk or posedge reset)
begin
if(reset)
clk_n <= 1;
else if(cnt_n == BAUD_HALF)
clk_n <= ~clk_n;
else if(cnt_n == BAUD_TICK)
clk_n <= ~clk_n;
end
assign clk_out = clk_n | clk_p;
endmodule
奇数分频相对比较麻烦, 步骤如下:
1)需要两个计数器:clk 上升沿计数器; clk 下降沿计数器。这两个计数器实现N计数。
2)分别在clk 的上升沿和下降沿 实现 clk_p, clk_n。当cnt_x == N/2 – 0.5 时,clk_x 反转; 当cnt_x == N 时,clk_x 再次反转; 这样生成的时钟clk_x 是一个占空比不等的时钟(高电平 – 低电平 = 1)即要么高电平多0.5, 或者低电平多0.5 个时钟周期。
3)clk_out = clk_p | clk_n; 平衡占空比不等的现象,通过使用 或操作 ,实现%50 、%50 占空比的时钟。
仿真程序:
`timescale 1ns / 1ps
module tb
(
);
parameter PERIOD = 20 ; //50Mhz
reg clk;
reg reset = 1;
initial
begin
clk = 0;
#(PERIOD/2);
forever
#(PERIOD/2) clk = ~clk;
end
initial
begin
reset = 1;
@(posedge clk);
reset = 0;
#800000;
$stop;
end
wire clk_out1;
odd_div #
(
.N (7)
)
odd_div_inst
(
.clk (clk),
.clk_out (clk_out1),
.reset (reset)
);
wire clk_out2;
even_div #
(
.N (4)
)
even_div_inst
(
.clk (clk),
.clk_out (clk_out2),
.reset (reset)
);
endmodule
仿真波形:

从仿真波形上可以看出,通过或操作实现 %50 , %50 的占空比时钟,从而实现计数分频。 偶数分频相对简单一些,只要通过计数分频即可。
老师,关于奇分频的程序仿真中,摒弃复位期间的波形,我得到以下结论:
1.clk_p和clk_n的复位期间的同为高电平时,若得到占空比为50%的clk_out波形,则clk_out=clk_p|clk_n;
2.clk_p和clk_n的复位期间的同为低电平时,若得到占空比为50%的clk_out波形,则clk_out=clk_p&clk_n。
是的,复位为1 时, 使用 | 。 复位为0时,使用 &