【笔记】关于整数分频的思考(Verilog HDL)(Digital Logic)

 

Instruction

 

本文主要讨论整数分频器的原理以及实现。关键的问题就是分频的时钟什么时候翻转。

 

Design

 

1. 偶数倍分频

原理:比如4分频,需要一个模4的计数器,占空比50%,计数为0~3循环,当计数到一半时,即计数输出cnt<2时翻转。

代码如下:div4

 

  
    
1 // 四分频
2   module div4(clk,rst_n,o_clk);
3 input clk,rst_n;
4 output o_clk;
5 reg o_clk;
6
7 reg [ 1 : 0 ]cnt;
8
9 always @( posedge clk or negedge rst_n)
10 begin
11 if ( ! rst_n)
12 cnt <= 0 ;
13 else if (cnt == 3 )
14 cnt <= 0 ;
15 else
16 cnt <= cnt + 1 ;
17 end
18
19 always @( posedge clk or negedge rst_n)
20 begin
21 if ( ! rst_n)
22 o_clk <= 0 ;
23 else if (cnt < 2 )
24 o_clk <= 1 ;
25 else
26 o_clk <= 0 ;
27 end
28
29   endmodule
30  

 

仿真结果

001

 

2. 奇数倍分频

原理:比如3分频,需要一个模3的计数器,占空比50%,即计数到一半(非整数)时翻转。方法是用2个模3的计数器,一个

在时钟的上升沿计数,一个在下降沿计数。对这2个计数分别分频,然后相或。利用上升沿河下降沿刚好相差半个时钟周期来

实现翻转,注意的是,1|0=1,所以设为1的部分占得部分较少,确切的说为cnt<1,即0。

代码如下:div3

 

  
    
1 // 三分频
2   module div3(clk,rst_n,o_clk);
3 input clk,rst_n; // 输入时钟和复位
4   output o_clk; // 输出时钟
5  
6 reg [ 1 : 0 ]cnt_p; // 上升沿计数
7   reg [ 1 : 0 ]cnt_n; // 下降沿计数
8   reg clk_p; // 上升沿分频的时钟
9   reg clk_n; // 下降沿分频的时钟
10  
11 assign o_clk = clk_p | clk_n; // 分频得到的两时钟相或得到输出时钟
12
13 // 上升沿计数
14   always @( posedge clk or negedge rst_n)
15 begin
16 if ( ! rst_n)
17 cnt_p <= 0 ;
18 else if (cnt_p == 2 ) // 模3的计数器
19   cnt_p <= 0 ;
20 else
21 cnt_p <= cnt_p + 1 ;
22 end
23 // 上升沿分频
24   always @( posedge clk or negedge rst_n)
25 begin
26 if ( ! rst_n)
27 clk_p <= 0 ;
28 else if (cnt_p < 1 ) // cnt=0
29   clk_p <= 1 ;
30 else // cnt=1,2
31   clk_p <= 0 ;
32 end
33
34 // 下降沿计数
35   always @( negedge clk or negedge rst_n)
36 begin
37 if ( ! rst_n)
38 cnt_n <= 0 ;
39 else if (cnt_n == 2 )
40 cnt_n <= 0 ;
41 else
42 cnt_n <= cnt_n + 1 ;
43 end
44 // 下降沿分频
45   always @( negedge clk or negedge rst_n)
46 begin
47 if ( ! rst_n)
48 clk_n <= 0 ;
49 else if (cnt_n < 1 ) // cnt_n=0
50 clk_n <= 1 ;
51 else
52 clk_n <= 0 ; // cnt_n=1,2
53 end
54
55 endmodule
56

 

仿真结果

001

3. 任意整数分频

原理:总结1和2,设整数位N,当N为偶数时,cnt<N/2就翻转,当N为奇数时,cnt<(N-1)/2翻转。不论N为奇数、偶数,翻

转的条件都可以表示为cnt<(N>>1)。这是一个编程的技巧,无它,也是看别人的代码,学到的。

代码如下:divn

 

  
    
1 // 任意整数分频
2 module divn(clk,rst_n,o_clk);
3 input clk,rst_n;
4 output o_clk;
5
6 parameter WIDTH = 3 ; // 计数器的位宽
7 parameter N = 6 ; // 要分频的整数
8
9 reg [WIDTH - 1 : 0 ]cnt_p;
10 reg [WIDTH - 1 : 0 ]cnt_n;
11 reg clk_p;
12 reg clk_n;
13
14 assign o_clk = (N == 1 ) ? clk:(N[ 0 ] ? (clk_p | clk_n):clk_p);
15
16 always @( posedge clk or negedge rst_n)
17 begin
18 if ( ! rst_n)
19 cnt_p <= 0 ;
20 else if (cnt_p == (N - 1 ))
21 cnt_p <= 0 ;
22 else
23 cnt_p <= cnt_p + 1 ;
24 end
25
26 always @( posedge clk or negedge rst_n)
27 begin
28 if ( ! rst_n)
29 clk_p <= 0 ;
30 else if (cnt_p < (N >> 1 ))
31 clk_p <= 1 ;
32 else
33 clk_p <= 0 ;
34 end
35
36 always @( negedge clk or negedge rst_n)
37 begin
38 if ( ! rst_n)
39 cnt_n <= 0 ;
40 else if (cnt_n == (N - 1 ))
41 cnt_n <= 0 ;
42 else
43 cnt_n <= cnt_n + 1 ;
44 end
45
46 always @( negedge clk or negedge rst_n)
47 begin
48 if ( ! rst_n)
49 clk_n <= 0 ;
50 else if (cnt_n < (N >> 1 ))
51 clk_n <= 1 ;
52 else
53 clk_n <= 0 ;
54 end
55
56 endmodule
57

 

仿真结果

001

 

这里有2个技巧:

1. 14 assign o_clk=(N==1)?clk:(N[0]?(clk_p|clk_n):clk_p);这行决定分频输出的类型;

2.30 else if(cnt_p<(N>>1)),这里的(N>>1)比N/2好用多了,同样的表达方式通吃奇偶。

   比如在50 else if(cnt_n<(N>>1)),这里就等价于(N-1)/2.

 

 

Result & Analysis

 

前两个看懂了,任意整数分频就不难理解。关键还是奇数分频的算法,分别分频再求或,太奇妙了。至于3里的编程技巧,都是模

仿无双的代码。(无双是谁?^_^)。细节部分,每个分频用2个always块的好处是,推荐这种代码风格。功能性更单一,便于阅

读,也算是人都能读懂、综合的代码,综合器去综合应该没什么意外。

你可能感兴趣的:(Verilog)