1. Overview

1.1. Introduction

In this chapter, we will see various keywords of MyHDL which can be used to create the synthesizable FPGA-designs. We assume the familiarity with the various terms of FPGA designs e.g. signals, process block, always block and reg etc. which are discussed in Verilog/SystemVerilog/VHDL tutorials. Please refer to Verilog/SystemVerilog/VHDL tutorials to understand various terms which are used in FPGA designs.

Similar to Verilog/SystemVerilog/VHDL tutorials, in this chapter, we will implement the ‘2-bit comparator’ using different methods. Please see the Chapter “Overview” of Verilog/VHDL tutorial for the details of the designs.

Important

  • In this tutorial, the designs of Verilog/SystemVerilog/VHDL-tutorials are re-implemented using MyHDL.
  • In the comments of MyHDL designs, the corresponding verilog/vhdl-file-names are shown, which can be downloaded from the website.
  • Please see the Verilog/VHDL tutorials for better understanding of the resultant designs.

1.1.1. Installation and Codes

Important

  • The tutorial is created with MyHDL 1.0 and Python 3.6.2.
  • Click here to donwload the Python codes of the tutorial. Run the Python codes to generate the VHDL/Verilog files.

1.1.2. Keywords

Following is the list of MyHDL-keywords which are used in this tutorial. Also relationship between these keywords with Verilog/VHDL are shown in below table,

Keywords Verilog VHDL
Signal reg, wire signal
bool reg, wire std_logic
intbv(0)[N:0] reg, wire unsigned
enum reg, wire type
if,else if,else or case if,else or case
tuple of int case statement for ROM case statement for ROM
list of bool reg array of std_logic
list of intbv reg array of unsigned
def module entity
@always @always process
@always_comb @always* process(all)
@always_seq @always with initial values of reg/wire process with initial values of signals
ResetSignal used with @always_seq to define reset signal
posedge/negedge posedge/negedge rising_edge/falling_edge

1.2. First design

In this section, we will implement a simple ‘and gate’ using MyHDL, to see the various components of the MyHDL. This simple example will help us to understand the basic interconnection between the VHDL/Verilog-keywords with MyHDL-keywords.

1.2.1. Define ‘and’ gate

  • Below is the Python code which implements the ‘and gate’,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # gateEx.py
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    
    # Module : and gate
    @block
    def andEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : and gate
        # i.e. implement and gate using combinational logic
        @always_comb
        def and_behave():
            z.next = x & y # and operation
    
        # return instances individually
        # return and_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    

    Note

    Above design can be loosely connected with Verilog/VHDL design as follows,

    • The first method, i.e. ‘andEx’, is similar to module/entity of the Verilog/VHDL design.
    • The second method, i.e. ‘and_behave’, defines the implementation method i.e. combinational design, sequential design or latches, which is similar to always/architecture block of the Verilog/VHDL designs.
    • ‘z.next = x & y’ is same as non-blocking assignments i.e. ‘z <= x & y’ in Verilog or ‘z <= x and y’ in VHDL.

1.2.2. Instantiate ‘and’ gate

The above design needs to be instantiated to convert it into VHDL/Verilog code.

  • Run following code to convert the Python code to Verilog and VHDL codes,

    $ python gateEx_convert.py
    

    Note

    • The keyword ‘Signal’ is similar to signal in VHDL or reg/wire in Verilog
    • Note that, the ‘x’, ‘y’ and ‘z’ are not signals, but the ports.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # gateEx_convert.py
    # convert the gateEx.py to VHDL/Verilog code
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    from gateEx import andEx
    
    x = Signal(bool(0)) # signal of type boolean size 1-bit
    y = Signal(bool(0))
    z = Signal(bool(0))
    
    # convert into Verilog code
    andEx_verilog = andEx(x, y, z)
    
    # positional assigments
    # andEx_verilog = andEx(x=x, y=y, z=z)
    
    # initial_values = True will initialize the signal
    # note that there is no signal in this design (all are ports)
    andEx_verilog.convert(hdl="Verilog", initial_values=True)
    
    # convert into VHDL code : convert without instantiation
    andEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
  • The generated Verilog code is shown below,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // File: andEx.v
    // Generated by MyHDL 1.0dev
    // Date: Thu Oct 12 20:34:17 2017
    
    `timescale 1ns/10ps
    
    module andEx (
        x,
        y,
        z
    );
    // input : x, y
    // output : z
    
    input x;
    input y;
    output z;
    wire z;
    
    assign z = (x & y);
    
    endmodule
    
  • The generated VHDL code is shown below. Note that, a package ‘pck_myhdl_10.vhd’ is also created by MyHDL, which contains various functions in it.

    Note

    • Do not forget to add the ‘pck_myhdl_10.vhd’ file in the project while synthesizing the code.
    • Or comment the line ‘use work.pck_myhdl_10.all;’ from the generated VHDL file, if it is not required.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    -- File: andEx.vhd
    -- Generated by MyHDL 1.0dev
    -- Date: Thu Oct 12 20:34:17 2017
    
    
    library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
    use std.textio.all;
    
    use work.pck_myhdl_10.all;
    
    entity andEx is
        port (
            x: in std_logic;
            y: in std_logic;
            z: out std_logic
        );
    end entity andEx;
    -- input : x, y
    -- output : z
    
    architecture MyHDL of andEx is
    begin
    
    z <= (x and y);
    
    end architecture MyHDL;
    

1.2.3. Multiple designs in one file

  • Unlike VHDL/Verilog designs, in MyHDL we can add multiple modules in a single file. In the below code, two modules i.e. ‘andEx’ and ‘xorEx’ are added in the single file i.e. ‘gateEx.py’.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    # gateEx.py
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    
    # Module : and gate
    @block
    def andEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : and gate
        # i.e. implement and gate using combinational logic
        @always_comb
        def and_behave():
            z.next = x & y # and operation
    
        # return instances individually
        # return and_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
    # Module : xor gate
    @block
    def xorEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : xor gate
        # i.e. implement xor gate using combinational logic
        @always_comb
        def xor_behave():
            z.next = x ^ y # xor operation
    
        # return instances individually
        # return xor_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
  • Then ‘gateEx_convert.py’ file is used to generate the Verilog code for these two modules.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # gateEx_convert.py
    # convert the gateEx.py to VHDL/Verilog code
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    from gateEx import andEx, xorEx
    
    x = Signal(bool(0)) # signal of type boolean size 1-bit
    y = Signal(bool(0))
    z = Signal(bool(0))
    
    # convert into Verilog code
    andEx_verilog = andEx(x, y, z)
    
    # initial_values = True will initialize the signal
    # note that there is no signal in this design (all are ports)
    andEx_verilog.convert(hdl="Verilog", initial_values=True)
    
    # convert into VHDL code  : convert without instantiation
    andEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
    
    # convert into Verilog code
    xorEx_verilog = xorEx(x, y, z)
    
    # initial_values = True will initialize the signal
    # note that there is no signal in this design (all are ports)
    xorEx_verilog.convert(hdl="Verilog", initial_values=True)
    
    # convert into VHDL code  : convert without instantiation
    xorEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    

1.2.4. Design and conversion codes in the same files

It is handy to keep the designs and conversion codes in the same file, rather than writing each piece of code in separate files. In this section, we merge the design and conversion code of the ‘and gate’ and ‘xor gate’ in one file.

  • For writing the design and conversion code in the same file, we need to define one boilerplate, i.e. ” if __name__ = ‘__main__’:, which tells the python that the entry point of the code is the ‘main()’ function, as shown below.

  • If we execute the file ‘gateEx.py’, then the results will be same as previous sections.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    # gateEx.py
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    
    # Module : and gate
    @block
    def andEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : and gate
        # i.e. implement and gate using combinational logic
        @always_comb
        def and_behave():
            z.next = x & y # and operation
    
        # return instances individually
        # return and_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
    # Module : xor gate
    @block
    def xorEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : xor gate
        # i.e. implement xor gate using combinational logic
        @always_comb
        def xor_behave():
            z.next = x ^ y # xor operation
    
        # return instances individually
        # return xor_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
    
    def main():
        x = Signal(bool(0)) # signal of type boolean size 1-bit
        y = Signal(bool(0))
        z = Signal(bool(0))
    
        # convert into Verilog code
        andEx_verilog = andEx(x, y, z)
    
        # initial_values = True will initialize the signal
        # note that there is no signal in this design (all are ports)
        andEx_verilog.convert(hdl="Verilog", initial_values=True)
    
        # convert into VHDL code  : convert without instantiation
        andEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
    
        # convert into Verilog code
        xorEx_verilog = xorEx(x, y, z)
    
        # initial_values = True will initialize the signal
        # note that there is no signal in this design (all are ports)
        xorEx_verilog.convert(hdl="Verilog", initial_values=True)
    
        # convert into VHDL code  : convert without instantiation
        xorEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
    if __name__ == '__main__':
        main()
    

1.2.5. Updated port-names for pin assignments using .csv file

In previous section, we implemented the comparator using various methods and converted those designs to Verilog codes. Also, we learn to implement the structural modeling using MyHDL.

Note

  • In this section, we will create the top level design with updated port names (according to pin-assignment .csv file, provided in Verilog/VHDL tutorials), so that we need not to change the pin-names in the csv file according to each new design.
  • Also, the different signal values (i.e. SW and output value) are assigned to single output port (i.e. LEDG). For this, we need to create one additional ‘always’ block as shown in the module ‘top_xorEx’.
  • Further unlike VHDL/Verilog, for two switches SW[2:0] is used in MyHDL (not SW[1:0]).
  • The keyword ‘intbv’ is used to define the vector-signal instead of ‘bool’, as the ‘bool’ keyword is not subscriptable.
  • Lastly, if we want to change the port name for ‘and gate’ as well, then we need to write another top level module for andEx.
  • In the below code, a top level design is created for the design in Section 1.2. Following port-mapping is done here,

    • For the ‘xor gate’, the input ports are connect to SW.
    • The inputs and output of ‘xor gate’ are connected to LEDG.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    # gateEx.py
    # the resultant Verilog code is same as Listing 'andEx.v of Verilog tutorial
    
    from myhdl import *
    
    # Module : and gate
    @block
    def andEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : and gate
        # i.e. implement and gate using combinational logic
        @always_comb
        def and_behave():
            z.next = x & y # and operation
    
        # return instances individually
        # return and_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
    # Module : xor gate
    @block
    def xorEx(x, y, z):
        """ input : x, y
            output : z
        """
    
        # behavior : xor gate
        # i.e. implement xor gate using combinational logic
        @always_comb
        def xor_behave():
            z.next = x ^ y # xor operation
    
        # return instances individually
        # return xor_behave
    
        # this is more convenient as it returns all the instances automatically
        return instances()
    
    
    # top level entity for 'xor' gate
    @block
    def top_xorEx(SW, LEDG):
        # instantiate xorEx : display only output on green LED (LEDG)
        xorEx_verilog = xorEx(x=SW(0), y=SW(1), z=LEDG)
    
        return instances()
    
    def main():
        x = Signal(bool(0)) # signal of type boolean size 1-bit
        y = Signal(bool(0))
        z = Signal(bool(0))
    
        switch = Signal(intbv(0)[2:0]) # 2 switches
        led = Signal(intbv(0)[3:0]) # 3 green LED
    
        # convert into Verilog code
        andEx_verilog = andEx(x, y, z)
    
        # initial_values = True will initialize the signal
        # note that there is no signal in this design (all are ports)
        andEx_verilog.convert(hdl="Verilog", initial_values=True)
    
        # convert into VHDL code  : convert without instantiation
        andEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
    
        # convert into Verilog code
        xorEx_verilog = xorEx(x, y, z)
    
        # initial_values = True will initialize the signal
        # note that there is no signal in this design (all are ports)
        xorEx_verilog.convert(hdl="Verilog", initial_values=True)
    
        # convert into VHDL code  : convert without instantiation
        xorEx(x, y, z).convert(hdl="VHDL", initial_values=True)
    
        # modified port name according to pin-assignment file
        top_xorEx(switch, led).convert(hdl="Verilog", initial_values=True)
    
    if __name__ == '__main__':
        main()
    
  • The corresponding Verilog code for “xor gate” is shown below. Now, the below can be loaded on the FPGA with the pin-assignment file provided in the Verilog/VHDL tutorials.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // File: top_xorEx.v
    // Generated by MyHDL 1.0dev
    // Date: Thu Oct 12 21:40:22 2017
    
    `timescale 1ns/10ps
    
    module top_xorEx (
        SW,
        LEDG
    );
    
    input [1:0] SW;
    output [2:0] LEDG;
    wire [2:0] LEDG;
    
    assign LEDG = (SW[0] ^ SW[1]);
    
    endmodule
    

1.3. Comparator

In this section, we will implement the 1-bit comparator and the 2-bit comparator with different modeling styles,

1.3.1. 1-bit comparator

  • The ‘@always’ decorator along with sensitivity list can be used for implementing the procedural assignments; whereas ‘@always_comb’ without sensitivity list can be used to implement the ‘continuous assignment’. In this section, the 1-bit comparator is implemented using procedural and continuous assignments.

    Important

    • The continuous or sequential assignments are inferred automatically based on the logics inside the block. More specifically, the combinational design can be implemented by using ‘sequential statements’ or ‘continuous statements’ based on the logic inside the ‘always_comb’ block.
    • Further, both the designs, i.e. combinational and procedural, are written in one file (comparator_1_bit.py), but the corresponding Verilog designs are generated as separate files (comparator_1_bit_proc.v and comparator_1_bit_comb.v).
  • Below code will generate the Verilog code with continuous and procedural assignments,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    # comparator_1_bit.py
    
    # the resultant Verilog code is same as Listing 'comparator1Bit.v
    # of Verilog tutorial
    
    from myhdl import *
    
    # 1-bit comparator using procedural assignment
    @block
    def comparator_1_bit_proc(x, y, eq):
        """ x, y : input
            eq : output
    
            eq = 1 when x = y else 0
        """
    
        s0 = Signal(bool(0))
        s1 = Signal(bool(0))
    
        @always(s0, s1)
        def comparator_1_bit_behave():
            s0.next = ~x & ~y
            s1.next = x & y
            eq.next = s0 | s1
    
        return comparator_1_bit_behave
    
    
    # 1-bit comparator using continuous assignment
    @block
    def comparator_1_bit_comb(x, y, eq):
        """ x, y : input
            eq : output
    
            eq = 1 when x = y else 0
        """
    
        @always_comb
        def comparator_1_bit_behave():
            eq.next = (~x & ~y) | (x & y)
    
        return comparator_1_bit_behave
    
    def main():
        x = Signal(bool(0)) # signal of type boolean size 1 bit
        y = Signal(bool(0))
        eq = Signal(bool(0))
    
        # convert into Verilog code
        comparator_verilog_procedure = comparator_1_bit_proc(x=x, y=y,
                    eq=eq).convert(hdl="Verilog", initial_values=True)
        comparator_verilog_continuous = comparator_1_bit_comb(x=x, y=y,
                    eq=eq).convert(hdl="Verilog", initial_values=True)
    
    if __name__ == '__main__':
        main()
    
  • Two separate verilog files will be generated by above code, which are shown below.

    Note

    Since we set the “initial_values=True” therefore the signals s0 and s1 are initialized with 0 in the file “comparator_1_bit_proc.v”.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // File: comparator_1_bit_proc.v
    // Generated by MyHDL 1.0dev
    // Date: Fri Oct 13 07:52:12 2017
    
    
    `timescale 1ns/10ps
    
    module comparator_1_bit_proc (
        x,
        y,
        eq
    );
    // x, y : input
    // eq : output
    //
    // eq = 1 when x = y else 0
    
    input x;
    input y;
    output eq;
    reg eq;
    
    reg s1 = 0;
    reg s0 = 0;
    
    always @(s0, s1) begin: COMPARATOR_1_BIT_PROC_COMPARATOR_1_BIT_BEHAVE
        s0 <= ((~x) & (~y));
        s1 <= (x & y);
        eq <= (s0 | s1);
    end
    
    endmodule
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // File: comparator_1_bit_comb.v
    // Generated by MyHDL 1.0dev
    // Date: Fri Oct 13 07:52:12 2017
    
    
    `timescale 1ns/10ps
    
    module comparator_1_bit_comb (
        x,
        y,
        eq
    );
    // x, y : input
    // eq : output
    //
    // eq = 1 when x = y else 0
    
    input x;
    input y;
    output eq;
    wire eq;
    
    assign eq = (((~x) & (~y)) | (x & y));
    
    endmodule
    

1.3.2. 2-bit comparator

  • Following code implements the two bit comparator,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    # comparator_2_bit.py
    # the resultant Verilog code is same as Listing 'comparator2Bit.v
    # of Verilog tutorial
    
    from myhdl import *
    
    @block
    def comparator_2_bit(x, y, eq):
        """ x, y : input
            eq : output
    
            eq = 1 when x = y else 0
        """
    
        # 'intbv' is used instead of 'bool' as 'bool' is not subscriptable
        s = Signal(intbv(0)[4:0])
    
        @always(s)
        def comparator_2_bit_behave():
            s[0].next =  ~x[1] & ~x[0] & ~y[1] & ~y[0]
            s[1].next =  ~x[1] & x[0] & ~y[1] & y[0]
            s[2].next =  x[1] & ~x[0] & y[1] & ~y[0]
            s[3].next =  x[1] & x[0] & y[1] & y[0]
            eq.next = s[0] | s[1] | s[2] | s[3]
    
        return comparator_2_bit_behave
    
    def main():
        # 'intbv' is used instead of 'bool' as 'bool' is not subscriptable
        x = Signal(intbv(0)[2:0]) # signal of type boolean size 1-bit
        y = Signal(intbv(0)[2:0])
        eq = Signal(bool(0))
    
        # convert into Verilog code
        comparator_verilog = comparator_2_bit(x=x, y=y, eq=eq)
        comparator_verilog.convert(hdl="Verilog", initial_values=True)
    
    if __name__ == '__main__':
        main()
    
  • The resultant Verilog code is shown below,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // File: comparator_2_bit.v
    // Generated by MyHDL 1.0dev
    // Date: Fri Oct 13 08:21:35 2017
    
    
    `timescale 1ns/10ps
    
    module comparator_2_bit (
        x,
        y,
        eq
    );
    // x, y : input
    // eq : output
    //
    // eq = 1 when x = y else 0
    
    input [1:0] x;
    input [1:0] y;
    output eq;
    reg eq;
    
    reg [3:0] s = 0;
    
    always @(s) begin: COMPARATOR_2_BIT_COMPARATOR_2_BIT_BEHAVE
        s[0] <= ((((~x[1]) & (~x[0])) & (~y[1])) & (~y[0]));
        s[1] <= ((((~x[1]) & x[0]) & (~y[1])) & y[0]);
        s[2] <= (((x[1] & (~x[0])) & y[1]) & (~y[0]));
        s[3] <= (((x[1] & x[0]) & y[1]) & y[0]);
        eq <= (((s[0] | s[1]) | s[2]) | s[3]);
    end
    
    endmodule
    

1.3.3. Structural modeling

In structural modeling the predefined designs are connected together to create the larger system. In this section, we will use two 1-bit comparator to create a 2-bit comparator.

Note

Unlike VHDL/Verilog, the structural model defined in the MyHDL does not instantiate the smaller unit but re-implement it, as shown in this section.

  • Two bit comparator design is shown below,

    Warning

    • We can not use the ‘intbv’ to define the output-signal to use it in the instantiation by “slicing it”, i.e. below line can not be synthesized.
    • “Signal has multiple driver error” is shown for this, as a part of the slice will be updated by one instantiation, whereas the other part will be updated at other instantiation. In the other words, the output signal will be updated at different ‘always-block’ of Verilog.
    • Also, note that for input signal small brackets are used i.e a(0), b(0).
    s = Signal(intbv(0)[2:0])
    eq0 = comparator_1_bit(x=a(0), y=b(0), eq=s(0)) # this will not work
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    # comparator_2_bit_struct.py
    # this code is similar to comparator2BitStruct.v of verilog tutorial
    
    from myhdl import *
    from comparator_1_bit import comparator_1_bit_proc
    
    @block
    def comparator_2_bit_struct(a, b, eq):
    
        # s = Signal(intbv(0)[2:0]) # this does not work
        s0 = Signal(bool(0))
        s1 = Signal(bool(0))
    
        # instantiation : 1-bit comparator
        # note square brackets are not used i.e. a(0) and b(0)
        eq0 = comparator_1_bit_proc(x=a(0), y=b(0), eq=s0)
        eq1 = comparator_1_bit_proc(x=a(1), y=b(1), eq=s1)
    
        @always(s0, s1)
        def comparator_2_bit_behave():
            if ((s0 == 1) & (s1 == 1)) :
                eq.next = 1
            else :
                eq.next = 0
        return comparator_2_bit_behave, eq0, eq1
    
    def main():
        x = Signal(intbv(0)[2:0])
        y = Signal(intbv(0)[2:0])
        eq = Signal(bool(0))
    
        # convert into Verilog code
        comparator_verilog = comparator_2_bit_struct(a=x, b=y, eq=eq)
        comparator_verilog.convert(hdl="Verilog", initial_values=True)
    
    if __name__ == '__main__' :
        main()
    
  • The resultant Verilog code is shown below.

    Warning

    Note that the 1-bit comparator design is not instantiated here, but reimplemented in the 2-bit comparator (see lines 39-43 and 46-50).

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    // File: comparator_2_bit_struct.v
    // Generated by MyHDL 1.0dev
    // Date: Fri Oct 13 11:57:42 2017
    
    
    `timescale 1ns/10ps
    
    module comparator_2_bit_struct (
        a,
        b,
        eq
    );
    
    
    
    input [1:0] a;
    input [1:0] b;
    output eq;
    reg eq;
    
    reg s1 = 0;
    reg s0 = 0;
    reg comparator_1_bit_proc_1_s1 = 0;
    reg comparator_1_bit_proc_1_s0 = 0;
    reg comparator_1_bit_proc_2_s1 = 0;
    reg comparator_1_bit_proc_2_s0 = 0;
    
    
    always @(s0, s1) begin: COMPARATOR_2_BIT_STRUCT_COMPARATOR_2_BIT_BEHAVE
        if (((s0 == 1) & (s1 == 1))) begin
            eq <= 1;
        end
        else begin
            eq <= 0;
        end
    end
    
    
    always @(comparator_1_bit_proc_1_s0, comparator_1_bit_proc_1_s1) begin: COMPARATOR_2_BIT_STRUCT_COMPARATOR_1_BIT_PROC_1_COMPARATOR_1_BIT_BEHAVE
        comparator_1_bit_proc_1_s0 <= ((~a[0]) & (~b[0]));
        comparator_1_bit_proc_1_s1 <= (a[0] & b[0]);
        s0 <= (comparator_1_bit_proc_1_s0 | comparator_1_bit_proc_1_s1);
    end
    
    
    always @(comparator_1_bit_proc_2_s0, comparator_1_bit_proc_2_s1) begin: COMPARATOR_2_BIT_STRUCT_COMPARATOR_1_BIT_PROC_2_COMPARATOR_1_BIT_BEHAVE
        comparator_1_bit_proc_2_s0 <= ((~a[1]) & (~b[1]));
        comparator_1_bit_proc_2_s1 <= (a[1] & b[1]);
        s1 <= (comparator_1_bit_proc_2_s0 | comparator_1_bit_proc_2_s1);
    end
    
    endmodule
    

1.4. Conclusion

In this chapter, we saw various features of MyHDL to implement the synthesizable designs. The @always and @always_comb blocks are used for implementing the continuous assignment, procedural assignment and structural designs. Also, we saw basic differences between the pure Verilog design and the Verilog design generated by the MyHDL. Further, we saw the method to create the top level module according to ‘pin-assignment-file’. Lastly, we learn the method by which the design and it’s conversion functions can be written in the same file.