一、原理
约翰逊(Johnson)计数器又称扭环计数器,是一种用n位触发器(位宽)来表示2n个状态的计数器。它有一个非常明显地好处,相邻两组数只有一位不同,具体如下例子所示,因此在计数过程中不会存在竞争冒险问题。
以4bit扭环形计数器为例,能表示8(2×4)种状态,相邻两组数之间,仅有1bit不同:
0000 1000 1100 1110 1111 0111 0011 0001 0000 1000
相邻的两个数据,高三位直接平移到第三位,最低位取反后平移到最高位。 例如 1100 → 1110 (110_0 → 1_110)key
二、代码
module johnson_counter#(parameter WIDTH = 4)(
input clk,
input rst_n,
output reg [WIDTH-1:0] cnt
);
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= { WIDTH { 1'b0 }}; //0000
else
cnt <= { ~cnt[0], cnt[WIDTH-1:1] }; //rst_n=1后,第一个上升沿 1_000
end
endmodule
三、电路
从电路图可以看出,在时钟上升沿还没到前,把Q端的4bit数据分成两路处理,Q端数据的高三位(cnt[3:1])平移到低三位(cnt[2:0])。同时Q端数据的最低位(cnt[0])取反后平移到最高位(cnt[3]) 。最后操作完的数据 {cnt[3],cnt[2:0]} 就是D端的数据。
四、测试代码
`timescale 1ns / 1ps
module johnson_counter_tst #(parameter WIDTH = 4);
reg clk;
reg rst_n;
wire [WIDTH-1:0] cnt;
johnson_counter U_johnson_counter(
.clk(clk),
.rst_n(rst_n),
.cnt(cnt)
);
initial
begin
clk=0;
rst_n=0;
#20;
rst_n=1;
end
always #10 clk=~clk;
endmodule
rst_n 拉高后且遇到上升沿才会变化,所以波形图的 20~30ns之间是不会变化的。cnt的位宽是4,cnt[3:0],使用扭环形计数器可以表示8种 cnt 状态,分别是0000 1000 1100 1110 1111 0111 0011 0001 0000 1000