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中读写地址仍然是使用二进制编码。
与普通二进制码之间的转换
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