# Copyright (c) 2011, Peter A. Bigot, licensed under New BSD (see COPYING)
# This file is part of msp430mcu (http://sourceforge.net/projects/mspgcc/)
#
# Read through all the devices, and identify those where eliding the
# memoryType field would result in inconsistencies in chip
# characteristics.  This mostly applies to ValueLine devices; for
# example the msp430g2131 and msp430f2131 differ in RAM size and FLASH
# start and size.

import os
import msp430mcu

include_data = { }

# Set of genericized mcu names for which the devices.csv table
# indicates a difference in peripherals or address map among the
# devices that have the same genericized mcu name.
device_mismatch_xmcus = set()

# Set of genericized mcu names for which there are multiple devices
# with the same generic name, and they do not share the same legacy
# header file (hence, have a difference in peripherals or other
# characteristics that was not indicated in the devices table).
header_mismatch_xmcus = set()

# Set of genericized mcu names for which multiple devices have the
# same generic name, even if they appear otherwise identical.
multi_mismatch_xmcus = set()

msp430mcu.load_devices()

xmap = { }
mcus = msp430mcu.KnownDevices
for mcu in mcus:
    xmcu = mcu.generic_mcu
    xmap.setdefault(xmcu, set()).add(mcu.mcu)
    
    # See if we already have data on an MCU that uses this genericized
    # part number.  If so, see whether the characteristics of the new
    # chip are consistent with the old one.
    old_mcu = include_data.setdefault(xmcu, mcu)
    if mcu != old_mcu:
        mismatches = mcu.checkCompatible(old_mcu)
        if mismatches:
            print 'MISMATCH: %s differ from %s:' % (mcu.mcu, old_mcu.mcu)
            print '\t%s' % ('\n\t'.join([ '%s != %s' % _m for _m in mismatches ]))
            device_mismatch_xmcus.add(xmcu)
        
nof_variants = 0
for xmcu in sorted(xmap.keys()):
    fmcu = msp430mcu.MCU.Lookup(xmcu.replace('430_', '430f'))
    if fmcu is None:
        print '%s does not have F variant' % (xmcu,)
        nof_variants += 1
print '%d generics have an F variant' % (len(xmap) - nof_variants,)

match_xmcus = set(include_data.keys())
match_xmcus.difference_update(device_mismatch_xmcus)

# Create a map identifying the generic header for each chip which has
# one.
legacy_map = { }
for line in file(msp430mcu.analysis_path('chip-equiv.txt')).readlines():
    devices = [ _s.replace('.h', '') for _s in line.split() ]
    legacy = devices.pop(0)
    for d in devices:
        legacy_map[d] = legacy

# Use the legacy map to identify genericized devices for which
# specific devices have different headers.
for xmcu in sorted(match_xmcus):
    if 1 < len(xmap[xmcu]):
        multi_mismatch_xmcus.add(xmcu)
    legacy_headers = set([ legacy_map.get(_m, _m) for _m in xmap[xmcu] ])
    if 1 < len(legacy_headers):
        header_mismatch_xmcus.add(xmcu)

match_xmcus.difference_update(header_mismatch_xmcus)
match_xmcus.difference_update(multi_mismatch_xmcus)

nomatch_mcus = set()

emit_if = '#if'
def emitMcuInclude (mcu, tag, xmcu=None):
    global iof
    global emit_if
    
    tmcu = []
    if xmcu is not None:
        tmcu.append(xmcu)
        tmcu.append(xmcu.replace('_', 'X'))
    tmcu.append(mcu)
    iof.write("""%s %s
#include <%s.h>   /* %s */
""" % (emit_if, ' || '.join([ 'defined(__%s__)' % (_t.upper(),) for _t in tmcu ]), mcu, tag))
    emit_if = '#elif'
    
for xmcu in sorted(device_mismatch_xmcus):
    print 'Device characteristic mismatch: %s' % (' '.join(xmap[xmcu]),)
    nomatch_mcus.update(xmap[xmcu])

for xmcu in sorted(header_mismatch_xmcus):
    print "Header mismatch: %s: %s" % (xmcu, ' '.join([ '%s:%s' % (_m, legacy_map.get(_m, '-')) for _m in xmap[xmcu] ]))
    nomatch_mcus.update(xmap[xmcu])

for xmcu in sorted(multi_mismatch_xmcus):
    print "Multiple instances: %s = %s" % (xmcu, ' '.join(xmap[xmcu]))
    nomatch_mcus.update(xmap[xmcu])

for xmcu in sorted(match_xmcus):
    print 'Genericized: %s = %s' % (xmcu, ' '.join(xmap[xmcu]))

print '%d mcus, %d conflict' % (len(mcus), len(nomatch_mcus))

iof = file(msp430mcu.analysis_path('msp430.h'), 'w')

value_map = { 'upstream_version': file(os.path.join(msp430mcu.upstream_dir, '.version')).readline().strip(),
              'release_version': file(os.path.join(msp430mcu.msp430mcu_root, '.version')).readline().strip() }

value_map['cpu_430'] = msp430mcu.CPU_MSP430.enum_value
value_map['cpu_430x'] = msp430mcu.CPU_MSP430X.enum_value
value_map['cpu_430xv2'] = msp430mcu.CPU_MSP430XV2.enum_value
value_map['mpy_none'] = msp430mcu.MPY_NONE.enum_value
value_map['mpy_type_16'] = msp430mcu.MPY.TYPE_16
value_map['mpy_type_32'] = msp430mcu.MPY.TYPE_32
value_map['mpy_type_any'] = msp430mcu.MPY.TYPE_16 | msp430mcu.MPY.TYPE_32
value_map['mpy_has_se'] = msp430mcu.MPY.HAS_SE
value_map['mpy_has_dw'] = msp430mcu.MPY.HAS_DW

iof.write("""/* Map MCU preprocessor definitions to chip-specific include files.
 *
 * This file is automatically generated from TI-provided data.  Each device
 * is mapped to a genericized name by ignoring differences in memory type,
 * end-equipment optimization, and some other variances.  Preprocessor
 * directives are generated to include the appropriate header for each
 * device.  Generic names, such as msp430x1611, are recognized only if
 * they are sufficient to uniquely identify a device.  When this is not
 * the case, a comment indicates why a generic is excluded: normally
 * because devices have different peripherals or memory maps.
 */
#ifndef __msp430_h_
#define __msp430_h_

/** Date upstream material received from TI */
#define __MSP430MCU_UPSTREAM__ %(upstream_version)s
/** Date of msp430mcu package release */
#define __MSP430MCU__ %(release_version)s

/** Bit-markers for type of CPU present.
 * Check against __MSP430_CPU__ preprocessor symbol. */
#define MSP430_CPU_MSP430 0x%(cpu_430)04x
#define MSP430_CPU_MSP430X 0x%(cpu_430x)04x
#define MSP430_CPU_MSP430XV2 0x%(cpu_430xv2)04x

/** Bit-markers for type of hardware multiplier present.
 * Check against __MSP430_MPY__ (undefined if no hardware multiplier). */
#define MSP430_MPY_NONE 0x%(mpy_none)04x
#define MSP430_MPY_TYPE_16 0x%(mpy_type_16)04x
#define MSP430_MPY_TYPE_32 0x%(mpy_type_32)04x
#define MSP430_MPY_TYPE_ANY 0x%(mpy_type_any)04x
#define MSP430_MPY_HAS_SE 0x%(mpy_has_se)04x
#define MSP430_MPY_HAS_DW 0x%(mpy_has_dw)04x
#define MSP430_MPY_16 MSP430_MPY_TYPE_16
#define MSP430_MPY_16SE (MSP430_MPY_16 | MSP430_MPY_HAS_SE)
#define MSP430_MPY_32 (MSP430_MPY_TYPE_16 | MSP430_MPY_TYPE_32 | MSP430_MPY_HAS_SE)
#define MSP430_MPY_32DW (MSP430_MPY_32 | MSP430_MPY_HAS_DW)

""" % value_map)
for xmcu in sorted(xmap.keys()):
    mcus = xmap[xmcu]
    if xmcu in device_mismatch_xmcus:
        [ emitMcuInclude(_m, 'Device mismatch %s' % (xmcu,)) for _m in mcus]
    elif xmcu in header_mismatch_xmcus:
        [ emitMcuInclude(_m, 'Header mismatch %s' % (xmcu,)) for _m in mcus]
    elif xmcu in multi_mismatch_xmcus:
        [ emitMcuInclude(_m, 'Multi-device %s' % (xmcu,)) for _m in mcus]
    else:
        assert 1 == len(mcus)
        mcu = mcus.copy().pop()
        emitMcuInclude(mcu, 'Genericizable')

iof.write("""
#else
#warning Unable to identify and include MCU header, use -mmcu=MCU
#endif

#endif /* __msp430_h_ */
""")
