#!/usr/bin/env python
"""This file contains a mapping generator for the tsar_generic_iob platform"""

from math import log
from mapping import Mapping

################################################################################
#   file   : arch.py  (for the tsar_generic_iob architecture)
#   date   : may 2014
#   author : Alain Greiner
#
#   modified by:
#       Cesar Fuguet
#           - Adding distributed ROMs used by the distributed reconfiguration
#             procedure
#
################################################################################
#  This platform includes 6 external peripherals, accessible through two
#  IO_Bridge components located in cluster [0,0] and cluster [x_size-1,
#  y_size-1]. Available peripherals are: TTY, BDV, FBF, ROM, NIC, CMA.
#
#  The "constructor" parameters are:
#  - x_size         : number of clusters in a row
#  - y_size         : number of clusters in a column
#  - nb_procs       : number of processors per cluster
#  - nb_ttys        : number of TTY channels
#  - fbf_width      : frame_buffer width = frame_buffer heigth
#
#  The "hidden" parameters (defined below) are:
#  - NB_NICS        : number of NIC channels
#  - X_IO           : cluster_io x coordinate
#  - Y_IO           : cluster_io y coordinate
#  - X_WIDTH        : number of bits for x coordinate
#  - Y_WIDTH        : number of bits for y coordinate
#  - P_WIDTH        : number of bits for processor local id field
#  - PADDR_WIDTH    : number of bits for physical address
#  - IRQ_PER_PROC   : number of input IRQs per processor
#  - USE_RAMDISK    : use a ramdisk when True
#  - PERI_INCREMENT : virtual address increment for replicated peripherals
#  - PTAB_INCREMENT : virtual address increment for replicated page tables
#  - SCHED_INCREMENT: virtual address increment for replicated schedulers
################################################################################

# define architecture constants
PADDR_WIDTH = 40
NB_NICS = 2
FBF_WIDTH = 128
X_WIDTH = 4
Y_WIDTH = 4
P_WIDTH = 2
X_IO = 0
Y_IO = 0
IRQ_PER_PROC = 4
USE_RAMDISK = False

# virtual address increment for distributed memory segments in the GietVM OS
PERI_INCREMENT = 0x10000
PTAB_INCREMENT = 0x200000
SCHED_INCREMENT = 0x10000

def pmsb(x, y):
    """This function returns the physical most signicant bits for the
    cluster(x,y)"""

    return (x << X_WIDTH) | y

def arch(x_size=2,
         y_size=2,
         nb_procs=4,
         nb_ttys=1,
         fbf_width=FBF_WIDTH):
    """This function describes the tsar_generic_iob platform and defines its
    parameters"""

    # parameters checking
    assert (nb_procs <= (1 << P_WIDTH))

    assert ((x_size >= 1) and (x_size <= (1 << X_WIDTH)))

    assert ((y_size >= 1) and (y_size <= (1 << Y_WIDTH)))

    assert ((nb_ttys >= 1) and (nb_ttys <= 16))

    assert (((X_IO == 0) and (Y_IO == 0)) or
            ((X_IO == (x_size - 1)) and (Y_IO == (y_size - 1))))

    platform_name = 'reconf-tsar_iob_%d_%d_%d' % (x_size, y_size, nb_procs)

    # define physical segments
    ram_base = 0x0000000000
    ram_size = 0x10000000 / (x_size * y_size)

    xcu_base = 0x00B0000000
    xcu_size = 0x1000                     # 4 Kbytes

    dma_base = 0x00B1000000
    dma_size = 0x1000 * nb_procs          # 4 Kbytes * nb_procs

    mmc_base = 0x00B2000000
    mmc_size = 0x1000                     # 4 Kbytes

    drom_base = 0x00BFC00000
    drom_size = 0x8000                    # 32 Kbytes

    offset_io = pmsb(X_IO, Y_IO) << (PADDR_WIDTH - X_WIDTH - Y_WIDTH)

    bdv_base = 0x00B3000000 + offset_io
    bdv_size = 0x1000                     # 4 kbytes

    tty_base = 0x00B4000000 + offset_io
    tty_size = 0x1000                     # 4 Kbytes

    nic_base = 0x00B5000000 + offset_io
    nic_size = 0x80000                    # 512 kbytes

    cma_base = 0x00B6000000 + offset_io
    cma_size = 0x1000 * 2 * NB_NICS       # 4 kbytes * 2 * NB_NICS

    fbf_base = 0x00B7000000 + offset_io
    fbf_size = fbf_width * fbf_width      # fbf_width * fbf_width bytes

    pic_base = 0x00B8000000 + offset_io
    pic_size = 0x1000                     # 4 Kbytes

    sim_base = 0x00B9000000 + offset_io
    sim_size = 0x1000                     # 4 kbytes

    iob_base = 0x00BE000000 + offset_io
    iob_size = 0x1000                     # 4 kbytes

    rom_base = 0x00BA000000
    rom_size = 0x8000                     # 32 Kbytes

    # create mapping
    mapping = Mapping(name=platform_name,
                      x_size=x_size,
                      y_size=y_size,
                      nprocs=nb_procs,
                      x_width=X_WIDTH,
                      y_width=Y_WIDTH,
                      p_width=P_WIDTH,
                      paddr_width=PADDR_WIDTH,
                      coherence=True,
                      irq_per_proc=IRQ_PER_PROC,
                      use_ramdisk=USE_RAMDISK,
                      x_io=X_IO,
                      y_io=Y_IO,
                      peri_increment=PERI_INCREMENT,
                      ram_base=ram_base,
                      ram_size=ram_size)

    # external peripherals (accessible in cluster[0,0] only for this mapping)
    mapping.addPeriph('IOB', base=iob_base, size=iob_size,
                      ptype='IOB')

    mapping.addPeriph('BDV', base=bdv_base, size=bdv_size,
                      ptype='IOC', subtype='BDV')

    mapping.addPeriph('TTY', base=tty_base, size=tty_size,
                      ptype='TTY', channels=nb_ttys)

    mapping.addPeriph('NIC', base=nic_base, size=nic_size,
                      ptype='NIC', channels=NB_NICS)

    mapping.addPeriph('CMA', base=cma_base, size=cma_size,
                      ptype='CMA', channels=2*NB_NICS)

    mapping.addPeriph('FBF', base=fbf_base, size=fbf_size,
                      ptype='FBF', arg=fbf_width)

    mapping.addPeriph('SIM', base=sim_base, size=sim_size,
                      ptype='SIM')

    mapping.addPeriph('ROM', base=rom_base, size=rom_size,
                      ptype='ROM')

    pic = mapping.addPeriph('PIC', base=pic_base, size=pic_size,
                            ptype='PIC', channels=32)

    mapping.addIrq(pic, index=0, isrtype='ISR_NIC_RX', channel=0)
    mapping.addIrq(pic, index=1, isrtype='ISR_NIC_RX', channel=1)
    mapping.addIrq(pic, index=2, isrtype='ISR_NIC_TX', channel=0)
    mapping.addIrq(pic, index=3, isrtype='ISR_NIC_TX', channel=1)
    mapping.addIrq(pic, index=4, isrtype='ISR_CMA', channel=0)
    mapping.addIrq(pic, index=5, isrtype='ISR_CMA', channel=1)
    mapping.addIrq(pic, index=6, isrtype='ISR_CMA', channel=2)
    mapping.addIrq(pic, index=7, isrtype='ISR_CMA', channel=3)
    mapping.addIrq(pic, index=8, isrtype='ISR_BDV', channel=0)

    for i in xrange(nb_ttys):
        mapping.addIrq(pic, index=16+i, isrtype='ISR_TTY_RX', channel=i)

    # hardware components replicated in all clusters
    for x in xrange(x_size):
        for y in xrange(y_size):
            offset = pmsb(x, y) << (PADDR_WIDTH - X_WIDTH - Y_WIDTH)

            mapping.addRam('RAM', base=ram_base + offset, size=ram_size)

            mapping.addPeriph('MMC', base=mmc_base + offset, size=mmc_size,
                              ptype='MMC')

            mapping.addPeriph('DROM', base=drom_base + offset, size=drom_size,
                              ptype='DROM')

            dma = mapping.addPeriph('DMA', base=dma_base + offset,
                                    size=dma_size, ptype='DMA',
                                    channels=nb_procs)

            xcu = mapping.addPeriph('XCU', base=xcu_base + offset,
                                    size=xcu_size, ptype='XCU',
                                    channels=nb_procs * IRQ_PER_PROC,
                                    arg=16)

            # MMC IRQ replicated in all clusters
            mapping.addIrq(xcu, index=0, isrtype='ISR_MMC')

            # DMA IRQ replicated in all clusters
            for i in xrange(dma.channels):
                mapping.addIrq(xcu, index=1+i, isrtype='ISR_DMA',
                               channel=i)

            # processors
            for p in xrange(nb_procs):
                mapping.addProc(x, y, p)

    ############################################################################
    # GIET_VM specifics

    # define bootloader vsegs base addresses
    bmapping_vbase = 0x00001000           # ident
    bmapping_size = 0x0007F000            # 508 Kbytes

    bcode_vbase = 0x00080000              # ident
    bcode_size = 0x00040000               # 256 Kbytes

    bdata_vbase = 0x000C0000              # ident
    bdata_size = 0x00080000               # 512 Kbytes

    bstack_vbase = 0x00140000             # ident
    bstack_size = 0x00050000              # 320 Kbytes

    # define kernel vsegs base addresses and sizes
    kcode_vbase = 0x80000000
    kcode_size = 0x00080000               # 512 Kbytes

    kdata_vbase = 0x80100000
    kdata_size = 0x00080000               # 512 Kbytes

    kinit_vbase = 0x80800000
    kinit_size = 0x00080000               # 512 Kbytes

    kuncdata_vbase = 0x80180000
    kuncdata_size = 0x00001000            # 4 Kbytes

    kptab_vbase = 0xC0000000
    kptab_size = 0x00200000               # 512 Kbytes

    ksched_vbase = 0xF0000000             # distributed in all clusters
    ksched_size = 0x2000 * nb_procs       # 8 kbytes per processor

    # global vsegs for boot_loader / identity mapping
    mapping.addGlobal('seg_boot_mapping', bmapping_vbase, bmapping_size,
                      'CXW_', vtype='BLOB', x=0, y=0, pseg='RAM',
                      identity=True, local=False, big=True)

    mapping.addGlobal('seg_boot_code', bcode_vbase, bcode_size,
                      'CXW_', vtype='BUFFER', x=0, y=0, pseg='RAM',
                      identity=True, local=False, big=True)

    mapping.addGlobal('seg_boot_data', bdata_vbase, bdata_size,
                      'CXW_', vtype='BUFFER', x=0, y=0, pseg='RAM',
                      identity=True, local=False, big=True)

    mapping.addGlobal('seg_boot_stack', bstack_vbase, bstack_size,
                      'CXW_', vtype='BUFFER', x=0, y=0, pseg='RAM',
                      identity=True, local=False, big=True)

    # the code global vsegs for kernel can be replicated in all clusters
    # if the page tables are distributed in all clusters.
    for x in xrange(x_size):
        for y in xrange(y_size):
            mapping.addGlobal('seg_kernel_code', kcode_vbase, kcode_size,
                              'CXW_', vtype='ELF', x=x, y=y, pseg='RAM',
                              binpath='build/kernel/kernel.elf',
                              local=True, big=True)

            mapping.addGlobal('seg_kernel_init', kinit_vbase, kinit_size,
                              'CXW_', vtype='ELF', x=x, y=y, pseg='RAM',
                              binpath='build/kernel/kernel.elf',
                              local=True, big=True)

            offset = pmsb(x, y) * PTAB_INCREMENT
            mapping.addGlobal('seg_kernel_ptab_%d_%d' % (x, y),
                              kptab_vbase + offset, kptab_size, 'C_W_',
                              vtype='PTAB', x=x, y=y, pseg='RAM',
                              local=False, big=True)


            offset = pmsb(x, y) * SCHED_INCREMENT
            mapping.addGlobal('seg_kernel_sched_%d_%d' % (x, y),
                              ksched_vbase + offset, ksched_size, 'C_W_',
                              vtype='SCHED', x=x, y=y, pseg='RAM',
                              local=False, big=False)

    # shared global vsegs for kernel
    mapping.addGlobal('seg_kernel_data', kdata_vbase, kdata_size,
                      'CXW_', vtype='ELF', x=0, y=0, pseg='RAM',
                      binpath='build/kernel/kernel.elf', local=False,
                      big=True)

    mapping.addGlobal('seg_kernel_uncdata', kuncdata_vbase, kuncdata_size,
                      'CXW_', vtype='ELF', x=0, y=0, pseg='RAM',
                      binpath='build/kernel/kernel.elf', local=False,
                      big=True)

    # global vsegs for external peripherals / identity mapping
    mapping.addGlobal('seg_iob', iob_base, iob_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='IOB', local=False, big=False)

    mapping.addGlobal('seg_bdv', bdv_base, bdv_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='BDV', local=False, big=False)

    mapping.addGlobal('seg_tty', tty_base, tty_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='TTY', local=False, big=False)

    mapping.addGlobal('seg_nic', nic_base, nic_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='NIC', local=False, big=False)

    mapping.addGlobal('seg_cma', cma_base, cma_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='CMA', local=False, big=False)

    mapping.addGlobal('seg_fbf', fbf_base, fbf_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='FBF', local=False, big=True)

    mapping.addGlobal('seg_pic', pic_base, pic_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='PIC', local=False, big=False)

    mapping.addGlobal('seg_sim', sim_base, sim_size, '__W_', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='SIM', local=False, big=False)

    # for the Giet-VM is not necessary to map the replicated ROMs
    mapping.addGlobal('seg_rom', drom_base, drom_size, 'CX__', vtype='PERI',
                      x=X_IO, y=Y_IO, pseg='ROM', local=False, big=True)

    # global vsegs for internal peripherals
    for x in xrange(x_size):
        for y in xrange(y_size):
            offset = pmsb(x, y) * PERI_INCREMENT

            mapping.addGlobal('seg_xcu_%d_%d' % (x, y), xcu_base + offset,
                              xcu_size, '__W_', vtype='PERI', x=x, y=y,
                              pseg='XCU', local=False, big=False)

            mapping.addGlobal('seg_dma_%d_%d' % (x, y), dma_base + offset,
                              dma_size, '__W_', vtype='PERI', x=x, y=y,
                              pseg='DMA', local=False, big=False)

            mapping.addGlobal('seg_mmc_%d_%d' % (x, y), mmc_base + offset,
                              mmc_size, '__W_', vtype='PERI', x=x, y=y,
                              pseg='MMC', local=False, big=False)

    return mapping

def main(x, y, p, hard_path, xml_path, dts_path):
    """main function: it generates the map.xml and the hard_config.h file based
    on the Mapping object returned by arch()"""
    mapping = arch(x_size=x,
                   y_size=y,
                   nb_procs=p)

    with open(xml_path, "w") as map_xml:
        map_xml.write(mapping.xml())
    with open(hard_path, "w") as hard_config:
        hard_config.write(mapping.hard_config())
    with open(dts_path, "w") as linux_dts:
        linux_dts.write(mapping.linux_dts())

################################# platform test ################################
import sys
if __name__ == '__main__':
    main(x=int(sys.argv[1]),
         y=int(sys.argv[2]),
         p=int(sys.argv[3]),
         hard_path="hard_config.test.h",
         xml_path="map.test.xml",
         dts_path="linux.dts")

# 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

