常用計數器的verilog實現(binary、gray、one-hot、LFSR、環形、扭環形)


2013-06-15 22:11:35

常用計數器的verilog實現(binary、gray、one-hot、LFSR、環形、扭環形)

代碼測試功能正確,時間有限,錯誤難免;如有錯誤,歡迎指正。


 binary(二進制)計數器

很簡單,可根據需要完成同步或異步復位、置數、使能的功能。

在ISE的language template中有各種計數器,可進行參考。下面給出一個帶有同步復位、使能、置數端的計數器。

8bit二進制計數器的代碼:

 1 module binary_counter(
 2                             rst_n,
 3                             clk,
 4                             en,
 5                             load,
 6                             cnt_load,
 7                             cnt
 8                                  );
 9                                  
10 parameter CNT_SIZE = 8;
11 
12 input rst_n;
13 input clk;
14 input en;
15 input load;
16 input [CNT_SIZE - 1 : 0] cnt_load;
17 
18 output [CNT_SIZE - 1 : 0] cnt;
19 reg [CNT_SIZE - 1 : 0] cnt;
20 
21 //在ISE的language template中有各種計數器,可進行參考
22 //下面給出一個帶有同步復位、使能、置數端的計數器
23 
24 always@(posedge clk)
25     if(!rst_n)
26         cnt <= 8'd0;
27     else if(en)
28         if(load)
29             cnt <= cnt_load;
30         else 
31             cnt <= cnt + 1;
32 
33 endmodule

  gray(格雷碼)計數器

優點、應用場合
  • 格雷碼的特點決定了它適用於數據傳輸,比如在異步時鍾域之間傳遞計數結果而用到的計數器。常見的異步FIFO空滿信號的信號就是用格雷碼進行比較的(因為格雷碼計數器計數時相鄰的數之間只有一個bit發生了變化,例如:000-001-011-010-110-111-101-100
  • 也常用在狀態機的狀態編碼
  • 而由於格雷碼是一種變權碼,每一位碼沒有固定的大小,很難直接進行比較大小和算術運算,因此在實際的數據運算中並不使用格雷碼,如異步FIFO中讀寫地址仍然是使用二進制編碼
簡介
Gray Code是由貝爾實驗室的Frank Gray在20世紀40年代提出的(是1880年由法國工程師Jean-Maurice-EmlleBaudot發明的), 用來在使用PCM(Pusle Code Modulation)方法傳送訊號時避免出錯,並於1953年3月17日取得美國專利。
格雷碼是一種絕對 編碼方式,典型格雷碼是一種具有 反射特性和循環特性的 單步自補碼,它的循環、單步特性 消除了隨機取數時出現重大誤差的可能,它的反射、自補特性使得求反非常方便格雷碼屬於可靠性編碼,是一種錯誤最小化的 編碼方式,因為,雖然自然二進制碼可以直接由數/模轉換器轉換成 模擬信號,但在某些情況,例如從十進制的3轉換為4時二進制碼的每一位都要變,能使數字電路產生很大的尖峰電流脈沖。而 格雷碼則沒有這一缺點,它在相鄰位間轉換時,只有一位產生變化。它大大地減少了由一個狀態到下一個狀態時邏輯的混淆。由於這種編碼相鄰的兩個碼組之間只有一位不同,因而在用於風向的轉角位移量-數字量的轉換中,當風向的轉角位移量發生微小變化(而可能引起數字量發生變化時, 格雷碼僅改變一位,這樣與其它編碼同時改變兩位或多位的情況相比更為可靠,即可減少出錯的可能性。

 與普通二進制碼之間的轉換

一般的,普通二進制碼與格雷碼可以按以下方法互相轉換( 如果覺得不好記,會混淆,實際應用時,可舉例驗證):
二進制碼->格雷碼(編碼):從最右邊一位起,依次將每一位與左邊一位異或(XOR),作為對應格雷碼該位的值,最左邊一位不變(相當於左邊是0);
即gray_cnt = (bin_cnt>>1) ^ bin_cnt;
格雷碼-〉二進制碼(解碼):從左邊第二位起,將每位與左邊一位解碼后的值異或,作為該位解碼后的值(最左邊一位依然不變)。
bin_cnt[7] = gray_cnt[7];
bin_cnt[6] = gray_cnt[6]^bin_cnt[7]=gray_cnt[6]^gray_cnt[7];
bin_cnt[5] = gray_cnt[5]^bin_cnt[6]=gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
bin_cnt[4] = gray_cnt[4]^bin_cnt[5]=gray_cnt[4]^gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
bin_cnt[3] = gray_cnt[3]^bin_cnt[2]=gray_cnt[3]^gray_cnt[4]^gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
bin_cnt[2] = gray_cnt[2]^bin_cnt[3]=gray_cnt[2]^gray_cnt[3]^gray_cnt[4]^gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
bin_cnt[1] = gray_cnt[1]^bin_cnt[2]=gray_cnt[1]^gray_cnt[2]^gray_cnt[3]^gray_cnt[4]^gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
bin_cnt[0] = gray_cnt[0]^bin_cnt[1]=gray_cnt[0]^gray_cnt[1]^gray_cnt[2]^gray_cnt[3]^gray_cnt[4]^gray_cnt[5]^gray_cnt[6]^gray_cnt[7];
verilog實現:
格雷碼計數可以用兩種方式實現,一種是狀態機,但是如果計數器的位數很大,比如6位,就得用至少64個狀態,非常麻煩,另外一種方法是設計一個二進制計數器,通過它來計數,然后利用binary-gray的編碼就可以得到對應的格雷碼計數器。
還有一種方法,不是很常用,也很復雜,是通過組合邏輯直接產生格雷碼的,而不需要先產生二進制,再進行轉換。個人感覺沒什么優點,具體參見文章:“多位格雷碼計數器的VerilogHDL描述方法”。

 8bit格雷碼計數器的代碼:

 1 module gray_counter(
 2                             rst_n,
 3                             clk,
 4                             bin_cnt,  //輸出二進制,可用於同步時鍾域的計算、比較等
 5                             gray_cnt     //輸出格雷碼,可用於異步傳輸
 6                                  );
 7 
 8 parameter CNT_SIZE = 8;
 9 
10 input rst_n;
11 input clk;
12 
13 output [CNT_SIZE - 1 : 0] bin_cnt;
14 output [CNT_SIZE - 1 : 0] gray_cnt;
15 
16 reg [CNT_SIZE - 1 : 0] bin_cnt_tmp;
17 wire [CNT_SIZE - 1 : 0] gray_cnt_tmp;
18 
19 reg [CNT_SIZE - 1 : 0] bin_cnt;
20 reg [CNT_SIZE - 1 : 0] gray_cnt;
21 
22 //二進制轉換為格雷碼
23 assign gray_cnt_tmp = (bin_cnt_tmp>>1) ^ bin_cnt_tmp;
24 
25 //二進制計數
26 always@(posedge clk)
27     if(!rst_n)
28         begin
29             bin_cnt_tmp <= 8'd0;
30         end    
31     else 
32         bin_cnt_tmp <= bin_cnt_tmp + 1;
33 
34 //輸出打一拍    
35 always@(posedge clk)
36     if(!rst_n)
37         begin
38             bin_cnt <= 8'd0;
39             gray_cnt <= 8'd0;
40         end    
41     else 
42         begin
43             bin_cnt <= bin_cnt_tmp;    
44             gray_cnt <= gray_cnt_tmp;                
45         end
46          
47 endmodule

 


 one-hot(獨熱碼)計數器

簡介

所謂的獨熱碼是指對任意給定的狀態,狀態向量中只有1位為1,其余位都是為0。n狀態的狀態機需要n個觸發器。這種狀態機的速度與狀態的數量無關,僅取決於到某特定狀態的轉移數量,速度很快。當狀態機的狀態增加時,如果使用二進制編碼,那么狀態機速度會明顯下降。而采用獨熱碼,雖然多用了觸發器,但由於狀態譯碼簡單,節省和簡化了組合邏輯電路。獨熱編碼還具有設計簡單、修改靈活、易於綜合和調試等優點。對於寄存器數量多、而門邏輯相對缺乏的FPGA器件,采用獨熱編碼可以有效提高電路的速度和可靠性,也有利於提高器件資源的利用率。獨熱編碼有很多無效狀態,應該確保狀態機一旦進入無效狀態時,可以立即跳轉到確定的已知狀態。通過獨熱碼可是實現簡單的有限狀態機。 

只有一位為1,也就是下面的環形計數器產生的計數序列。如4bit one-hot計數器的計術序列即為:0001-0010-0100-1000循環。

這種計數器的優點是速度快,且每次只有兩個bit發生跳變,而且不需外加譯碼電路,可以直接以各個觸發器輸出端的1狀態表示計數。

主要缺點是沒有有效利用電路的狀態,對於nbit,有2^n-n個狀態沒有利用。

應用:在狀態機的狀態編碼時,經常用到;實際上,大多情況下這種計數器不被稱作計數器,而是狀態編碼的一種。

 one-hot(獨熱碼)計數器與環形移位計數器相同,見下面環形計數器代碼。 


基於移位寄存器的計數器

移位寄存器為何可用作計數器?

通過移位寄存器可以產生不同狀態,在時鍾下,電路狀態循環變化,用電路的不同狀態能夠表示輸入時鍾的數目,從而作為時鍾脈沖的計數器。但這種計數器有一個缺點,就是計數序列不是通常的遞增或遞減,常用作偽隨機數發生器。

主要包括LFSR計數器、環形計數器、扭環形計數器(又稱約翰遜計數器)三種。

三種都可歸結於由寄存器與一個反饋回路組成,只不過對於環形計數器,沒有反饋回路;對於扭環形計數器,反饋回路只是將最高位取反,作為最低位的輸入;而LFSR的反饋回路比較復雜,對於不同的位數,由不同的生成多項式指定。


 LFSR(線性反饋移位寄存器,又稱為偽隨機序列發生器)

在通信領域lfsr 有着很廣泛的應用,比如說M序列,擾碼,信道編碼,密碼學這方面都有很廣泛的應用,而不僅僅用於計數器。

 LFSR Applications (LFSR應用)
• Pattern Generators
• Counters
• Built-in Self-Test (BIST)
• Encryption
• Compression
• Checksums
• Pseudo-Random Bit Sequences (PRBS)

注意到 LFSR總是將 0狀態轉化成0狀態,  因此對於一個n級LFSR, 最多可輸出周期為2^(n− 1)的周期序列.

An address counter supplies sequential addresses, but there is no need for a conventional binary address sequence. Any repetitive pattern is
acceptable, and a linear feedback shift register counter is the most efficient.

(也就是說,地址計數器提供順序地址,但是沒必要是傳統的二進制地址序列,任何重復的序列都是可接受的,而LFSR計數器是最有效的

Conventional binary counters use complex or wide fan-in logic to generate high end carry signals. A much simpler structure sacrifices the binary count sequence, but achieves very high speed with very simple logic, easily packing two bits into every CLB. Such Linear Feedback Shift-Register (LFSR) counters are also known as pseudo random sequence generators.

傳統的二進制計數器用復雜的或大扇入邏輯產生進位信號,LFSR以犧牲二進制計數序列為代價,用相當簡單的結構與邏輯實現很高的速度,這種移位寄存器又稱為偽隨機序列發生器;LFSR計數器的計數序列就是偽隨機序列

A possible disadvantage is that the count sequence is not the normal bina r y increment or decrement sequence.

LFSR的缺點是計數序列不是通常的遞增或遞減

LFSR計數器優缺點:

Comparison to other counter types „ Comparison  to  other  counter  types
„ PROS:
„ Requires ver y little log ic to imp lement(邏輯資源少
„ Even long counters are very efficient(速度高
ƒ Low gate count
ƒ Hi g h sp eed gp
„ Easy to test for faults - typically only need 2*n clocks(容易測試錯誤
„ CONS:
Pr i m iti ve forms must  b e  i n iti a li zed to va lid s ta te(本原形式必須初始化為有效狀態
„ Some applications require binary count sequences(某些場合需要二進制計數序列
„ Not easy to predict count sequence (不易預測計數序列

LFSR的產生需要一個生成多項式,生成多項式指定反饋邏輯中抽頭的有無。

LFSR不同長度的序列產生可根據下表得到(也就是生成多項式中的系數):

This table lists the appropriate taps for maximum-length LFSR counters of up to 168 bits. The basic description and the table for the first 40 bits was originally published in XCELL and reprinted on page 9-24 of the 1993 and 1994 Xilinx Data Books.

Responding to repeated requests, the list is here extended to 168 bits. This information is based on unpublished research done by Wayne Stahnke while he was at Fairchild Semiconductor in 1970.

 

References

P. Alfke, “Efficient Shift Registers, LFSR, Counters, and  Long Pseudo-Random Sequence Generators,” XAPP 052, July 7,1996 (Version 1.1)

說明:

實際中,LFSR計數器的實現有兩大類:一對多與多對一。根據反饋回路是異或還是異或之后再經過一個非門,分為XOR與XNOR。

下面是多對一、XNOR的方式,此處的代碼僅僅是模擬LFSR計數器的工作,具體使用時需根據需要進行改進。

下面給出8bit LFSR計數器的verilog代碼。

根據上表,可知tap為8、6、5、4,因此,代碼如下:

 

 1 module lfsr_counter(rst_n,
 2                             clk,
 3                             cnt
 4                                  );
 5 
 6 parameter CNT_SIZE = 8;
 7 
 8 input rst_n;
 9 input clk;
10 
11 output [CNT_SIZE - 1 : 0] cnt;
12 
13 reg [CNT_SIZE - 1 : 0] cnt;
14 
15 always@(posedge clk)
16     if(!rst_n)
17         cnt <= 8'b0000_0000;  //初始值
18     else
19         begin
20             cnt[7:1] <= cnt[6:0];         //移位寄存
21             cnt[0] <= ~(^{cnt[7],cnt[5:3]});  //xnor
22         end
23 
24 endmodule

 


 環形計數器

也是基於移位寄存器的計數器,對於n個移位寄存器構成的計數器,只有n個有效狀態。

設置一個初始狀態,通過移位即可得到。

說明:

實際中,因為該計數器有2^n-n個無效狀態,因此存在自鎖的問題,這可以通過設計可以自啟動(自動從無效狀態轉移到有效狀態,進入有效循環)的電路來解決。自啟動的設計可通過修改狀態邏輯實現,本質是改變無效狀態的次態,使其為有效狀態。

下面的代碼僅僅是簡單的實現,模擬環形計數器的工作方式,並沒有過多的考慮自啟動的問題。

代碼:

 1 module circle_counter(rst_n,
 2                             clk,
 3                             cnt
 4                                  );
 5 
 6 parameter CNT_SIZE = 8;
 7 
 8 input rst_n;
 9 input clk;
10 
11 output [CNT_SIZE - 1 : 0] cnt;
12 
13 reg [CNT_SIZE - 1 : 0] cnt;
14 
15 always@(posedge clk)
16     if(!rst_n)
17         cnt <= 8'b0000_0001;  //初始值
18     else
19         //cnt <= cnt>>1;
20         cnt <= {cnt[0],cnt[CNT_SIZE - 1 : 1]};    //注意是循環移位,而非簡單的移位
21 
22 endmodule

 


 約翰遜(扭環形)計數器

也是基於移位寄存器的計數器,是對環形計數器的改進,對於n個移位寄存器構成的計數器,有2n個有效狀態。

說明:

與環形計數器類似,實際中,因為該計數器有2^n-2n個無效狀態,因此存在自鎖的問題,這可以通過設計可以自啟動(自動從無效狀態轉移到有效狀態,進入有效循環)的電路來解決。自啟動的設計可通過修改狀態邏輯實現,本質是改變無效狀態的次態,使其為有效狀態。

下面的代碼僅僅是簡單的實現,模擬環形計數器的工作方式,並沒有過多的考慮自啟動的問題。

設置一個初始狀態,將最高位取反,作為最低位的輸入,通過移位即可得到。

代碼:

 

 1 module john_counter(rst_n,
 2                             clk,
 3                             cnt
 4                                  );
 5 
 6 parameter CNT_SIZE = 8;
 7 
 8 input rst_n;
 9 input clk;
10 
11 output [CNT_SIZE - 1 : 0] cnt;
12 
13 reg [CNT_SIZE - 1 : 0] cnt;
14 
15 always@(posedge clk)
16     if(!rst_n)
17         cnt <= 8'b0000_0000;  //初始值
18     else
19         cnt <= {~cnt[0],cnt[CNT_SIZE - 1 : 1]};    //注意是循環移位,而非簡單的移位
20 
21 endmodule

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM