#  hammer-generate-mdf
#  Helper script to generate an MDF description of a memory from a
#  ReplSeqMem config file.
#
#  See LICENSE for licence details.

import argparse
from collections import namedtuple
import json
import re
import sys

MEM_REGEX = r"name ([^\s]+) depth (\d+) width (\d+) ports ((?:[^\s]+?\s?(?!mask_gran))+)\s?(?:mask_gran (\d+))?"

Mem = namedtuple('Mem', 'name depth width ports mask_gran')

def parseLine(line):
    """
    Parse a line using the given regex above or return None.
    """
    match = re.match(MEM_REGEX, line)
    if match is None:
        return None

    name = match.groups()[0]
    depth = int(match.groups()[1])
    width = int(match.groups()[2])
    ports = match.groups()[3].split(',')
    mask_gran = match.groups()[4]
    if mask_gran is not None:
        mask_gran = int(mask_gran)
    result = Mem(name=name, depth=depth, width=width, ports=ports, mask_gran=mask_gran)
    return result

def memToJSON(mem):
    result = {}
    result['type'] = "sram"
    result['name'] = mem.name
    result['depth'] = mem.depth
    result['width'] = mem.width
    result['ports'] = []
    num_read = -1
    num_write = -1
    num_rw = -1
    for port_type in mem.ports:
        port = {}
        port_name = "" # e.g. "RW0"
        if port_type == "read":
            num_read += 1
            port_name = "R%d" % num_read
        elif port_type.endswith("write"):
            num_write += 1
            port_name = "W%d" % num_write
        elif port_type.endswith("rw"):
            num_rw += 1
            port_name = "RW%d" % num_rw
        else:
            raise ValueError("Invalid port type " + port_type)

        port['clock port name'] = port_name + "_clk"
        port['address port name'] = port_name + "_addr"
        port['chip enable port name'] = port_name + "_en"

        if port_type == "read":
            port['output port name'] = port_name + "_data"
        elif "RW" in port_name:
            port['output port name'] = port_name + "_rdata"

        if port_type.endswith("write"):
            port['input port name'] = port_name + "_data"
        elif "RW" in port_name:
            port['input port name'] = port_name + "_wdata"
            port['write enable port name'] = port_name + "_wmode"

        if mem.mask_gran is not None:
            port['mask granularity'] = mem.mask_gran
            if "RW" in port_name:
                port['mask port name'] = port_name + "_wmask"
            else:
                port['mask port name'] = port_name + "_mask"

        result['ports'].append(port)
    return result

def run(args):
    try:
        lines = [line.strip() for line in open(args.macros)]
    except IOError:
        print("Could not read macros file: " + args.macros)
        return 1

    json_objs = []

    for line in lines:
        mem = parseLine(line)
        if mem is None:
            print('Failed to parse line "' + line + '" in ' + args.macros)
            return 1
        json_objs.append(memToJSON(mem))

    json_str = json.dumps(json_objs, sort_keys=True, indent=4)
    try:
        f = open(args.output, 'w')
        f.write(json_str)
    except IOError:
        print("Could not write output file: " + args.output)
        return 1

    return 0

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--macros", required=True, help="Config file generated by ReplSeqMem")
    parser.add_argument("--output", required=True, help="MDF output for MacroCompiler")
    args = parser.parse_args()
    sys.exit(run(args))
