七种常见计数器总结(格雷码计数器、环形计数器、约翰逊计数器、FLSR、简易时分秒数字秒表等

365速度发国际大厅 时间: 2025-07-12 06:45:44 作者: admin 查阅次数: 8725 公众评价: 240
七种常见计数器总结(格雷码计数器、环形计数器、约翰逊计数器、FLSR、简易时分秒数字秒表等

数字IC经典电路设计

经典电路设计是数字IC设计里基础中的基础,盖大房子的第一部是打造结实可靠的地基,每一篇笔者都会分门别类给出设计原理、设计方法、verilog代码、Testbench、仿真波形。然而实际的数字IC设计过程中考虑的问题远多于此,通过本系列希望大家对数字IC中一些经典电路的设计有初步入门了解。能力有限,纰漏难免,欢迎大家交流指正。快速导航链接如下:

个人主页链接

1.数字分频器设计

2.序列检测器设计

3.序列发生器设计

4.序列模三检测器设计

5.奇偶校验器设计

6.自然二进制数与格雷码转换

7.线性反馈移位寄存器LFSR

8.四类九种移位寄存器总结

9.串并转换

一、可复位/置数计数器1.1 可复位/置数计数器根据要求可以知道有四个输入和两个输出,主要包括累加计数、置位、指示信号,此题较简单,分开设计即可。

模块的接口信号图如下:

1.2 Verilog代码要求:设计一个位宽为4的带复位端和置位端的计数器,并且计数器输出信号递增每次到达0,指示信号zero拔高至“1”,当置位信号set 有效时,将当前输出置为输入的数值set_num。

代码语言:c代码运行次数:0运行复制//可复位可置位的简易计数器

module count_bin01(

input clk,

input rst_n,

input set,

input [3:0] set_num,

output reg [3:0]number,

output reg zero

);

reg [3:0] cnt;

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

cnt <= 0;

end

else if(set) begin //置数

cnt <= set_num;

end

else begin

cnt <= (cnt==15)?0:cnt+1; //累加计数,到顶清零

end

end

//计数圈数的指示信号

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

zero <= 0;

end

else begin

zero <= (cnt==0)?1:0;

end

end

//时序逻辑打一拍子输出

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

number <= 0;

end

else begin

number <= cnt;

end

end

endmodule1.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps;////仿真时间单位1ns 仿真时间精度1ps

module count_bin01_tb();

//信号申明

reg clk;

reg rst_n;

reg set;

reg [3:0] set_num;

wire [3:0] number;

wire zero;

//模块实例化(将申明的信号连接起来即可)

count_bin01 u_count_bin01(

.clk (clk),

.rst_n (rst_n),

.set (set),

.set_num (set_num),

.number (number),

.zero (zero)

);

//生成时钟信号

always #5 clk =~ clk;

//为输入数据赋值

initial begin

clk = 0;

set = 0;

set_num = 4'b1010;

#5 rst_n = 1;

#5 rst_n =0;

#5 rst_n = 1;

#400 set = 1;

#100 set = 0;

#1000;

$finish;

end

endmodule1.4 仿真结果二、双向(可加可减)计数器2.1 双向(可加可减)计数器一个十进制计数器模块,当mode信号为1,计数器输出信号递增,当mode信号为0,计数器输出信号递减。每次到达0,给出指示信号zero。此题较简单,分开设计即可。

模块的接口信号图如下:

2.2 Verilog代码要求:设计一个双向计数器,分别实现从0 ~ 9加法计数和9 ~ 0减法计数,并且计数器输出信号每次到达0,指示信号zero拔高至“1”。

代码语言:c代码运行次数:0运行复制//设计双向(可加可减)计数器

module count_bin02(

input clk,

input rst_n,

input mode,

output [3:0]number,

output reg zero

);

//双向计数模块

reg[3:0] number_r; //定义中间寄存器

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

number_r<=0;

end

else if(mode)begin//选择加计数器

number_r<=(number_r==9) ? 0 : number_r + 1; //累加计数,到顶清零

end

else if(~mode) begin //选择减计数器

number_r<=(number_r==0) ? 9 : number_r - 1; //累加计数,到底置9

end

else begin

number_r<=number_r;

end

end

assign number =number_r; //组合逻辑输出

//计数圈数的指示信号

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

zero<=0;

end

else if(number==0) begin

zero<=1;

end

else begin

zero<=0;

end

end

endmodule2.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps; //仿真时间单位1ns 仿真时间精度1ps

module count_bin02_tb();

//信号申明

reg clk;

reg rst_n;

reg mode;

wire [3:0] number;

wire zero;

//模块实例化(将申明的信号连接起来即可)

count_bin02 u_count_bin02(

.clk (clk),

.rst_n (rst_n),

.mode (mode),

.number (number),

.zero (zero)

);

always #5 clk =~ clk; //生成时钟信号

//为输入数据赋值

initial begin

clk = 0;

mode = 1;

#5 rst_n = 1;

#5 rst_n = 0;

#5 rst_n = 1;

#150 mode = 0;

#1000;

$finish;

end

endmodule2.4 仿真结果三、格雷码计数器3.1 格雷码计数器此处详细可以查看上篇自然二进制数与格雷码转换。什么是格雷码?

格雷码(Gray code)是一种二进制数码系统,格雷码的特点是从一个数变为相邻的一个数时,只有一个数据位发生跳变,由于这种特点,就可以避免二进制编码计数组合电路中出现的亚稳态。在某些应用中,格雷码具有排除歧义和减少数据传输错误的功能。四位格雷码和自然二进制数关系如下图所示:

自然二进制如何转换成格雷码?

从自然二进制码到格雷码的转换具体方法是:从二进制的最低位起,依次起与相邻左边的一位数进行异或逻辑运算,并且作为对应格雷码该位的值,最高位保持不变。简而言之就是,将二进制码与逻辑右移的二进制码进行异或可得到格雷码。具体原理图如下所示:

格雷码有哪些优点和应用场合?

避免计数器状态的冗余转换,在格雷码中,两个连续的数值仅仅只有一位不同,而在二进制码中两个连续的数值可能会有多位不同,这会导致在计数器发生器中产生大量的冗余状态转换。格雷码可以通过降低状态转换次数来设计出更简单的计数器。

降低传输干扰和误差。在数据传输过程中,如果使用二进制码,由于两个相邻的数值可能会有多位不同,数据在传输过程中可能会因为电磁干扰等原因而发生错误。而使用格雷码则能够避免这种情况,因为任何相邻的两个数值之间只有一位不同。格雷码广泛应用在FIFO、跨时钟域的通信(CDC)、RAM地址寻址计数器、数据纠错等电路设计中。

格雷码的特点决定了它适用于数据传输,比如在异步时钟域之间传递计数结果而用到的计数器。常见的异步FIFO空满信号的信号就是用格雷码进行比较的(因为格雷码计数器计数时相邻的数之间只有一个bit发生了变化,例如:000-001-011-010-110-111-101-100)。

也常用在状态机的状态编码。

而由于格雷码是一种变权码,每一位码没有固定的大小,很难直接进行比较大小和算术运算,因此在实际的数据运算中并不使用格雷码,如异步FIFO中读写地址仍然是使用二进制编码。

格雷码计数可以用三种方式实现

一种是状态机,但是如果计数器的位数很大,比如6位,就得用至少64个状态,非常麻烦。一种是设计一个二进制计数器,通过它来计数,然后利用binary-gray的编码就可以得到对应的格雷码计数器。还有一种方法,不是很常用,也很复杂,是通过组合逻辑直接产生格雷码的,而不需要先产生二进制,再进行转换。但是较为复杂,应用场合没前两者多。本文采用简单易操作的的第二种方法。二进制码转格雷码的基本思路:从最右边一位起,依次将每一位与左边一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。详情可以查看自然二进制数与格雷码转换。

模块的接口信号图如下:

3.2 Verilog代码要求:实现4bit位宽的格雷码计数器。

代码语言:c代码运行次数:0运行复制//设计四位格雷码计数器

module counter_gray(

input clk,

input rst_n,

output reg [3:0] cnt_gray,

output reg [3:0] cnt_bin

);

//自然二进制计数器

reg [3:0] cnt_bin_r;

always@(posedge clk or negedge rst_n) begin

if(!rst_n) begin

cnt_bin_r <= 0;

end

else begin

cnt_bin_r <= cnt_bin_r + 1'b1;

end

end

//自然二进制码转格雷码

wire [3:0] cnt_gray_r;

assign cnt_gray_r = (cnt_bin_r >> 1) ^ cnt_bin_r;

//时序逻辑打一拍子输出

always@(posedge clk or negedge rst_n) begin

if(!rst_n) begin

cnt_gray <= 0;

cnt_bin <= 0;

end

else begin

cnt_gray <= cnt_gray_r;

cnt_bin <= cnt_bin_r;

end

end

endmodule3.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps;//仿真时间单位1ns 仿真时间精度1ps

module counter_gray_tb();

//信号申明

reg clk;

reg rst_n;

wire [3:0] cnt_gray;

wire [3:0] cnt_bin;

//模块实例化(将申明的信号连接起来即可)

counter_gray u_counter_gray(

.clk (clk),

.rst_n (rst_n),

.cnt_gray (cnt_gray),

.cnt_bin (cnt_bin)

);

always #5 clk =~ clk;//生成时钟信号

//为输入数据赋值

initial begin

clk = 0;

#5 rst_n = 1;

#5 rst_n = 0;

#5 rst_n = 1;

#1000;

$finish;

end

endmodule3.4 仿真结果四、环形计数器和扭环形计数器4.1 环形计数器和扭环形计数器什么是环形计数器?

环形计数器是基于移位寄存器的计数器,对于n个移位寄存器构成的计数器,只有n个有效状态。设置一个初始状态,通过移位即可进行循环。如下图所示为环形计数器的电路结构以及循环的有效和无效编码。

Tips:实际中,因为该计数器有2^n-n 个无效状态,因此存在自锁的问题,这可以通过设计可以自启动(自动从无效状态转移到有效状态,进入有效循环)的电路来解决。自启动的设计可通过修改状态逻辑实现,本质是改变无效状态的次态,使其为有效状态。

什么是独热码?

所谓的独热码是指对任意给定的状态,状态向量中只有1位为1,其余位都是为0。独热码经常用在状态机的状态编码中。n状态的状态机需要n个触发器。当状态机的状态增加时,如果使用二进制编码,那么状态机速度会明显下降,且由于翻转的寄存器较多容易出编码错误。而采用独热码,虽然多用了触发器,但由于状态译码简单,节省和简化了组合逻辑电路。独热编码还具有设计简单、修改灵活、易于综合和调试等优点。对于寄存器数量多、而门逻辑相对缺乏的FPGA器件,采用独热编码可以有效提高电路的速度和可靠性,也有利于提高器件资源的利用率。独热编码有很多无效状态,应该确保状态机一旦进入无效状态时,可以立即跳转到确定的已知状态。通过独热码可是实现简单的有限状态机。

one-hot(独热码)计数器与环形移位计数器实际上相同

独热码只有一位为1,也就是下面的环形计数器产生的计数序列。如4bit one-hot计数器的计术序列即为:0001-0010-0100-1000循环。

这种计数器的优点是速度快,且每次只有两个bit发生跳变,而且不需外加译码电路,可以直接以各个触发器输出端的1状态表示计数。主要缺点是没有有效利用电路的状态,对于 n bit,有2^n-n 个状态没有利用。

应用:在状态机的状态编码时,经常用到。实际上,大多情况下这种独热码计数器不被称作计数器,而是状态编码的一种。

什么是扭环形计数器?

扭环形计数器又称约翰逊计数器,是基于移位寄存器的计数器,是对环形计数器的改进,对于n个移位寄存器构成的计数器,有 2n 个有效状态。如下图所示为扭环形计数器的电路结构以及循环的有效和无效编码。

Tips:与环形计数器类似,实际中,因为该计数器有2^n-2n 个无效状态,因此存在自锁的问题,这可以通过设计可以自启动(自动从无效状态转移到有效状态,进入有效循环)的电路来解决。自启动的设计可通过修改状态逻辑实现,本质是改变无效状态的次态,使其为有效状态。

扭环形计数器仍然有很多状态是无效的,一旦计数器进入这些状态就会陷入死循环,无法正常工作。计数器的初始状态必须位于有效循环的几种状态之中才能启动。通过添加门电路可以拆掉无效循环,也就是可以自启动的扭环形计数器。

设置一个初始状态,将最高位取反,作为最低位的输入,通过移位即可得到。下面的代码仅仅是简单的实现,模拟环形计数器和扭环形计数器的工作方式,并没有过多的考虑自启动的问题。

4.2 Verilog代码要求:实现4bit位宽的计数器,可实现环形计数器独热码输出和扭环形计数器(约翰逊计数器)输出。

代码语言:c代码运行次数:0运行复制//设计环形计数器和扭环形计数器

module counter_circle #(

parameter WIDTH = 4 //定义数据位宽

)(

input clk,

input rst_n,

output [WIDTH - 1 : 0] counter_circle,

output [WIDTH - 1 : 0] counter_john

);

//环形计数器(独热码计数器)模块

reg [WIDTH - 1 : 0] counter_circle_r; //中间寄存器

always@(posedge clk or negedge rst_n) begin

if(!rst_n) begin

counter_circle_r <= 4'b0001;

end

else begin

counter_circle_r <= {counter_circle_r[0],counter_circle_r[WIDTH - 1 : 1]};

end

end

//扭环形计数器(约翰逊计数器)模块

reg [WIDTH - 1 : 0] counter_john_r; //中间寄存器

always@(posedge clk or negedge rst_n) begin

if(!rst_n) begin

counter_john_r <= 4'b0000;

end

else begin

counter_john_r <= {~counter_john_r[0],counter_john_r[WIDTH - 1 : 1]};

end

end

//组合逻辑输出

assign counter_circle = counter_circle_r;

assign counter_john = counter_john_r;

endmodule4.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps

module counter_circle_tb #(

parameter WIDTH = 4 //定义数据位宽

);

//信号申明

reg clk;

reg rst_n;

wire [WIDTH - 1 : 0] counter_circle;

wire [WIDTH - 1 : 0] counter_john;

//模块实例化(将申明的信号连接起来即可)

counter_circle u_counter_circle(

.clk (clk),

.rst_n (rst_n),

.counter_circle (counter_circle),

.counter_john (counter_john)

);

always #5 clk = ~clk; //生成时钟信号

//为输入数据赋值

initial begin

clk = 0;

rst_n = 1;

#5 rst_n = 0;

#5 rst_n = 1;

#100 $finsh;

end

endmodule4.4 仿真结果五、线性反馈移位寄存器LFSR计数器此处更详细可以参考线性反馈移位寄存器LFSR(斐波那契LFSR(多到一型)和伽罗瓦LFSR(一到多型)|verilog代码|Testbench|仿真结果)。5.1 斐波那契LFSR5.1.1 斐波那契LFSR斐波那契LFSR为多到一型LFSR,即多个触发器的输出经过异或逻辑来驱动一个触发器的输入。反馈多项式为 f(x)=x^3 + x^2 +1 ,即x_1 的输入为x_3 和x_2 的输出异或后的结果,电路图如下所示:

输出序列的顺序为:111-110-100-001-010-101-011-111

5.1.2 verilog代码代码语言:c代码运行次数:0运行复制//三级斐波那契LFSR设计

//反馈多项式为 f(x)=x^3 + x^2 +1

module lfsr_fibonacci(

input clk,

input rst_n,

output reg [2:0] q

);

//时序逻辑LFSR移位模块

always @(posedge clk or rst_n) begin

if (!rst_n) begin

q <= 3'b111; //种子值为111

end

else begin

q <= {q[1],q[0],q[1]^q[2]}; //根据三级斐波那契LFSR电路拼接输出

end

end

endmodule5.1.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps

module lfsr_fibonacci_tb();

//信号申明

reg clk;

reg rst_n;

wire [2:0] q;

//模块实例化(将申明的信号连接起来即可)

lfsr_fibonacci u_lfsr_fibonacci(

.clk (clk),

.rst_n (rst_n),

.q (q)

);

always #5 clk = ~clk; //生成时钟信号

//为输入数据赋值

initial begin

clk = 1;

rst_n = 1;

#5 rst_n = 0;

#5 rst_n = 1;

#1000

$stop;

end

endmodule5.1.4 仿真结果5.2 伽罗瓦LFSR5.2.1 伽罗瓦LFSR伽罗瓦LFSR为一到多型LFSR,即一个触发器的输出经过异或逻辑来驱动多个触发器的输入。对于同样的反馈多项式x^3+x^2+1 而言:触发器x_1 的输入通常来源于触发器x_2 的输出,x_3 (最高项)的输入通常来自于x_1 的输出,此多项式中剩余触发器的输入是x_1 的输出与前级输出异或的结果,x_2 的输入由x_1 的输出与x_3 的输出通过异或运算得到。其电路图如下所示:

输出序列的顺序为:111-101-100-010-001-110-011-111

5.2.2 verilog代码代码语言:c代码运行次数:0运行复制//三级伽罗瓦LFSR设计

//反馈多项式为 f(x)=x^3 + x^2 +1

module lfsr_galois(

input clk,

input rst_n,

output reg [2:0] q

);

//时序逻辑LFSR移位模块

always @(posedge clk or rst_n) begin

if (!rst_n) begin

q <= 3'b111; //种子值为111

end

else begin

q <= {q[0],q[2]^q[0],q[1]}; //根据三级伽罗瓦LFSR电路拼接输出

end

end

endmodule5.2.3 Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps

module lfsr_galois_tb();

//信号申明

reg clk;

reg rst_n;

wire [2:0] q;

//模块实例化(将申明的信号连接起来即可)

lfsr_galois u_lfsr_galois(

.clk (clk),

.rst_n (rst_n),

.q (q)

);

always #5 clk = ~clk; //时钟信号生成

//为输入数据赋值

initial begin

clk = 1;

rst_n = 1;

#5 rst_n = 0;

#5 rst_n = 1;

#1000

$stop;

end

endmodule5.2.4 仿真结果六、简易时分秒数字秒表6.1 简易时分秒数字秒表其实本质还是二进制计数器,只不过判断逻辑稍微多一些。首先是秒:复位后可以开始计数,当计数器到达最大值即59后清零;其次是分:复位后且只能在秒到达最大值后才能计数,当计数器到达最大值即59后清零。最后是时,复位后且只能在分到达最大值后才能计数,当计数器到达最大值即23后清零。

6.2Verilog代码要求:实现一个时分秒的简易秒表。

代码语言:c代码运行次数:0运行复制//实现时分秒简易数字秒表

module count_calendar(

input clk,

input rst_n,

output [4:0] hour,

output [5:0] minute,

output [5:0] second

);

//时分秒最大计数

parameter SECOND_MAX = 59;

parameter MINUTE_MAX = 59;

parameter HOUR_MAX = 23;

//秒计数器

reg [5:0] second_r;

always@(posedge clk or negedge rst_n) begin

if (!rst_n) begin

second_r <= 6'd0;

end

else if (second_r == SECOND_MAX) begin

second_r <= 6'd0;

end

else begin

second_r <= second_r + 1'b1;

end

end

//分计数器

reg [5:0] minute_r;

always@(posedge clk or negedge rst_n) begin

if (!rst_n) begin

minute_r <= 6'd0;

end

else if ( (minute_r == MINUTE_MAX) && (second_r == SECOND_MAX) ) begin

minute_r <= 6'd0;

end

else if ( second_r >= SECOND_MAX) begin

minute_r <= minute_r + 1'b1;

end

else begin

minute_r <= minute_r;

end

end

//时计数器

reg [5:0] hour_r;

always@(posedge clk or negedge rst_n) begin

if (!rst_n) begin

hour_r <= 5'd0;

end

else if ( (hour_r == HOUR_MAX) && (minute_r == MINUTE_MAX) && (second_r == SECOND_MAX) ) begin

hour_r <= 5'd0;

end

else if ( (minute_r == MINUTE_MAX) && (second_r == SECOND_MAX) ) begin

hour_r <= hour_r + 1'b1;

end

else begin

hour_r <= hour_r;

end

end

//组合逻辑输出

assign hour = hour_r;

assign minute = minute_r;

assign second = second_r;

endmodule6.3Testbench代码语言:c代码运行次数:0运行复制`timescale 1ns/1ps; //仿真时间单位1ns 仿真时间精度1ps

module count_calendar_tb();

//信号申明

reg clk;

reg rst_n;

wire [4:0] hour;

wire [5:0] minute;

wire [5:0] second;

//模块实例化(将申明的信号连接起来即可)

count_calendar u_count_calendar(

.clk (clk),

.rst_n (rst_n),

.hour (hour),

.minute (minute),

.second (second)

);

//调用系统命令——监视

initial begin

$monitor ("hour = %b, minute = %b, second = %b",

hour, minute, second);

end

always #5 clk =~ clk;//生成时钟信号

//为输入数据赋值

initial begin

clk = 0;

#5;

rst_n = 1;

#5;

rst_n=0;

#5;

rst_n = 1;

#100000;

$finish;

end

endmodule6.4 仿真结果七、总结可以将计数器大致分为这几类:

普通二进制计数器:大致包括可复位/置数计数器、双向(可加可减)计数器等,这类计数器代码逻辑简单,直观易懂。格雷码计数器:格雷码因翻转的次数少,因此具有减少数据传输错误的作用。格雷码广泛应用在FIFO、跨时钟域的通信(CDC)、RAM地址寻址计数器、数据纠错等电路设计中。产生格雷码有状态机法、自然二进制转格雷码、组合逻辑产生这三种方法。移位寄存器类计数器:包括环形计数器、扭环形计数器(又称约翰逊计数器)、LFSR计数器,三种都可归结于由寄存器与一个反馈回路组成,只不过对于环形计数器,没有反馈回路;对于扭环形计数器,反馈回路只是将最低位取反,作为最高位的输入;而LFSR的反馈回路比较复杂,对于不同的位数,由不同的生成多项式指定。一定程度上说,生成可循环序列的过程就是计数的过程!

关联

在蕲春不知道去哪里买衣服?自从来到这条街......
365速度发国际大厅

在蕲春不知道去哪里买衣服?自从来到这条街......

📅 07-06 👁️ 4224
2025最好用的电脑PC端浏览器排行榜
365速度发国际大厅

2025最好用的电脑PC端浏览器排行榜

📅 06-30 👁️ 8587
cf刀锋,cf飞虎队和刀锋哪个更值得入手
365bet官方体育投注

cf刀锋,cf飞虎队和刀锋哪个更值得入手

📅 07-12 👁️ 7887

链接