#(3*DIV_CLK)
rst_n = 0;
#(6*DIV_CLK)
rst_n = 1;
#(20*DIV_CLK);
end
//源时钟信号生成
always #DIV_CLK clk = ~clk;
//模块例化
clk_div_fraction u_clk_div_fraction
(.clk (clk),
.rst_n (rst_n),
.clk_frac (clk_frac)
);
endmodule
4.4 仿真结果
把5.4小数分频和2.6小数分频进行比较,图一是5.4小数分频仿真时序图,可以看到波形较为整齐;图二是2.6小数分频仿真时序图,可以看到波形较为杂乱;
Tips:5.4小数分频并不是每一段都是均匀的长度(即局部不满足小数分频,总体满足小数分频)
那么是什么原因造成的呢?
从前面的基本原理可以知道,通过双模前置法得到的小数分频波形是差强人意的,5.4小数分频通过5分频和6分频差值得到,2.6小数分频通过2分频和3分频差值得到,同样差一个cnt,对于2.6小数分频的波形破坏要比5.4小数分频要严重。总而言之就是:局部不满足小数分频,总体满足小数分频。
五、半整数分频
5.1 占空比50%半整数分频
对于使用小数分频法得到的,以3.5分频为例,需要使用一个四分频和一个三分频,七个周期内,输出两个1,但是信号时序难以得到保障,时钟信号的质量得不到保证。
那有没有新的方法可以优化半整数分频呢?
可以这样实现半整数分频:
(1)在源时钟上升沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。
(2)在源时钟下降沿分别产生由 4 个和 3 个源时钟周期组成的 2 个分频时钟。
(3)两个分频时钟做相位一个延迟半个源时钟周期,一个提前半个源时钟周期。将两次产生的时钟进行“或”操作,便可以得到周期均匀的 3.5 倍分频时钟。分频波形示意图如下所示。
5.2 Verilog代码
//半整数分频电路设计
module clk_div_half
(
input rst_n,
input clk,
output clk_div
);
parameter DIV_CLK = 7; //3.5分频的高低电平总个数
reg [3:0] cnt; //总计数器
reg clk_p; //上升沿触发生成的时钟信号
reg clk_n; //下降沿触发生成的时钟信号
//计数器模块
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 1'b0 ;
end
else if (cnt == DIV_CLK-1) begin //从0计数,到6清零
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
//上升沿触发生成的时钟信号模块
//计数器到0和4并且在上升沿触发信号翻转
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_p <= 1'b0;
end
else if (cnt == 0) begin //计数器到0信号翻转
clk_p <= 1;
end
else if (cnt == (DIV_CLK/2)+1) begin //计数器到4信号翻转
clk_p <= 1 ;
end
else begin
clk_p <= 0 ;
end
end
//下降沿触发生成的时钟信号模块
//计数器到1和4并且在下降沿触发信号翻转
always@(negedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_n <= 1'b0 ;
end
else if(cnt == 1) begin //计数器到1信号翻转
clk_n <= 1 ;
end
else if (cnt == (DIV_CLK/2)+1 ) begin //计数器到4信号翻转
clk_n<= 1 ;
end
else begin
clk_n <= 0 ;
end
end
//或逻辑运算得到占空比50%的3.5半整数分频信号
assign clk_div = clk_p | clk_n;
endmodule
5.3 Testbench
`timescale 1ns/1ps //时间刻度:单位1ns,精度1ps
module clk_div_half_tb;
//信号申明
reg clk;
reg rst_n;
wire clk_div;
parameter DIV_CLK0 = 5; //定义源时钟信号一周期时间
//复位信号生成
initial begin
clk = 0; //时钟信号赋初值
rst_n = 1; //复位信号赋初值
#(3*DIV_CLK0)
rst_n = 0;
#(6*DIV_CLK0)
rst_n = 1;
#(20*DIV_CLK0);
end
//源时钟信号生成
always #DIV_CLK0 clk = ~clk;
//模块例化
clk_div_half u_clk_div_half
(.clk (clk),
.rst_n (rst_n),
.clk_div (clk_div)
);
endmodule
5.4仿真结果
六、状态机分频
6.1状态机分频
Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。状态机可分为 2 类:Moore 状态机和 Mealy 状态机。
例如完成一个四分频且占空比为25%的分频器,此时可以列出四种状态,状态机在四种状态不断切换,根据下一个输出状态只与当前状态有关而与输出无关,可以知道此分频器可根据Moore状态机完成。
6.2 verilog代码
module clk_div_FSM
(
input wire clk,
input wire rst_n,
output reg clk_FSM
);
//定义四种状态
parameter S0 = 2'b00;
parameter S1 = 2'b01;
parameter S2 = 2'b10;
parameter S3 = 2'b11;
reg [1:0] state; //定义目前状态
reg [1:0] next_state; //下一状态
//信号复位模块
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
state <= S0;
end
else begin
state <= next_state;
end
end
//状态转换模块(相当于用状态机写计数器)
always @(*) begin
case (state)
S0: next_state = S1;
S1: next_state = S2;
S2: next_state = S3;
S3: next_state = S0;
endcase
end
//信号输出模块
always @(*) begin
if(state == S0) begin
clk_FSM = 1'b1;
end
else begin
clk_FSM = 1'b0;