在某些情况下需要对系统时钟分频后的时钟进行周期检测,引出周期标志信号以便在后续其他情况的使用。虽然在大多数情况下我们能够知道分频后的时钟是系统时钟的几倍分频,但为增强在分频时钟改变情况下周期标志信号的复用性或对未知时钟的周期检测,可以考虑以下方法或思想,以延申到类似情况下使用。
文章目录
- 检测方法
- 方法一:上升沿判断
- 方法二:时钟移位判断
- 实现与仿真
- Verilog实现
- 仿真测试
检测方法
方法一:上升沿判断
将时钟打一拍后取clk&(~clk_reg)
,上升沿时该值会拉高一个时钟周期。
也可以用下降沿进行判断方法类似。
方法二:时钟移位判断
对时钟左移判断移位后的值,移位后dived_clk_buf==2'd1
即为上升沿到来,dived_clk_buf==2'd2
即为下降沿到来。
reg [1:0] dived_clk_buf ;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
dived_clk_buf <= 2'd3;
else
dived_clk_buf <= {dived_clk_buf[0],dived_clk};//移位时钟以判断周期
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
XX;
else if(dived_clk_buf==2'd1)
XXX;
else
XXXX;
end
实现与仿真
Verilog实现
由两部分组成:产生分频时钟模块+周期检测标志模块;改变分频时钟模块的分频周期可在仿真测试文件中例化时进行参数修改。
产生分频时钟模块:
//========================================================================
// Author :YprgDay
//========================================================================
module dived_clk
#(
parameter DIV_FREQUENCY = 4 ,//分频数(只允许偶分频),dived_clk周期为DIV_FREQUENCY*clk的周期
parameter PERIOD_WIDTH_MAX = 2 ,//(DIV_FREQUENCY-1)对应的二进制位宽
parameter CNT_PERIOD_MAX = DIV_FREQUENCY-1 ,
parameter CNT_HALF_PERIOD_MAX = CNT_PERIOD_MAX >> 1//计数分频中值
)
(
input wire rst_n ,
input wire clk ,
output reg dived_clk
);
reg [PERIOD_WIDTH_MAX-1:0] cnt_period ;//时钟分频计数
//分频时钟
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
dived_clk <= 0;
end
else if(cnt_period == CNT_PERIOD_MAX)begin
dived_clk <= 0;
end
else if(cnt_period == CNT_HALF_PERIOD_MAX)begin
dived_clk <= 1;
end
else begin
dived_clk <= dived_clk;
end
end
//分频计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
cnt_period <= 0;
end
else if(cnt_period == CNT_PERIOD_MAX)begin
cnt_period <= 0;
end
else begin
cnt_period <= cnt_period + 1'b1;
end
end
endmodule
周期检测标志模块:
//========================================================================
// Author :YprgDay
//========================================================================
module clk_period(
input wire rst_n ,
input wire clk ,
input wire dived_clk ,
output wire clk_flag1 ,
output wire clk_flag2
);
reg dived_clk_reg ;
reg [1:0] dived_clk_buf ;
//==========================< 方法一 >============================
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
dived_clk_reg <= 0;
end
else begin
dived_clk_reg <= dived_clk;
end
end
assign clk_flag1 = dived_clk&(~dived_clk_reg);
//==========================< 方法二 >============================
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
dived_clk_buf <= 2'd3;
else
dived_clk_buf <= {dived_clk_buf[0],dived_clk};//左移
end
assign clk_flag2 = (dived_clk_buf==2'd1);
endmodule
仿真测试
仿真产生系统时钟与复位信号。
//========================================================================
// Author :YprgDay
//========================================================================
`timescale 1ns/1ns
module tb_clk_period();
parameter CLK_PERIOD = 10 ;//设置时钟信号周期
parameter HALF_CLK_PERIOD = CLK_PERIOD/2;//生成时钟信号半周期
reg i_rst_n ;
reg i_clk ;
wire dived_clk ;
wire clk_flag1 ;
wire clk_flag2 ;
//==========================< Clock block >============================
always #HALF_CLK_PERIOD i_clk = ~i_clk;
//==========================< i_rst_n block >============================
initial begin
i_clk = 1'b1 ;
i_rst_n <= 1'b0 ;
#40
i_rst_n <= 1'b1 ;
end
dived_clk
#(
.DIV_FREQUENCY (8),//分频数(只允许偶分频),dived_clk周期为DIV_FREQUENCY*clk的周期
.PERIOD_WIDTH_MAX (3)//(DIV_FREQUENCY-1)对应的二进制位宽
)
u_dived_clk
(
.rst_n (i_rst_n ),
.clk (i_clk ),
.dived_clk (dived_clk )
);
clk_period u_clk_period(
.rst_n (i_rst_n ),
.clk (i_clk ),
.dived_clk (dived_clk ),
.clk_flag1 (clk_flag1 ),
.clk_flag2 (clk_flag2 )
);
endmodule
仿真效果如下:
可以看出方法一的时钟标志信号在时钟上升沿后拉高;方法二的时钟标志信号在时钟上升沿后下一拍拉高。