1. 程式人生 > 其它 >使用FPGA實現奇數50%佔空比分頻

使用FPGA實現奇數50%佔空比分頻

技術標籤:FPGA

奇數分頻: 如果要實現佔空比為50%的奇數倍分頻,不能同偶數分頻一樣計數器記到一半的時候輸出時鐘翻轉,那樣得不到佔空比50%的時鐘。以待分頻時鐘CLK為例,如果以偶數分頻的方法來做奇數分頻,在CLK上升沿觸發,將得到不是50%佔空比的一個時鐘訊號(正週期比負週期多一個時鐘或者少一個時鐘);但是如果在CLK下降沿也觸發,又得到另外一個不是50%佔空比的時鐘訊號,這兩個時鐘相位正好相差半個CLK時鐘週期。通過這兩個時鐘訊號進行邏輯運算我們可以巧妙的得到50%佔空比的時鐘。

總結如下:對於實現佔空比為50%的N倍奇數分頻,首先進行上升沿觸發進行模N計數,計數選定到某一個值進行輸出時鐘翻轉,然後經過(N-1)/2再次進行翻轉得到一個佔空比非50%奇數n分頻時鐘。再者同時進行下降沿觸發的模N計數,到和上升沿觸發輸出時鐘翻轉選定值相同值時,進行輸出時鐘時鐘翻轉,同樣經過(N-1)/2時,輸出時鐘再次翻轉生成佔空比非50%的奇數n分頻時鐘。兩個佔空比非50%的n分頻時鐘進行邏輯運算(正週期多的相與,負週期多的相或),得到佔空比為50%的奇數n分頻時鐘。

在這裡插入圖片描述

Verilog程式碼

// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ******************************************************************** // File name : divide.v // Module name : divide // Author : STEP // Description : clock divider // Web : www.stepfpga.com // // -------------------------------------------------------------------- // Code Revision History : //
-------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:任意整數時鐘分頻 module divide ( clk,rst_n,clkout); input clk,rst_n; //輸入訊號,其中clk連線到FPGA的C1腳,頻率為12MHz output clkout; //輸出訊號,可以連線到LED觀察分頻的時鐘 //parameter是verilog裡常數語句 parameter WIDTH = 3; //計數器的位數,計數的最大值為 2**WIDTH-1 parameter N = 5; //分頻係數,請確保 N < 2**WIDTH-1,否則計數會溢位 reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p為上升沿觸發時的計數器,cnt_n為下降沿觸發時的計數器 reg clk_p,clk_n; //clk_p為上升沿觸發時分頻時鐘,clk_n為下降沿觸發時分頻時鐘 //上升沿觸發時計數器的控制 always @ (posedge clk or negedge rst_n ) //posedge和negedge是verilog表示訊號上升沿和下降沿 //當clk上升沿來臨或者rst_n變低的時候執行一次always裡的語句 begin if(!rst_n) cnt_p<=0; else if (cnt_p==(N-1)) cnt_p<=0; else cnt_p<=cnt_p+1; //計數器一直計數,當計數到N-1的時候清零,這是一個模N的計數器 end //上升沿觸發的分頻時鐘輸出,如果N為奇數得到的時鐘佔空比不是50%;如果N為偶數得到的時鐘佔空比為50% always @ (posedge clk or negedge rst_n) begin if(!rst_n) clk_p<=0; else if (cnt_p<(N>>1)) //N>>1表示右移一位,相當於除以2去掉餘數 clk_p<=0; else clk_p<=1; //得到的分頻時鐘正週期比負週期多一個clk時鐘 end //下降沿觸發時計數器的控制 always @ (negedge clk or negedge rst_n) begin if(!rst_n) cnt_n<=0; else if (cnt_n==(N-1)) cnt_n<=0; else cnt_n<=cnt_n+1; end //下降沿觸發的分頻時鐘輸出,和clk_p相差半個時鐘 always @ (negedge clk) begin if(!rst_n) clk_n<=0; else if (cnt_n<(N>>1)) clk_n<=0; else clk_n<=1; //得到的分頻時鐘正週期比負週期多一個clk時鐘 end assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p; //條件判斷表示式 //當N=1時,直接輸出clk //當N為偶數也就是N的最低位為0,N(0=0,輸出clk_p //當N為奇數也就是N最低位為1,N(0=1,輸出clk_p&clk_n。正週期多所以是相與 endmodule

測試檔案
進行功能模擬時需要編寫testbench測試檔案。verilog裡的testbench檔案和原始檔一樣也是.v檔案,模擬能讓我們更直觀的觀察訊號波形,可以先閱讀Diamond的使用瞭解如何使用Diamond中整合的模擬工具。

// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name    : divide_tb.v
// Module name  : divide_tb
// Author       : STEP
// Description  : clock divider
// Web          : www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History : 
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2017/03/02   |Initial ver
// --------------------------------------------------------------------
// Module Function:divide.v時鐘分頻器的測試檔案
 
          `timescale 1ns/100ps                             //模擬時間單位/時間精度,時間單位要大於或者等於時間精度
 
          module divide_tb();                              //測試檔案也是一個module,因為用於模擬所以無需輸入輸出訊號
 
          reg    clk,rst_n;                                //需要產生的激勵訊號定義,激勵訊號需要過程塊產生所以定義為reg型變數
          wire   clkout;                                   //需要觀察的輸出訊號定義,定義為wire型變數
 
          //初始化過程塊
          initial 
	  begin 
		 clk = 0;
		 rst_n = 0;
		 #25                                      //#表示延時25個時間單位
		 rst_n = 1;                               //產生了一個初始25ns低電平,然後變高電平的復位訊號
	  end
 
          always #10 clk = ~clk;                          //每隔10ns翻轉一次clk訊號,也就是產生一個時鐘週期20ns的clk,頻率為50MHz  
 
          //module呼叫例化格式
          divide  #(.WIDTH(4),.N(11))  u1 (                         //#後面的()中為引數傳遞,如果不傳遞引數就是所呼叫模組中的引數預設值
                                                                   //divide表示所要例化的module名稱,u1是我們定義的例化名稱,必須以字母開頭
						.clk	(clk),     //輸入輸出訊號連線。 .clk表示module本身定義的訊號名稱;(clk)表示我們在這裡定義的激勵訊號
						.rst_n	(rst_n),   //在testbench裡定義的訊號名稱可以與所要呼叫module的埠訊號名稱不同
						.clkout	(clkout)   
					  );
         endmodule
//分頻,實現從500MHZ到100MHZ的時鐘,這樣可以保持輸出和輸入時鐘同步,觀測輸出資料是否是正常的
//reg clk_p;
//reg [3:0] cnt_p;
//parameter   N = 4'd5;
//[email protected](posedge clk_500M or negedge rst_n)
//begin
//	if(!rst_n)
//		cnt_p <= 4'd0;
//	else if(cnt_p == N-1)
//		cnt_p <= 4'd0;
//	else
//	 cnt_p <= cnt_p + 1'b1;
//end
//always @(posedge clk_500M or negedge rst_n)
//begin
//	if(!rst_n)
//		clk_p <= 1'b0;
//	else if(cnt_p == (N-1)/2)
//		clk_p <= ~clk_p;
//	else if(cnt_p == N-1)
//		clk_p <= ~clk_p;
//	else 
//		clk_p <= clk_p;
//end
//reg clk_n;
//reg [3:0] cnt_n;
//[email protected](posedge clk_500M or negedge rst_n)
//begin
//	if(!rst_n)
//		cnt_n <= 4'd0;
//	else if(cnt_n == N-1)
//		cnt_n <= 4'd0;
//	else
//	 cnt_n <= cnt_n + 1'b1;
//end
//always @(posedge clk_500M or negedge rst_n)
//begin
//	if(!rst_n)
//		clk_n <= 1'b0;
//	else if(cnt_n == (N-1)/2)
//		clk_n <= ~clk_n;
//	else if(cnt_n == N-1)
//		clk_n <= ~clk_n;
//	else 
//		clk_n <= clk_n;
//end
//assign clk_100L = clk_p|clk_n;
/////////////////////////////////////////

參考文獻
[1] FPGA時鐘分頻