Select Git revision
gen_rom_mmap.py

Eric Kooistra authored
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)