偶数分频
可以采用同步整数分频思路,使用摩尔状态机或者计数器(序列机)思想都可以,在这里提供一个同步7分频的摩尔状态机的实现思路:
module clk_divide(
input wire clk ,
input wire rst_n ,
output wire clk_o
);
//==================1. 同步整数分频器====================//
//基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频
localparam S0 = 7'b0000_001 , //S0状态:clk_o输出0
S1 = 7'b0000_010 , //S1状态:clk_o输出0
S2 = 7'b0000_100 , //S2状态:clk_o输出0
S3 = 7'b0001_000 , //S3状态:clk_o输出0
S4 = 7'b0010_000 , //S4状态:clk_o输出1
S5 = 7'b0100_000 , //S5状态:clk_o输出1
S6 = 7'b1000_000 ; //S1状态:clk_o输出1
reg [6:0] state ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= S0;
clk_o <= 1'b0;
end
else begin
case(state)
S0: begin state <= S1; clk_o <= 1'b0; end
S1: begin state <= S2; clk_o <= 1'b0; end
S2: begin state <= S3; clk_o <= 1'b0; end
S3: begin state <= S4; clk_o <= 1'b0; end
S4: begin state <= S5; clk_o <= 1'b1; end
S5: begin state <= S6; clk_o <= 1'b1; end
S6: begin state <= S0; clk_o <= 1'b1; end
default:begin state <= S0; clk_o <= 1'b1; end
endcase
end
end
testbench如下所示:
`timescale 1ns/1ns
`define CLK_CYCLE 20
module tb_clk_divide;
reg clk ;
reg rst_n ;
wire clk_o ;
clk_divide u_clk_divide(
.clk (clk) ,
.rst_n (rst_n) ,
.clk_o (clk_o)
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
#201;
rst_n = 1'b1;
end
always #(`CLK_CYCLE / 2) clk = ~clk;
endmodule
仿真波形为:
奇数分频
方法一
主要思路如下:
首先基于时钟的上升沿和时钟的下降沿构造两个计数器,两个计数器都是技术到分频的最大值置0.之后分别给予两个计数器,声明两个变量即clk_div1和clk_div2。两者在各自的时钟沿下,若小于最大计数值的一半,则拉高信号。否则拉低信号。最后将这两个变量信号相或就可以得到最后的分频时钟啦。
module clk_divide(
input wire clk ,
input wire rst_n ,
output wire clk_o
);
//==================2. 奇数分频器=======================//
//方法1
parameter CLK_DIV = 7 ; //奇数分频的频数
reg [3:0] cnt_pos ; //上升沿计数器
reg [3:0] cnt_neg ; //下降沿计数器
reg clk_div1 ; //分频信号1
reg clk_div2 ; //分频信号2
always @(posedge clk or negedge rst_n) begin //时钟上升沿触发
if(!rst_n) begin
cnt_pos <= 'd0;
end
else if(cnt_pos == CLK_DIV - 1'b1) begin //计数最大值置0
cnt_pos <= 'd0;
end
else
cnt_pos <= cnt_pos + 1'b1;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_div1 <= 1'b0;
end
else if(cnt_pos < CLK_DIV / 2) begin //小于计数值的一半,拉高clk_div1
clk_div1 <= 1'b1;
end
else begin+
clk_div1 <= 1'b0;
end
end
always @(negedge clk or negedge rst_n) begin //时钟下降沿触发
if(!rst_n) begin
cnt_neg <= 'd0;
end
else if(cnt_neg == CLK_DIV - 1) begin //计数到最大值置0
cnt_neg <= 'd0;
end
else begin
cnt_neg <= cnt_neg + 1'b1;
end
end
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_div2 <= 1'b0;
end
else if(cnt_neg < CLK_DIV / 2) begin //小于计数值的一半,拉高clk_div2
clk_div2 <= 1'b1;
end
else begin
clk_div2 <= 1'b0;
end
end
assign clk_o = clk_div1 | clk_div2; //将clk_div1和clk_div2或运算
仿真截图:
方法二
第二个方法是来自《硬件架构的艺术》上的方法,基于其思路,实现代码如下,代码中有详细的注释,便不在赘述了。
module clk_divide(
input wire clk ,
input wire rst_n ,
output wire clk_o
);
//方法2(来自《硬件架构的艺术》)
//步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数
parameter N = 5 ;
reg [3:0] cnt_up ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_up <= 'd0;
end
else if(cnt_up == N-1) begin
cnt_up <= 'd0;
end
else begin
cnt_up <= cnt_up + 1'b1;
end
end
//步骤2:使用两个触发器,按照如下方式产生使能信号
//tff1_en:在计数值为0时使能
//tff2_en:在计数值为(N+1)/2时使能
wire tff1_en ;
wire tff2_en ;
assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0;
assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0;
//步骤三:产生以下信号
//div1:TFF1的输出--->由输入时钟的上升沿触发
//div2:TFF2的输出--->由输入时钟的下降沿触发
reg div1 ;
reg div2 ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
div1 <= 1'b0;
end
else if(tff1_en) begin
div1 <= ~div1;
end
else begin
div1 <= div1;
end
end
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
div2 <= 1'b0;
end
else if(tff2_en) begin
div2 <=~div2;
end
else begin
div2 <= div2;
end
end
//步骤四:将div1和div2信号异或
assign clk_o = div1 ^ div2;
endmodule
仿真如下:
全部代码
//时钟分频器
//2022/09/01
//======================================================//
//==================1. 同步整数分频器====================//
//==================2. 奇数分频器=======================//
module clk_divide(
input wire clk ,
input wire rst_n ,
output wire clk_o
);
/*
//==================1. 同步整数分频器====================//
//基于摩尔状态机实现7分频(一段式状态机) 同样可以实现偶数分频
localparam S0 = 7'b0000_001 , //S0状态:clk_o输出0
S1 = 7'b0000_010 , //S1状态:clk_o输出0
S2 = 7'b0000_100 , //S2状态:clk_o输出0
S3 = 7'b0001_000 , //S3状态:clk_o输出0
S4 = 7'b0010_000 , //S4状态:clk_o输出1
S5 = 7'b0100_000 , //S5状态:clk_o输出1
S6 = 7'b1000_000 ; //S1状态:clk_o输出1
reg [6:0] state ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= S0;
clk_o <= 1'b0;
end
else begin
case(state)
S0: begin state <= S1; clk_o <= 1'b0; end
S1: begin state <= S2; clk_o <= 1'b0; end
S2: begin state <= S3; clk_o <= 1'b0; end
S3: begin state <= S4; clk_o <= 1'b0; end
S4: begin state <= S5; clk_o <= 1'b1; end
S5: begin state <= S6; clk_o <= 1'b1; end
S6: begin state <= S0; clk_o <= 1'b1; end
default:begin state <= S0; clk_o <= 1'b1; end
endcase
end
end
*/
//==================2. 奇数分频器=======================//
/*
//方法1
parameter CLK_DIV = 7 ; //奇数分频的频数
reg [3:0] cnt_pos ; //上升沿计数器
reg [3:0] cnt_neg ; //下降沿计数器
reg clk_div1 ; //分频信号1
reg clk_div2 ; //分频信号2
always @(posedge clk or negedge rst_n) begin //时钟上升沿触发
if(!rst_n) begin
cnt_pos <= 'd0;
end
else if(cnt_pos == CLK_DIV - 1'b1) begin //计数最大值置0
cnt_pos <= 'd0;
end
else
cnt_pos <= cnt_pos + 1'b1;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_div1 <= 1'b0;
end
else if(cnt_pos < CLK_DIV / 2) begin //小于计数值的一半,拉高clk_div1
clk_div1 <= 1'b1;
end
else begin+
clk_div1 <= 1'b0;
end
end
always @(negedge clk or negedge rst_n) begin //时钟下降沿触发
if(!rst_n) begin
cnt_neg <= 'd0;
end
else if(cnt_neg == CLK_DIV - 1) begin //计数到最大值置0
cnt_neg <= 'd0;
end
else begin
cnt_neg <= cnt_neg + 1'b1;
end
end
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_div2 <= 1'b0;
end
else if(cnt_neg < CLK_DIV / 2) begin //小于计数值的一半,拉高clk_div2
clk_div2 <= 1'b1;
end
else begin
clk_div2 <= 1'b0;
end
end
assign clk_o = clk_div1 | clk_div2; //将clk_div1和clk_div2或运算
*/
//方法2(来自《硬件架构的艺术》)
//步骤1:创建由时钟上升沿触发的0-(N-1)的计数器,N为分频系数
parameter N = 5 ;
reg [3:0] cnt_up ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_up <= 'd0;
end
else if(cnt_up == N-1) begin
cnt_up <= 'd0;
end
else begin
cnt_up <= cnt_up + 1'b1;
end
end
//步骤2:使用两个触发器,按照如下方式产生使能信号
//tff1_en:在计数值为0时使能
//tff2_en:在计数值为(N+1)/2时使能
wire tff1_en ;
wire tff2_en ;
assign tff1_en = (cnt_up == 0) ? 1'b1 : 1'b0;
assign tff2_en = (cnt_up == (N+1)/2) ? 1'b1 : 1'b0;
//步骤三:产生以下信号
//div1:TFF1的输出--->由输入时钟的上升沿触发
//div2:TFF2的输出--->由输入时钟的下降沿触发
reg div1 ;
reg div2 ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
div1 <= 1'b0;
end
else if(tff1_en) begin
div1 <= ~div1;
end
else begin
div1 <= div1;
end
end
always @(negedge clk or negedge rst_n) begin
if(!rst_n) begin
div2 <= 1'b0;
end
else if(tff2_en) begin
div2 <=~div2;
end
else begin
div2 <= div2;
end
end
//步骤四:将div1和div2信号异或
assign clk_o = div1 ^ div2;
endmodule