0 环境配置
软件:Windows 10、modelsim SE10.4、vscode
- 安装modelsimSE 10.4,版本号为SE10.4。
- 安装vscode。
- 安装插件
Verilog-HDL/SystemVerilog/Bluespec SystemVerilog
、Verilog HDL
、Verilog Snippet
,以优化编程体验。
1 分频电路简介
基本概念
分频电路将输入时钟信号的频率进行分割,从而产生特定频率的时钟信号。频率等分在波形图上表现在周期倍增。以5分频为例,100MHz的时钟进行5分频,输出20MHz的时钟;其周期扩大到5倍,即每个新时钟在时间长度上包含5个旧时钟,每个时钟周期从10ns变为50ns。
程序框图与端口信号
Port name | Direction | Type | Description |
clk_in | input | wire | 24MHz时钟 |
rstn | input | wire | 复位 |
clk_out | output | wire | n分频后的时钟 |
分频电路的分类
可分为偶数分频、奇数分频与小数分频。其中半整数分频是小数分频的特殊情况。
2 偶数分频
将输入时钟信号的频率分为n份,时钟周期扩大到原来的n倍。
如下图所示,输入时钟信号为100MHz,进行二分频、四分频、六分频、八分频后,分别得到50MHz、25MHz、16.67MHz、12.5MHz的时钟,表现为时钟周期分别扩大到原来的2倍、4倍、6倍、8倍。
2.1 法1:触发器级联
设计思路
将触发器的反向输出端~Q
连接到输入端Q
,可构成二分频电路。将二分频后的时钟作为下一级触发器的时钟端,可构成 4 分频。按此方式逐级级联,完成2的n次幂分频。
该方法适用于分频系数小的情况。若分频系数过大,可以使用计数翻转的方式进行偶数分频,详见法2与法3。
程序设计
module even_div(
input wire clk,
input wire rstn,
output wire clk_div2,
output wire clk_div4,
output wire clk_div8
);
//————————————————————————法1:2的n次幂分频————————————————————————
reg clk_div2_r;
reg clk_div4_r;
reg clk_div8_r;
//2分频
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_div2_r <=0;
else
clk_div2_r <=~clk_div2_r;
end
assign clk_div2 =clk_div2_r;
//4分频
always @(posedge clk_div2 or negedge rstn) begin
if(~rstn)
clk_div4_r <=0;
else
clk_div4_r <=~clk_div4_r;
end
assign clk_div4 =clk_div4_r;
//8分频
always @(posedge clk_div4 or negedge rstn) begin
if(~rstn)
clk_div8_r <=0;
else
clk_div8_r <=~clk_div8_r;
end
assign clk_div8 =clk_div8_r;
endmodule //even_div
tb文件
`timescale 1ns/1ns
module tb_even_div();
reg clk;
reg rstn;
wire clk_div2;
wire clk_div4;
wire clk_div8;
//时钟
localparam T = 10;
initial begin
clk =0;
forever #(T/2) clk =~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
even_div even_div_inst (
.clk (clk),
.rstn (rstn),
.clk_div2 (clk_div2),
.clk_div4 (clk_div4),
.clk_div8 (clk_div8)
);
endmodule
波形仿真
如图,100MHz的时钟进行2、4、8分频,仿真结果无误。
2.2 法2:计数翻转
设计思路
偶数n分频,即分频后的时钟clk_out
包含n个原时钟clk
,因此在原时钟上升沿计数,最大计数到n-1后,清零重新计数。用clk_r
存储n分频后的时钟,,当计数达到n/2-1与n-1时,进行时钟翻转,从而得到占空比为50%的n分频时钟。
以6分频为例:012,翻转新时钟,此时经过三个原周期;345,翻转新时钟,此时又经过三个原周期。从而得到六分频时钟。
程序设计
module even_div
#(
parameter n = 6
)
(
input wire clk,
input wire rstn,
output wire clk_out
);
reg [$clog2(n)-1:0] cnt;
reg clk_r;
//————————————————————————法2:任意偶数分频————————————————————————
//计数
always @(posedge clk or negedge rstn) begin
if(~rstn)
cnt <=0;
else if(cnt==n-1)
cnt <=0;
else
cnt <=cnt + 1'b1;
end
//翻转
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_r <=0;
else if(cnt==n/2-1 || cnt==n-1) //如6分频,计数0~5,012,翻转,345,翻转
clk_r <=~clk_r;
end
assign clk_out =clk_r;
endmodule //even_div
tb文件
`timescale 1ns/1ns
module tb_even_div();
localparam n = 6;
reg clk;
reg rstn;
wire clk_out;
//时钟
localparam T =10;
initial begin
clk =0;
forever #(T/2) clk = ~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
even_div # (
.n (n)
)
even_div_inst (
.clk (clk),
.rstn (rstn),
.clk_out (clk_out)
);
endmodule
波形仿真
如图,100MHz的时钟进行6分频,计数到2(n/2-1)和5(n-1)时翻转,仿真结果无误。
2.3 法3:计数翻转(优化版)
设计思路
法2是在计数达到n/2-1与n-1时进行翻转,但实际上0~n/2-1与n/2~n-1的计数是一致的,都为n/2,因此可以只取前半段,每当达到n/2-1时进行一次翻转,可以有效节约计数器资源。
程序设计
module even_div
#(
parameter n = 6
)
(
input wire clk,
input wire rstn,
output wire clk_out
);
reg [$clog2(n/2)-1:0] cnt;
reg clk_r;
//————————————————————————法3:任意偶数分频(优化版)————————————————————————
//计数
always @(posedge clk or negedge rstn) begin
if(~rstn)
cnt <=0;
else if(cnt==n/2-1)
cnt <=0;
else
cnt <=cnt + 1'b1;
end
//翻转
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_r <=0;
else if(cnt==n/2-1) //如6分频,计数0~2,012,翻转,012,翻转
clk_r <=~clk_r;
end
assign clk_out =clk_r;
endmodule //even_div
tb文件
`timescale 1ns/1ns
module tb_even_div();
localparam n = 6;
reg clk;
reg rstn;
wire clk_out;
//时钟
localparam T =10;
initial begin
clk =0;
forever #(T/2) clk = ~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
even_div # (
.n (n)
)
even_div_inst (
.clk (clk),
.rstn (rstn),
.clk_out (clk_out)
);
endmodule
波形仿真
如图,100MHz的时钟进行6分频,计数到2(n/2-1)时翻转,仿真结果无误。
3 奇数分频
将输入时钟信号的频率分为n份,时钟周期扩大到原来的n倍。
如下图所示,输入时钟信号为70MHz,进行七分频后,得到10MHz的时钟,表现为时钟周期分别扩大到原来的7倍。简单计数器无法得到占空比为50%的时钟,下图为七分频后,占空比为1/7~6/7的时钟。
3.1 法1:简单奇数分频
设计思路
奇数n分频无法等分为两份n/2的整数,因此若用类似偶数分频的简单计数方法,无法直接得到占空比为50%的n分频时钟,此时需要选择尽可能均匀的新时钟,最接近的两份是(n-1)/2与(n+1)/2。
以7分频为例,最均匀的整数分配为4+3,在计数达到3((n-1)/2)与6(n-1)时,进行新时钟翻转,即可得到占空比为3/7的七分频时钟。上述情况默认了复位下时钟被赋值低电平,若将复位下赋予时钟高电平,则可得到占空比为4/7的七分频时钟。
程序设计
module odd_div
#(
parameter n = 7
)
(
input wire clk,
input wire rstn,
output wire clk_out
);
reg [$clog2(n)-1:0] cnt;
reg clk_r;
//————————————————————————法1:简单奇数分频————————————————————————
//计数
always @(posedge clk or negedge rstn) begin
if(~rstn)
cnt <=0;
else if(cnt==n-1)
cnt <=0;
else
cnt <=cnt + 1'b1;
end
//翻转
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_r <=0;
else if(cnt==(n-1)/2 || cnt==n-1)
clk_r <=~clk_r;
//else保持省略
end
assign clk_out =clk_r;
endmodule
tb文件
`timescale 1ns/1ns
module tb_odd_div();
localparam n = 7;
reg clk;
reg rstn;
wire clk_out;
//时钟
localparam T =10;
initial begin
clk =0;
forever #(T/2) clk = ~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
//例化
odd_div # (
.n(n)
)
odd_div_inst (
.clk (clk),
.rstn (rstn),
.clk_out (clk_out)
);
endmodule
波形仿真
如下图,得到占空比3/7的七分频时钟。
3.2 法2:均匀奇数分频
设计思路
简单计数翻转无法得到占空比为50%的奇数n分频电路,其根本原因在于无法分成相等的两份整数,最均匀的为两份是(n-1)/2个原时钟周期与(n+1)/2个原时钟周期。再此基础上,若是补上半个时钟周期,即可得到n/2的均匀时钟。提前半个时钟周期即在下降沿进行新时钟翻转,补上半个时钟周期即两次结果进行或操作。
如下图七分频所示,两个7分频时钟在计数器相同数值、不同边沿下产生,相位差为半个时钟周期,占空比都为3/7。将2个时钟进行或操作,得到占空比为0.5的7分频时钟。
程序设计
module odd_div
#(
parameter n = 7
)
(
input wire clk,
input wire rstn,
output wire clk_out
);
reg [$clog2(n)-1:0] cnt;
reg clk_r1, clk_r2;
//————————————————————————法2:均匀奇数分频————————————————————————
//计数
always @(posedge clk or negedge rstn) begin
if(~rstn)
cnt <=0;
else if(cnt==n-1)
cnt <=0;
else
cnt <=cnt + 1'b1;
end
//上升沿翻转
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_r1 <=0;
else if(cnt==(n-1)/2 || cnt==n-1)
clk_r1 <=~clk_r1;
//else保持省略
end
//下降沿翻转
always @(negedge clk or negedge rstn) begin
if(~rstn)
clk_r2 <=0;
else if(cnt==(n-1)/2 || cnt==n-1)
clk_r2 <=~clk_r2;
end
assign clk_out =(clk_r1 | clk_r2);
endmodule
tb文件
`timescale 1ns/1ns
module tb_odd_div();
localparam n = 7;
reg clk;
reg rstn;
wire clk_out;
//时钟
localparam T =10;
initial begin
clk =0;
forever #(T/2) clk = ~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
//例化
odd_div # (
.n(n)
)
odd_div_inst (
.clk (clk),
.rstn (rstn),
.clk_out (clk_out)
);
endmodule
波形仿真
如下图,得到占空比50%的七分频时钟。
4 任意整数分频
设计思路
结合奇数分频与偶数分频。判断分频系数n的奇偶性后选择性输出。
程序设计
module integer_div
#(
parameter n = 7
)
(
input wire clk,
input wire rstn,
output wire clk_out
);
reg [$clog2(n)-1:0] cnt;
reg clk_r1, clk_r2;
wire clk_even, clk_odd;
//————————————————————————————任意整数分频————————————————————————————
always @(posedge clk or negedge rstn) begin
if(~rstn)
cnt <=0;
else if(cnt==n-1)
cnt <=0;
else
cnt <=cnt + 1'b1;
end
//上升沿翻转
always @(posedge clk or negedge rstn) begin
if(~rstn)
clk_r1 <=0;
else if(cnt==(n-1)/2 || cnt==n-1) //7分频,计数0~6,0123翻转,456翻转,低多高少,或门
clk_r1 <=~clk_r1; //else不写默认保持,听群里说不写更稳。
end
//下降沿翻转
always @(negedge clk or negedge rstn) begin
if(~rstn)
clk_r2 <=0;
else if(cnt==(n-1)/2 || cnt==n-1) //7分频,计数0~6,0123翻转,456翻转,低多高少,或门
clk_r2 <=~clk_r2;
end
//选择
assign clk_even =clk_r1;
assign clk_odd =(clk_r1 | clk_r2);
assign clk_out =(n%2==1)? clk_odd:clk_even;
endmodule
tb文件
`timescale 1ns/1ns
module tb_integer_div();
localparam n = 5;
reg clk;
reg rstn;
wire clk_out;
//时钟
localparam T =10;
initial begin
clk =0;
forever #(T/2) clk = ~clk;
end
//赋值
initial begin
rstn =0;
repeat(4) @(posedge clk);
rstn =1;
#1000
$finish();
end
//例化
integer_div # (
.n (n)
)
integer_div_inst (
.clk (clk),
.rstn (rstn),
.clk_out (clk_out)
);
endmodule
波形仿真
100MHz时钟,三分频如下图。
四分频如下图。
五分频如下图。
六分频如下图。