Introduction

On this page we will present the design of a reversable 4 bit Johnson counter. A Johnson counter has a special structure that permits glitch-free decoding.

This example is originally from the Xilinx ISE design environment. If you wish, you can download the free version of the Xilinx ISE to review the original design and use it as a reference.

Specification

A Johnson counter basically consists of a circular shift register with an invertor in the loop.

The specification of the counter operation is as follows:

The counter is triggered on the rising edge of the clock (clk). A low pulse on the goLeft input will cause the counter to start shifting left from its current state. A low pulse on the goRight input will cause the counter to start shifting right from its current state. A low pulse on the stop input will cause the counter to hold its current state until goLeft or goRight is pulsed. After power-up, the counter is stopped with all outputs low (LEDs lit).

MyHDL code

The MyHDL code for this design looks as follows.

from myhdl import *

ACTIVE = 0
DirType = enum('RIGHT', 'LEFT')

@block
def jc2(goLeft, goRight, stop, clk, q):

    """ A bi-directional 4-bit Johnson counter with stop control.

    I/O pins:
    --------
    clk      : input free-running slow clock 
    goLeft   : input signal to shift left (active-low switch)
    goRight    : input signal to shift right (active-low switch)
    stop     : input signal to stop counting (active-low switch)
    q        : 4-bit counter output (active-low LEDs; q[0] is right-most)

    """

    dir = Signal(DirType.LEFT)
    run = Signal(False)

    @always(clk.posedge)
    def logic():
        # direction
        if goRight == ACTIVE:
            dir.next = DirType.RIGHT
            run.next = True
        elif goLeft == ACTIVE:
            dir.next = DirType.LEFT
            run.next = True
        # stop
        if stop == ACTIVE:
            run.next = False
        # counter action
        if run:
            if dir == DirType.LEFT:
                q.next[4:1] = q[3:]
                q.next[0] = not q[3]
            else:
                q.next[3:] = q[4:1]
                q.next[3] = not q[0]

    return logic

We use an enumerated type for the direction state dir. We prefer this for clarity when the encoding is not specified.

Test bench

The following is a test bench for a basic verification of the design.

from myhdl import *

ACTIVE, INACTIVE = bool(0), bool(1)

from jc2 import jc2

@block
def test():

    goLeft, goRight, stop, clk = [Signal(INACTIVE) for i in range(4)]
    q = Signal(intbv(0)[4:])

    @always(delay(10))
    def clkgen():
        clk.next = not clk

    jc2_inst = jc2(goLeft, goRight, stop, clk, q)

    @instance
    def stimulus():
        for i in range(3):
            yield clk.negedge
        for sig, nrcycles in ((goLeft, 10), (stop, 3), (goRight, 10)):
            sig.next = ACTIVE
            yield clk.negedge
            sig.next = INACTIVE
            for i in range(nrcycles-1):
                yield clk.negedge
        raise StopSimulation

    @instance
    def monitor():
        print("goLeft goRight stop clk q")
        print("------------------------------")
        while True:
            yield clk.negedge
            yield delay(1)
            pStr = str('{:^6} {:^6} {:^5} '.format(
                int(goLeft), int(goRight), int(stop)))
            yield clk.posedge
            pStr += " C "
            yield delay(1)
            pStr += ' '+bin(q, 4)
            print(pStr)

    return clkgen, jc2_inst, stimulus, monitor

simInst = test()
simInst.config_sim(trace=True, tracebackup=False)
simInst.run_sim()

We use a number of decorated functions to create a clock generator, a stimulus generator, and a response monitor, together with an instance of the design under test. Such a setup is quite typical.

When we simulate this test bench, the output is as follows:

goLeft goRight stop clk q
----------------------------
  1      1      1    C  0000
  1      1      1    C  0000
  0      1      1    C  0000
  1      1      1    C  0001
  1      1      1    C  0011
  1      1      1    C  0111
  1      1      1    C  1111
  1      1      1    C  1110
  1      1      1    C  1100
  1      1      1    C  1000
  1      1      1    C  0000
  1      1      1    C  0001
  1      1      0    C  0011
  1      1      1    C  0011
  1      1      1    C  0011
  1      0      1    C  0011
  1      1      1    C  0001
  1      1      1    C  0000
  1      1      1    C  1000
  1      1      1    C  1100
  1      1      1    C  1110
  1      1      1    C  1111
  1      1      1    C  0111
  1      1      1    C  0011
  1      1      1    C  0001

You can see the basic operation of the Johnson counter in action.

The presented test bench is rather basic. It is fine to get a quick idea about the design behavior, but it is inadequate as a verification tool. First, some corner case are not verified, such as the simultaneous occurence of some of the input signals. Also, it relies on visual inspection which is prone to human error and not suited for regression testing.

A better approach would be to write a self-checking test bench that checks Johnson counter properties automatically, plus a number of directed and random tests to cover corner cases. In addition, such a test bench should be written using a unit test framework such as Python's unittest package, to facilitate test writing and to make it part of a regression test suite. Consult the MyHDL manual for more info on such techniques.

Automatic conversion to Verilog or VHDL

A working MyHDL design intended for implementation can be converted to Verilog or VHDL automatically, using the convInst.convert() function. Remember: there is no point in converting when the design doesn't work. The simulation will catch much more errors than the converter. In MyHDL, like in Python, the run-time rules.

Conversion to Verilog can be done with code like the following:

def convert():
    left, right, stop, clk = [Signal(INACTIVE) for i in range(4)]
    q = Signal(intbv(0)[4:])
    convInst = jc2 (left, right, stop, clk, q)
    convInst.convert(hdl='Verilog')
    convInst.convert(hdl='VHDL')

convert()

As you see, conversion works on an instantiated, elaborated design.

The resulting Verilog code is as follows:

module jc2 (
    goLeft,
    goRight,
    stop,
    clk,
    q
);
// A bi-directional 4-bit Johnson counter with stop control.
// 
// I/O pins:
// --------
// clk      : input free-running slow clock 
// goLeft   : input signal to shift left (active-low switch)
// goRight    : input signal to shift right (active-low switch)
// stop     : input signal to stop counting (active-low switch)
// q        : 4-bit counter output (active-low LEDs; q[0] is right-most)

input goLeft;
input goRight;
input stop;
input clk;
output [3:0] q;
reg [3:0] q;

reg [0:0] dir;
reg run;



always @(posedge clk) begin: JC2_LOGIC
    if ((goRight == 0)) begin
        dir <= 1'b0;
        run <= 1'b1;
    end
    else if ((goLeft == 0)) begin
        dir <= 1'b1;
        run <= 1'b1;
    end
    if ((stop == 0)) begin
        run <= 1'b0;
    end
    if (run) begin
        if ((dir == 1'b1)) begin
            q[4-1:1] <= q[3-1:0];
            q[0] <= (!q[3]);
        end
        else begin
            q[3-1:0] <= q[4-1:1];
            q[3] <= (!q[0]);
        end
    end
end

endmodule

The tests to the dir variable in the MyHDL code are mapped to a Verilog case statement. This is because the Verilog convertor handles comparisons to enumerated type items in a special way. The goal is to describe finite state machines efficiently. For example, it is possible to specify a different encoding using the encoding attribute of the enumerated type. The choices are binary (the default), one_hot, and one_cold. Of course, with only 2 states like in this case, this functionality is not relevant. But in general, it is an additional advantage of the use of enumerated types.

Alternative design

This is not yet the end of the story.

The original design in the Xilinx ISE distribution contains 3 views: Abel, Verilog, and VHDL. Interestingly, the Verilog and VHDL views are inconsistent: they have a different behavior. In Verilog terminology, the Verilog view uses blocking assignments only, while the VHDL view uses non-blocking (= signal) assignments only. The VHDL view seems to be consistent with the Abel view and with the supplied test vectors, so it has been the basis of the design that has been discussed so far. However, it is interesting to investigate what the implications of the Verilog view would be on the MyHDL code. Actually, the role of blocking and non-blocking assignments in Verilog is poorly understood, and I believe MyHDL can help to clarify the issues. (The reason is that MyHDL, like VHDL but unlike Verilog, makes a clear difference between signals and variables).

Consider how long it takes before a control input change influences the counter output. In the discussed design, it takes two clock edges: one edge to set the state registers dir and run, and one edge to see the influence of the state registers on the count. You can verify this behavior from the test bench output. (Note that inputs are sampled before the clock edge, and outputs after the clock edge).

Now suppose we would prefer to influence the count output after a single clock edge. Can this be done? The answer is positive: however, to support that we will need to use variables instead of signals for the state registers dir and run.

The modified MyHDL code looks as follows:

from myhdl import *

ACTIVE = 0
DirType = enum('RIGHT', 'LEFT')

@block
def jc2_alt(goLeft, goRight, stop, clk, q):

    """ A bi-directional 4-bit Johnson counter with stop control.

    I/O pins:
    --------

    clk      : input free-running clock 
    goLeft   : input signal to shift left (active-low switch)
    goRight  : input signal to shift right (active-low switch)
    stop     : input signal to stop counting (active-low switch)
    q        : 4-bit counter output (active-low LEDs; q[0] is right-most)

    """

    @instance
    def logic():
        dir = DirType.LEFT
        run = False
        while True:
            yield clk.posedge
            # direction
            if goRight == ACTIVE:
                dir = DirType.RIGHT
                run = True
            elif goLeft == ACTIVE:
                dir = DirType.LEFT
                run = True
            # stop
            if stop == ACTIVE:
                run = False
            # counter action
            if run:
                if dir == DirType.LEFT:
                    q.next[4:1] = q[3:]
                    q.next[0] = not q[3]
                else:
                    q.next[3:] = q[4:1]
                    q.next[3] = not q[0]

    return logic

The instance decorator is used to create the logic generator from the corresponding generator function. The main functionality of that function is wrapped in a while True loop to keep the generator alive "forever". The first statement in the loop is a yield statement that specifies the sensitivity to the clock edge. Variables dir and run are local state variables, that keep their previous value through each iteration of the while loop.

You may wonder why we couldn't use the always decorator with a clock edge argument as before. The reason is that in that case, the decorated function is a classic (= non-generator) function that would be called on every clock edge. However, local variables loose their value whenever a function returns, so they cannot hold state. Therefore, we need to use a more general approach and code the desired behavior explicitly in a generator function.

This example is more subtle and complex than it may seem at first sight. As said before, variables dir and run are state variables and will therefore require a flip-flop in an implementation. However, they are also used "combinatorially": when they change, they may influence the counter operation "in the same clock cycle", that is, before the flip-flop output changes. This is perfectly fine behavior and no problem for synthesis tools, but it tends to confuse a lot of designers. This coding style is also a favourite theme of this author :-) ( --- Jan Decaluwe).

To verify whether we now have the desired behavior, we can run the original test bench on the alternative design:

Alternative design
------------------
goLeft goRight stop clk q
------------------------------
  1      1      1    C  0000
  1      1      1    C  0000
  0      1      1    C  0001
  1      1      1    C  0011
  1      1      1    C  0111
  1      1      1    C  1111
  1      1      1    C  1110
  1      1      1    C  1100
  1      1      1    C  1000
  1      1      1    C  0000
  1      1      1    C  0001
  1      1      1    C  0011
  1      1      0    C  0011
  1      1      1    C  0011
  1      1      1    C  0011
  1      0      1    C  0001
  1      1      1    C  0000
  1      1      1    C  1000
  1      1      1    C  1100
  1      1      1    C  1110
  1      1      1    C  1111
  1      1      1    C  0111
  1      1      1    C  0011
  1      1      1    C  0001
  1      1      1    C  0000

If you compare this output with the previous one, you will see that the counter response to a control input change is now indeed one clock cycle earlier.

Like before, we can convert the MyHDL code automatically to Verilog:

module jc2_alt (
    goLeft,
    goRight,
    stop,
    clk,
    q
);
// A bi-directional 4-bit Johnson counter with stop control.
// 
// I/O pins:
// --------
// 
// clk      : input free-running clock 
// goLeft   : input signal to shift left (active-low switch)
// goRight  : input signal to shift right (active-low switch)
// stop     : input signal to stop counting (active-low switch)
// q        : 4-bit counter output (active-low LEDs; q[0] is right-most)

input goLeft;
input goRight;
input stop;
input clk;
output [3:0] q;
reg [3:0] q;




always @(posedge clk) begin: JC2_ALT_LOGIC
    reg [1-1:0] dir;
    reg run;
    if ((goRight == 0)) begin
        dir = 1'b0;
        run = 1'b1;
    end
    else if ((goLeft == 0)) begin
        dir = 1'b1;
        run = 1'b1;
    end
    if ((stop == 0)) begin
        run = 1'b0;
    end
    if (run) begin
        if ((dir == 1'b1)) begin
            q[4-1:1] <= q[3-1:0];
            q[0] <= (!q[3]);
        end
        else begin
            q[3-1:0] <= q[4-1:1];
            q[3] <= (!q[0]);
        end
    end
end

endmodule

Verilog's always block is more general than the MyHDL always decorator, because you cannot use local state variables with the latter. However, even though the MyHDL code for the alternative design doesn't use the always decorator, the convertor is smart enough to see that it can still use it in the Verilog code in this case.

Note that blocking and non-blocking assignments are mixed in the always block. This is necessary to match the behavior of the alternative MyHDL design. In the Verilog world, you may encounter rules that forbid such a coding style. Such rules are typically created by designers that prefer to focus on "thinking hardware" instead of thinking in terms of code, even when synthesis tools are perfectly able to create efficient hardware from that code. The best you can do with such rules is to ignore them.