1#-------------------------------------------------------------------------------
2# This script takes an xml file which describes hardware options and produces
3# header files in the target directory which are used by the embedded
4# software.
5#-------------------------------------------------------------------------------
6
7import datetime
8import os
9import os.path
10import xml.etree.ElementTree as ET
11import sys
12from pathlib import Path
13
14
15# --------------------------------------------------------------------------------------------
16# mpfs_configuration_generator.py version
17#
18# 0.6.4 Added the following generated files:
19#            hw_mssio_mux_alternate.h
20#            hw_nvm_map.h
21# 0.6.3 target folder name change from "fpga_config" -> fpga_design config, filename
22#       hw_platform.h changed to fpga_design_config.h ,
23#       bug fix related to multiple xml file selection and added libero design information
24#       constants in fpga_design_config.h/ removed date,version and design information from all the files
25#       except fpga_design_config.h
26#
27# 0.6.2 added support for multiple xml file found in input folder
28#       /empty xml file check/ xml filename arg in current folder/
29#       if multiple files are there then the file with the latest time stamp will
30#       be selected.
31# 0.6.1 changed target folder name from soc_config to fpga_config
32#
33# 0.5.2 Aries Embedded Feedback: remove trailing spaces.
34# 0.5.1 Added check that the source XML document is more recent than content of already existing
35# SoC configuration files.
36#
37# 0.4.2 Allowed to only specify the folder where the input XML
38#       file is located. Use file ending with _mss_cfg.xml if one exists, any other .xml file in
39#        the input folder otherwise.
40#
41# 0.4.1 Modified the arguments to allow specifying
42#       the folder where the soc_config folder should be generated.
43#
44# 0.3.4 fixed comment formatting bug in hw_memory.h generation
45# 0.3.3 updated copyright format
46# 0.3.2 removed leading zeros from decimal values ( clock rates)
47# -------------------------------------------------------------------------------------------------------
48def get_script_ver():
49    '''
50    This changes anytime anytime the mpfs_configuration_generator.py script
51    changes. This does not necessarily mean the xml format has been updated in
52    get_xml_ver()
53    :return: script version
54    '''
55    return "0.6.4"
56
57
58
59
60# -----------------------------------------------------------------------------
61# xml file to parse
62# Also an xml files listing tags used for reference
63# -----------------------------------------------------------------------------
64reference_xml_file = \
65    ('hardware_des_xml,src_example,mpfs_hw_ref_defaults.xml,default',
66     'hardware_des_xml,src_example,mpfs_hw_ref_ddr3_100Mhz_ext_clk.xml,ddr3_100Mhz_ref')
67
68xml_tag_file = 'hardware_des_xml,src_example,mpfs_hw_tag_reference.xml'
69
70
71# -----------------------------------------------------------------------------
72# xml tags, the structure here should follow the readme.md description
73# contained in the root folder for tags
74# Please note: The tag in the first column ( mss_xxx) is the same as the
75# directory name (/fpga_design_config/mss_xxx)
76# the fourth item lets program know how to format info in header file
77#         fm_reg - appears as reg with fields
78#         fm_define - appears as define with value, no fields
79# the six item lets program know how to format value, decimal or hex
80# -----------------------------------------------------------------------------
81xml_tags = ('mss_memory_map,map,mem_elements,fm_define,none,hex',
82            'mss_memory_map,apb_split,registers,fm_struct,none,hex',
83            'mss_memory_map,cache,registers,fm_struct,none,hex',
84            'mss_memory_map,pmp_h0,registers,fm_struct,HART0_,hex64',
85            'mss_memory_map,pmp_h1,registers,fm_struct,HART1_,hex64',
86            'mss_memory_map,pmp_h2,registers,fm_struct,HART2_,hex64',
87            'mss_memory_map,pmp_h3,registers,fm_struct,HART3_,hex64',
88            'mss_memory_map,pmp_h4,registers,fm_struct,HART4_,hex64',
89            'mss_memory_map,mpu_fic0,registers,fm_struct,FIC0_,hex64',
90            'mss_memory_map,mpu_fic1,registers,fm_struct,FIC1_,hex64',
91            'mss_memory_map,mpu_fic2,registers,fm_struct,FIC2_,hex64',
92            'mss_memory_map,mpu_crypto,registers,fm_struct,CRYPTO_,hex64',
93            'mss_memory_map,mpu_gem0,registers,fm_struct,GEM0_,hex64',
94            'mss_memory_map,mpu_gem1,registers,fm_struct,GEM1_,hex64',
95            'mss_memory_map,mpu_usb,registers,fm_struct,USB_,hex64',
96            'mss_memory_map,mpu_mmc,registers,fm_struct,MMC_,hex64',
97            'mss_memory_map,mpu_scb,registers,fm_struct,SCB_,hex64',
98            'mss_memory_map,mpu_trace,registers,fm_struct,TRACE_,hex64',
99            'mss_memory_map,nvm_map,registers,fm_define,none,decimal',
100            'mss_io,io_mux,registers,fm_reg,none,hex',
101            'mss_io,io_mux_alt,registers,fm_reg,none,hex',
102            'mss_io,hsio,registers,fm_reg,none,hex',
103            'mss_sgmii,tip,registers,fm_reg,none,hex',
104            'mss_ddr,options,registers,fm_reg,none,hex',
105            'mss_ddr,io_bank,registers,fm_reg,none,hex',
106            'mss_ddr,mode,registers,fm_reg,none,hex',
107            'mss_ddr,off_mode,registers,fm_reg,none,hex',
108            'mss_ddr,segs,registers,fm_reg,none,hex',
109            'mss_ddr,ddrc,registers,fm_reg,none,hex',
110            'mss_clocks,clocks,registers,fm_define,none,decimal',
111            'mss_clocks,mss_sys,registers,fm_define,MSS_,hex',
112            'mss_clocks,mss_pll,registers,fm_define,MSS_,hex',
113            'mss_clocks,sgmii_pll,registers,fm_reg,SGMII_,hex',
114            'mss_clocks,ddr_pll,registers,fm_reg,DDR_,hex',
115            'mss_clocks,mss_cfm,registers,fm_reg,MSS_,hex',
116            'mss_clocks,sgmii_cfm,registers,fm_reg,SGMII_,hex',
117            'mss_general,mss_peripherals,registers,fm_reg,none,hex',)
118
119
120# -----------------------------------------------------------------------------
121#  Header files to generate
122#------------------------------------------------------------------------------
123header_files = ('fpga_design_config,memory_map,hw_memory.h',
124                'fpga_design_config,memory_map,hw_apb_split.h',
125                'fpga_design_config,memory_map,hw_cache.h',
126                'fpga_design_config,memory_map,hw_pmp_hart0.h',
127                'fpga_design_config,memory_map,hw_pmp_hart1.h',
128                'fpga_design_config,memory_map,hw_pmp_hart2.h',
129                'fpga_design_config,memory_map,hw_pmp_hart3.h',
130                'fpga_design_config,memory_map,hw_pmp_hart4.h',
131                'fpga_design_config,memory_map,hw_mpu_fic0.h',
132                'fpga_design_config,memory_map,hw_mpu_fic1.h',
133                'fpga_design_config,memory_map,hw_mpu_fic2.h',
134                'fpga_design_config,memory_map,hw_mpu_crypto.h',
135                'fpga_design_config,memory_map,hw_mpu_gem0.h',
136                'fpga_design_config,memory_map,hw_mpu_gem1.h',
137                'fpga_design_config,memory_map,hw_mpu_usb.h',
138                'fpga_design_config,memory_map,hw_mpu_mmc.h',
139                'fpga_design_config,memory_map,hw_mpu_scb.h',
140                'fpga_design_config,memory_map,hw_mpu_trace.h',
141                'fpga_design_config,memory_map,hw_nvm_map.h',
142                'fpga_design_config,io,hw_mssio_mux.h',
143                'fpga_design_config,io,hw_mssio_mux_alternate.h',
144                'fpga_design_config,io,hw_hsio_mux.h',
145                'fpga_design_config,sgmii,hw_sgmii_tip.h',
146                'fpga_design_config,ddr,hw_ddr_options.h',
147                'fpga_design_config,ddr,hw_ddr_io_bank.h',
148                'fpga_design_config,ddr,hw_ddr_mode.h',
149                'fpga_design_config,ddr,hw_ddr_off_mode.h',
150                'fpga_design_config,ddr,hw_ddr_segs.h',
151                'fpga_design_config,ddr,hw_ddrc.h',
152                'fpga_design_config,clocks,hw_mss_clks.h',
153                'fpga_design_config,clocks,hw_clk_sysreg.h',
154                'fpga_design_config,clocks,hw_clk_mss_pll.h',
155                'fpga_design_config,clocks,hw_clk_sgmii_pll.h',
156                'fpga_design_config,clocks,hw_clk_ddr_pll.h',
157                'fpga_design_config,clocks,hw_clk_mss_cfm.h',
158                'fpga_design_config,clocks,hw_clk_sgmii_cfm.h',
159                'fpga_design_config,general,hw_gen_peripherals.h')
160
161MAX_LINE_WIDTH = 80
162
163
164# -----------------------------------------------------------------------------
165# Read the xml file into ET
166# -----------------------------------------------------------------------------
167def read_xml_file(s):
168    file_dir = os.path.join(*s)
169    tree = ET.parse(file_dir.strip())
170    root = tree.getroot()  # type: object
171    return root
172
173
174# -----------------------------------------------------------------------------
175#  Routine to make a folder
176# -----------------------------------------------------------------------------
177def safe_make_folder(i):
178    '''Makes a folder (and its parents) if not present'''
179    try:
180        os.makedirs(i)
181    except:
182        pass
183
184
185# -----------------------------------------------------------------------------
186# Create the directory structure
187# -----------------------------------------------------------------------------
188def create_hw_dir_struct(root_folder, TOP):
189    '''Creates directory structure off root, subdirectories passed in a tupple'''
190    for folder in TOP:
191        safe_make_folder(root_folder + '/' + folder)
192
193
194# -----------------------------------------------------------------------------
195#  Generate the copyright notice at the top of the header file
196# -----------------------------------------------------------------------------
197def WriteCopyright(root, theFile, filename, creator):
198    '''
199    generate copyright notice based on the following:
200    #/*******************************************************************************
201    # * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions.
202    # *
203    # * SPDX-License-Identifier: MIT
204    # *
205    # * MPFS HAL Embedded Software
206    # *
207    # */
208    :param root:
209    :param theFile:
210    :param filename:
211    :param creator:
212    :return:
213    '''
214    theFile.write('/**********************************************************'
215                  '*********************\n')
216    theFile.write(" * Copyright 2019-" + str(datetime.datetime.now().year) + " Microchip FPGA Embedded Systems Solutions.\n")
217    theFile.write(' *\n')
218    theFile.write(' * SPDX-License-Identifier: MIT\n')
219    theFile.write(' *\n')
220    theFile.write(" * @file " + filename + "\n")
221    theFile.write(" * @author " + creator + "\n")
222    theFile.write(' *\n')
223    if theFile.name == "fpga_design_config\fpga_design_config.h":
224        for child in root:
225            if child.tag == "design_information":
226                for child1 in child:
227                    if child1.tag == "design_name":
228                        theFile.write(' * Libero design name: ' + child1.text.strip() + "\n")
229                    if child1.tag == "libero_version":
230                        theFile.write(' * Generated using Libero version: ' + child1.text.strip() + "\n")
231                    if child1.tag == "mpfs_part_no":
232                        theFile.write(' * MPFS part number used in design: ' + child1.text.strip() + "\n")
233                    if child1.tag == "creation_date_time":
234                        theFile.write(' * Date generated by Libero: ' + child1.text.strip() + "\n")
235                    if child1.tag == "xml_format_version":
236                        theFile.write(' * Format version of XML description: ' + child1.text.strip() + "\n")
237        theFile.write(' * PolarFire SoC Configuration Generator version: ' + get_script_ver() + "\n")
238
239    strings = ('', ' Note 1: This file should not be edited. If you need to modify a parameter',\
240  ' without going through regenerating using the MSS Configurator Libero flow ' ,' or editing the associated xml file',\
241  ' the following method is recommended: \n',\
242  ' 1. edit the following file ',' boards/your_board/platform_config/mpfs_hal_config/mss_sw_config.h\n',\
243  ' 2. define the value you want to override there.',' (Note: There is a commented example in the platform directory)\n',\
244  ' Note 2: The definition in mss_sw_config.h takes precedence, as',\
245  ' mss_sw_config.h is included prior to the generated header files located in', ' boards/your_board/fpga_design_config'\
246           )
247    for string in strings:
248        theFile.write(' *' + string + "\n")
249    theFile.write(' *\n */\n')
250
251
252# -----------------------------------------------------------------------------
253#  the header start define
254# -----------------------------------------------------------------------------
255def start_define(theFile, filename):
256    filename = filename[:-2]  # remove .h from file name
257    theFile.write('\n#ifndef ' + filename.upper() + '_H_')
258    theFile.write('\n#define ' + filename.upper() + '_H_\n\n')
259
260
261# -----------------------------------------------------------------------------
262#  start c plus define
263# -----------------------------------------------------------------------------
264def start_cplus(theFile, filename):
265    theFile.write('\n#ifdef __cplusplus\n')
266    theFile.write('extern ' + ' \"C\"' + ' {\n')
267    theFile.write('#endif\n\n')
268
269
270# -----------------------------------------------------------------------------
271#  end define associated with header start define
272# -----------------------------------------------------------------------------
273def end_define(theFile, filename):
274    filename = filename[:-2]  # remove .h from file name
275    theFile.write('\n#endif /*' + ' #ifdef ' + filename.upper() + '_H_ */\n\n')
276
277
278# -----------------------------------------------------------------------------
279#  end c++ define
280# -----------------------------------------------------------------------------
281def end_cplus(theFile, filename):
282    theFile.write('\n#ifdef __cplusplus\n}\n#endif\n\n')
283
284
285# -----------------------------------------------------------------------------
286#  write line, break into chunks
287# -----------------------------------------------------------------------------
288def write_line(headerFile , reg_description):
289    ''' write line, break into chunks '''
290    word_list = reg_description.split()  # list of words
291    sentence = word_list[0] + ' '
292    word_list.pop(0)
293    for word in word_list:
294        if (len(sentence + word + ' ') > MAX_LINE_WIDTH):
295            headerFile.write(sentence.rstrip() + '\n')
296            sentence = word + ' '
297        else:
298            sentence = sentence + word + ' '
299    if len(sentence) > 0:
300        headerFile.write(sentence.rstrip() + '\n')
301
302
303# -----------------------------------------------------------------------------
304# Iterate through registers and produce header file output
305# -----------------------------------------------------------------------------
306def generate_register(headerFile, registers, tags):
307    '''
308    Parse registers tag for register tags and print to header file
309    :param headerFile: header file to print to
310    :param registers: registers in a tag
311    :param tags: Some tags used to determine print format
312    :return:
313    '''
314    for register in registers:
315        # if tag 4 is set, pre-append register name with tag[4] value
316        if tags[4] != 'none':
317            pre_append = tags[4]
318            name = 'LIBERO_SETTING_' + pre_append + register.get('name')
319        else:
320            name = 'LIBERO_SETTING_' + register.get('name')
321        name_of_reg = name
322        description = register.get('description')
323        name_gap = 15
324        if len(name) > 15:
325            name_gap = len(name)
326        s = '#define' + ' ' + name.ljust(name_gap, ' ')
327        name = register.get('name') + "_OFF_MODE"
328        name_gap = 15
329        if len(name) > 15:
330            name_gap = len(name)
331        stest1 = '#define' + ' ' + name.ljust(name_gap, ' ')
332        field_list = []
333        reg_value = 0
334        reg_value_default = 0
335        for field in register:
336            if field.tag == "field":
337                gap = 30
338                if len(field.get('name')) > gap:
339                    gap = len(field.get('name')) + 4
340                sfield = '    /* ' + field.get('name').ljust(gap, ' ')
341                stemp = '    [' + field.get('offset') + ':' + field.get('width') + ']'
342                stemp = stemp.ljust(12, ' ')
343                sfield += stemp
344                sfield += field.get('Type')
345                if (field.get('Type') == 'RW'):
346                    sfield += ' value= ' + field.text.strip()
347                    temp_val = ((int(field.text.strip(), 16)) << int(field.get('offset')))
348                    reg_value += temp_val
349                sfield += ' */\n'
350                # add the field to list of fields
351                field_list.extend([sfield])
352        if tags[5] == 'decimal':
353            value = format(reg_value, '01X')
354            default_value = format(reg_value_default, '08X')
355        elif tags[5] == 'hex64':
356            value = '0x' + format(reg_value, '016X')  + 'ULL'
357            default_value = '0x' + format(reg_value_default, '08X')
358        else :
359            value = '0x' + format(reg_value, '08X')  + 'UL'
360            default_value = '0x' + format(reg_value_default, '08X')
361        name_gap = 4
362        if len(s) >= name_gap:
363            name_gap = len(s) + 4
364        s = s.ljust(name_gap, ' ') + value + '\n'
365        reg_description = '/*' + description + ' */\n'
366        headerFile.write('#if !defined ' + '(' + name_of_reg + ')\n')
367        # Write out the register description, max chars per line 80
368        write_line(headerFile , reg_description)
369        headerFile.write(s)
370        for x in range(len(field_list)):
371            headerFile.write(field_list[x])
372        headerFile.write('#endif\n')
373
374
375# -----------------------------------------------------------------------------
376# Iterate through tag mem_elements looking for mem elements produce header file
377# output
378# -----------------------------------------------------------------------------
379def generate_mem_elements(headerFile, mem_elements, tags):
380    '''
381    Parse registers tag for mem tags and print to header file
382    :param headerFile:
383    :param registers:
384    :return:
385    '''
386    for mem in mem_elements:
387        name = 'LIBERO_SETTING_' + mem.get('name')
388        name_of_reg = name
389        name_size = name + '_SIZE'
390        description = mem.get('description')
391        name_gap = 15
392        if len(name) > 15:
393            name_gap = len(name)
394        s = '#define' + ' ' + name.ljust(name_gap, ' ')
395        s1 = '#define' + ' ' + name_size.ljust(name_gap, ' ')
396        # get the values
397        mem_value = mem.text.strip()
398        mem_size = mem.get('size')
399        # make sure space between name and value 4 spaces
400        name_gap = 4
401        if len(s) >= name_gap:
402            name_gap = len(s) + 4
403        # make sure space between name and value 4 spaces
404        name_size_gap = 4
405        if len(s1) >= name_size_gap:
406            name_size_gap = len(s1) + 4
407        # create the strings for writing
408        s = s.ljust(name_gap, ' ') + mem_value +  '\n'
409        reg_description = '/*' + description + ' */\n'
410        s1 = s1.ljust(name_size_gap, ' ') + mem_size \
411             + '    /* Length of memory block*/ \n'
412        headerFile.write('#if !defined ' + '(' + name_of_reg + ')\n')
413        headerFile.write(reg_description)
414        headerFile.write(s)
415        headerFile.write(s1)
416        headerFile.write('#endif\n')
417
418
419# -----------------------------------------------------------------------------
420# generate a header file
421# -----------------------------------------------------------------------------
422def generate_header( file, real_root, root, file_name, tags):
423    creator = "Microchip-FPGA Embedded Systems Solutions"
424    with open(file, 'w+') as headerFile:
425        # write the copyright header
426        WriteCopyright(real_root, headerFile, file_name, creator)
427        start_define(headerFile, file_name)
428        start_cplus(headerFile, file_name)
429        if tags != None :
430            for child in root:
431                if child.tag == "registers":
432                    generate_register(headerFile, child, tags)
433                if child.tag == "mem_elements":
434                    generate_mem_elements(headerFile, child, tags)
435                for child2 in child:
436                    if child2.tag == "registers":
437                        generate_register(headerFile, child2, tags)
438        else:
439            headerFile.write('/* No content from MSS Configurator generated for this file. */\n')
440            headerFile.write('/* An older version of MSS Configurator has been used.       */\n')
441        end_cplus(headerFile, file_name)
442        end_define(headerFile, file_name)
443
444
445# -----------------------------------------------------------------------------
446# fpga_design_config.h header file generation.
447# -----------------------------------------------------------------------------
448
449def write_libero_config_info(root,theFile):
450    script_version = get_script_ver().split('.')
451    tags_dic = {"design_name":"LIBERO_SETTING_DESIGN_NAME","libero_version":"LIBERO_SETTING_MSS_CONFIGURATOR_VERSION","mpfs_part_no" :"LIBERO_SETTING_MPFS_PART "\
452        ,"creation_date_time":"LIBERO_SETTING_GENERATION_DATE","xml_format_version":"LIBERO_SETTING_XML_VERSION"}
453
454    #max constant name size + some extra buffer space
455    max_gap = max([len(v) for k,v in tags_dic.items()]) + 8
456
457    fixed_gap = 12
458    xml_version = []
459    for child in root:
460            if child.tag == "design_information":
461                for child1 in child:
462                    if child1.tag in tags_dic:
463                        gap = max_gap - (len(tags_dic[child1.tag]))
464                        theFile.write('#define  '+ tags_dic[child1.tag].ljust(4,' ') + " "*(gap + fixed_gap)  + "\"" + child1.text.strip() + "\"" + "\n")
465                        if child1.tag == "xml_format_version":
466                            xml_version = child1.text.strip().split('.')
467
468    const = {"LIBERO_SETTING_XML_VERSION_MAJOR": xml_version[0],"LIBERO_SETTING_XML_VERSION_MINOR":xml_version[1],"LIBERO_SETTING_XML_VERSION_PATCH":xml_version[2], "LIBERO_SETTING_HEADER_GENERATOR_VERSION":'.'.join(script_version),"LIBERO_SETTING_HEADER_GENERATOR_VERSION_MAJOR":script_version[0],"LIBERO_SETTING_HEADER_GENERATOR_VERSION_MINOR":script_version[1],"LIBERO_SETTING_HEADER_GENERATOR_VERSION_PATCH":script_version[2]}
469
470    # write hard coded constants in the fpga_design_config.h file.
471    for k,v in const.items():
472        gap = max_gap - len(k)
473        if k == "LIBERO_SETTING_HEADER_GENERATOR_VERSION":
474            theFile.write('#define  '+ k.ljust(4,' ') + " "*(gap + fixed_gap)  + "\"" + v + "\"" + "\n")
475        else:
476            theFile.write('#define  '+ k + " "*(gap + fixed_gap)  + v  + "\n")
477    #new line
478    theFile.write("\n")
479
480
481def generate_reference_header_file(ref_header_file, root, header_files):
482    creator = "Embedded Software"
483    # itemName ="io_mux configuration"
484    s = ref_header_file.split(',')
485    file = os.path.join(*s)
486    file_name = s[-1]
487    with open(file, 'w+') as headerFile:
488        # write the copyright header
489
490        WriteCopyright(root, headerFile, file_name, creator)
491        # add exclusive define
492        start_define(headerFile, file_name)
493        # include all the headers
494
495        #define Libero design information constants
496        write_libero_config_info(root,headerFile)
497        index = 0
498        for child in header_files:
499            c = header_files[index].split(',')
500            c.remove('fpga_design_config')
501            # include_file = os.path.join(*c)
502            # as we need formatting correct for linux and windows
503            include_file = c[0] + '/' + c[1]
504            headerFile.write('#include \"' + include_file + '\"\n')
505            index += 1
506        # add the c++ define
507        start_cplus(headerFile, file_name)
508        # no content in this case
509        comment = '/* No content in this file, used for referencing header */\n'
510        headerFile.write(comment)
511        # end the c++ define
512        end_cplus(headerFile, file_name)
513        end_define(headerFile, file_name)
514
515
516# -----------------------------------------------------------------------------
517#  Generate all the header files, passed in output_header_files
518# -----------------------------------------------------------------------------
519def generate_header_files(output_header_files, input_xml_file, input_xml_tags):
520    # read in an xml file
521    s = input_xml_file.split(',')
522
523    root = read_xml_file(s)
524    index = 0
525    while index < len(input_xml_tags):
526        ref_tags = input_xml_tags[index].split(',')
527        s = output_header_files[index].split(',')
528        file_name = s[-1]
529        dir_name = s[-2]
530        file_dir = os.path.join(*s)
531        found_match = 0
532        for child in root:
533            if child.tag == 'mss_' + dir_name:
534                for child1 in child:
535                    if child1.tag == ref_tags[1]:
536                        found_match = 1
537                        break
538        #
539        # Next, create file based on xml content
540        #
541        if found_match == 1:
542            generate_header(file_dir, root, child1, file_name, ref_tags)
543        else:
544            generate_header(file_dir, root, child1, file_name, None)
545        index += 1
546
547    '''
548    generate a header which references all the generated headers
549    '''
550    file_name = 'fpga_design_config,fpga_design_config.h'
551    generate_reference_header_file(file_name, root, output_header_files)
552
553
554# -----------------------------------------------------------------------------
555# Return absolute path created from working directory location and relative
556# path passed as argument. Handles path to an XML file and path to a folder.
557# -----------------------------------------------------------------------------
558def get_full_path(in_path):
559    print(in_path)
560    cwd = os.getcwd()
561    print(cwd)
562    filename = ''
563    temp = in_path
564    if in_path.endswith('.xml'):
565        path_comp = in_path.split('/')
566        last = len(path_comp) - 1
567        filename = path_comp[last]
568
569        in_path = in_path.replace(filename, '')
570        print(in_path)
571    if in_path == '':
572        filename = temp
573        in_path = os.getcwd()
574        print(in_path)
575    else:
576        xml_list = []
577        dir_entries = os.listdir(in_path)
578        for dir_entry in dir_entries:
579
580            if dir_entry.endswith('.xml'):
581                xml_list.append(dir_entry)
582            else:
583                if dir_entry.endswith('_mss_cfg.xml'):
584                    xml_list.append(dir_entry)
585                    break
586       #This section  will sort the xml file by the latest timestamp
587        if len(xml_list) > 1:
588
589            xml_list = sort_by_timestamp(xml_list,in_path)
590            filename = xml_list[-1]
591            #prompt the selected filename
592            print("selected xml file is : {}".format(filename))
593        else:
594            if len(xml_list) != 0:
595                filename = xml_list[0]
596
597        print(in_path)
598
599    try:
600        print("trying to change directory")
601        os.chdir(in_path)
602        full_path = os.getcwd()
603    except IOError:
604        print("caught IO error ")
605        sys.exit()
606
607    os.chdir(cwd)
608    full_path = full_path + '/' + filename
609    if is_empty_file(full_path):
610        print("\nxml File is empty")
611        sys.exit()
612    else:
613        return full_path
614
615
616# -------------------------------------------------------
617# check is fpath is a file and empty
618# -------------------------------------------------------
619def is_empty_file(fpath):
620    return os.path.isfile(fpath) and os.path.getsize(fpath) == 0
621
622# -------------------------------------------------------
623# sort file names on the basis of time stamp
624# -------------------------------------------------------
625def sort_by_timestamp(file_name,file_path):
626    cwd = os.getcwd()
627    try :
628        os.chdir(file_path)
629        path = os.getcwd()
630    except IOError :
631        print("not a valid folder name--------------")
632        sys.exit()
633
634
635    Files = [path + '/' + file_name[i] for i in range(len(file_name))]
636    Files.sort(key=os.path.getmtime)
637    s_file_name = []
638    for i in range(len(Files)):
639        s_file_name.append(Files[i].split('/')[-1])
640
641    print("sorted list of files\n",s_file_name)
642    os.chdir(cwd)
643    return s_file_name
644
645# -----------------------------------------------------------------------------
646# helper for showing help information
647# -----------------------------------------------------------------------------
648def show_help():
649    print ('no of args you entered = ' + str(len(sys.argv) - 1))
650    print ('mpfs_configuration_generator.py :')
651    print (' This program reads xml hardware definition, outputs: header files')
652    print \
653        (' Usage: python3 mpfs_configuration_generator.py [xml file path] [output folder path] ')
654    print('path can be absolute as well as relative \n')
655    input(' Please run again with correct arguments')
656
657
658# -----------------------------------------------------------------------------
659#    main function
660#    todo: add options from the command line
661# -----------------------------------------------------------------------------
662def main_config_generator():
663    '''
664    This script takes an xml file which describes hardware options and produces
665    header files in the target directory which are used by the embedded
666    software.
667    Currently there are Two command line arguments
668    arg0: path to the folder containing xml file.
669    arg1: path of the folder where the fpga_design_config will be generated.
670    Note - If multiple xml files are present then the one with the latest time stamp
671    will be selected.
672
673    '''
674
675    #
676    #  check/parse  arguments
677    #
678    nb_arguments = len(sys.argv) - 1
679    if nb_arguments < 2:
680        show_help()
681        sys.exit()
682    fullCmdArguments = sys.argv
683    # - further arguments
684    argumentList = fullCmdArguments[1:]
685    input_xml_file = argumentList[0]
686    input_xml_file = get_full_path(input_xml_file)
687
688    if nb_arguments >= 2:
689        output_folder_name = argumentList[1]
690        output_folder_name = get_full_path(output_folder_name)
691        os.chdir(output_folder_name)
692
693    debug_reg_csv = False
694    if nb_arguments >= 4:
695        if argumentList[3] == 'debug_regs':
696            debug_reg_csv = True
697    if nb_arguments >= 3:
698        if argumentList[2] == 'generate_refernce_xml':
699            gen_xml = True
700        else:
701            gen_xml = False
702    #
703    # Check version of python interpreter, helps debugging
704    # Currently runs on python version 2 and 3
705    #
706    print ('python interpreter details:',sys.version_info)
707    if sys.version_info > (3, 0):
708    #    # Python 3 code in this block
709        print ('python interpreter running is version 3')
710    else:
711    #    # Python 2 code in this block
712        print ('python interpreter running is version 2')
713
714    # Create directory structure for the header files
715    #
716    root_folder = 'fpga_design_config'
717    TOP = ['clocks', 'ddr', 'io', 'memory_map', 'sgmii', 'general']
718    create_hw_dir_struct(root_folder, TOP)
719    #
720    # Next, read in XML content and create header files
721    #
722    generate_header_files(header_files, input_xml_file, xml_tags)
723    print('Hardware configuration header files created in directory:', os.path.join(output_folder_name, 'fpga_design_config'))
724
725if __name__ == "__main__":
726    main_config_generator()
727
728
729