Verilog实现奇分频电路

发布时间 2023-03-28 21:00:23作者: 余你余生

在FPGA中,计数器电路用途很广,一般计数器电路都可作为分频电路。实现占空比为50的偶分频电路很好实现。但实现占空比为50的奇分频电路有点难度。下面给出一个简单例子,记录学习奇分频电路的过程。

实现占空比为50的5分频电路,高低电平应都为2.5个时钟周期。即应当在上升沿和下降沿都要采样,这样才会产生出0.5个时钟周期的差。

公式为 奇分频 N  边沿的采集 每隔 N/2    翻转    和  每隔 N-1  翻转。

 

 仿真波形图如下所示:

上升沿:

下降沿:

整体:

 

其中在上升沿采集中,低电平持续3个时钟周期,高电平持续2个时钟周期。其中在下升沿采集中,低电平持续3个时钟周期,高电平持续2个时钟周期。上升沿与下降沿之间错0.5个时钟周期,即整体为2.5+2.5=5。以后奇分频都可以这样操作。

下附代码和测试激励。

module divfreq(
    input                   clk, 
    input                   rst_n, 
    output                  clk1x,
    output    reg           clk1xpose,
    output    reg           clk1xnege, 
    output    reg [2:0]     coutpose,
    output    reg [2:0]     coutnege
);
parameter  N=5;
parameter div1 = N/2 , div2 = N-1;  // div1 = 5 / 2, div2 = 5 - 1

always@(posedge clk or negedge rst_n) //上升沿触发波形
    begin
        if(!rst_n)
            clk1xpose = 0;
        else if(coutpose == div1)
            clk1xpose = ~clk1xpose;
        else if(coutpose == div2)
            clk1xpose = ~clk1xpose;
        else
            clk1xpose = clk1xpose;
    end

always@(negedge clk or negedge rst_n)   //下降沿触发波形
    begin
        if(!rst_n)
            clk1xnege = 0;
        else if(coutnege == div1)
            clk1xnege = ~clk1xnege;
        else if(coutnege == div2)
            clk1xnege = ~clk1xnege;
        else
            clk1xnege = clk1xnege;
    end
 
always@(posedge clk or negedge rst_n)  //上升沿计数5
    begin
        if(!rst_n)
            coutpose = 0;
        else if(coutpose == div2)
            coutpose = 0;
        else
            coutpose = coutpose + 1;
    end
 
always@(negedge clk or negedge rst_n) //下降沿计数5
    begin
        if(!rst_n)
            coutnege = 0;
    else if(coutnege == div2)
            coutnege = 0;
    else
            coutnege = coutnege + 1;
    end
    
assign clk1x = clk1xpose | clk1xnege; //输出波形

endmodule
`timescale 1ns/1ns
module tb_divfreq  ();

reg              clk        ; 
reg              rst_n      ;
wire             clk1x      ;
wire             clk1xpose  ;
wire             clk1xnege  ;
wire [2:0]       coutpose   ;
wire [2:0]       coutnege   ;


initial
    begin
        clk     =  1'b1       ;
        rst_n   <=  1'b0      ;
        #10
        rst_n   <=1'b1        ;
    end 

always  #10  clk=~clk;

divfreq     divfreq_inst     (
    .clk        (clk)    , 
    .rst_n      (rst_n)    , 
    .clk1x      (clk1x)    ,
    .clk1xpose  (clk1xpose)    ,
    .clk1xnege  (clk1xnege)    , 
    .coutpose   (coutpose)    ,
    .coutnege   (coutnege)
);

endmodule

后附正点原子中的另外一种代码风格,但整体思路一致。

波形仿真:

上升沿:

下降沿:

整体:

后附正点原子奇分频代码和激励代码。

module divfreq1
( 
    input clk ,  // system clock 50Mhz on board
    input rst_n,  // system rst, low active 
    output out_clk // output signal
);

parameter N = 5 ;

reg [N/2 :0] cnt_1 ;
reg [N/2 :0] cnt_2 ;

reg out_clk1 ;
reg out_clk2 ;

//=====================================================================
// ------------------------- MAIN CODE -------------------------------
//=====================================================================
always @(posedge clk or negedge rst_n) begin //上升沿输出 out_clk1
    if(!rst_n) begin
        out_clk1 <= 0;
        cnt_1 <= 1; //这里计数器从 1 开始
    end
    else begin
        if(out_clk1 == 0) begin
            if(cnt_1 == N/2+1) begin
                out_clk1 <= ~out_clk1;
                cnt_1 <= 1;
            end
            else
            cnt_1 <= cnt_1+1;
        end
        else if(cnt_1 == N/2) begin
            out_clk1 <= ~out_clk1;
            cnt_1 <= 1;
        end
    else
        cnt_1 <= cnt_1+1;
    end
end

always @(negedge clk or negedge rst_n) begin //下降沿输出 out_clk2
    if(!rst_n) begin
        out_clk2 <= 0;
        cnt_2 <= 1; //这里计数器从 1 开始
    end
    else begin
        if(out_clk2 == 0) begin
            if(cnt_2 == N/2+1) begin
                out_clk2 <= ~out_clk2;
                cnt_2 <= 1;
            end
            else
                cnt_2 <= cnt_2+1;
        end
        else if(cnt_2 == N/2) begin
            out_clk2 <= ~out_clk2;
            cnt_2 <= 1;
        end
        else
            cnt_2 <= cnt_2+1;
    end
end

assign out_clk = out_clk1 | out_clk2;   //输出最后波形

endmodule
`timescale 1ns/1ns
module tb_divfreq1  ();

reg              clk        ; 
reg              rst_n      ;
wire             out_clk      ;


initial
    begin
        clk     =  1'b1       ;
        rst_n   <=  1'b0      ;
        #10
        rst_n   <=1'b1        ;
    end 

always  #10  clk=~clk;

divfreq1  divfreq1_inst
( 
    .clk     (clk)       ,  // system clock 50Mhz on board
    .rst_n   (rst_n)        ,  // system rst, low active 
    .out_clk (out_clk)            // output signal
);

endmodule

参考资料:

1、正点原子逻辑设计指南

2、某奇分频笔试题