#!/usr/bin/env python import sys ########################################################################################## # File : arch_classes.py # Date : 2016 # Author : Alain Greiner # Copyright (c) UPMC Sorbonne Universites ######################################################################################### # This file contains the python classes required to define a generic hardware # architecture for the ALMOS-MK operating system. # It handle 4 types of objects: clusters, cores, devices and irqs. # - The number of cluster is variable (can be one). # - The cluster topology can be a 2D mesh or a simpla array. # - The number of cores per cluster is variable (can be zero). # - The number of addressable devices per cluster is variable. # - The size of the physical memory bank per cluster is variable. # An adressable device can be a physical memory bank or a peripheral. # Each cluster cover a fixed size segment in physical address space, # that is defined by (PADDR_WIDTH - X_WIDTH - Y_WIDTH) ######################################################################################### # Implementation Note: # The objects used to describe an architecture are distributed in the python structure: # For example the set of cores and the set of devices are split in several subsets # (one subset of cores and one subset of devices per cluster). # In the generated C binary data structure, all objects of same type # are stored in a linear array (one single array for all cores for example). # For all objects, we compute and store in the python object a "global index" # corresponding to the index in this global array, and this index can be used as # a pseudo-pointer to identify a specific object of a given type. ######################################################################################### ######################################################################################### # Define global parameters ######################################################################################### ARCHINFO_SIGNATURE = 0xBABE2016 # magic number indicating a valid C BLOB PAGE_SIZE = 0x1000 # to check peripherals alignment ######################################################################################### # These arrays define the supported types of peripherals. # They must be kept consistent with values defined in file arch_info.h. ######################################################################################### DEVICE_TYPES_STR = [ 'RAM', # 0 'DMA', # 1 'FBF', # 1 'IOB', # 3 'IOC_BDV', # 4 'IOC_HBA', # 5 'IOC_SDC', # 6 'IOC_SPI', # 7 'IOC_RDK', # 8 'MMC', # 9 'MWR_CPY', # 10 'MWR_GCD', # 11 'MWR_DCT', # 12 'NIC', # 13 'ROM', # 14 'SIM', # 15 'TIM', # 16 'TTY', # 17 'XCU', # 18 'PIC', # 19 'CMA', # 20 ] DEVICE_TYPES_INT = [ 0x00000000, # 0 0x00010000, # 1 0x00020000, # 1 0x00030000, # 3 0x00040000, # 4 0x00040001, # 5 0x00040002, # 6 0x00040003, # 7 0x00040004, # 8 0x00050000, # 9 0x00060000, # 10 0x00060001, # 11 0x00060002, # 12 0x00070000, # 13 0x00080000, # 14 0x00090000, # 15 0x000A0000, # 16 0x000B0000, # 17 0x000C0000, # 18 0x000D0000, # 19 0x000E0000, # 20 ] ######################################################################################### class Archinfo( object ): ######################################################################################### def __init__( self, name, # architecture instance name x_size, # number of clusters in a row y_size, # number of clusters in a column cores_max, # max number of cores per cluster devices_max, # max number of devices per cluster paddr_width, # number of bits in physical address x_width, # number of bits for x coordinate y_width, # number of bits for y coordinate irqs_per_core, # number or IRQs from XCU to one core io_cxy, # IO cluster identifier boot_cxy, # boot cluster identifier cache_line, # number of bytes in cache line reset_address, # Preloader physical base address p_width ): # TSAR specific : number of bits to code core lid assert ( x_size <= (1<> (self.paddr_width - self.x_width - self.y_width) x = cxy >> (self.y_width); y = cxy & ((1 << self.y_width) - 1) cluster_id = (x * self.y_size) + y assert (x < self.x_size) and (y < self.y_size) assert (base & (PAGE_SIZE-1) == 0) assert (ptype in DEVICE_TYPES_STR) # call device constructor device = Device( base, size, ptype, channels, arg0, arg1, arg2, arg3 ) # register device in cluster self.clusters[cluster_id].devices.append( device ) # update device global index device.index = self.total_devices self.total_devices += 1 return device ################################ add an input IRQ in a device def addIrq( self, dstdev, # destination device (PIC or XCU) port, # input IRQ port index srcdev, # source device channel = 0, # source device channel is_rx = False ): # I/O operation direction assert (dstdev.ptype == 'XCU') or (dstdev.ptype == 'PIC') assert (port < dstdev.arg0) # call Irq constructor irq = Irq( port , srcdev, channel , is_rx ) # register IRQ in destination device dstdev.irqs.append( irq ) # update IRQ global index irq.index = self.total_irqs self.total_irqs += 1 # pointer from the source to the interrupt controller device if (srcdev.irq_ctrl == None): srcdev.irq_ctrl = dstdev if (srcdev.irq_ctrl != dstdev): print '[genarch error] in addIrq():' print ' two different interrupt controller for the same device' sys.exit(1) return irq ########################## add a core in a cluster def addCore( self, gid, # global hardware identifier cxy, # cluster identifier lid ): # local index in cluster assert ((cxy >> self.y_width) < self.x_size) assert ((cxy & ((1<>self.y_width y = cxy & ((1< nbytes_packed byte array byte_stream = bytearray() length = len( s ) if length < (nbytes - 1): for b in s: byte_stream.append( b ) for x in xrange(nbytes-length): byte_stream.append( '\0' ) else: print '[genarch error] in str2bytes()' print ' string %s too long' % s sys.exit(1) return byte_stream ################################### def int2bytes( self, nbytes, val ): # integer => nbytes litle endian byte array byte_stream = bytearray() for n in xrange( nbytes ): byte_stream.append( (val >> (n<<3)) & 0xFF ) return byte_stream ################ def xml( self ): # compute string for xml file generation s = '\n\n' s += ' 0 ) : assert ( nb_mwr_types == 1 ) # Compute total number of cores, devices and irqs nb_total_cores = self.total_cores nb_total_devices = self.total_devices nb_total_irqs = self.total_irqs # boot core has (cxy == boot_cxy) and (lid == 0) for cluster in self.clusters: if( cluster.cxy == self.boot_cxy ): boot_core_gid = cluster.cores[0].gid # compute mask to get local physical address (cluster independant) local_paddr_width = self.paddr_width - self.x_width - self.y_width local_physical_mask = (1<> self.y_width) s += '#define Y_IO %d\n' % (self.io_cxy & ((1< 0 ): core_id = self.cores[0].index else: core_id = 0 # compute global index for first device in cluster if ( len(self.devices) > 0 ): device_id = self.devices[0].index else: device_id = 0 byte_stream = bytearray() byte_stream += mapping.int2bytes(4,self.cxy) # cxy byte_stream += mapping.int2bytes(4,len(self.cores)) # cores in cluster byte_stream += mapping.int2bytes(4,core_id ) # first core global index byte_stream += mapping.int2bytes(4,len(self.devices)) # devices in cluster byte_stream += mapping.int2bytes(4, device_id ) # first device global index if ( verbose ): print 'nb_cores = %d' % len( self.cores ) print 'core_id = %d' % core_id print 'nb_devices = %d' % len( self.devices ) print 'device_id = %d' % device_id return byte_stream ################################################################################## class Core ( object ): ################################################################################## def __init__( self, gid, cxy, lid ): self.index = 0 # global index / set by addProc() self.gid = gid # hardware identifier self.cxy = cxy # cluster identifier self.lid = lid # local index in cluster return ################################### def xml( self ): # xml for a core return ' \n' % (self.gid, self.lid) #################################################################### def cbin( self, mapping, verbose, expected ): # C binary for Proc if ( verbose ): print '*** cbin for core [%d] in cluster %x' \ % (self.lid, self.cxy) # check index if (self.index != expected): print '[genarch error] in Core.cbin()' print ' core global index = %d / expected = %d' \ % (self.index,expected) sys.exit(1) byte_stream = bytearray() byte_stream += mapping.int2bytes( 4 , self.gid ) # hardware identifier byte_stream += mapping.int2bytes( 2 , self.cxy ) # cluster identifier byte_stream += mapping.int2bytes( 2 , self.lid ) # local index return byte_stream ################################################################################## class Device ( object ): ################################################################################## def __init__( self, base, size, ptype, channels = 1, arg0 = 0, arg1 = 0, arg2 = 0, arg3 = 0 ): self.index = 0 # global device index ( set by addDevice() ) self.base = base # associated segment base self.size = size # associated segment size (bytes) self.ptype = ptype # device type self.channels = channels # number of channels self.arg0 = arg0 # optional (semantic depends on ptype) self.arg1 = arg1 # optional (semantic depends on ptype) self.arg2 = arg2 # optional (semantic depends on ptype) self.arg3 = arg3 # optional (semantic depends on ptype) self.irqs = [] # set of input IRQs (for PIC and XCU only) self.irq_ctrl = None # interrupt controller for this device return ###################################### def xml( self ): # xml for a device s = ' 0 ): irq_id = self.irqs[0].index else: irq_id = 0 # compute device type numerical value ptype_id = 0xFFFFFFFF for x in xrange( len(DEVICE_TYPES_STR) ): if ( self.ptype == DEVICE_TYPES_STR[x] ): ptype_id = DEVICE_TYPES_INT[x] if ( ptype_id == 0xFFFFFFFF ): print '[genarch error] in Device.cbin()' print ' undefined device type %s' % self.ptype sys.exit(1) byte_stream = bytearray() byte_stream += mapping.int2bytes(4,ptype_id) # device type byte_stream += mapping.int2bytes(8,self.base) # segment base address byte_stream += mapping.int2bytes(8,self.size) # segment size byte_stream += mapping.int2bytes(4,self.channels) # number of channels byte_stream += mapping.int2bytes(4,self.arg0) # optionnal arg0 byte_stream += mapping.int2bytes(4,self.arg1) # optionnal arg1 byte_stream += mapping.int2bytes(4,self.arg2) # optionnal arg2 byte_stream += mapping.int2bytes(4,self.arg3) # optionnal arg3 byte_stream += mapping.int2bytes(4,len(self.irqs)) # number of input irqs byte_stream += mapping.int2bytes(4 ,irq_id) # first irq global index if ( verbose ): print 'ptype = %d' % ptype_id print 'base = %x' % self.base print 'size = %x' % self.size print 'nb_irqs = %d' % len( self.irqs ) print 'irq_id = %d' % irq_id return byte_stream ################################################################################## class Irq ( object ): ################################################################################## def __init__( self, port, dev, channel, is_rx ): assert port < 32 self.index = 0 # global index ( set by addIrq() ) self.port = port # input IRQ port index self.dev = dev # source device self.channel = channel # source device channel self.is_rx = is_rx # source device direction return ################################ def xml( self ): # xml for Irq s = ' \n' \ % ( self.port, self.dev.ptype, self.channel, self.is_rx ) return s #################################################################### def cbin( self, mapping, verbose, expected ): # C binary for Irq if ( verbose ): print '*** cbin for irq[%d] / type = %s' \ % (self.index , self.isrtype) # check index if (self.index != expected): print '[genarch error] in Irq.cbin()' print ' irq global index = %d / expected = %d' \ % (self.index,expected) sys.exit(1) # compute source device type numerical value dev_id = 0xFFFFFFFF for x in xrange( len(DEVICE_TYPES_STR) ): if ( self.dev.ptype == DEVICE_TYPES_STR[x] ): dev_id = DEVICE_TYPES_INT[x] if ( dev_id == 0xFFFFFFFF ): print '[genarch error] in Irq.cbin()' print ' undefined device type %s' % self.dev.ptype sys.exit(1) byte_stream = bytearray() byte_stream += mapping.int2bytes( 4, dev_id ) byte_stream += mapping.int2bytes( 1, self.channel ) byte_stream += mapping.int2bytes( 1, self.is_rx ) byte_stream += mapping.int2bytes( 1, self.port ) byte_stream += mapping.int2bytes( 1, 0 ) if ( verbose ): print 'dev_id = %d' % dev_id print 'channel = %d' % self.channel print 'is_rx = %d' % self.is_rx print 'port = %s' % self.port return byte_stream # Local Variables: # tab-width: 4; # c-basic-offset: 4; # c-file-offsets:((innamespace . 0)(inline-open . 0)); # indent-tabs-mode: nil; # End: # # vim: filetype=python:expandtab:shiftwidth=4:tabstop=4:softtabstop=4