class Configurator(Unit):
    """Class for handling configuration dictionaries."""
    
    def __init__(self, root, conf):
#         self.conf = conf
        Unit.__init__(self, root, "cfg", conf=conf)
       
    def build(self):
        for unit in self.units:
            class_load(unit)(self._parent)
    
    def _reload_cfg_index(self):
        self.reload_index()
    
    def reload_index(self):
        self.root_index = self._parent.index()

    def apply_cfg_filt(self, filt):
        for pat in fnmatch.filter(self.conf.keys(), filt):
            name_pat, attr = pat.partition(".")[::2]
            for qname in fnmatch.filter(self.root_index.keys(), name_pat):  
                self.root_index[qname].__setattr__(attr, self.conf[pat]) class Component:
class System(Component):
    def __init__(self, name=None, parent=None, **kwargs):
    def __repr__(self):
    def __setitem__(self, key, val):
    def __getitem__(self, key):
    def inst(self, name, cls, *args, **kwargs):
    def search(self, pattern='*', of_type=None, depth=0, pattern_relative=True):
    def __init__(self, name=''):
    def __getattr__(self, name):
    def _create_index_rec(self, comp):
    def update_index(self):
    def set_config(self, config):
    def get_config(self, comp_qualified_name, attr_name):
    def get_all_attrs(self, comp_qualified_name):
    def update_params(self, comp_qualified_name, params):
def sydsys(sid = 0):
def restart_sydsys(sid = 0):
def all2kwargs(func, *args, **kwargs):
def compinit(func):
    def wrapper(self, name=None, parent=None, *args, **kwargs): class Unit(Component):
    
    def __init__(self, parent, name, **kwargs):
        Component.__init__(self, name)
        self.__dict__.update(kwargs)
        if parent:
            parent += self
            self._reload_cfg_index()
            self._apply_cfg()
        
        self.build()

    def build(self):
        pass
    
    def _reload_cfg_index(self):
        self.find('/cfg').reload_index()
        
    def _apply_cfg(self):
        self.find('/cfg').apply_cfg_filt(self.qualified_name + '*') class Channel(Component):
    """Instances of this class allow the information they carry to be read
    and written in various interfaces (by various protocols)"""

    @compinit    
    def __init__(self, name, parent, **kwargs):
        self.slaves = []
        self.master = None
        self.master_connected = False
    
    def __irshift__(self, other):
        self.drive(other)
        return self

    def __rshift__(self, other):
        self.drive(other)
        return self
    
    def sink(self, intf):
        if self.master is not None:
            raise Exception("Can only have one master per channel!")
        
        self.master = intf
        self.master_connected = True
        intf._drive(self)
        
        for s in self.slaves:
            s._connect(self.master)
        
#         self._gen_drivers()
#         intf._mch = self
    
    def __ilshift__(self, other):
        self.sink(other)
        return self
    
    def __lshift__(self, other):
        self.sink(other)
        return self
    
    def drive(self, intf):
        self.slaves.append(intf)
        intf._sink(self)

        if self.master_connected:
            intf._connect(self.master) class isig(Intf):
    _intf_type = 'isig'

    @compinit
    def __init__(self, name, parent, dtype, dflt=None):
        super().__init__(name, parent)
        
        self._mch = None
        self._sch = None
        self._sig = None
        self._sourced = False
        self._dtype = dtype
        self._dflt = self._dtype.conv(dflt)
        self._sinks = set()
#         self.inst("e", EventSet, missing_event_handle=self._missing_event)
        self.e = EventSet('e', self, missing_event_handle=self._missing_event)
        
    def con_driver(self, intf):
        pass
    
#     def _connect(self, master):
#         if not self._sourced:
#             self._conn_to_intf(master)
        
    def _from_isig(self, other):
        if self._get_dtype() is other._get_dtype():
            self._sig = other
            for event in self.e.search(of_type=Event):
                getattr(other.e, event).subscribe(event)
            
            self._sourced = True
        else:
            self.inst('_p_dtype_convgen', Process, self._pfunc_dtype_convgen, [], pargs=(other,))
   
    
    def _pfunc_dtype_convgen(self, other):
        while(1):
            data_recv = other.bpop()
            data_conv_gen = convgen(data_recv, self._dtype)
             
            try:
                while True:
                    self.bpush(next(data_conv_gen))
            except StopIteration as e:
                if e.value is not None:
                    self.bpush(e.value)

    def _drive(self, channel):
        self._mch = channel

    def _sink(self, channel):
        self._sch = channel
    
    def _prep_write(self, val):
        try:
            val = val.read()
        except AttributeError:
            pass
        
        val = self._get_dtype().conv(val)
        
        if not self._sourced:
            self._sig = Signal(val=copy.deepcopy(self._dflt), event_set = self.e)
            self._sourced = True
            
        return val
    
    def bpush(self, val):
        val = self._prep_write(val)
        self._sig.bpush(val)
        
    def push(self, val):
        val = self._prep_write(val)
        self._sig.push(val)
    
    def write(self, val):
        val = self._prep_write(val)
        self._sig.write(val)
    
    def read_next(self):
        if not self._sourced:
            return copy.deepcopy(self._dflt)
        else:
            return self._sig._next
    
    def read(self):
        if not self._sourced:
            return copy.deepcopy(self._dflt)
        else:
            return self._sig.read()
        
    def bpop(self):
        if not self._sourced:
            ddic['sim'].wait(self.e['enqueued'])
            
        return self._sig.bpop()
    
    def deref(self, key):
        return SlicedIntf(self, key)
    
    def get_queue(self):
        if not self._sourced:
            return []
        else:
            return self._sig.get_queue()
    
    def _missing_event(self, event_set, name):
        event = Event(name, self.e)
        
        if self._sourced:
            if self._sig.e is not event_set:
                getattr(self._sig.e, name).subscribe(event)

        return event def Bit(w):
def bit_normalization(self, other):
    def __init__(self, val=None, vld=None):
    def _replace(self, key, val):
    def _hdl_gen_ref(self, conv):
    def _hdl_gen_decl(cls):
    def _hdl_gen_call(cls, conv=None, node=None):
    def deref(self, key):
    def __iter__(self):
    def _rnd(cls, rnd_gen):
    def __setitem__(self, key, val):
    def __getitem__(self, key):
    def bitstr(self):
    def __str__(self):
    def __len__(self):
    def _full(self):
    def _icon(self, other):
    def __concat__(self, other):
    def __mod__(self, other):
    def _from_bit(cls, other):
    def _from_int(cls, other):
    def __bool__(self):    
    def __add__(self, other):
    def __radd__(self, other):
    def __sub__(self, other):
    def __mul__(self, other):
    def __truediv__(self, other):
    def __rshift__(self, other):
    def __lshift__(self, other):
    def __xor__(self, other):
    def __rxor__(self, other):
    def __and__(self, other):
    def __rand__(self, other):
    def __or__(self, other):
    def __ror__(self, other):
    def __neg__(self):
    def __pos__(self):
    def __abs__(self):
    def __invert__(self):
    def __int__(self):
    def __float__(self):
    def __oct__(self):
    def __hex__(self):
    def __index__(self):
    def __eq__(self, other):
    def __ne__(self, other):
    def __lt__(self, other):
    def __le__(self, other):
    def __gt__(self, other):
    def __ge__(self, other):
class bit(TypeBase): class Delay(object):
    
    """Class to model delay events."""
    def unsubscribe(self, obj):
        ddic['sim'].delay_pop(obj)
        
    def subscribe(self, obj):
        ddic['sim'].delay_add(obj, self._time)

    def toVerilog(self):
        return "#{0}".format(self._time)

    def __init__(self, val):
        """Return a delay instance.

        Required parameters:
        val -- a natural integer representing the desired delay
        """
        self._time = val def proc(self, name, senslist = None):
    proc = Process(self.name + '.' + name, getattr(self, name), senslist)
    setattr(self, name, proc)
    return proc class Module(Component):
    
#         for attr in dir(self):
#             if not attr.startswith('_'):
#                 func = getattr(self, attr)
#                 if callable(func):
#                     if hasattr(func, "_senslist"):
#                         self.add(Process(self, func, func._senslist))

    def proc(self, name, senslist = None):
        proc = Process(self.name + '.' + name, getattr(self, name), senslist)
        setattr(self, name, proc)
        return proc

    def _get_intfs(self):
        return self.findall(of_type=Intf) class Process(Component, greenlet):
    """Wrapper class for functions that implement processes in user modules.
    
    Class turns function in the greenlet task.""" 

    @compinit
    def __init__(self, name, parent, func, sim : Dependency('sim'), senslist=None, pargs = (), pkwargs = {}, **kwargs):
        self.func = func

        self.senslist = senslist
        self.pargs = pargs
        self.pkwargs = pkwargs
        self.sim = sim

        if self.senslist is None:
            if func.__self__:
#                 parent_name = '.'.join(self.name.split('.')[:-1])
#                 qname_intfs = {c.name: c for c in system.search(parent_name + '.*', of_type=Intf)}
                qname_intfs = {c.name: c for c in func.__self__.search(of_type=Intf)}
                intfs = {}
                for k,v in qname_intfs.items():
                    intfs[k.rsplit('.', 1)[1]] = v
                 
                (inputs, outputs) = getio_vars(func, intfs=intfs)
            
                self.senslist = inputs - outputs

        self._exit_func = None 
        self.sim.proc_reg(self)
        greenlet.__init__(self)
    
    def run(self):
        if self.senslist:
            while(1):
                self.sim.wait(*list(self.senslist))
                self.func(*self.pargs, **self.pkwargs)
        else:
            self.func(*self.pargs, **self.pkwargs)
            self.sim.wait()
            
    def exit_func(self):
        if self._exit_func:
            self._exit_func() class Simulator(Component):
    '''Simulator kernel.'''

    def __init__(self, sched : Dependency('scheduler'), duration = 0, max_delta_count=1000, **kwargs):
        self.delay_pool = {}
        self.trig_pool = set()
        self.update_pool = set()     
        self._ready_pool = set()
        self._proc_pool = []
        self.running = False
        self.duration = duration
        self.sched = sched
        self.max_delta_count = max_delta_count

        # Create events for Simulator extensions to hook to.
        self.events = {
                       'init_start'     : SimEvent(),
                       'run_start'      : SimEvent(),
                       'run_end'        : SimEvent(),
                       'delta_start'    : SimEvent(),
                       'post_evaluate'  : SimEvent(),
                       'delta_end'      : SimEvent(),
                       'delta_settled'  : SimEvent(),
                       'timestep_start' : SimEvent(),
                       'timestep_end'   : SimEvent(),
                       }
        
#         self.inst('top', class_load(top))
#         self.sched = Scheduler(log_task_switching=False)
#         self.inst('sched', Scheduler, log_task_switching=False)

        #         Unit.__init__(self, parent, "sim")
    
    def gen_drivers(self):
        for _, comp in sydsys().findall(self.name + '.top*').items():
            if hasattr(comp, '_gen_drivers'):
                comp._gen_drivers()

#     def find_sources(self):
#         finished_all = True
#         for comp in system.search(self.name + '.top*', of_type=Intf).items():
# #         for _, comp in system.findall(self.name + '.top*').items():
#             if hasattr(comp, '_find_sources'):
#                 finished = comp._find_sources()
#                 if not finished:
#                     finished_all = False
#                     
#         return finished_all
    
    def run(self):
        self.sched.switch(self, self.duration)
   
    def _run(self, duration=0, quiet=0):
        """Start the simulator scheduler loop."""
        
        # Instantiate the user module hierarchy
        self._initialize()
        
        if duration:
            self.duration = duration
        
        self.max_time = self.time + self.duration
 
        self.running = True
        self.events['run_start'](self)
        self._finished = False
        
        while not self._finished:
            self.delta_count = 0
            self.events['timestep_start'](self.time, self)
            # Perform delta cycle loop as long as the events are triggering
            while self._ready_pool or self.trig_pool:
                #Perform one delta cycle
                self.events['delta_start'](self.time, self.delta_count, self)
                 
                self._evaluate()
                 
                self.events['post_evaluate'](self.time, self.delta_count, self)
                 
                self._update()
                 
                self.events['delta_end'](self.time, self.delta_count, self)
                 
                self.delta_count += 1
                 
                if self.delta_count > self.max_delta_count:
                    self._finalize()
                    self.events['run_end'](self)
                    self._finished = True
                    raise greenlet.GreenletExit
                    raise Exception("Maximum number of delta cycles reached: {0}".format(self.max_delta_count))
                
                if not (self._ready_pool or self.trig_pool):
                    self.events['delta_settled'](self)
                
#                 print('-----------------------------------------')
                 
            self.events['timestep_end'](self.time, self)
             
            # All events have settled, let's advance time
            if not self._advance_time():
                self._finalize()
                self.events['run_end'](self)
                self._finished = True
                raise greenlet.GreenletExit 
    
    def _initialize(self):
        
        self.max_time = None
        self.time = 0
        
#         self.gen_drivers()
        
#         while (not self.find_sources()):
#             pass
        
#         self.top_module = self.top_module_cls('top', None)

    def _unsubscribe(self, proc):
        """Unsubscribe the process from all events from  its sensitivity list."""
        
        events = getattr(proc, 'events', None)
        if not isinstance(events, set):
            if hasattr(events, '__iter__'):
                events = set([e for e in events])
            elif events is not None:
                events = set([events])
            
        if events:
            for e in events:
                e.unsubscribe(proc)
#                 try:
#                     e.unsubscribe(proc)
#                 except:
#                     raise
#                     e.event_def.unsubscribe(proc)
    
    def _subscribe(self, proc, events):
        """Subscribe the process to all events from  its sensitivity list."""
        
        proc.events = events
        
#         for e in unif_enum(events):
        for e in events:
            e.subscribe(proc)
#             try:
#                 e.subscribe(proc)
#             except AttributeError:
#                 e.subscribe(proc)
#                 e.event_def.subscribe(proc)
   
    def _evaluate(self):
        """Run all processes scheduled for execution. Resolve all triggered events afterwards."""
         
        # Run the ready processes
        while self._ready_pool:
            proc = self._ready_pool.pop()
            self._unsubscribe(proc)
            events = proc.switch()
            if events is not None:
                self._subscribe(proc, events)
            else:
                # If process supplied no waiting events, it is to be terminated
                self._proc_pool.remove(proc)
                proc.exit_func()
        
        # Resolve all triggered events          
        while self.trig_pool:
            trig = self.trig_pool.pop()
            trig.resolve(self._ready_pool)

    def _update(self):
        """Ask all signals to _update their values, and trigger new events. """
        
        for s in self.update_pool:
            s._update()
            
        self.update_pool.clear()
        
    def _advance_time(self):
        """Advanced time to the earliest scheduled process in delay pool and 
        return True, or return False if there are no more scheduled processes"""
        
        if self.delay_pool:
            t_new = None
            
            while self.delay_pool:
                w = min(self.delay_pool, key=self.delay_pool.get)
                if not t_new:
                    t_new = self.delay_pool[w]
                elif self.delay_pool[w] > t_new:
                    break
                    
                self.delay_pool.pop(w)
                self._ready_pool.add(w)

            self.time = t_new
            self.delta_count = 0
            
            if self.time > self.max_time:
                return False
            else:
                return True
        else:
            return False
        
    def _finalize(self):
        """Call the exit functions of all processes."""
        
        for p in self._proc_pool:
            p.exit_func()
            
        self._finished = True

    def proc_reg(self, proc):
        """Register a process with simulator kernel."""
        self._ready_pool.add(proc)
        self._proc_pool.append(proc)
    
    def update (self, sig):
        self.update_pool.add(sig)
    
    def wait(self, *events):
        """Delay process execution by waiting for events."""
        self.sched.switch(events)
    
    def delay_add(self, proc, time):
        """Register process to be scheduled for execution after given time."""
        self.delay_pool[proc] = time + self.time
    
    def delay_pop(self,proc):
        """Remove process from the delay schedule."""
        self.delay_pool.pop(proc, None)

    def trigger(self, event):
        """Register event to trigger pool."""
        self.trig_pool.add(event)
from sydpy.configurator import Configurator
from sydpy.component import Component, system, compinit
from sydpy.unit import Unit
from sydpy.channel import Channel
from sydpy.intfs.isig import isig
from sydpy.types import bit8, bit
from sydpy._delay import Delay
from sydpy.module import proc, Module
from sydpy.process import Process
from sydpy.simulator import Simulator

class Generator(Component):
    
    @compinit 
    def __init__(self, chout, dtype=bit, **kwargs):
        chout <<= self.inst('sout', isig, dtype=dtype, dflt=0)
        self.inst('p_gen', Process, self.gen, [Delay(20)])

    def gen(self):
        self.sout <<= self.sout + 1
     
class Sink(Component):
    @compinit
    def __init__(self, chin, **kwargs):

        chin >>= self.inst('sin', isig, dtype=bit8, dflt=0)