# Copyright (c) 2011, Peter A. Bigot, licensed under New BSD (see COPYING)
# This file is part of msp430mcu (http://sourceforge.net/projects/mspgcc/)
#
# Check for consistency between devices.csv and individual headers.
# Generate cpu/mpy/iv data file: chipflags.csv

import re
import csv
import glob
import msp430mcu

import os.path

# List of generic MCU identifiers
generics = [ ]
# Map from a generic MCU identifier to list of devices described by the generic
chip_equiv = { }
# Map from a specific MCU device to the generic device that describes it
chip_generic = { }
for line in file(msp430mcu.analysis_path('chip-equiv.txt')).readlines():
    # Strip off .h extension
    chips = [ _fn[:-2] for _fn in line.split() ]
    generic = chips.pop(0)
    suffix = generic[generic.find('430x')+4:]
    if 0 > suffix.find('x'):
        # Not really a generic; multiple chips with identical headers
        # e.g. msp430f415 msp430f417.  @TODO@ Generalize this, currently
        # handles only the one case.
        chips = [ generic.replace('x', 'f') ]
    generics.append(generic)
    assert generic not in chip_equiv
    chip_equiv[generic] = chips
    [ chip_generic.setdefault(_c, generic) for _c in chips ]

msp430mcu.load_devices()

# Map from MCU identifier to a tuple (cpu, mpy, ivlen, hex_ivaddr)
mcus = { }
for mcu in msp430mcu.KnownDevices:
    mcus[mcu.mcu] = ( mcu.cpu, mcu.mpy, mcu.vectors.segments, hex(mcu.vectors.origin))

# mcu -- chip to which data pertains
# xmcu -- genericized chip name per TI standards
# cpu -- 430, 430x, 430xv2
# mpy -- none, 16, 16se, 32, 32dw
# ivcnt -- 16, 32, 64
# ivaddr -- start address of vectors section, as hex literal
chipflags = csv.writer(file(msp430mcu.analysis_path('chipflags.csv'), 'w'))
chipflags.writerow( ('# mcu', 'xmcu', 'cpu', 'mpy', 'ivcnt', 'ivaddr') )
chipflags.writerows([ (_mcu, chip_generic.get(_mcu))+mcus.get(_mcu) for _mcu in sorted(mcus.keys()) ])


def grepline (pattern, lines):
    """Find first element of lines in which pattern exists and return
    the second token in the line.

    Generally this is finding lines that check for or define
    functional preprocessor symbol definitions."""
    
    for l in lines:
        if 0 <= l.find(pattern):
            return l.split()[1]
    return None

print '''# Sanity-check header contents against MCU characteristics.  Known
# inconsistencies:
#
# - MSP430FE42x devices have a hardware multiplier (noted in header),
#   but it is unavailable when the second watt-hour meter CPU is
#   active (disabled in characteristics)
#
# - MSP430x09x devices have a MSP430X cpu but the extended
#   instructions do not work correctly.  Byte access to word
#   registers is OK.
#
'''
headers = glob.glob(msp430mcu.upstream_path('*430*.h'))
for h in headers:
    mcu = os.path.split(h)[1]
    if 'msp430.h' == mcu:
        continue
    mcu = mcu[:mcu.find('.')]
    if not (mcu in mcus):
        if not (mcu in chip_equiv):
            print 'ERROR: Header %s has no device description' % (mcu,)
        continue
    (cpu, mpy, ivlen, ivaddr) = mcus[mcu]
    header_contents = file(h).readlines()
    tag = grepline('_CPU__', header_contents)
    if tag is None:
        hcpu = msp430mcu.CPU_MSP430
    else:
        hcpu = msp430mcu.CPU.LookupByTag(tag.split('_')[4][3:].lower())
    assert hcpu is not None

    # Re msp430x092 devices: "The Header has the tag so that the byte
    # access to word registers are enabled but the compiler should not
    # use the extended instructions set for this CPU as it has some
    # issues on this devices.  It also only has the limited memory of
    # <64k."
    if hcpu != cpu:
        print 'WARNING: %s inconsistent cpu %s %s' % (mcu, cpu, hcpu)
    tag = grepline('_HAS_MPY', header_contents)
    if tag is None:
        hmpy = msp430mcu.MPY_NONE
    else:
        tag = tag.split('_')[4][3:]
        if '' == tag:
            tag = '16'
        hmpy = msp430mcu.MPY.LookupByTag(tag)
    assert hmpy is not None

    # Re msp430fe43x devices: "This devices have a 2nd CPU in it which
    # is used to do the energy processing for Watt Hour Meter. If this
    # is enabled the MPY is used by the 2nd CPU and therefore not
    # available for the main CPU. To avoid that it is used by the
    # linker for the math functions it is not set for the device
    # features in the devices.csv file."
    if hmpy.enum_value != (mpy.enum_value & hmpy.enum_value):
        print 'WARNING: %s inconsistent mpy %s hdr %s' % (mcu, mpy, hmpy)
    #print '%s: %s %s ; %s %s ; %d' % (mcu, cpu, hcpu, mpy, hmpy, ivlen)
    
    
