#!/usr/bin/env python

"""
Takes in an xml file containing xml name and loc definitions from part0_pins.xml 
as well as an xdc file containing get_ports and PACKAGE_PIN definitions resepectively.
Changes the loc= field of the XML file to reflect the XDC value of PACKAGE_PIN.
Creates a new file called 852_c0 - c3 based on input.

Requirement: the XML and XDC file have to have:
1) Specific format (look at sample files provided)
2) The same number of signals 
3) The same name for the signals

Usage: python xml_xdc_chngloc.py c0.xml c0.xdc
c0.xml = XML file containing xml pin definitions from part0_pins.xml c0 or c1 or c2 or c3
ex: /opt/Xilinx/Vivado/2018.2/data/boards/board_files/vcu1525/1.1/
c0.xdc = XDC file containing get_ports and PACKAGE_PIN definitions

Step 1:
Open part0_pins.xml and create 4 seperate files containing the memory definitions
Step 2:
Run this script for each file and generate 852_c0 - c3 files
Step 3
Join them together and add other definitions other than the memory interfaces
"""

import re
import sys
from operator import itemgetter

import xml.etree.ElementTree as ET

if len (sys.argv) != 3 :
    print "Usage: python xml_xdc_chngloc.py c0.xml c0.xdc\n"
    print "c0.xml = XML file containing xml pin definitions from part0_pins.xml c0 or c1 or c2 or c3"
    print "ex: /opt/Xilinx/Vivado/2018.2/data/boards/board_files/vcu1525/1.1/\n"
    print "c0.xdc = XDC file containing get_ports and PACKAGE_PIN definitions"
    raise SystemExit

tree = ET.parse(sys.argv[1])
#tree = ET.parse('c0.xml')
root = tree.getroot()

def natural_sort(l): 
    convert = lambda text: int(text) if text.isdigit() else text.lower() 
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
    return sorted(l, key = alphanum_key)

def sorted_nicely_2( l ):
    """ Sorts the given iterable in the way that is expected.
 
    Required arguments:
    l -- The iterable to be sorted.
 
    """
    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
    return sorted(l, key = alphanum_key)

def sorted_nicely(l, key):
    """ Sort the given iterable in the way that humans expect."""
    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda item: [ convert(c) for c in re.split('([0-9]+)', key(item)) ]
    return sorted(l, key = alphanum_key)

#DEBUG
#for pin in root.iter('pin'):
#    name = pin.get('name')
#    print name 

name = []
loc = []
merged_list_xml = []
merged_list_xml_sn = []

for pin in root.iter('pin'):
    name.append(pin.get('name'))
    loc.append(pin.get('loc'))
#DEBUG
#print name
#print loc
#print "\n"

merged_list_xml = [(name[i], loc[i]) for i in range(0, len(name))] 
merged_list_xml_sn = sorted_nicely(merged_list_xml, itemgetter(0))
#DEBUG
#print "merged_list_xml"
#print merged_list_xml
#print "\n"

list_of_lists_xml = [list(elem) for elem in merged_list_xml_sn]
#DEBUG
#print "list_of_lists_xml"
#print list_of_lists_xml
#print "\n"

#save_path_file = "part1_pins_test.xml"

search_str0 = "get_ports"
search_str1 = "PACKAGE_PIN"

xdc_name = []
xdc_pinloc = []
xdc_pinloc1 = []
merged_list_xdc = []
merged_list_xdc_sn = []

with open(sys.argv[2], 'r') as search_file:
#with open(sys.argv[1], 'r') as search_file:
    for line in search_file:
        if line.strip().find(search_str1) != -1:
            result2 = line.split(" ")[3] 
	    if result2 == "":
	        print("PACKAGE_PIN not read from XDC file. XDC file needs to be reformated, change tab to space if applicable.")
	        print("PACKAGE_PIN is" , result2)
		print("change to the following format ex: set_property  PACKAGE_PIN AU31 [get_ports {c0_ddr4_act_n}]")
	        raise SystemExit
            #print result2 #DEBUG
            result3 = line.split(" ")[5]
	    if result3 == "":
	        print("get_ports not read from XDC file. XDC file needs to be reformated, change tab to space if applicable.")
	        print("get_ports is" , result3)
		print("change to the following format ex: set_property PACKAGE_PIN AU31 [get_ports {c0_ddr4_act_n}]")
	        raise SystemExit
            #print result3 #DEBUG
            result3 = result3.replace("{", "")
            result3 = result3.replace("}", "")
            result3 = result3.replace("[", "")
            result3 = result3.replace("]", "")
            result3 = result3.replace("\n", "")
            #print result3 #DEBUG
            result3 = result3.replace("c0_ddr4_cs_n0","c0_ddr4_cs0_n")
            result3 = result3.replace("c0_ddr4_cs_n1","c0_ddr4_cs1_n")
            result3 = result3.replace("c0_ddr4_cs_n2","c0_ddr4_cs2_n")
            result3 = result3.replace("c0_ddr4_cs_n3","c0_ddr4_cs3_n")
            result3 = result3.replace("c1_ddr4_cs_n0","c1_ddr4_cs0_n")
            result3 = result3.replace("c1_ddr4_cs_n1","c1_ddr4_cs1_n")
            result3 = result3.replace("c1_ddr4_cs_n2","c1_ddr4_cs2_n")
            result3 = result3.replace("c1_ddr4_cs_n3","c1_ddr4_cs3_n")
            result3 = result3.replace("c2_ddr4_cs_n0","c2_ddr4_cs0_n")
            result3 = result3.replace("c2_ddr4_cs_n1","c2_ddr4_cs1_n")
            result3 = result3.replace("c2_ddr4_cs_n2","c2_ddr4_cs2_n")
            result3 = result3.replace("c2_ddr4_cs_n3","c2_ddr4_cs3_n")
            result3 = result3.replace("c3_ddr4_cs_n0","c3_ddr4_cs0_n")
            result3 = result3.replace("c3_ddr4_cs_n1","c3_ddr4_cs1_n")
            result3 = result3.replace("c3_ddr4_cs_n2","c3_ddr4_cs2_n")
            result3 = result3.replace("c3_ddr4_cs_n3","c3_ddr4_cs3_n")
            xdc_name.append(result3)
            xdc_pinloc.append(result2)

#DEBUG
#print "xdc_name"
#print xdc_name
#print "xdc_pinloc"
#print xdc_pinloc
#print "\n"

merged_list_xdc = [(xdc_name[i], xdc_pinloc[i]) for i in range(0, len(xdc_name))] 
#DEBUG
#print "merged_list_xdc before sort!"
#print merged_list_xdc
#print "\n"

merged_list_xdc_sn = sorted_nicely(merged_list_xdc, itemgetter(0))
#DEBUG
#print "merged_list_xdci SORTED"
#print merged_list_xdc
#print "\n"

list_of_lists_xdc = [list(elem) for elem in merged_list_xdc_sn]
#DEBUG
#print "list_of_lists_xdc"
#print list_of_lists_xdc
#print "\n"

#CHECK
if len(list_of_lists_xdc) != len(list_of_lists_xml):
     print ("Length DO NOT match")
     print 'length of xml is "{}"'.format(len(list_of_lists_xml))
     print 'length of xdc is "{}"'.format(len(list_of_lists_xdc))
     for xml, xdc in zip(list_of_lists_xml, list_of_lists_xdc):
         print xml, xdc

new_list_of_lists = []
new_list_of_lists2 = []
new_list_of_lists3 = []
index = 0
while index < len(merged_list_xml):
    #CHECK
    if list_of_lists_xml[index][0] != list_of_lists_xdc[index][0]:
         print("Out of order! Or you called non matching xml & xdc file combo!")
         print (list_of_lists_xml[index-1][0], list_of_lists_xdc[index-1][0])
    #WORK
    if list_of_lists_xml[index][0] == list_of_lists_xdc[index][0]:
         #print index #DEBUG
         new_list_of_lists.append(list_of_lists_xml[index][0])
         new_list_of_lists.append(list_of_lists_xdc[index][1])
         new_list_of_lists2.append(list_of_lists_xdc[index][1])
         new_list_of_lists3.append(list_of_lists_xml[index][0])
    index += 1
#DEBUG
#print new_list_of_lists3[102], new_list_of_lists2[102]
#print new_list_of_lists3[137], new_list_of_lists2[137]
#print new_list_of_lists3[140], new_list_of_lists2[140]
#print new_list_of_lists3[141], new_list_of_lists2[141]

#print new_list_of_lists[206], new_list_of_lists[207], new_list_of_lists[240], new_list_of_lists[241]
#print new_list_of_lists[238], new_list_of_lists[239], new_list_of_lists[272], new_list_of_lists[273]
#print "\n"
#print new_list_of_lists[204], new_list_of_lists[205]
#print new_list_of_lists[272], new_list_of_lists[273]
#print "\n"

#The bruteforce/hardcoded swaps below are implemented as a protoype and need to be changed to something more pythonic
#The reason is the xml file has a certain and once the xdc data is ordered the orders dont match.

pindict = dict(zip(new_list_of_lists3,new_list_of_lists2))

#Compare name field of xml file to the get_ports field of new_list_of_lists3 and update the loc field.
for pin in root.iter('pin'):
    if not (pin.get('name') in pindict):
        print ("The pin in the XML file does not exist in the list data structure.")
        print (pin.get('name'))    
    pin.set('loc',pindict[pin.get('name')])
testlist = []

#Print xml file with updated loc fields from XDC file
if sys.argv[1] == "c0.xml":
    tree.write('852_c0.xml_new')
elif sys.argv[1] == "c1.xml":
    tree.write('852_c1.xml_new')
elif sys.argv[1] == "c2.xml":
    tree.write('852_c2.xml_new')
elif sys.argv[1] == "c3.xml":
    tree.write('852_c3.xml_new')
#tree.write('852_c0.xml')
