奇數分頻和偶數分頻的Verilog實現——對分頻本質上的理解

 

實現信號分頻的Verilog方法有很多種,如果從本質上理解了分頻這一思路則遇到任何的分頻情況都能迎刃而解。因此首先來談談什麼是分頻。

對於偶數分屏,如四分頻,我們可以發現其分頻後的時鐘的跳變,無論是上升沿還是下降沿都是統一在原始時鐘信號的上升沿如clk1, 或者是下降沿如clk2。如下圖所示;

因此對於偶數的分頻,我們可以用一個計數器來實現;

module div_2(clk,rst,clk2);
input clk,rst;
output clk2;
reg [2:0] cnt;
always@(posedge clk or negedge rst)
    if(!rst)
		begin
			cnt<=0;
			clk2<=0;
		end
    else if(cnt == 3'b011)
		begin
			cnt <= 0
			clk2 = clk2;
		end
	else if(cnt == 0)
		begin
			cnt <= cnt +1;
			clk2 = ~clk2
		end
	else
		begin
			cnt <= cnt + 1;
			clk2 = clk;
		end
	
endmodule

而對於奇數分頻,顯然一個輸出時鐘的週期包含原始週期個數爲奇數個,則該輸出的時鐘的跳變則必定會出現在原始時鐘的不同跳變沿上,如圖2所示,這是一個5分頻的信號圖。可以看到原始信號clk的上升沿和下降沿都可能存在着clk_out的跳變。而計數器實現的倍頻信號則只能實現但跳變沿的輸出時鐘,因此,該奇數分頻的信號需要兩個不同跳變沿的輸出信號(Sig_1和Sig_2)進行合成。所以通過分析clk_out的波形,即當clk_out在clk上升沿時跳變時(紅色線),則讓Sig_2起作用;反之,則讓Sig_1起作用(藍線)。因此我們可以得到兩個不是50%佔空比的時鐘信號Sig_1和Sig_2.因此我們可以開始用計數器實現這一功能,但相比偶數計數器,這兩個計數器需要在0和中間某個數字處進行翻轉,當設計5分頻時,該數字爲3。推理可得當需要設計N分頻時鐘時,需要在0和(N+1)/2處設置翻轉。

示例如下,當設計13分頻的時鐘信號時需要在0和7處進行翻轉:

module tclk_gena(clk, rst_n,tclk);
	input clk;
	input rst_n;

	reg [4:0] cnt1;
	reg [4:0] cnt2;
	output wire tclk;
	
	reg clk1;
	reg clk2;

	always@(posedge clk or negedge rst_n)
		if(!rst_n)
			begin
				cnt1 <= 0;
				clk1 = 1'b1;
			end	
		else if(cnt1 == 0)
			begin
				cnt1 <= cnt1 + 1'b1;
				clk1 = ~clk1;
			end	
		else if(cnt1 == 4'b00111)
			begin
				cnt1 <= cnt1 + 1'b1;
				clk1 = ~clk1;
			end
		else if(cnt1 == 4'b01100)
			begin
				cnt1 <= 4'b00000;
				clk1 <= clk1;
			end
		else
			begin
			cnt1 <= clk1;
			cnt1 <= cnt1 + 4'b1;
			end
	
	always@(negedge clk or negedge rst_n)
		if(!rst_n)
			begin
				cnt2 <= 0;
				clk2 = 1'b1;
			end	
		else if(cnt2 == 0)
			begin
				cnt2 <= cnt2 + 1'b1;
				clk2 = ~clk2;
			end	
		else if(cnt2 == 4'b00111)
			begin
				cnt2 <= cnt2 + 1'b1;
				clk2 = ~clk2;
			end
		else if(cnt2 == 4'b01100)
			begin
				cnt2 <= 4'b00000;
				clk2 <= clk2;
			end
		else
			begin
			cnt2 <= clk2;
			cnt2 <= cnt2 + 4'b1;
			end
		
	assign tclk = ~(clk1 | clk2);
		
endmodule 

測試代碼如下

`timescale 1ns/1ns
`define clk_period 20
module tclk_gena_tb;

	reg clk;
	reg rst_n;
	
	wire tclk;
	
	tclk_gena tclk_gena(
			 .clk(clk),
			 .rst_n(rst_n),
			 .tclk(tclk)
			 );
	initial
	clk = 1'b1;
	always #(`clk_period/2) clk = ~clk;
	initial
	begin
	rst_n = 0;
	#(`clk_period*20);
	rst_n = 1;
	#(`clk_period*200);
	$stop;
	end
	
	
endmodule 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章