• 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
    波形


  • 相关阅读:
    C++ allocator
    C++操作符重载
    Theron (C++ concurrency library) 读后感
    第五章 [BX]和loop指令
    第四章 第一个程序
    第三章 寄存器(内存访问)
    第二章 寄存器
    第一章
    jquery下ajax异步执行操作笔记
    CSSFlex布局
  • 原文地址:https://www.cnblogs.com/mingmingruyue99/p/7202000.html
Copyright © 2020-2023  润新知