
__doc__ = '''
This file is a Cluster library. It contains classes implementing the
netlist of a cluster, for different tsar versions.
'''

class Cluster:
    '''
    A generic netlist of a cluster, which must be subclassed to
    implement caches&dma instanciation
    '''
    
    def __init__(self, pf, ringp, ringc, mtp, mtc, mtx, proc_count, cluster_no, cluster_base):
        self.pf = pf
        self.ringp = ringp
        self.ringc = ringc
        self.mtp = mtp
        self.mtc = mtc
        self.mtx = mtx
        self.cluster_no = cluster_no

        self.generate(proc_count, cluster_base)

    def generate(self, proc_count, cluster_base):
        '''
        The core netlist, where caches and components are created
        '''
        self.cpu = []
        for i in range(proc_count):
            c = self.create_cpu(i,
                                cluster_base
                                + 0x10200000
                                + 0x01000000 * (i+1)
                                )
            self.cpu.append(c)

        memc, xram = self.create_memcache( segments = [(cluster_base, 0x02000000)] )
        rom  = self.create_rom( segments = [(0xbfc00000, 0x00400000)], memc = memc )
        tty  = self.create_tty( addr = 0xd0200000 )
        dma  = self.create_dma( addr = 0xd1200000 )
        xicu = self.create_xicu( addr = 0xd2200000, hwi_count = 3, cpu_count = proc_count )

        xicu.hwi[0] // tty.irq[0]
        xicu.hwi[1] // dma.irq

        for p, cpu in enumerate(self.cpu):
            for i in range(6):
                xicu.irq[i+p*6] // cpu.irq[i]

    def create_rom(self, segments, memc):
        name = 'rom%d'%self.cluster_no
        rom = self.pf.create('caba:vci_simple_ram', name, latency = 1)
        self.ringp.to_target.new() // rom.vci
        for addr, size in segments:
            rom.addSegment(name, address = addr, size = size, cacheable = True)
        return rom

    def create_tty(self, addr):
        name = 'tty%d'%self.cluster_no
        tty = self.pf.create('caba:vci_multi_tty', name, names = ['tty0'])
        self.ringp.to_target.new() // tty.vci
        tty.addSegment(name, address = addr, size = 0x10, cacheable = False)
        return tty

    def create_xicu(self, addr, hwi_count, cpu_count):
        name = 'xicu%d'%self.cluster_no
        xicu = self.pf.create('caba:vci_xicu', name,
                              pti_count = cpu_count,
                              hwi_count = hwi_count,
                              wti_count = cpu_count,
                              irq_count = 6*cpu_count,
                              )
        self.ringp.to_target.new() // xicu.vci
        xicu.addSegment(name, address = addr, size = 0x1000, cacheable = False)
        return xicu
    

class ClusterV3(Cluster):
    '''
    A TsarV3 implementation, using vci_cc_vcache_wrapper2_v1,
    vci_mem_cache_v3 and vci_dma_tsar_v2.
    '''
    
    def create_rom(self, segments, memc):
        # Here is a hack needed for TsarV3:
        #
        # memcache must have segments corresponding to the rom in
        # order to handle cleanup messages emitted by the L1 caches on
        # the coherency network (else, they could be routed to wrong
        # places)
        name = 'rom%d'%self.cluster_no
        for addr, size in segments:
            memc.addSegment(name+'_c', address = addr, size = size, cacheable = True, mt = self.mtc)
        return Cluster.create_rom(self, segments, memc)

    def create_cpu(self, cpuid, addr):
            c = self.pf.create(
                'caba:vci_cc_vcache_wrapper2_v1',
                'proc_%d_%d' % (self.cluster_no, cpuid),
                iss_t = "common:mips32el",
                proc_id = cpuid,
                icache_ways = 4,
                icache_sets = 64,
                icache_words = 16,
                itlb_ways = 4,
                itlb_sets = 16,
                dcache_ways = 4,
                dcache_sets = 64,
                dcache_words = 16,
                dtlb_ways = 4,
                dtlb_sets = 16,
                write_buf_size = 16,
                )
            self.ringc.to_initiator.new() // c.vci_ini_c
            self.ringc.to_target.new() // c.vci_tgt
            self.ringp.to_initiator.new() // c.vci_ini_rw

            c.addSegment('proc_%d_%d' % (self.cluster_no, cpuid),
                         address = addr,
                         size = 0x10,
                         cacheable = False,
                         )

            return c

    def create_memcache(self, segments):
        memc = self.pf.create('caba:vci_mem_cache_v3', 'memc',
                              mtx = self.mtx,
                              vci_ixr_index = (self.cluster_no,),
                              nways = 16,
                              nsets = 256,
                              nwords = 16,
                              heap_size = 1024
                              )
        self.ringc.to_target.new() // memc.vci_tgt_cleanup
        self.ringp.to_target.new() // memc.vci_tgt
        self.ringc.to_initiator.new() // memc.vci_ini

        xram = self.pf.create('caba:vci_simple_ram', 'xram',
                              mt = self.mtx,
                              ident = (self.cluster_no,),
                              latency = 1,
                 )
        memc.vci_ixr // xram.vci

        for addr, size in segments:
            # Here DSX knows the only way to address xram is through its
            # vci port. It also knows the only associated mapping_table.
            xram.addSegment('ram_x', address = addr, size = size, cacheable = True)

            # For these segments, there is ambiguity for the mapping_table
            # we are talking about, so we specify mt = ...
            memc.addSegment('ram_p', address = addr, size = size, cacheable = True, mt = self.mtp)
            memc.addSegment('ram_c', address = addr, size = size, cacheable = True, mt = self.mtc)
        return memc, xram

    def create_dma(self, addr):
        name = 'dma%d'%self.cluster_no
        dma = self.pf.create('caba:vci_dma_tsar_v2', name, burst_size = 64)
        self.ringp.to_target.new() // dma.vci_target
        self.ringp.to_initiator.new() // dma.vci_initiator
        dma.addSegment(name, address = addr, size = 0x14, cacheable = False)
        return dma
