mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
Fix a deprecation, to be removed in Python 3.10. See https://docs.python.org/3/library/collections.html: "Deprecated since version 3.3, will be removed in version 3.10: Moved Collections Abstract Base Classes to the collections.abc module. For backwards compatibility, they continue to be visible in this module through Python 3.9."
287 lines
9.2 KiB
Python
Executable file
287 lines
9.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# Copyright lowRISC contributors.
|
|
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import collections.abc
|
|
import os
|
|
import shlex
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
_DEFAULT_CONFIG_FILE = 'ibex_configs.yaml'
|
|
|
|
|
|
class ConfigException(Exception):
|
|
pass
|
|
|
|
|
|
def _verify_config(name, config_dict):
|
|
"""Checks a config_dict matches expectations.
|
|
|
|
A config_dict is the dictionary mapping parameters to values for a
|
|
particular config, it must obey the following rules:
|
|
- It's a mapping object e.g. OrderedDict
|
|
- Its values can only be strings, integers or booleans
|
|
|
|
Args:
|
|
name: The name of the config being checked (used to form useful error
|
|
messages)
|
|
config_dict: The config_dict to check, must be a mapping object
|
|
|
|
Returns:
|
|
Nothing, an exception is thrown if an issue is found
|
|
|
|
Raises:
|
|
ConfigException: An issue was found with config_dict
|
|
"""
|
|
|
|
if not isinstance(config_dict, collections.abc.Mapping):
|
|
raise ConfigException('Config ' + name +
|
|
' must have dictionary giving parameters')
|
|
|
|
for k, v in config_dict.items():
|
|
if isinstance(v, int):
|
|
continue
|
|
if isinstance(v, str):
|
|
continue
|
|
if isinstance(v, bool):
|
|
continue
|
|
|
|
raise ConfigException('Parameter ' + k + ' for config ' + name +
|
|
' must be string, int or bool got ' +
|
|
str(type(v)))
|
|
|
|
|
|
def _verify_config_parameters(config_dicts):
|
|
"""Verifies all parameters across config_dicts match expectations.
|
|
|
|
Each configuration must obey the following fules:
|
|
- Each config has the same set of parameters specified
|
|
|
|
Args:
|
|
config_dicts: A dictionary of configurations, maps from configuration
|
|
name to a configuration (itself a dictionary)
|
|
|
|
Returns:
|
|
Nothing, an exception is thrown if an issue is found
|
|
|
|
Raises:
|
|
ConfigException: An issue was found with config_dicts
|
|
"""
|
|
|
|
parameters = set()
|
|
|
|
first = True
|
|
|
|
for name, config_dict in config_dicts.items():
|
|
parameters_this_config = set()
|
|
|
|
for parameter, value in config_dict.items():
|
|
if first:
|
|
parameters.add(parameter)
|
|
|
|
parameters_this_config.add(parameter)
|
|
|
|
if first:
|
|
first = False
|
|
else:
|
|
parameter_difference = parameters ^ parameters_this_config
|
|
if parameter_difference:
|
|
raise ConfigException('Config ' + name +
|
|
' has differing parameters ' +
|
|
','.join(parameter_difference))
|
|
|
|
|
|
def get_config_dicts(config_file):
|
|
"""Extracts a dictionary of configuration dictionaries from a file object
|
|
|
|
Given a file object parses YAML from it to obtain a dictionary of
|
|
configurations
|
|
|
|
Args:
|
|
config_file: A file object for a file containing the YAML configuration
|
|
file
|
|
|
|
Returns:
|
|
A dictionary of configurations, maps from a configuration name to a
|
|
configuration (itself a dictionary mapping parameters to values)
|
|
|
|
Raises:
|
|
ConfigException: An issue was found with the configuration file
|
|
"""
|
|
|
|
try:
|
|
config_yaml = yaml.load(config_file, Loader=yaml.SafeLoader)
|
|
except yaml.YAMLError as e:
|
|
raise ConfigException('Could not decode yaml:\n' + str(e))
|
|
|
|
for k, v in config_yaml.items():
|
|
_verify_config(k, v)
|
|
|
|
_verify_config_parameters(config_yaml)
|
|
|
|
return config_yaml
|
|
|
|
|
|
class FusesocOpts:
|
|
def setup_args(self, arg_subparser):
|
|
output_argparser = arg_subparser.add_parser(
|
|
'fusesoc_opts', help=('Outputs options for fusesoc'))
|
|
output_argparser.set_defaults(output_fn=self.output)
|
|
|
|
def output(self, config_dict, args):
|
|
fusesoc_cmd = []
|
|
for parameter, value in config_dict.items():
|
|
if isinstance(value, bool):
|
|
# For fusesoc boolean parameters are set to true if given on the
|
|
# command line otherwise false. It doesn't support an explicit
|
|
# --param=True style
|
|
if value:
|
|
fusesoc_cmd.append(shlex.quote('--' + parameter))
|
|
else:
|
|
fusesoc_cmd.append(
|
|
shlex.quote('--' + parameter + '=' + str(value)))
|
|
|
|
return ' '.join(fusesoc_cmd)
|
|
|
|
|
|
class SimOpts:
|
|
def __init__(self, cmd_name, description, param_set_fn, define_set_fn,
|
|
hierarchy_sep):
|
|
self.cmd_name = cmd_name
|
|
self.description = description
|
|
self.param_set_fn = param_set_fn
|
|
self.define_set_fn = define_set_fn
|
|
self.hierarchy_sep = hierarchy_sep
|
|
|
|
def setup_args(self, arg_subparser):
|
|
output_argparser = arg_subparser.add_parser(
|
|
self.cmd_name,
|
|
help=('Outputs options for {0}'.format(self.description)))
|
|
|
|
output_argparser.add_argument(
|
|
'--ins_hier_path',
|
|
help=('Hierarchical path to the instance to set '
|
|
'configuration parameters on'),
|
|
default='')
|
|
output_argparser.add_argument(
|
|
'--string_define_prefix',
|
|
help=('Prefix to add to defines that are used to '
|
|
'pass string parameters'),
|
|
default='')
|
|
output_argparser.set_defaults(output_fn=self.output)
|
|
|
|
def output(self, config_dict, args):
|
|
if (args.ins_hier_path != ''):
|
|
ins_hier_path = args.ins_hier_path + self.hierarchy_sep
|
|
else:
|
|
ins_hier_path = ''
|
|
|
|
sim_opts = []
|
|
|
|
for parameter, value in config_dict.items():
|
|
if isinstance(value, str):
|
|
parameter_define = args.string_define_prefix + parameter
|
|
define_set_str = self.define_set_fn(parameter_define, value)
|
|
|
|
if define_set_str:
|
|
sim_opts.append(shlex.quote(define_set_str))
|
|
else:
|
|
if isinstance(value, bool):
|
|
val_str = '1' if value else '0'
|
|
else:
|
|
val_str = str(value)
|
|
|
|
param_set_str = self.param_set_fn(ins_hier_path + parameter,
|
|
val_str)
|
|
|
|
if param_set_str:
|
|
sim_opts.append(shlex.quote(param_set_str))
|
|
|
|
return ' '.join(sim_opts)
|
|
|
|
|
|
def get_config_file_location():
|
|
"""Returns the location of the config file
|
|
|
|
Default is _DEFAULT_CONFIG_FILE and the IBEX_CONFIG_FILE environment
|
|
variable overrides the default"""
|
|
|
|
return os.environ.get('IBEX_CONFIG_FILE', _DEFAULT_CONFIG_FILE)
|
|
|
|
|
|
def main():
|
|
outputters = [
|
|
FusesocOpts(),
|
|
SimOpts('vcs_opts', 'VCS compile',
|
|
lambda p, v: '-pvalue+' + p + '=' + v,
|
|
lambda d, v: '+define+' + d + '=' + v, '.'),
|
|
SimOpts('riviera_sim_opts', 'Riviera simulate',
|
|
lambda p, v: '-g/' + p + '=' + v,
|
|
lambda d, v: None, '/'),
|
|
SimOpts('riviera_compile_opts', 'Riviera compile',
|
|
lambda p, v: None,
|
|
lambda d, v: '+define+' + d + '=' + v, '/'),
|
|
SimOpts('xlm_opts', 'Xcelium compile',
|
|
lambda p, v: '-defparam ' + p + '=' + v,
|
|
lambda d, v: '-define ' + d + '=' + v, '.'),
|
|
]
|
|
|
|
argparser = argparse.ArgumentParser(description=(
|
|
'Outputs Ibex configuration parameters for a named config in a number '
|
|
'of formats. If not specified on the command line the config will be '
|
|
'read from {0}. This default can be overridden by setting the '
|
|
'IBEX_CONFIG_FILE environment variable. Some output types support '
|
|
'arguments to see help for them pass a config_name and an output type '
|
|
'followed by --help').format(get_config_file_location()))
|
|
|
|
argparser.add_argument('config_name',
|
|
help=('The name of the Ibex '
|
|
'configuration to output'))
|
|
|
|
argparser.add_argument('--config_filename',
|
|
help='Config file to read',
|
|
default=get_config_file_location())
|
|
|
|
arg_subparser = argparser.add_subparsers(
|
|
title='output type',
|
|
help='Format to output the configuration parameters in')
|
|
|
|
for outputter in outputters:
|
|
outputter.setup_args(arg_subparser)
|
|
|
|
args = argparser.parse_args()
|
|
|
|
try:
|
|
config_file = open(args.config_filename)
|
|
config_dicts = get_config_dicts(config_file)
|
|
|
|
if args.config_name not in config_dicts:
|
|
print('ERROR: configuration',
|
|
args.config_name,
|
|
'not found in',
|
|
args.config_filename,
|
|
file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
print(args.output_fn(config_dicts[args.config_name], args))
|
|
except ConfigException as ce:
|
|
print('ERROR: failure to process configuration from',
|
|
args.config_filename,
|
|
ce,
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
except FileNotFoundError:
|
|
print('ERROR: could not find configuration file',
|
|
args.config_filename,
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|