mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
315 lines
11 KiB
Python
Executable file
315 lines
11 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 os
|
|
import shlex
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
_DEFAULT_CONFIG_FILE = 'ibex_configs.yaml'
|
|
|
|
|
|
class ConfigException(Exception):
|
|
pass
|
|
|
|
|
|
class Config:
|
|
'''An object representing an Ibex configuration'''
|
|
known_fields = [
|
|
('RV32E', bool),
|
|
('RV32M', str),
|
|
('RV32B', str),
|
|
('RegFile', str),
|
|
('BranchTargetALU', bool),
|
|
('WritebackStage', bool),
|
|
('ICache', bool),
|
|
('ICacheECC', bool),
|
|
('ICacheScramble', bool),
|
|
('BranchPredictor', bool),
|
|
('DbgTriggerEn', bool),
|
|
('SecureIbex', bool),
|
|
('PMPEnable', bool),
|
|
('PMPGranularity', int),
|
|
('PMPNumRegions', int),
|
|
('MHPMCounterNum', int),
|
|
('MHPMCounterWidth', int)
|
|
]
|
|
|
|
def __init__(self, yml):
|
|
if not isinstance(yml, dict):
|
|
raise ValueError('Configuration object is not a dict')
|
|
|
|
yaml_keys = set(yml.keys())
|
|
known_keys = {fld for (fld, typ) in Config.known_fields}
|
|
|
|
extra_keys = yaml_keys - known_keys
|
|
if extra_keys:
|
|
raise ValueError(f'Configuration object has '
|
|
f'unknown keys: {extra_keys}')
|
|
|
|
missing_keys = known_keys - yaml_keys
|
|
if missing_keys:
|
|
raise ValueError(f'Configuration object has '
|
|
f'missing keys: {extra_keys}')
|
|
|
|
self.params = yml
|
|
|
|
self.rv32e = Config.read_bool('RV32E', yml)
|
|
self.rv32m = Config.read_str('RV32M', yml)
|
|
self.rv32b = Config.read_str('RV32B', yml)
|
|
self.reg_file = Config.read_str('RegFile', yml)
|
|
self.branch_target_alu = Config.read_bool('BranchTargetALU', yml)
|
|
self.writeback_stage = Config.read_bool('WritebackStage', yml)
|
|
self.icache = Config.read_bool('ICache', yml)
|
|
self.icache_ecc = Config.read_bool('ICacheECC', yml)
|
|
self.icache_scramble = Config.read_bool('ICacheScramble', yml)
|
|
self.branch_predictor = Config.read_bool('BranchPredictor', yml)
|
|
self.dbg_trigger_en = Config.read_bool('DbgTriggerEn', yml)
|
|
self.secure_ibex = Config.read_bool('SecureIbex', yml)
|
|
self.pmp_enable = Config.read_bool('PMPEnable', yml)
|
|
self.pmp_granularity = Config.read_int('PMPGranularity', yml)
|
|
self.pmp_num_regions = Config.read_int('PMPNumRegions', yml)
|
|
self.mhpm_counter_num = Config.read_int('MHPMCounterNum', yml)
|
|
self.mhpm_counter_width = Config.read_int('MHPMCounterWidth', yml)
|
|
|
|
@staticmethod
|
|
def read_bool(fld, yml):
|
|
val = yml[fld]
|
|
if isinstance(val, bool):
|
|
return val
|
|
if isinstance(val, int):
|
|
if 0 <= val <= 1:
|
|
return val != 0
|
|
|
|
raise ValueError(f'{fld} value is {val}, which is out of '
|
|
'range for a boolean type.')
|
|
raise ValueError(f'{fld} value is {val!r}, but we expected a bool.')
|
|
|
|
@staticmethod
|
|
def read_int(fld, yml):
|
|
val = yml[fld]
|
|
if isinstance(val, int):
|
|
return val
|
|
raise ValueError(f'{fld} value is {val!r}, but we expected an int.')
|
|
|
|
@staticmethod
|
|
def read_str(fld, yml):
|
|
val = yml[fld]
|
|
if isinstance(val, str):
|
|
return val
|
|
raise ValueError(f'{fld} value is {val!r}, but we expected a string.')
|
|
|
|
|
|
class Configs:
|
|
def __init__(self, yml):
|
|
if not isinstance(yml, dict):
|
|
raise ValueError('Configurations dictionary is not a dict')
|
|
|
|
self.configs = {}
|
|
for cfg_name, cfg_yaml in yml.items():
|
|
try:
|
|
self.configs[cfg_name] = Config(cfg_yaml)
|
|
except ValueError as err:
|
|
raise ValueError(f'Error when reading '
|
|
f'{cfg_name!r} config: {err}') from None
|
|
|
|
|
|
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, args):
|
|
fusesoc_cmd = []
|
|
for fld, typ in Config.known_fields:
|
|
val = config.params[fld]
|
|
fusesoc_cmd.append(shlex.quote(f'--{fld}={val}'))
|
|
|
|
return ' '.join(fusesoc_cmd)
|
|
|
|
class QueryOpts:
|
|
def setup_args(self, arg_subparser):
|
|
output_argparser = arg_subparser.add_parser(
|
|
'query_fields', help=('Query config fields'))
|
|
output_argparser.add_argument(
|
|
'fields', type=str, nargs='+',
|
|
help='Which fields to query the value of')
|
|
|
|
output_argparser.set_defaults(output_fn=self.output)
|
|
|
|
def output(self, config, args):
|
|
query_result = []
|
|
for fld in args.fields:
|
|
if fld in config.params:
|
|
val = config.params[fld]
|
|
query_result.append(f'{fld}={val}')
|
|
else:
|
|
query_result.append(f'{fld} not found in config')
|
|
|
|
return '\n'.join(query_result)
|
|
|
|
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, args):
|
|
if (args.ins_hier_path != ''):
|
|
ins_hier_path = args.ins_hier_path + self.hierarchy_sep
|
|
else:
|
|
ins_hier_path = ''
|
|
|
|
sim_opts = []
|
|
|
|
for fld, typ in Config.known_fields:
|
|
val = config.params[fld]
|
|
|
|
if typ is str:
|
|
parameter_define = args.string_define_prefix + fld
|
|
define_opts = self.define_set_fn(parameter_define, val)
|
|
sim_opts += [shlex.quote(arg) for arg in define_opts]
|
|
else:
|
|
assert typ in [bool, int]
|
|
|
|
# Explicitly convert to 0/1 (handling genuine booleans)
|
|
val_as_int = int(val)
|
|
|
|
full_param = ins_hier_path + fld
|
|
param_opts = self.param_set_fn(full_param, str(val_as_int))
|
|
sim_opts += [shlex.quote(arg) for arg in param_opts]
|
|
|
|
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 parse_config(config_name, config_filename):
|
|
"""Parses the selected config file and returns selected config information.
|
|
|
|
Arguments:
|
|
|
|
config_name: Name of the chosen Ibex core config
|
|
|
|
config_filename: Name of the configuration filename to be parsed
|
|
|
|
Returns: the chosen Ibex config as a Config object.
|
|
|
|
Raises an exception if there is an error loading or parsing the YAML, or if
|
|
the YAML doesn't define a configuration with the requested name.
|
|
|
|
"""
|
|
with open(config_filename) as config_file:
|
|
try:
|
|
yml = yaml.load(config_file, Loader=yaml.SafeLoader)
|
|
except yaml.YAMLError as err:
|
|
raise ConfigException(f'Could not decode yaml: {err}')
|
|
|
|
try:
|
|
configs = Configs(yml)
|
|
except ValueError as err:
|
|
raise ConfigException(f'{config_filename!r}: {err}') from None
|
|
|
|
config = configs.configs.get(config_name)
|
|
if config is None:
|
|
raise ValueError(f'Configuration {config_name!r} not found '
|
|
'in YAML at {config_filename!r}.')
|
|
|
|
return config
|
|
|
|
|
|
def main():
|
|
outputters = [
|
|
FusesocOpts(),
|
|
QueryOpts(),
|
|
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: [], '/'),
|
|
SimOpts('riviera_compile_opts', 'Riviera compile',
|
|
lambda p, v: [],
|
|
lambda d, v: ['+define+' + d + '=' + v], '/'),
|
|
SimOpts('questa_sim_opts', 'Questa simulate',
|
|
lambda p, v: ['-g/' + p + '=' + v],
|
|
lambda d, v: [], '/'),
|
|
SimOpts('questa_compile_opts', 'Questa compile',
|
|
lambda p, v: [],
|
|
lambda d, v: ['+define+' + d + '=' + v], '/'),
|
|
SimOpts('xlm_opts', 'Xcelium compile',
|
|
lambda p, v: ['-defparam', p + '=' + v],
|
|
lambda d, v: ['-define', d + '=' + v], '.'),
|
|
SimOpts('dsim_compile_opts', 'DSim compile',
|
|
lambda p, v: ['+define+' + p + '=' + v],
|
|
lambda 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(
|
|
help='Format to output the configuration parameters in',
|
|
dest='output_fn',
|
|
metavar='output_type')
|
|
|
|
for outputter in outputters:
|
|
outputter.setup_args(arg_subparser)
|
|
|
|
args = argparser.parse_args()
|
|
|
|
if args.output_fn is None:
|
|
print('ERROR: No output format specified.')
|
|
sys.exit(1)
|
|
|
|
parsed_ibex_config = parse_config(args.config_name, args.config_filename)
|
|
print(args.output_fn(parsed_ibex_config, args))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|