• verilog时钟分频设计


    1.偶分频模块设计

    偶分频意思是时钟模块设计最为简单。首先得到分频系数M和计数器值N。

    M = 时钟输入频率 / 时钟输出频率

    N = M / 2

    如输入时钟为50M,输出时钟为25M,则M=2,N=1。偶分频则意味着M为偶数。

    以M=4,N=2为例,我们希望得到的输出时钟时序如下:


    因此只需要将counter以clk_in为时钟驱动计数,当counter = (N-1)时,clk_out翻转即可。

    verilog代码如下,其中WIDTH为(N的位宽-1):

    module time_adv_even #(
    parameter N = 2,
    	WIDTH = 7
    )
    (
        input clk,
        input rst,
        output reg clk_out
        );
    
    reg [WIDTH:0]counter;
    always @(posedge clk or posedge rst) begin
    	if (rst) begin
    		// reset
    		counter <= 0;
    	end
    	else if (counter == N-1) begin
    		counter <= 0;
    	end
    	else begin
    		counter <= counter + 1;
    	end
    end
    
    always @(posedge clk or posedge rst) begin
    	if (rst) begin
    		// reset
    		clk_out <= 0;
    	end
    	else if (counter == N-1) begin
    		clk_out <= !clk_out;
    	end
    end
    
    endmodule

    testbench测试8分频即N=4,ISE仿真结果如下:



    2.奇分频模块设计

    奇分频需要通过两个时钟共同得到。首先得到分频系数M和计数器值N。

    M = 时钟输入频率 / 时钟输出频率

    N = (M-1) / 2

    如输入时钟为50M,输出时钟为10M,则M=5,N=2。奇分频则意味着M为奇数。

    以M=5,N=2为例,我们希望得到的输出时钟时序如下:


    其中clk_out为最终输出时钟,clk_out1和clk_out2为辅助时钟生成。

    计数器counter由0技术至(M-1)。

    clk_out1在在clk_in的上升延跳变,条件是counter==(N-1)或(M-1)。

    clk_out2在在clk_in的下降延跳变,条件是counter==(N-1)或(M-1)。

    之后clk_out = clk_out1 & clk_out2即可得到M分频的时钟。

    verilog代码如下,其中WIDTH为(N的位宽-1):

    module time_adv_odd #(
    parameter N = 2,
    	WIDTH = 7
    	 )(
        input clk,
        input rst,
        output clk_out
        );
    
    reg [WIDTH:0]counter;
    always @(posedge clk or posedge rst) begin
    	if (rst) begin
    		// reset
    		counter <= 0;
    	end
    	else if (counter == (N << 1)) begin
    		counter <= 0;
    	end
    	else begin
    		counter <= counter + 1;
    	end
    end
    
    reg clk_out1;
    always @(posedge clk or posedge rst) begin
    	if (rst) begin
    		// reset
    		clk_out1 <= 0;
    	end
    	else if (counter == N-1) begin
    		clk_out1 <= !clk_out1;
    	end
    	else if (counter == (N << 1)) begin
    		clk_out1 <= !clk_out1;
    	end
    end
    
    reg clk_out2;
    always @(negedge clk or posedge rst) begin
    	if (rst) begin
    		// reset
    		clk_out2 <= 0;
    	end
    	else if (counter == N-1) begin
    		clk_out2 <= !clk_out2;
    	end
    	else if (counter == (N << 1)) begin
    		clk_out2 <= !clk_out2;
    	end
    end
    
    assign clk_out = clk_out1 & clk_out2;
    endmodule
    testbench测试9分频即N=4,ISE仿真结果如下:



    3.半分频模块设计

    半分频即2.5分频等,设计最为复杂。首先得到分频系数M:

    M = 时钟输入频率 / 时钟输出频率

    如输入为50M,输入为20M则分频系数为2.5。此次设计未能完成占空比为50%的半分频。

    以M=2.5为例,我们希望得到的输出时钟时序如下:


    可以看出输出时钟的两个上升沿之间为2.5个输入时钟周期。

    设计的关键在于信号维持半个周期的处理,因此引入了辅助信号clk_cnt,clk_vld。

    clk_vld信号受到clk_out的驱动,检测到clk_out的上升沿时,信号翻转。

    clk_cnt信号受到clk_in驱动,受clk_vld控制,当clk_vld==0时,clk_cnt = clk_in;当clk_vld==1时,clk_cnt = !clk_in。

    counter信号受clk_cnt驱动,计数(M-0.5)时归零。

    clk_out信号受clk_cnt驱动,当counter == (M-1.5)或counter == (M-0.5)时翻转。(此处在仿真时做了改变,见下方)

    M=2.5时,时序分析如下:

    第一步:reset之后,clk_vld==0,clk_cnt = clk_in,counter由0开始计数;

    第二步:counter == 1时,clk_out在clk_cnt的上升沿处跳变为1,引起clk_vld->1,进而clk_cnt = !clk_in,这意味着clk_cnt立即由1归为0;

    第三步:半个周期后,clk_cnt 在此迎来上升沿,此时counter == 2,clk_out在clk_cnt的上升沿处跳变为0,counter也归0;(实现了信号维持半个周期)

    第四步:继续正常计数,counter == 1时,clk_out在clk_cnt的上升沿处跳变为1,引起clk_vld->0,进而clk_cnt = clk_in,这意味着clk_cnt立即由1归为0;

    第五步:重复至第一步。


    然而在有一次的电路仿真中,可能受到仿真工具时钟采样影响,调整为

    当counter == (M-0.5)或counter == 0时翻转

    实现了正确的时钟分频。因此该值可能需要根据工具和开发板调整,或者说这样的设计是不可靠的,当然了如果需要半分频时候最好还是通过pll实现吧。

    理论上是当counter == (M-0.5)或counter == (M-1.5)时翻转没错,给出的代码和波形也是这样的

    verilog代码如下,参数M实际为分频系数-0.5(即3.5->3),WIDTH为(M的位宽-1):

    module time_adv_half #(
    	parameter M = 2,
    		WIDTH = 7
    )(
        input clk,
        input rst,
        output reg clk_out
        );
    
    wire clk_cnt;
    assign clk_cnt = (clk_vld) ? !clk : clk;
    
    reg [WIDTH : 0]counter;
    always @(posedge clk_cnt or posedge rst) begin
    	if (rst) begin
    		// reset
    		counter <= 0;
    	end
    	else if (counter == M) begin
    		counter <= 0;
    	end
    	else begin
    		counter <= counter + 1;
    	end
    end
    
    reg clk_vld;
    always @(posedge clk_out or posedge rst) begin
    	if (rst) begin
    		// reset
    		clk_vld <= 0;
    	end
    	else begin
    		clk_vld <= !clk_vld;
    	end
    end
    
    always @(posedge clk_cnt or posedge rst) begin
    	if (rst) begin
    		// reset
    		clk_out <= 0;
    	end
    	else if (counter == M-1) begin
    		clk_out <= !clk_out;
    	end
    	else if (counter == M) begin
    		clk_out <= !clk_out;
    	end
    end
    
    endmodule
    M=3时候的仿真波形如下:


    可以看出clk_out两个上升沿之间为3.5个输入时钟周期。


    附:testbench

     `timescale 1 ns / 1 ps
    module TEST_gate;
    	reg clk, rst;
    	wire clk_out_even, clk_out_odd, clk_out_half;
    	
    	initial begin  
    		clk = 1'b0;  
    		forever #10 clk = ~clk;  
    	end
    	
    	initial begin  
    		rst = 1'b0;  
    		#2 rst = 1'b1; 
    		#9 rst = 1'b0; 
    	end
    
        time_adv_even #(
    	   .N(4)
    		,.WIDTH(5)
    	   )u0
    		(
        	.clk 		(clk)
        	,.rst 		(rst)
        	,.clk_out 	(clk_out_even)
        	);
    		
    		time_adv_odd #(
    	   .N(4)
    		,.WIDTH(5)
    	   )u1
    		(
        	.clk 		(clk)
        	,.rst 		(rst)
        	,.clk_out 	(clk_out_odd)
        	);
    		
    		time_adv_half #(
    	   .M(3)
    		,.WIDTH(5)
    	   )u2
    		(
        	.clk 		(clk)
        	,.rst 		(rst)
        	,.clk_out 	(clk_out_half)
        	);
    endmodule
    波形


  • 相关阅读:
    OpenCascade Ray Tracing Rendering
    Create New Commands in Tcl
    OpenCascade Modeling Algorithms Fillets and Chamfers
    OpenCascade Modeling Algorithms Boolean Operations
    Construction of Primitives in Open Cascade
    Open Cascade Data Exchange STL
    Tcl Tk Introduction
    Open Cascade DataExchange IGES
    Netgen mesh library : nglib
    Hello Netgen
  • 原文地址:https://www.cnblogs.com/mingmingruyue99/p/7202000.html
Copyright © 2020-2023  润新知