diff --git a/tools/oneclick/components/component.py b/tools/oneclick/components/component.py new file mode 100644 index 0000000000000000000000000000000000000000..e0522bb6be03a989d42841c7413e6d3835bc2eed --- /dev/null +++ b/tools/oneclick/components/component.py @@ -0,0 +1,460 @@ +import multiprocessing as mp +import time +import common as cm + +############################################################################### +# Base classes +############################################################################### + +class Signal(object): + def __init__(self, name, stype): + self.name = name + self.stype = stype + self.vhdl_signal_declaration = 'SIGNAL %s : %s;\n' %(name, stype) + +class StreamPort(object): + def __init__(self, component_name, name, mode, stream_index, data_width): + self.component_name = component_name + + if stream_index != None: + self.name = name + '[' + str(stream_index) + ']' + else: + self.name = name + + self.mode = mode + self.stream_index = stream_index + self.data_width = data_width + self.pipe_end = None + + self.port_type = 't_dp_sosi' + self.signal = Signal(component_name+'_'+name, self.port_type) + + #'>' operator: Connect + def __gt__(self, other): + return self.__connect__(other) + + def __connect__(self, other): + if not(self.pipe_end == None): + # The pipe end is already assigned, this is the case for composite components that + # have wired their entity port to internal instance ports. + # If self is a source port (mode='out'), this means that self.pipe_end is already occupied + # with a pipe sink end from which this entity port receives from an internal source entity. + # To make a connection between other and self, all we need to do is move the existing pipe's snk end + # from self to other. The source end remains connected to self's internal instance. + other.pipe_end = self.pipe_end + elif not(other.pipe_end == None): + # The pipe end is already assigned, this is the case for composite components that + # have wired their entity port to internal instance ports. + # If other is a sink port (mode='in'), this means that other.pipe_end is already occupied + # with a pipe source end from which this entity port send to an internal sink entity. + # To make a connection between other and self, all we need to do is move the existing pipe's src end + # from other to self. The sink end remains connected to other's internal instance. + self.pipe_end = other.pipe_end + else: + snk_end, src_end = mp.Pipe(duplex=False) + self.pipe_end = src_end + other.pipe_end = snk_end + + return Connection( (self.component_name, self.name), (other.component_name, other.name) ) + + def send(self, item): + self.pipe_end.send(item) + + def recv(self): + while True: + try: + yield self.pipe_end.recv() + except EOFError: + break + +class StreamPortArray(list): + def __init__(self, component_name, name, mode, nof_streams, data_width): + list.__init__(self) + self.component_name = component_name + self.name = name + self.mode = mode + self.nof_streams = nof_streams + self.data_width = data_width + for stream_index in range(nof_streams): + self.append(StreamPort(component_name, name, mode, stream_index, data_width)) + + self.port_type = 't_dp_sosi_arr(%s-1 DOWNTO 0)' %nof_streams + self.signal = Signal(component_name+'_'+name, self.port_type) + + #'>' operator: Connect + def __gt__(self, other): + return self.__connect__(other) + + def __connect__(self, other): + for stream_port_self, stream_port_other in zip(self,other): + stream_port_self.__connect__(stream_port_other) + return Connection( (self.component_name, self.name), (other.component_name, other.name) ) + + def recv(self): + while True: + try: + items = [] + for stream_port in self: + items.append( stream_port.pipe_end.recv() ) + yield items + except EOFError: + break + +class Connection(): + def __init__(self, src, snk): + self.src_component_name = src[0] + self.src_port_name = src[1] + self.snk_component_name = snk[0] + self.snk_port_name = snk[1] + self.vhdl_assignment_left = self.snk_component_name + '_' + self.snk_port_name + self.vhdl_assignment_right = self.src_component_name + '_' + self.src_port_name + self.vhdl_assignment_operator = '<=' + self.vhdl_assignment = self.vhdl_assignment_left + ' ' + self.vhdl_assignment_operator + ' ' + self.vhdl_assignment_right + ';\n' + +class Component(mp.Process): + def __init__(self, name, components=[], connections=[], inst_nr=None): + mp.Process.__init__(self) + if inst_nr != None: + self.name = name+'_'+str(inst_nr) + else: + self.name = name + self.inst_nr = inst_nr + self.file_name = self.name + '.vhd' # Overridden by component subclasses; these assign an existing file name. + self.ports = {} + self.components = components + self.connections = connections + self.temp_connections = [] + + def start_components(self): + if self.components == []: + # This is not a composite component but a base component, start this instance itself + self.start() + else: + # This is a composite component, start the internal instances + for component in self.components: + print 'Starting', component + component.start_components() + + def terminate_components(self): + if self.components == []: + # This is not a composite component but a base component, terminate this instance itself + self.terminate() + else: + # This is a composite component, terminate the internal instances + for component in self.components: + print 'Terminating', component + component.terminate_components() + + def run_time(self, time_sec): + self.start_components() + time.sleep(time_sec) + self.terminate_components() + + def generate(self, target_file=None): + if self.components != []: + target_file = open(self.file_name, "w") + target_file.write('ASTRON HEADER\n') + target_file.write('INCLUDED LIBRARIES\n') + target_file.write('ENTITY DECLARATION\n') + target_file.write('ARCHITECTURE str OF %s IS\n' %self.name) + target_file.write('. CONSTANT DECLARATIONS\n') + + # Get the signal declaration from each internal component's port object + for component in self.components: + for port_name, port in component.ports.iteritems(): + target_file.write(port.signal.vhdl_signal_declaration) + + target_file.write('BEGIN\n') + + # Write this component's connections + for connection in cm.flatten(self.connections): +# print connection + target_file.write(connection.vhdl_assignment) + + # Iterate through internal components + for component in self.components: + component.generate() + target_file.write(component.get_vhdl_instance()) + + target_file.write('END str\n') + target_file.close() + + def get_vhdl_instance(self): + # Only base components have a pre-declared VHDL instance. Composites do not. + if hasattr(self, 'vhdl_instance'): + # I'm a base component...return my pre-declared VHDL instace string + return self.vhdl_instance + else: + # I'm a composite component. Create a VHDL instance here. + return 'A string that contains the %s VHDL instance\n' %self.name + + + def __set_port__(self, name, mode, dimensions): + """ + Mode: + . 'in' + . 'out' + Dimension examples: + . 32 = 1 stream, 32b (sosi) + . (2,32) = 2 streams, 32b each (sosi_arr) + . (2,4,32) = 2 buses, 4 streams/bus, 32b/stream (sosi_2arr) + . (8,2,4,32) = 8 buses, 2 sub-buses/bus, 4 streams/sub-bus, 32b/stream (sosi_3arr) + """ + def __set_port__(self, name, mode, dimensions): + if isinstance(dimensions, int): + # Single stream + self.ports[name] = StreamPort(self.name, name, mode, stream_index=None, data_width=dimensions) + elif len(dimensions)==2: + # Multi stream + self.ports[name] = StreamPortArray(self.name, name, mode, nof_streams=dimensions[0], data_width=dimensions[1]) + elif len(dimensions)==3: + pass + # Multi bus, multi stream +# self.ports[name] = StreamPort2Array(self.name, name, mode, nof_buses=dimensions[0], nof_streams=dimensions[1], data_width=dimensions[2]) + + def set_input(self, name, dimensions): + self.__set_port__(name=name, mode='in', dimensions=dimensions) + + def set_output(self, name, dimensions): + self.__set_port__(name=name, mode='out', dimensions=dimensions) + + #'>' operator: Connect + def __gt__(self, other): + return self.__connect__(other) + + def __connect__(self, other): + for port_name_self, port_self in self.ports.iteritems(): + if port_self.mode == 'out': + for port_name_other,port_other in other.ports.iteritems(): + if port_other.mode == 'in': + if type(port_self)==type(port_other): + # We want to be able to daisy chain components so we need to store intermediate results + # in the component on the right. + connection = port_self.__connect__(port_other) + other.temp_connections.append(connection) + result = self.temp_connections+[connection] + self.temp_connections = [] + return result + + +############################################################################### +# Component subclasses +############################################################################### + +class SingleCounter(Component): + def __init__(self, data_width=32, inst_nr=None): + Component.__init__(self, inst_nr=inst_nr, name='SingleCounter') + self.vhdl_instance = 'A string that contains the SingleCounter VHDL instance\n' + self.set_output('src_out', data_width) + def run(self): + for value in xrange(10): + if self.inst_nr==None: + cnt_offset=0 + else: + cnt_offset = self.inst_nr * 100 + item = value + cnt_offset + print '[Counter] src', self.inst_nr, ': sending', item + # Offset sent value by 100 for each instance number increment + self.ports['src_out'].send(item) + +class SinglePrinter(Component): + def __init__(self, data_width=32, inst_nr=None): + Component.__init__(self, inst_nr=inst_nr, name='SinglePrinter') + self.vhdl_instance = 'A string that contains the SinglePrinter VHDL instance\n' + self.set_input('snk_in', data_width) + def run(self): + for item in self.ports['snk_in'].recv(): + print '[Printer] snk', self.inst_nr, ': receiving', item + + +class Counter(Component): + def __init__(self, nof_streams, data_width=32, inst_nr=None): + Component.__init__(self, inst_nr=inst_nr, name='Counter') + self.vhdl_instance = 'A string that contains the Counter VHDL instance\n' + self.set_output('src_out_arr', (nof_streams, data_width)) + def run(self): + for value in xrange(10): + for src in self.ports['src_out_arr']: + item = value + src.stream_index*100 + print '[Counter] src', src.stream_index, ': sending', item + # Offset sent value by 100 for each incremental stream + src.send(item) + +class Through(Component): + def __init__(self, nof_streams, data_width=32, inst_nr=None): + Component.__init__(self, inst_nr=inst_nr, name='Through') + self.vhdl_instance = 'A string that contains the Through VHDL instance\n' + self.set_input('snk_in_arr', (nof_streams, data_width)) + self.set_output('src_out_arr', (nof_streams, data_width)) + def run(self): + for items in self.ports['snk_in_arr'].recv(): + for item,src in zip(items, self.ports['src_out_arr']): + print '[Through] snk', src.stream_index, ': forwarding', item, 'to src', src.stream_index + src.send(item) + +class Printer(Component): + def __init__(self, nof_streams, data_width=32, inst_nr=None): + Component.__init__(self, inst_nr=inst_nr, name='Printer') + self.vhdl_instance = 'A string that contains the Printer VHDL instance\n' + self.set_input('snk_in_arr', (nof_streams, data_width)) + def run(self): + for items in self.ports['snk_in_arr'].recv(): + for item_index, item in enumerate(items): + print '[Printer] snk', item_index, ': receiving', item + + +############################################################################### +# Help function for keypress +############################################################################### +def press_enter(): + try: + input("Press enter to continue") + except SyntaxError: + pass + + +############################################################################### +# Main +############################################################################### + +print +print '###############################################################################' +print '# Example 1: connect ports component-wise' +print '###############################################################################' +print + +nof_streams = 3 + +a = Counter(nof_streams) +b = Through(nof_streams) +c = Printer(nof_streams) + +connections = [ a>b>c ] + +d = Component('my_component_1', [a,b,c], connections) + +d.run_time(1) +d.generate() + +press_enter() + +print +print '###############################################################################' +print '# Example 2: connect ports array-wise' +print '###############################################################################' +print + +nof_streams = 3 + +a = Counter(nof_streams) +b = Through(nof_streams) +c = Printer(nof_streams) + +connections = [] +connections.append( a.ports['src_out_arr'] > b.ports['snk_in_arr' ] ) +connections.append( b.ports['src_out_arr'] > c.ports['snk_in_arr'] ) + +d = Component('my_component_2', [a,b,c], connections) + +d.run_time(1) +d.generate() + +press_enter() + +print +print '###############################################################################' +print '# Example 3: connect ports stream-wise (with stream 0 / stream 1 swap)' +print '###############################################################################' +print + +nof_streams = 3 + +a = Counter(nof_streams) +b = Through(nof_streams) +c = Printer(nof_streams) + +connections = [] +connections.append( a.ports['src_out_arr'][0] > b.ports['snk_in_arr' ][0] ) +connections.append( a.ports['src_out_arr'][1] > b.ports['snk_in_arr' ][1] ) +connections.append( a.ports['src_out_arr'][2] > b.ports['snk_in_arr' ][2] ) +connections.append( b.ports['src_out_arr'][1] > c.ports['snk_in_arr'][0] ) # We're swapping streams 0 and 1 here +connections.append( b.ports['src_out_arr'][0] > c.ports['snk_in_arr'][1] ) # We're swapping streams 0 and 1 here +connections.append( b.ports['src_out_arr'][2] > c.ports['snk_in_arr'][2] ) + +d = Component('my_component_3', [a,b,c], connections) + +d.run_time(1) +d.generate() + +press_enter() + +print +print '###############################################################################' +print '# Example 4: Mix single and multi stream components' +print '###############################################################################' +print + +nof_streams = 1 + +a = SingleCounter() +b = Through(nof_streams) +c = Printer(nof_streams) + +connections = [] +connections.append( a.ports['src_out'] > b.ports['snk_in_arr' ][0] ) +connections.append( b.ports['src_out_arr'] > c.ports['snk_in_arr'] ) + +d = Component('my_component_4', [a,b,c], connections) + +d.run_time(1) +d.generate() + +press_enter() + +print +print '###############################################################################' +print '# Example 5: Creating multi-instance composites from single stream components' +print '# . We replace the Counter with a new component that instantiates 3 SingeCounters' +print '# . We replace the Printer with a new component that instantiates 3 SingePrinters' +print '###############################################################################' +print + +nof_streams = 3 +data_width = 32 + +# Create [nof_streams] SingleCounter instances +a_list = [] +for i in range(nof_streams): + a_list.append(SingleCounter(inst_nr=i)) +# Create a new components wrapping these SingleCounter instances +a_multi = Component('a_multi', a_list) +# Declare the output of this new component +a_multi.set_output('src_out_arr', (nof_streams, data_width)) +# Forward the SingleCounter outputs to the new component's output +for i in range(nof_streams): + a_multi.components[i].ports['src_out'] > a_multi.ports['src_out_arr'][i] + +# Unchanged: the Through component +b = Through(nof_streams) + +# Create [nof_streams] SinglePrinter instances +c_list = [] +for i in range(nof_streams): + c_list.append(SinglePrinter(inst_nr=i)) +# Create a new components wrapping these SinglePrinter instances +c_multi = Component('c_multi', c_list) +# Declare the input of this new component +c_multi.set_input('snk_in_arr', (nof_streams, data_width)) +# Forward the new component's input to the SinglePrinter inputs +for i in range(nof_streams): + c_multi.ports['snk_in_arr'][i] > c_multi.components[i].ports['snk_in'] + +# Continue as before (in the other examples) +connections = [ a_multi>b>c_multi ] + +d = Component('my_component_5', [a_multi,b,c_multi], connections) + +d.run_time(1) +d.generate() + +