同步FIFO
1.序言
本文详细讲解了基于verilog的常见分频方法,并以牛客网时序逻辑部分VL40、VL41、VL42题为例。**注意:**使用自己编写的分频器时序性能并不好,对时序要求较高的话,建议使用ip核。
2.常见分频系数及实现
(1)偶数分频——以6分频电路为例
(A)分析
偶数(N)分频,直接使用一个N/2进制计数器(对应计数器状态为0~(N/2-1)),计数器记到最后一个状态,输出时钟状态反转。
(B)代码
以6分频电路为例。
代码:
module clk_divi_6(
input clk_in ,
input rst_n ,
output reg clk_out_6
);
//6/2=3进制计数器
reg [1:0] cnt_3;
always@(posedge clk_in or negedge rst_n)
if(!rst_n)
cnt_3 <= 2'd0;
else if(cnt_3 == 2'd2)
cnt_3 <= 2'd0;
else
cnt_3 <= cnt_3 + 1'b1;
always@(posedge clk_in or negedge rst_n)
if(!rst_n)
clk_out_6 <= 1'b0;
else if(cnt_3 == 2'd2)
clk_out_6 <= !clk_out_6;
else
clk_out_6 <= clk_out_6;
endmodule
testbench
`timescale 1ns / 1ps
module clk_divi_6_tb();
reg clk_in ;
reg rst_n ;
wire clk_out_6 ;
//输入时钟周期20ns,即50MHz
parameter T = 20;
initial begin
clk_in = 1'b0;
rst_n = 1'b0;
#(T);
rst_n = 1'b1;
end
always#(T/2) clk_in = !(clk_in);
clk_divi_6 u_clk_divi_6(
.clk_in (clk_in ),
.rst_n (rst_n ),
.clk_out_6(clk_out_6 )
);
endmodule
(C)仿真结果
(2)奇数分频——牛客网VL42(没有占空比要求)
(A)题目分析
以5分频为例,输出没有占空比要求,只要满足输出时钟周期为输入时钟周期的5倍即可。可以是3个时钟周期为高,两个时钟周期为低;也可以是4个时钟周期为高,1个时钟周期为低;
(B)例子牛客网VL42
注意:下图中波形示意图和题目要求不符合,波形示意图为占空比50%。虽然题目中说了占空比没有要求,但是正确答案是两个时钟周期为高,三个时钟周期为低的组合。
(C)代码
`timescale 1ns/1ns
module odd_div (
input wire rst ,
input wire clk_in,
output wire clk_out5
);
reg [2:0] cnt_5;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt_5 <= 'd0;
else if(cnt_5 == 3'd4)
cnt_5 <= 'd0;
else
cnt_5 <= cnt_5 + 1'b1;
end
assign clk_out5 = ((3'd0<cnt_5)&& (cnt_5<3'd3))?1'b1:1'b0;
endmodule
(3)奇数分频——牛客网VL40(占空比50%)
A)分频分析
以7分频为例,占空比50%,输出3.5个时钟周期为高电平,3.5个时钟周期为低电平。常用的方法是输入时钟上升沿和下降沿联合检测。利用时钟上升沿检测(posedge clk)控制的变量clk_reg_pose保持三个时钟周期高电平,时钟下降沿检测(negedge clk)控制的变量clk_reg_nege保持三个时钟周期高电平,利用高电平的区域存在0.5个时钟周期的错位,相或输出,就得到了3.5个时钟高电平,3.5个时钟低电平的分频输出。
B)题目描述
C)代码
`timescale 1ns/1ns
module odo_div_or
(
input wire rst ,
input wire clk_in,
output wire clk_out7
);
//7进制计数器
reg [2:0] cnt_7;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt_7 <= 'd0;
else if(cnt_7 == 3'd6)
cnt_7 <= 'd0;
else
cnt_7 <= cnt_7 + 1'b1;
end
//上升沿触发状态指示信号
reg clk_reg_pose;
always@(posedge clk_in or negedge rst)begin
if(!rst)
clk_reg_pose <= 1'b0;
else if(cnt_7 == 3'd3)
clk_reg_pose <= 1'b1;
else if(cnt_7 == 3'd6)
clk_reg_pose <= 1'b0;
else
clk_reg_pose <= clk_reg_pose;
end
//下降沿触发状态指示信号
reg clk_reg_nege;
always@(negedge clk_in or negedge rst)begin
if(!rst)
clk_reg_nege <= 1'b0;
else if(cnt_7 == 3'd3)
clk_reg_nege <= 1'b1;
else if(cnt_7 == 3'd6)
clk_reg_nege <= 1'b0;
else
clk_reg_nege <= clk_reg_nege;
end
assign clk_out7 = clk_reg_pose|clk_reg_nege;
endmodule
(4)小数分频——牛客网VL41
(A)##############分频说明######################(很重要)
小数分频,比如8.7倍分频,无法实现输出的每一个时钟就是输入时钟的8.7分频。实际的实现方法是最小整数倍法,即8.7个输入时钟对应1个输出时钟,那么8.710=87个输入时钟对应110=10个输出时钟。所以只要保证输出时钟为10个,并不要求输出时钟周期完全一样。
因为是8.7分频,所以可以由8.7两边的整数倍分频构成:
a)8分频和9分频
设输出的10个时钟周期为X个8分频和Y个九分频,得到二元一次方程:
没个周期对应输入时钟周期数:
8X+9Y=87;
总的输出时钟周期数:
X+Y=10;
解得:X=3,Y=7
这种搭配并不是随意的,有要求:
1.在要求分频数8.7的两边;
2.上述两个二元一次方程有整数解;
3.也可以由3/4或者更多中分频方式构成。
(B)题目描述
(C)题目解析
a)用一个87进制计数器进行时序控制;
87个状态,状态0~86;
b)前三个8分频输出:
先输出3个8分频输出,即对应计数器前24个状态;
输出相较于计数器延迟一拍,对应计数器1~24
c)后7个9分频输出:
后输出7个9分频输出,即对应计数器后63个状态;
(注意题目中的波形可以看出,9分频不是占空比50%,而是分成4和5;)
输出相较于计数器延迟一拍,对应计数器25~86,0
(d)整合输出
将8分频和9分频输出相或,得到输出8.7分频时钟。
(D)代码
`timescale 1ns/1ns
module div_M_N(
input wire clk_in,
input wire rst,
output wire clk_out
);
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
//87进制计数器
reg [6:0] cnt_87;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt_87 <= 'd0;
else if(cnt_87 == 7'd86)
cnt_87 <= 'd0;
else
cnt_87 <= cnt_87 + 1'b1;
end
//87进制计数器分为两段,1——24,25——86,0
//前一段为3个8进制计数器
reg [2:0] cnt_8;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt_8 <= 3'd0;
else if(cnt_87 < 7'd24)
if(cnt_8 == 3'd7)
cnt_8 <= 3'd0;
else
cnt_8 <= cnt_8 +1'b1;
else
cnt_8 <= 'd0;
end
wire clk_8;
assign clk_8 = ((3'd0<cnt_8)&&(cnt_8<3'd5))?1'b1:1'b0;
//后一段为7个9进制计数器,占空比不是50%,分成4和5
reg [3:0] cnt_9;
always@(posedge clk_in or negedge rst)begin
if(!rst)
cnt_9 <= 'd0;
else if(7'd24 <= cnt_87)
if(cnt_9 == 4'd8)
cnt_9 <= 'd0;
else
cnt_9 <= cnt_9 + 1'b1;
else
cnt_9 <=4'b0;
end
//9分频占空比为50%
// reg clk_9_pose;
// always@(posedge clk_in or negedge rst)begin
// if(!rst)
// clk_9_pose <= 1'b0;
// else if(7'd24 <= cnt_87)
// if(cnt_9 == 'd0)
// clk_9_pose <= 1'b1;
// else if(cnt_9 == 4'd4)
// clk_9_pose <= 1'b0;
// else
// clk_9_pose <= clk_9_pose;
// else
// clk_9_pose <= 1'b0;
// end
// reg clk_9_nege;
// always@(negedge clk_in or negedge rst)begin
// if(!rst)
// clk_9_nege <= 1'b0;
// else if(7'd24 <= cnt_87)
// if(cnt_9 == 'd1)
// clk_9_nege <= 1'b1;
// else if(cnt_9 == 4'd5)
// clk_9_nege <= 1'b0;
// else
// clk_9_nege <= clk_9_nege;
// else
// clk_9_nege <= 1'b0;
// end
wire clk_9;
assign clk_9 = ((3'd0<cnt_9)&&(cnt_9<3'd5))?1'b1:1'b0;
assign clk_out = clk_8|clk_9;
endmodule
(5)使用IP核实现分频
如果对时钟要求较高,建议使用IP核实现分频,后续会出时钟IP核的文章。