手撕代码——任意奇数分频
一、奇数分频器原理与设计
在上文《手撕代码——任意偶数分频》中,我们编写任意偶数分频的Verilog代码,对时钟进行偶数分频,只需要用到时钟的上升沿或者下降沿即可,而要进行N倍奇数分频,需要同时用到时钟的上升沿和下降沿。对上升沿和下降沿分别设计一个上升沿计数器posedge_cnt和一个下降沿计数器negedge_cnt,计数器的位宽为 l o g 2 N log_2N log2N。
//上升沿计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
posedge_cnt <= 'd0;
else if(posedge_cnt == DIV_PARAM-1)
posedge_cnt <= 'd0;
else
posedge_cnt <= posedge_cnt + 1'b1;
end
//下降沿计数器
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
negedge_cnt <= 'd0;
else if(negedge_cnt == DIV_PARAM-1)
negedge_cnt <= 'd0;
else
negedge_cnt <= negedge_cnt + 1'b1;
end
同时根据上升沿计数器和下降沿计数器得到两个不同的时钟信号clk_1和clk_2,对这两个时钟信号clk_1和clk_2进行运算,得到目的分频时钟信号。在这里,可以通过或操作得到目的时钟信号。
//或操作
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_1 <= 1'b0;
else if(posedge_cnt > (DIV_PARAM>>1))
clk_1 <= 1'b1;
else
clk_1 <= 1'b0;
end
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_2 <= 1'b0;
else if(negedge_cnt > (DIV_PARAM>>1))
clk_2 <= 1'b1;
else
clk_2 <= 1'b0;
end
assign clk_o = clk_1 || clk_2;
同样的,也可以通过与操作得到目的分频时钟信号。
//与操作
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_1 <= 1'b0;
else if(posedge_cnt > (DIV_PARAM>>1)-1)
clk_1 <= 1'b1;
else
clk_1 <= 1'b0;
end
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_2 <= 1'b0;
else if(negedge_cnt > (DIV_PARAM>>1)-1)
clk_2 <= 1'b1;
else
clk_2 <= 1'b0;
end
assign clk_o = clk_1 && clk_2;
也可以通过异或操作得到目的分频时钟信号。
//异或操作
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_1 <= 1'b0;
else if(posedge_cnt == DIV_PARAM-1)
clk_1 <= ~clk_1;
else
clk_1 <= clk_1;
end
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_2 <= 1'b0;
else if(negedge_cnt == (DIV_PARAM>>1))
clk_2 <= ~clk_2;
else
clk_2 <= clk_2;
end
assign clk_o = clk_1 ^ clk_2;
二、完整代码与仿真文件
任意奇数分频器代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/22 11:37:51
// Design Name:
// Module Name: clk_divide_odd
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module clk_divide_odd
#(
parameter DIV_PARAM = 3 //分频系数(奇数)
)
(
input sys_clk ,
input sys_rst_n,
output clk_o
);
parameter CNT_WIDTH = $clog2(DIV_PARAM); //计数器位宽
reg [CNT_WIDTH-1:0] posedge_cnt; //上升沿计数器
reg [CNT_WIDTH-1:0] negedge_cnt; //下降沿计数器
reg clk_1; //时钟1
reg clk_2; //时钟2
//上升沿计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
posedge_cnt <= 'd0;
else if(posedge_cnt == DIV_PARAM-1)
posedge_cnt <= 'd0;
else
posedge_cnt <= posedge_cnt + 1'b1;
end
//下降沿计数器
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
negedge_cnt <= 'd0;
else if(negedge_cnt == DIV_PARAM-1)
negedge_cnt <= 'd0;
else
negedge_cnt <= negedge_cnt + 1'b1;
end
//---------------------------------------------------------//
//或操作
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_1 <= 1'b0;
else if(posedge_cnt > (DIV_PARAM>>1))
clk_1 <= 1'b1;
else
clk_1 <= 1'b0;
end
always @(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_2 <= 1'b0;
else if(negedge_cnt > (DIV_PARAM>>1))
clk_2 <= 1'b1;
else
clk_2 <= 1'b0;
end
assign clk_o = clk_1 || clk_2;
endmodule
仿真代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/22 14:13:28
// Design Name:
// Module Name: tb_clk_divide_odd
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_clk_divide_odd();
parameter DIV_PARAM = 5; //分频系数(奇数)
reg sys_clk ;
reg sys_rst_n;
wire clk_o ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #5 sys_clk = ~sys_clk;
clk_divide_odd
#(
.DIV_PARAM(DIV_PARAM) //分频系数(奇数)
)
clk_divide_odd
(
.sys_clk (sys_clk ),
.sys_rst_n(sys_rst_n),
.clk_o (clk_o )
);
endmodule
三、仿真结果
分别对使用或操作、与操作、异或操作的奇数分频器进行仿真。
(1)或操作奇数分频
(2)与操作奇数分频
(3)异或操作奇数分频
仿真通过。