#ipyxact example. Parses an IP-XACT XML file called generic_example.xml
#and prints out a C header of the register maps found
import argparse
import sys
import time

from ipyxact.ipyxact import Component

def parse_args():
    parser = argparse.ArgumentParser(
            description='Generate a C header from an IP-XACT file')
    parser.add_argument('ipxact_file', help='IP-XACT file to parse')
    parser.add_argument('-o', dest='output_file', help='Write output to file')
    parser.add_argument('-g', '--header-guard', dest='header_guard',
            help='Header guard. If not specified it will be derived from the '
                 'output file name else there will be no guard.')
    return parser.parse_args();

def open_output(output):
    return open(output, 'w') if output else sys.stdout

def get_header_guard(args):
    if args.header_guard:
        return args.header_guard
    elif args.output_file:
        return args.output_file.upper().replace('.', '_').replace('/', '_');
    else:
        return None

def write_prologue(of, args):
    guard = get_header_guard(args);
    of.write('/* Header generated by ipyxact ({}) */\n'
            .format(time.strftime("%Y-%m-%d %H:%M:%S")));
    if guard:
        of.write('#ifndef {}\n'.format(guard))
        of.write('#define {}\n\n'.format(guard))
    of.write('#include <stdint.h>\n\n')
    for bit in [8, 16, 32, 64]:
        of.write('#define IPYXACT_REG_{}(addr) (*(volatile uint{}_t*)(addr))\n'
                .format(bit, bit));
        of.write('#define IPYXACT_RO_REG_{}(addr) '
                '(*(const volatile uint{}_t*)(addr))\n'
                .format(bit, bit));
    of.write('\n')

def write_epilogue(of, args):
    guard = get_header_guard(args);
    if guard:
        of.write('#endif /* {} */\n'.format(guard))

def gen_mask(offset, width):
    mask = 0
    for i in range(offset, offset+width):
        mask += 1<<i
    return mask

def write_enum(field):
    of.write("typedef enum {\n")
    for es in field.enumeratedValues:
        for e in sorted(es.enumeratedValue, key=lambda x: x.value):
            of.write("    {}_{} = {},\n".format(
            field.name.lower(), e.name, e.value<<field.bitOffset))
    of.write("} {}_t;\n\n".format(field.name.lower()))

def write_reg_fields(reg, reg_name):
    for f in sorted(reg.field, key=lambda x: x.bitOffset):
        of.write("#define {}_{}_MASK 0x{:08X}\n".format(
            reg_name, f.name.upper().replace('-','_'),
            gen_mask(f.bitOffset, f.bitWidth)))
        if f.enumeratedValues:
            of.write('\n')
            write_enum(field)
    of.write('\n')

def write_memory_maps(of, memory_maps, offset=0, name=None):
    for m in memory_maps.memoryMap:
        if name:
            mname=name.upper()
        else:
            mname = m.name.upper()
        multiblock = len(m.addressBlock) > 1

        for block in m.addressBlock:
            if multiblock:
                bname = mname + '_' + block.name.upper()
            else:
                bname = mname
            for reg in sorted(block.register, key=lambda a: a.addressOffset):
                reg_name = '{}_{}'.format(
                        bname, reg.name.upper().replace('-', '_'))
                reg_addr = '0x{:08X}'.format(
                        offset + block.baseAddress + reg.addressOffset)
                if reg.size in [8, 16, 32, 64]:
                    reg_access = 'RO_' if reg.access == 'read-only' else ''
                    of.write('#define {} IPYXACT_{}REG_{}({})\n'.format(
                        reg_name, reg_access, reg.size, reg_addr))
                else:
                    of.write("#define {} {}\n".format(reg_name, reg_addr))

                if reg.field:
                    write_reg_fields(reg, reg_name)

if __name__ == '__main__':
    args = parse_args()
    with open(args.ipxact_file) as f:
        name = None
        offset = 0

        component = Component()
        component.load(f)

        with open_output(args.output_file) as of:
            write_prologue(of, args);
            write_memory_maps(of, component.memoryMaps, offset, name)
            write_epilogue(of, args);
