Skip to content
Snippets Groups Projects
Select Git revision
  • a419db90f3ed1e15dfa83ea3162c9c888d6ad951
  • master default protected
2 results

gen_rom_mmap.py

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    gen_rom_mmap.py 8.58 KiB
    #! /usr/bin/env python3
    # ##########################################################################
    # Copyright 2020
    # ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
    # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    # ##########################################################################
    
    # ##########################################################################
    #   Author           Date       Version comments
    #   Pieter Donker    June 2020  first version
    #
    ###############################################################################
    
    """
        Generate fpgamap file to copy in rom_system  [fpga_name].mmap
        the file for the ROM is selected in radiohdl/run_reg script .
    """
    
    import sys
    import os
    import logging
    from argparse import ArgumentParser
    from args_logger import MyLogger
    from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrary, WORD_SIZE
    import pprint
    
    
    def make_mask(width, offset=0):
        _hi_bit = offset + width - 1
        _lo_bit = offset
        if width == 1:
            _mask_str = 'b[{}]'.format(_lo_bit)      # use [i] instead of [i:i]
        else:
            _mask_str = 'b[{}:{}]'.format(_hi_bit, _lo_bit)
        #_mask_str = 'b[{}:{}]'.format(_hi_bit, _lo_bit)
        return _mask_str
    
    
    def gen_fpga_map(fpga, fpga_name):
        _map_str = []
    
        # pprint.pprint(fpga.address_map)
        print("Including slave ports for {}:".format(fpga_name))
        map_format_str = '  {:24s}  {:4s}  {:5s}  {:24s}  0x{:08x}  {:6d}  {:>5s}  {:>10s}'
        for slave_port_name, slave_port_info in fpga.address_map.items():
    
            # All elements in array have same info, so only need info from first element
            if slave_port_info['periph_num'] > 0:
                continue
    
            #print("slave_port_info = {}".format(slave_port_info))
            
            slave            = slave_port_info['slave']
            user_def_name    = slave.user_defined_name().upper()
            number_of_slaves = str(slave.number_of_slaves())
            base             = int(slave_port_info['base'])
            base_word        = int(base / WORD_SIZE)
    
            if isinstance(slave, RAM):
                print(' RAM  {:40s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
                _map_str.append(map_format_str.format(
                                user_def_name,
                                number_of_slaves,
                                'RAM',
                                'data',   # EK: TODO should come from yaml field_name
                                base_word,
                                slave.number_of_fields(),
                                slave.access_mode(),
                                '-'  # EK: TODO should use make_mask() to report field_width of RAM data
                                ))
    
            elif isinstance(slave, FIFO):
                print(' FIFO {:40s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
                _map_str.append(map_format_str.format(
                                user_def_name,
                                number_of_slaves,
                                'FIFO',
                                'data',   # EK: TODO should come from yaml field_name
                                base_word,
                                slave.number_of_fields(),
                                slave.access_mode(),
                                '-'  # EK: TODO should use make_mask() to report field_width of FIFO data, there seems no slave.fields
                                ))
    
            elif isinstance(slave, Register):
                slave_type       = "REG"   # EK: TODO get slave_type from slave ?
                print(' REG  {:40s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
                done = []
                for f in slave.fields:
                    field_name = f.name()
                    
                    #print(f.radix().lower())  # EK: TODO do not use radix in mmap ?
                    
                    # EK: TODO the check on f.number_of_fields() and on done should not be necessary, because the array of fields should only have one element in slave.fields. The slave.number_of_fields() should not have been expanded in slave.
                    if f.number_of_fields() > 1:
                        field_name = f.name().strip('0123456789')  # strip field array index
                    if field_name in done:
                        continue
                    if field_name not in done:
                        done.append(field_name)
                        
                    field_group = f.group_name()
                    if field_group == None:
                        field_group = '-'
                    
                    f_base = base_word + int(f.address_offset() / WORD_SIZE)
    
                    _map_str.append(map_format_str.format(
                                    user_def_name,
                                    number_of_slaves,
                                    slave_type,
                                    field_name,
                                    f_base,
                                    f.number_of_fields(),
                                    f.access_mode(),
                                    make_mask(f.width(), f.bit_offset())
                                    ))
                    
                    # only log table entry for first field of slave
                    user_def_name = '-'
                    number_of_slaves = '-'
                    slave_type = '-'
    
        _map_info = []
        _map_info.append('# fpga mm map for {}'.format(fpga_name))
        _map_info.append('# col 1:  slave port name = user defined QSYS name,  if - then it is part of previous slave.')
        _map_info.append('# col 2:  number of slaves, if - then it is part of previous slave.')
        _map_info.append('# col 3:  slave_type (REG, RAM, FIFO), if - then it is part of previous slave.')
        _map_info.append('# col 4:  field_name')
        _map_info.append('# col 5:  field start address (in words)')
        _map_info.append('# col 6:  field address span = number of fields (in words)')
        _map_info.append('# col 7:  field access_mode (RO, WO, RW)')
        _map_info.append('# col 8:  field bit mask')
        _map_info.append('#')
        _map_info.append('# col1                      col2  col3   col4                      col5        col6    col7   col8')
        _map_info.append('# ------------------------  ----  -----  ------------------------  ----------  ------  -----  ----------')
    
        out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), fpga.board_name.replace('uniboard','unb'), 'args', fpga_name, 'c')
        try:
            os.stat(out_dir)
        except FileNotFoundError:
            os.makedirs(out_dir)
    
        with open(os.path.join(out_dir, '{}.mmap'.format(fpga_name)), 'w') as file:
            file.write('\n'.join(_map_info))
            file.write('\n')
            file.write('\n'.join(_map_str))
    
    
    if __name__ == '__main__':
    
        parser = ArgumentParser(description='ARGS tool script to generate fpgamap.py M&C Python client include file')
        parser.add_argument('-f', '--fpga', required=True, help='ARGS fpga_name')
        parser.add_argument('-a', '--avalon', action='store_true', default=False, help="use config file qsys/sopc for base addresses")
        parser.add_argument('-v', '--verbosity', default='INFO', help="stdout log level can be [ERROR | WARNING | INFO | DEBUG]")
        args = parser.parse_args()
    
        fpga_name = args.fpga
        use_avalon = args.avalon
    
        libRootDir = os.path.expandvars('$ARGS_WORK')
    
        # setup first log system before importing other user libraries
        program_name = __file__.split('/')[-1].split('.')[0]
        out_dir = os.path.join(os.getenv('ARGS_GEAR'), 'log')
        my_logger = MyLogger(log_path=out_dir, file_name=program_name)
        my_logger.set_file_log_level('DEBUG')
        my_logger.set_stdout_log_level(args.verbosity)
    
        logger = my_logger.get_logger()
        logger.debug("Used arguments: {}".format(args))
    
        # Find and parse all *.fpga.yaml YAML files under libRootDir
        # For each FPGA file it extracts a list of the peripherals that are used by that FPGA
        # Here we select the FPGA YAML that matches up with the supplied fpga command line argument
        fpga_lib = FPGALibrary(root_dir=libRootDir, use_avalon_base_addr=use_avalon)
        fpga = fpga_lib.get_fpga(fpga_name)
    
        gen_fpga_map(fpga, fpga_name)