手撕代码——任意奇数分频

手撕代码——任意奇数分频

一、奇数分频器原理与设计

  在上文《手撕代码——任意偶数分频》中,我们编写任意偶数分频的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)或操作奇数分频

手撕代码——任意奇数分频_第1张图片
(2)与操作奇数分频

手撕代码——任意奇数分频_第2张图片
(3)异或操作奇数分频

手撕代码——任意奇数分频_第3张图片
  仿真通过。

你可能感兴趣的:(#,手撕代码,fpga开发,奇数分频,Verilog)