Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
MyHDL Arbiter
from myhdl import *
def arbiter(clk, devices):
request_list = [ device.request for device in devices ]
request_vector = ConcatSignal(*reversed(request_list))
grant_vector = Signal(intbv(0)[len(request_list):])
grant_list = [ device.grant for device in reversed(devices) ]
@always_comb
def logic():
grant_vector.next = request_vector & (-request_vector) # Will leave only LSB, i.e. 00100100 => 00000100
@always_comb
def assign():
for i in range(len(request_list)):
grant_list[i].next = grant_vector[i]
return instances()
class Device:
def __init__(self):
self.request = Signal(bool())
self.grant = Signal(bool())
def instance(self, clk):
counter = Signal(intbv())
@always(clk.posedge)
def logic():
if self.grant == 1:
counter.next = counter + 1
self.request.next = 1
return instances()
clk = Signal(bool())
devices = [ Device(), Device() ]
def top(clk):
a = arbiter(clk, devices)
di = [ device.instance(clk) for device in devices ]
return instances()
toVerilog(top, clk)
@josyb

This comment has been minimized.

Show comment Hide comment
@josyb

josyb Jan 3, 2018

I worked a bit with your code:

from myhdl import *

@block
def arbiter(clk, devices):
    request_list = [ device.request for device in devices ]
    request_vector = ConcatSignal(*reversed(request_list))
    grant_vector = Signal(intbv(0)[len(request_list):])
    grant_list = [ device.grant for device in reversed(devices) ]

    @always_comb
    def logic():
        grant_vector.next = request_vector & (-request_vector) # Will leave only LSB, i.e. 00100100 => 00000100

    @always_seq(clk.posedge, reset=None)
    def assign():
        for i in range(len(request_list)):
            grant_list[i].next = grant_vector[i]
            
    return logic, assign
    
class Device:
    def __init__(self):
        self.request = Signal(bool())
        self.grant = Signal(bool())
        
    @block
    def rtl(self, clk):
        counter = Signal(intbv(0)[8:])
        @always(clk.posedge)
        def logic():
            if self.grant == 1:
                counter.next = counter + 1
            self.request.next = 1
        return logic

@block
def top(clk, devices):
    a = arbiter(clk, devices)
    di = [ device.rtl(clk) for device in devices ]
    return instances()

# toVerilog(top, clk)
clk = Signal(bool())
devices = [ Device(), Device() ]

dfc = top(clk, devices)
dfc.name = 'top'
dfc.convert(hdl="Verilog")

this converts fine.
Note that I upgraded the code to MyHDL 1.0dev, introducing @block. It may (probably not ) work in MyHDL 0.9.

I then indulged myself and reworked your code into a more elaborated example showing my approach on class-based HDL design.
You now have a button per device to request the shared resource and the count in each device is now shown on a dual digit seven segment display

from __future__ import print_function
from collections import namedtuple

import myhdl
from myhdl import Signal, intbv, modbv, always_comb, always_seq, ConcatSignal, instances

if myhdl.__version__ == '1.0dev':
    from myhdl import block
else:
    from myhdl import toVHDL, toVerilog

    # define an 'empty' block decorator
    def block(ob):
        return ob


def _copy(sig):
    ''' a helper to create Signals without thinking too much '''
    if isinstance(sig.val, modbv):
        return Signal(modbv(0)[len(sig):])
    elif isinstance(sig.val, intbv):
        return Signal(intbv(0)[len(sig):])
    else:
        return Signal(bool(0))


ForwardPort = namedtuple('ForwardPort', ['rtlblock', 'port'])


class RtlProvider(object):
    ''' Base class for MyHdl generating classes '''

    # yes, it needs an @block decorator
    @block
    def rtlinstances(self, dlocals):
        ''' return the rtl() of the instantiated building blocks '''
        from myhdl._instance import _Instantiator

        keys = dlocals.keys()
        values = dlocals.values()
        # first resolve the ForwardPorts (if any)
        for value in values:
#             print( value )
            if isinstance(value, list):
                # we only support a one dimensional list
                if isinstance(value[0], RtlProvider):
                    for item in value:
                        refs = vars(item)
                        for dest, sig in refs.iteritems():
                            if isinstance(sig, ForwardPort):
                                item.__setattr__(dest, getattr(dlocals[sig.rtlblock], sig.port))
            elif isinstance(value, RtlProvider):
                if id(value) != id(self):
                    refs = vars(value)
                    for dest, sig in refs.iteritems():
                        if isinstance(sig, ForwardPort):
                            value.__setattr__(dest, getattr(dlocals[sig.rtlblock], sig.port))

        # now collect the rtl()
        l = []
        for key, value in zip(keys, values):
            if isinstance(value, list):
                if isinstance(value[0], RtlProvider):
                    idx = 0
                    for item in value:
                        if id(item) != id(self):
                            trtl = item.rtl()
                            if myhdl.__version__ == '1.0dev':
                                trtl.name = '{}{}'.format(key, idx)
                            idx += 1
                            l.append(trtl)
            elif isinstance(value, RtlProvider):
                if id(value) != id(self):
                    trtl = value.rtl()
                    if myhdl.__version__ == '1.0dev':
                        trtl.name = '{}'.format(key)
                    l.append(trtl)
                    
            elif isinstance(value, _Instantiator):
                l.append(value)
        return l

    def rtl(self):
        ''' placeholder to detect empty instances '''
        raise NotImplementedError('You need to define a rtl() method!')


class Arbiter(RtlProvider):

    def __init__(self, clk, devices):
        self.clk = clk
        self.devices = devices

    @block
    def rtl(self):
        request_list = [ device.request for device in self.devices ]
        request_vector = ConcatSignal(*reversed(request_list))
        grant_vector = Signal(intbv(0)[len(request_list):])
        grant_list = [ device.grant for device in reversed(self.devices) ]

        @always_seq(self.clk.posedge, reset=None)
        def logic():
            grant_vector.next = request_vector & (-request_vector)  # Will leave only LSB, i.e. 00100100 => 00000100

        @always_comb
        def assign():
            for i in range(len(request_list)):
                grant_list[i].next = grant_vector[i]

        return logic, assign

class SevenSegment(RtlProvider):
    ''' driving a seven segment display '''
    def __init__(self, A, Y):
        self.A = A
        self.Y = Y if Y is not None else Signal(intbv(0)[7:])

    @block
    def rtl(self):
        ''' the logic '''
        SEGMENTS = (# 0 to 4
                    # -          _    -     
                    #| |    |     |    |    | |
                    #            -    _      _
                    #| |    |   |      |      |
                    # _          _    _
                    0x3f, 0x06, 0x5b, 0x4f, 0x63,
                    # 5 to 9
                    # -   -      _     _    _
                    #|   |        |   | |  | |
                    # -   _            _    _
                    #  | | |      |   | |    |
                    # _   _            -    _
                    0x6d, 0x7e, 0x33, 0x7f, 0x77,
                    # a, b, c, d, e, f
                    # -           -           -    -
                    #| |  |      |      |    |    |
                    # -    _           -      -    -
                    #| |  | |    |    | |    |    |
                    #      -      -    -      -
                    0x7b, 0x7c, 0x39, 0x1e, 0x79, 0x71)

        @always_comb
        def sevensegment():
            self.Y.next = SEGMENTS[self.A]

        return sevensegment


class Device(RtlProvider):

    def __init__(self, clk, pb, grant=None, request=None, q=None):
        self.clk = clk
        self.pb = pb
        self.grant = grant if grant is not None else Signal(bool(0))
        self.request = request if request is not None else Signal(bool(0))
        self.q = q if q is not None else Signal(intbv(0)[8:])

    @block
    def rtl(self):
        counter = _copy(self.q)

        @always_seq(self.clk.posedge, reset=None)
        def logic():
            # `always request attention; this will permanently grant access to the first device ...
            self.request.next = self.pb
            if self.grant:
                counter.next = counter + 1

        @always_comb
        def assign():
            self.q.next = counter

        return logic, assign


class System(RtlProvider):

    def __init__(self, clk, pb1, pb2, ss11, ss12, ss21, ss22):
        self.clk = clk
        self.pb1 = pb1
        self.pb2 = pb2
        self.ss11 = ss11
        self.ss12 = ss12
        self.ss21 = ss21
        self.ss22 = ss22

    @block
    def rtl(self):
        di = []
        di.append(Device(self.clk, self.pb1))
        di.append(Device(self.clk, self.pb2))
        dss11 = SevenSegment(di[0].q(8,4),self.ss11)
        dss12 = SevenSegment(di[0].q(4,0),self.ss12)
        dss21 = SevenSegment(di[1].q(8,4),self.ss21)
        dss22 = SevenSegment(di[1].q(4,0),self.ss22)
        arb = Arbiter(self.clk, di)

        return self.rtlinstances(locals())
    

if __name__ == '__main__':

    @block
    def top(clk, pb1, pb2, ss11, ss12, ss21, ss22):
        toprtl = System(clk, pb1, pb2, ss11, ss12, ss21, ss22).rtl()
        if myhdl.__version__ == '1.0dev':
            toprtl.name = 'top'

        return toprtl

    def convert():
        clk = Signal(bool(0))
        ss11, ss12, ss21, ss22 = [Signal(modbv(0)[7:]) for _ in range(4)]
        pb1, pb2 = [Signal(bool(0)) for _ in range(2)]
        
        if myhdl.__version__ == '1.0dev':
            dfc = top(clk, pb1, pb2, ss11, ss12, ss21, ss22)
            dfc.convert('VHDL', name='top', std_logic_ports=True)
            dfc.convert('Verilog')
        else:
            toVHDL.standard = '2008'
            toVHDL.name = 'top'
#             toVHDL.std_logic_ports = True
            toVHDL(top, clk, pb1, pb2, ss11, ss12, ss21, ss22)
            toVerilog(top, clk, pb1, pb2, ss11, ss12, ss21, ss22)

    convert()

Again it will probably not work in MyHDL 0.9

josyb commented Jan 3, 2018

I worked a bit with your code:

from myhdl import *

@block
def arbiter(clk, devices):
    request_list = [ device.request for device in devices ]
    request_vector = ConcatSignal(*reversed(request_list))
    grant_vector = Signal(intbv(0)[len(request_list):])
    grant_list = [ device.grant for device in reversed(devices) ]

    @always_comb
    def logic():
        grant_vector.next = request_vector & (-request_vector) # Will leave only LSB, i.e. 00100100 => 00000100

    @always_seq(clk.posedge, reset=None)
    def assign():
        for i in range(len(request_list)):
            grant_list[i].next = grant_vector[i]
            
    return logic, assign
    
class Device:
    def __init__(self):
        self.request = Signal(bool())
        self.grant = Signal(bool())
        
    @block
    def rtl(self, clk):
        counter = Signal(intbv(0)[8:])
        @always(clk.posedge)
        def logic():
            if self.grant == 1:
                counter.next = counter + 1
            self.request.next = 1
        return logic

@block
def top(clk, devices):
    a = arbiter(clk, devices)
    di = [ device.rtl(clk) for device in devices ]
    return instances()

# toVerilog(top, clk)
clk = Signal(bool())
devices = [ Device(), Device() ]

dfc = top(clk, devices)
dfc.name = 'top'
dfc.convert(hdl="Verilog")

this converts fine.
Note that I upgraded the code to MyHDL 1.0dev, introducing @block. It may (probably not ) work in MyHDL 0.9.

I then indulged myself and reworked your code into a more elaborated example showing my approach on class-based HDL design.
You now have a button per device to request the shared resource and the count in each device is now shown on a dual digit seven segment display

from __future__ import print_function
from collections import namedtuple

import myhdl
from myhdl import Signal, intbv, modbv, always_comb, always_seq, ConcatSignal, instances

if myhdl.__version__ == '1.0dev':
    from myhdl import block
else:
    from myhdl import toVHDL, toVerilog

    # define an 'empty' block decorator
    def block(ob):
        return ob


def _copy(sig):
    ''' a helper to create Signals without thinking too much '''
    if isinstance(sig.val, modbv):
        return Signal(modbv(0)[len(sig):])
    elif isinstance(sig.val, intbv):
        return Signal(intbv(0)[len(sig):])
    else:
        return Signal(bool(0))


ForwardPort = namedtuple('ForwardPort', ['rtlblock', 'port'])


class RtlProvider(object):
    ''' Base class for MyHdl generating classes '''

    # yes, it needs an @block decorator
    @block
    def rtlinstances(self, dlocals):
        ''' return the rtl() of the instantiated building blocks '''
        from myhdl._instance import _Instantiator

        keys = dlocals.keys()
        values = dlocals.values()
        # first resolve the ForwardPorts (if any)
        for value in values:
#             print( value )
            if isinstance(value, list):
                # we only support a one dimensional list
                if isinstance(value[0], RtlProvider):
                    for item in value:
                        refs = vars(item)
                        for dest, sig in refs.iteritems():
                            if isinstance(sig, ForwardPort):
                                item.__setattr__(dest, getattr(dlocals[sig.rtlblock], sig.port))
            elif isinstance(value, RtlProvider):
                if id(value) != id(self):
                    refs = vars(value)
                    for dest, sig in refs.iteritems():
                        if isinstance(sig, ForwardPort):
                            value.__setattr__(dest, getattr(dlocals[sig.rtlblock], sig.port))

        # now collect the rtl()
        l = []
        for key, value in zip(keys, values):
            if isinstance(value, list):
                if isinstance(value[0], RtlProvider):
                    idx = 0
                    for item in value:
                        if id(item) != id(self):
                            trtl = item.rtl()
                            if myhdl.__version__ == '1.0dev':
                                trtl.name = '{}{}'.format(key, idx)
                            idx += 1
                            l.append(trtl)
            elif isinstance(value, RtlProvider):
                if id(value) != id(self):
                    trtl = value.rtl()
                    if myhdl.__version__ == '1.0dev':
                        trtl.name = '{}'.format(key)
                    l.append(trtl)
                    
            elif isinstance(value, _Instantiator):
                l.append(value)
        return l

    def rtl(self):
        ''' placeholder to detect empty instances '''
        raise NotImplementedError('You need to define a rtl() method!')


class Arbiter(RtlProvider):

    def __init__(self, clk, devices):
        self.clk = clk
        self.devices = devices

    @block
    def rtl(self):
        request_list = [ device.request for device in self.devices ]
        request_vector = ConcatSignal(*reversed(request_list))
        grant_vector = Signal(intbv(0)[len(request_list):])
        grant_list = [ device.grant for device in reversed(self.devices) ]

        @always_seq(self.clk.posedge, reset=None)
        def logic():
            grant_vector.next = request_vector & (-request_vector)  # Will leave only LSB, i.e. 00100100 => 00000100

        @always_comb
        def assign():
            for i in range(len(request_list)):
                grant_list[i].next = grant_vector[i]

        return logic, assign

class SevenSegment(RtlProvider):
    ''' driving a seven segment display '''
    def __init__(self, A, Y):
        self.A = A
        self.Y = Y if Y is not None else Signal(intbv(0)[7:])

    @block
    def rtl(self):
        ''' the logic '''
        SEGMENTS = (# 0 to 4
                    # -          _    -     
                    #| |    |     |    |    | |
                    #            -    _      _
                    #| |    |   |      |      |
                    # _          _    _
                    0x3f, 0x06, 0x5b, 0x4f, 0x63,
                    # 5 to 9
                    # -   -      _     _    _
                    #|   |        |   | |  | |
                    # -   _            _    _
                    #  | | |      |   | |    |
                    # _   _            -    _
                    0x6d, 0x7e, 0x33, 0x7f, 0x77,
                    # a, b, c, d, e, f
                    # -           -           -    -
                    #| |  |      |      |    |    |
                    # -    _           -      -    -
                    #| |  | |    |    | |    |    |
                    #      -      -    -      -
                    0x7b, 0x7c, 0x39, 0x1e, 0x79, 0x71)

        @always_comb
        def sevensegment():
            self.Y.next = SEGMENTS[self.A]

        return sevensegment


class Device(RtlProvider):

    def __init__(self, clk, pb, grant=None, request=None, q=None):
        self.clk = clk
        self.pb = pb
        self.grant = grant if grant is not None else Signal(bool(0))
        self.request = request if request is not None else Signal(bool(0))
        self.q = q if q is not None else Signal(intbv(0)[8:])

    @block
    def rtl(self):
        counter = _copy(self.q)

        @always_seq(self.clk.posedge, reset=None)
        def logic():
            # `always request attention; this will permanently grant access to the first device ...
            self.request.next = self.pb
            if self.grant:
                counter.next = counter + 1

        @always_comb
        def assign():
            self.q.next = counter

        return logic, assign


class System(RtlProvider):

    def __init__(self, clk, pb1, pb2, ss11, ss12, ss21, ss22):
        self.clk = clk
        self.pb1 = pb1
        self.pb2 = pb2
        self.ss11 = ss11
        self.ss12 = ss12
        self.ss21 = ss21
        self.ss22 = ss22

    @block
    def rtl(self):
        di = []
        di.append(Device(self.clk, self.pb1))
        di.append(Device(self.clk, self.pb2))
        dss11 = SevenSegment(di[0].q(8,4),self.ss11)
        dss12 = SevenSegment(di[0].q(4,0),self.ss12)
        dss21 = SevenSegment(di[1].q(8,4),self.ss21)
        dss22 = SevenSegment(di[1].q(4,0),self.ss22)
        arb = Arbiter(self.clk, di)

        return self.rtlinstances(locals())
    

if __name__ == '__main__':

    @block
    def top(clk, pb1, pb2, ss11, ss12, ss21, ss22):
        toprtl = System(clk, pb1, pb2, ss11, ss12, ss21, ss22).rtl()
        if myhdl.__version__ == '1.0dev':
            toprtl.name = 'top'

        return toprtl

    def convert():
        clk = Signal(bool(0))
        ss11, ss12, ss21, ss22 = [Signal(modbv(0)[7:]) for _ in range(4)]
        pb1, pb2 = [Signal(bool(0)) for _ in range(2)]
        
        if myhdl.__version__ == '1.0dev':
            dfc = top(clk, pb1, pb2, ss11, ss12, ss21, ss22)
            dfc.convert('VHDL', name='top', std_logic_ports=True)
            dfc.convert('Verilog')
        else:
            toVHDL.standard = '2008'
            toVHDL.name = 'top'
#             toVHDL.std_logic_ports = True
            toVHDL(top, clk, pb1, pb2, ss11, ss12, ss21, ss22)
            toVerilog(top, clk, pb1, pb2, ss11, ss12, ss21, ss22)

    convert()

Again it will probably not work in MyHDL 0.9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment