diff --git a/dv/uvm/core_ibex/Makefile b/dv/uvm/core_ibex/Makefile index 1ea33c46..37e679be 100644 --- a/dv/uvm/core_ibex/Makefile +++ b/dv/uvm/core_ibex/Makefile @@ -65,22 +65,14 @@ PMP_REGIONS := 16 # PMP Granularity PMP_GRANULARITY := 0 +IBEX_CONFIG := experimental-maxperf-pmp-bm + # TODO(udinator) - might need options for SAIL/Whisper/Spike ifeq (${ISS},ovpsim) ISS_OPTS += --override riscvOVPsim/cpu/PMP_registers=${PMP_REGIONS} ISS_OPTS += --override riscvOVPsim/cpu/PMP_grain=${PMP_GRANULARITY} endif -# Check which simulator is being used and add correct compile options -ifeq (${SIMULATOR},vcs) - COMPILE_OPTS += -pvalue+core_ibex_tb_top.dut.PMPNumRegions=${PMP_REGIONS} - COMPILE_OPTS += -pvalue+core_ibex_tb_top.dut.PMPGranularity=${PMP_GRANULARITY} -else ifeq (&{SIMULATOR},riviera) - SIM_OPTS +=-g/core_ibex_tb_top/dut/PMPNumRegions=${PMP_REGIONS} - SIM_OPTS +=-g/core_ibex_tb_top/dut/PMPGranularity=${PMP_GRANULARITY} -# TODO(udinator) - support dsim -endif - # This expands to '@' if VERBOSE is 0 or not set, and to the empty # string otherwise. Prefix commands with it in order that they only # get printed when VERBOSE. @@ -132,6 +124,31 @@ gen-dirs := $(OUT) $(OUT-SEED) $(metadata) $(OUT)/rtl_sim $(gen-dirs): %: mkdir -p $@ +# sim-cfg-mk is a makefile fragment that sets-up anything simulator specific, it +# is generated by sim_makefrag_gen.py +sim-cfg-mk = $(OUT)/.sim-cfg.mk + +# The include of $(sim-cfg-mk) below tells Make that it should ensure the file +# exists. This rule tells Make how to build it. We also want to ensure it's +# built on every run. Declaring the rule to be .PHONY doesn't work because it +# causes Make to no longer think the rule actually generates the file. If you +# do that, the file gets re-built, but Make doesn't read the new contents. So +# instead we depend on FORCE (a phony target). This ensures a rebuild, but also +# causes an infinite recursion: when we re-read this file to include the new +# contents of $(sim-cfg-mk), we run the rule again. To avoid that, we check for +# MAKE_RESTARTS, which is defined on re-runs. Phew! +ifndef MAKE_RESTARTS +$(sim-cfg-mk): FORCE | $(OUT) + @./sim_makefrag_gen.py $(SIMULATOR) $(IBEX_CONFIG) $(PRJ_DIR)/ibex $@ +endif + +include $(sim-cfg-mk) + +.PHONY: test-cfg +test-cfg: + @echo "COMPILE_OPTS" $(COMPILE_OPTS) + @echo "SIM_OPTS" $(SIM_OPTS) + ############################################################################### # Utility functions. # diff --git a/dv/uvm/core_ibex/sim_makefrag_gen.py b/dv/uvm/core_ibex/sim_makefrag_gen.py new file mode 100755 index 00000000..dddc3567 --- /dev/null +++ b/dv/uvm/core_ibex/sim_makefrag_gen.py @@ -0,0 +1,108 @@ +#!/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.path +import subprocess +import sys + +ibex_config_path = '' +ibex_config_name = '' +ibex_config_filename = '' + + +class GenError(Exception): + pass + + +def run_ibex_config(output_type, extra_args=None): + ibex_config_cmd = [ + ibex_config_path, + ibex_config_name, + '--config_filename', ibex_config_filename, + output_type + ] + + if extra_args: + ibex_config_cmd += extra_args + + result = subprocess.run(ibex_config_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + if result.returncode != 0: + raise GenError("Error running {0} got:\n{1}\n{2}".format( + ibex_config_path, str(result.stdout, 'utf-8'), + str(result.stderr, 'utf-8'))) + + return str(result.stdout, 'utf-8') + + +def gen_vcs_makefrag(): + vcs_compile_opts = run_ibex_config('vcs_opts', [ + '--ins_hier_path', 'core_ibex_tb_top', + '--string_define_prefix', 'IBEX_CFG_' + ]) + + return 'COMPILE_OPTS += {0}'.format(vcs_compile_opts) + + +def gen_riviera_makefrag(): + riviera_compile_opts = run_ibex_config('riviera_compile_opts', [ + '--ins_hier_path', 'core_ibex_tb_top', + '--string_define_prefix', 'IBEX_CFG_' + ]) + + riviera_sim_opts = run_ibex_config('riviera_sim_opts', [ + '--ins_hier_path', 'core_ibex_tb_top', + '--string_define_prefix', 'IBEX_CFG_' + ]) + + return ('COMPILE_OPTS += {0}' + 'SIM_OPTS += {1}').format(riviera_compile_opts, riviera_sim_opts) + + +def main(): + argparser = argparse.ArgumentParser(description=( + 'Generates a makefile fragment for use with the Ibex DV makefile that ' + 'sets up sim specific variables')) + + sim_fns = {'vcs': gen_vcs_makefrag, 'riviera': gen_riviera_makefrag} + + argparser.add_argument('sim', + help='Name of the simulator', + choices=sim_fns.keys()) + + argparser.add_argument( + 'config', help='Ibex config to generate makefile fragment for') + + argparser.add_argument('ibex_top', + help='Path to the top of an ibex repository') + + argparser.add_argument('makefrag_name', + help='Filename of the makefile fragment to write') + + args = argparser.parse_args() + + global ibex_config_path + global ibex_config_name + global ibex_config_filename + + ibex_config_path = os.path.join(args.ibex_top, 'util/ibex_config.py') + ibex_config_name = args.config + ibex_config_filename = os.path.join(args.ibex_top, 'ibex_configs.yaml') + + try: + with open(args.makefrag_name, 'w') as makefrag: + makefrag.write(sim_fns[args.sim]()) + except GenError as e: + print('Failure generating simulation options for', args.sim) + print(e) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index a0f1c365..0726a551 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -26,42 +26,63 @@ module core_ibex_tb_top; // CSR access interface core_ibex_csr_if csr_if(.clk(clk)); + // You cannot override string parameters in VCS via the command line so a `define is used instead + // that can be set from the command line. If no value has been specified this gives a default. + `ifndef IBEX_MULTIPLIER_IMPLEMENTATION + `define IBEX_MULTIPLIER_IMPLEMENTATION fast + `endif + + parameter bit PMPEnable = 1'b0; + parameter int unsigned PMPGranularity = 0; + parameter int unsigned PMPNumRegions = 4; + parameter bit RV32E = 1'b0; + parameter bit RV32M = 1'b1; + parameter bit RV32B = 1'b0; + parameter bit BranchTargetALU = 1'b0; + parameter bit WritebackStage = 1'b0; + parameter MultiplierImplementation = `"`IBEX_CFG_MultiplierImplementation`"; + ibex_core_tracing #( - .DmHaltAddr(`BOOT_ADDR + 'h0), - .DmExceptionAddr(`BOOT_ADDR + 'h4), - .PMPEnable(1'b1), - .BranchTargetALU(1'b1), - .WritebackStage(1'b1), - .RV32B(1'b1) + .DmHaltAddr (`BOOT_ADDR + 'h0 ), + .DmExceptionAddr (`BOOT_ADDR + 'h4 ), + .PMPEnable (PMPEnable ), + .PMPGranularity (PMPGranularity ), + .PMPNumRegions (PMPNumRegions ), + .RV32E (RV32E ), + .RV32M (RV32M ), + .RV32B (RV32B ), + .BranchTargetALU (BranchTargetALU ), + .WritebackStage (WritebackStage ), + .MultiplierImplementation (MultiplierImplementation) ) dut ( - .clk_i(clk), - .rst_ni(rst_n), - .test_en_i(1'b1), - .hart_id_i(32'b0), - .boot_addr_i(`BOOT_ADDR), // align with spike boot address - .irq_software_i(irq_vif.irq_software), - .irq_timer_i(irq_vif.irq_timer), - .irq_external_i(irq_vif.irq_external), - .irq_fast_i(irq_vif.irq_fast), - .irq_nm_i(irq_vif.irq_nm), - .fetch_enable_i(dut_if.fetch_enable), - .debug_req_i(dut_if.debug_req), - .data_req_o(data_mem_vif.request), - .data_gnt_i(data_mem_vif.grant), - .data_rvalid_i(data_mem_vif.rvalid), - .data_addr_o(data_mem_vif.addr), - .data_we_o(data_mem_vif.we), - .data_be_o(data_mem_vif.be), - .data_rdata_i(data_mem_vif.rdata), - .data_wdata_o(data_mem_vif.wdata), - .data_err_i(data_mem_vif.error), - .instr_req_o(instr_mem_vif.request), - .instr_gnt_i(instr_mem_vif.grant), - .instr_rvalid_i(instr_mem_vif.rvalid), - .instr_addr_o(instr_mem_vif.addr), - .instr_rdata_i(instr_mem_vif.rdata), - .instr_err_i(instr_mem_vif.error), - .core_sleep_o(dut_if.core_sleep) + .clk_i (clk ), + .rst_ni (rst_n ), + .test_en_i (1'b1 ), + .hart_id_i (32'b0 ), + .boot_addr_i (`BOOT_ADDR ), // align with spike boot address + .irq_software_i (irq_vif.irq_software ), + .irq_timer_i (irq_vif.irq_timer ), + .irq_external_i (irq_vif.irq_external ), + .irq_fast_i (irq_vif.irq_fast ), + .irq_nm_i (irq_vif.irq_nm ), + .fetch_enable_i (dut_if.fetch_enable ), + .debug_req_i (dut_if.debug_req ), + .data_req_o (data_mem_vif.request ), + .data_gnt_i (data_mem_vif.grant ), + .data_rvalid_i (data_mem_vif.rvalid ), + .data_addr_o (data_mem_vif.addr ), + .data_we_o (data_mem_vif.we ), + .data_be_o (data_mem_vif.be ), + .data_rdata_i (data_mem_vif.rdata ), + .data_wdata_o (data_mem_vif.wdata ), + .data_err_i (data_mem_vif.error ), + .instr_req_o (instr_mem_vif.request), + .instr_gnt_i (instr_mem_vif.grant ), + .instr_rvalid_i (instr_mem_vif.rvalid ), + .instr_addr_o (instr_mem_vif.addr ), + .instr_rdata_i (instr_mem_vif.rdata ), + .instr_err_i (instr_mem_vif.error ), + .core_sleep_o (dut_if.core_sleep ) ); // Data load/store vif connection diff --git a/util/ibex_config.py b/util/ibex_config.py index 70ecc3e6..58d236e1 100755 --- a/util/ibex_config.py +++ b/util/ibex_config.py @@ -127,19 +127,82 @@ def get_config_dicts(config_file): return config_yaml -def _config_dict_to_fusesoc_opts(config_dict): - fusesoc_cmd = [] - for parameter, value in config_dict.items(): - if isinstance(value, bool): - # For fusesoc boolean parameter 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))) +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) - return ' '.join(fusesoc_cmd) + 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(): @@ -152,28 +215,42 @@ def get_config_file_location(): def main(): - config_outputs = {'fusesoc_opts': _config_dict_to_fusesoc_opts} + 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, '/') + ] 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').format(get_config_file_location())) + '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('output_type', - help=('Format to output the ' - 'configuration parameters in'), - choices=config_outputs.keys()) - 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: @@ -189,9 +266,9 @@ def main(): sys.exit(1) - print(config_outputs[args.output_type](config_dicts[args.config_name])) + print(args.output_fn(config_dicts[args.config_name], args)) except ConfigException as ce: - print('ERROR: failure to read configuration from', + print('ERROR: failure to process configuration from', args.config_filename, ce, file=sys.stderr)