diff --git a/hw/dv/tools/dvsim/bazel.hjson b/hw/dv/tools/dvsim/bazel.hjson index 407bfb119bf38..09578cf042437 100644 --- a/hw/dv/tools/dvsim/bazel.hjson +++ b/hw/dv/tools/dvsim/bazel.hjson @@ -2,6 +2,14 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 { - sw_build_cmd: "bazel" + sw_build_cmd: [ + "python", + "{proj_root}/util/py/scripts/build_sw_collateral_for_sim.py", + "--sw-images='{sw_images}'", + "--sw-build-opts='{sw_build_opts}'", + "--sw-build-device={sw_build_device}", + "--seed={seed}", + "--run-dir={run_dir}", + ] sw_build_opts: [] } diff --git a/hw/dv/tools/dvsim/sim.mk b/hw/dv/tools/dvsim/sim.mk index 8cf7e4b1d88f0..c207e9d5a0529 100644 --- a/hw/dv/tools/dvsim/sim.mk +++ b/hw/dv/tools/dvsim/sim.mk @@ -15,7 +15,7 @@ all: build run build: build_result pre_build: - @echo "[make]: pre_build" + @echo -e "\n[make]: pre_build" mkdir -p ${build_dir} ifneq (${pre_build_cmds},) # pre_build_cmds are likely changing the in-tree sources. We hence use FLOCK @@ -26,173 +26,51 @@ ifneq (${pre_build_cmds},) endif gen_sv_flist: pre_build - @echo "[make]: gen_sv_flist" + @echo -e "\n[make]: gen_sv_flist" ifneq (${sv_flist_gen_cmd},) cd ${build_dir} && ${sv_flist_gen_cmd} ${sv_flist_gen_opts} endif do_build: gen_sv_flist - @echo "[make]: build" + @echo -e "\n[make]: build" cd ${sv_flist_gen_dir} && ${build_cmd} ${build_opts} post_build: do_build - @echo "[make]: post_build" + @echo -e "\n[make]: post_build" ifneq (${post_build_cmds},) cd ${build_dir} && ${post_build_cmds} endif build_result: post_build - @echo "[make]: build_result" + @echo -e "\n[make]: build_result" run: run_result pre_run: - @echo "[make]: pre_run" + @echo -e "\n[make]: pre_run" mkdir -p ${run_dir} ifneq (${pre_run_cmds},) cd ${run_dir} && ${pre_run_cmds} endif sw_build: pre_run - @echo "[make]: sw_build" + @echo -e "\n[make]: sw_build" ifneq (${sw_images},) - # Loop through the list of sw_images and invoke Bazel on each. - # `sw_images` is a space-separated list of tests to be built into an image. - # Optionally, each item in the list can have additional metadata / flags using - # the delimiter ':'. The format is as follows: - # ::: - # - # If one delimiter is detected, then the full string is considered to be the - # . If two delimiters are detected, then it must be - # followed by . The is considered optional. - # - # After the images are built, we use `bazel cquery ...` to locate the built - # software artifacts so they can be copied to the test bench run directory. - # We only copy device SW images, and do not copy host-side artifacts (like - # opentitantool) that are also dependencies of the Bazel test target that - # encode the software image targets. - set -e; \ - for sw_image in ${sw_images}; do \ - if [[ -z $$sw_image ]]; then \ - echo "ERROR: SW image \"$$sw_image\" is malformed."; \ - echo "Expected format: ::."; \ - exit 1; \ - fi; \ - prebuilt_path=`echo $$sw_image | cut -d: -f 1`; \ - bazel_target=`echo $$sw_image | cut -d: -f 2`; \ - index=`echo $$sw_image | cut -d: -f 3`; \ - flags=(`echo $$sw_image | cut -d: -f 4- --output-delimiter " "`); \ - bazel_label="`echo $$sw_image | cut -d: -f 1-2`"; \ - if [[ $${index} != 4 && $${index} != 5 ]]; then \ - if [[ $${flags[@]} =~ "silicon_creator" ]]; then \ - bazel_label="$${bazel_label}_silicon_creator"; \ - else \ - bazel_label="$${bazel_label}_$${sw_build_device}"; \ - fi; \ - bazel_cquery="labels(data, $${bazel_label}) union labels(srcs, $${bazel_label})"; \ - else \ - bazel_cquery="$${bazel_label}"; \ - fi; \ - cd ${proj_root}; \ - if [[ $${flags[@]} =~ "prebuilt" ]]; then \ - echo "SW image \"$$bazel_label\" is prebuilt - copying sources."; \ - cp ${proj_root}/$${prebuilt_path} $${run_dir}/`basename $${prebuilt_path}`; \ - else \ - echo "Building SW image \"$${bazel_label}\"."; \ - bazel_airgapped_opts=""; \ - bazel_opts="${sw_build_opts} --define DISABLE_VERILATOR_BUILD=true"; \ - if [[ -n $${BAZEL_OTP_DATA_PERM_FLAG} ]]; then \ - bazel_opts+=" --//util/design/data:data_perm=$${BAZEL_OTP_DATA_PERM_FLAG}"; \ - fi; \ - if [[ $${OT_AIRGAPPED} != true ]]; then \ - echo "Building \"$${bazel_label}\" on network connected machine."; \ - bazel_cmd="./bazelisk.sh"; \ - else \ - echo "Building \"$${bazel_label}\" on air-gapped machine."; \ - bazel_airgapped_opts+=" --define SPECIFY_BINDGEN_LIBSTDCXX=true"; \ - bazel_airgapped_opts+=" --distdir=$${BAZEL_DISTDIR}"; \ - bazel_airgapped_opts+=" --repository_cache=$${BAZEL_CACHE}"; \ - bazel_cmd="bazel"; \ - fi; \ - echo "Building with command: $${bazel_cmd} build $${bazel_opts} $${bazel_label}"; \ - $${bazel_cmd} build $${bazel_airgapped_opts} $${bazel_opts} $${bazel_label}; \ - kind=$$($${bazel_cmd} cquery $${bazel_airgapped_opts} \ - $${bazel_label} \ - --ui_event_filters=-info \ - --noshow_progress \ - --output=label_kind | cut -f1 -d' '); \ - if [[ $${kind} == "opentitan_test" ]]; then \ - for artifact in $$($${bazel_cmd} cquery $${bazel_airgapped_opts} $${bazel_opts} \ - $${bazel_label} \ - --ui_event_filters=-info \ - --noshow_progress \ - --output=starlark \ - `# An opentitan_test rule has all of its needed files in its runfiles.` \ - --starlark:expr='"\n".join([f.path for f in target.data_runfiles.files.to_list()])'); do \ - cp -f $${artifact} $${run_dir}/$$(basename $${artifact}); \ - if [[ $$artifact == *.bin && \ - -f "$$(echo $${artifact} | cut -d. -f 1).elf" ]]; then \ - cp -f "$$(echo $${artifact} | cut -d. -f 1).elf" \ - $${run_dir}/$$(basename -s .bin $${artifact}).elf; \ - fi; \ - done; \ - elif [[ $${kind} == "alias" || $${kind} == "opentitan_binary" ]]; then \ - for artifact in $$($${bazel_cmd} cquery $${bazel_airgapped_opts} $${bazel_opts} \ - $${bazel_label} \ - --ui_event_filters=-info \ - --noshow_progress \ - --output=starlark \ - `# An opentitan_binary rule has all of its needed files in its runfiles.` \ - --starlark:expr='"\n".join([f.path for f in target.files.to_list()])'); do \ - cp -f $${artifact} $${run_dir}/$$(basename $${artifact}); \ - if [[ $$artifact == *.bin && \ - -f "$$(echo $${artifact} | cut -d. -f 1).elf" ]]; then \ - cp -f "$$(echo $${artifact} | cut -d. -f 1).elf" \ - $${run_dir}/$$(basename -s .bin $${artifact}).elf; \ - fi; \ - done; \ - else \ - for dep in $$($${bazel_cmd} cquery $${bazel_airgapped_opts} $${bazel_opts} \ - $${bazel_cquery} \ - --ui_event_filters=-info \ - --noshow_progress \ - --output=starlark \ - `# Bazel 6 cquery outputs repository targets in canonical format (@//blabla) whereas bazel 5 does not, ` \ - `# so we use a custom starlark printer to remove in leading @ when needed.` \ - --starlark:expr='str(target.label)[1:] if str(target.label).startswith("@//") else target.label'); do \ - if [[ $$dep == //hw/top_*/ip_autogen/otp_ctrl/data* ]] || \ - ([[ $$dep != //hw* ]] && [[ $$dep != //util* ]] && [[ $$dep != //sw/host* ]]); then \ - for artifact in $$($${bazel_cmd} cquery $${bazel_airgapped_opts} $${bazel_opts} $${dep} \ - --ui_event_filters=-info \ - --noshow_progress \ - --output=starlark \ - --starlark:expr="\"\\n\".join([f.path for f in target.files.to_list()])"); do \ - cp -f $${artifact} $${run_dir}/$$(basename $${artifact}); \ - if [[ $$artifact == *.bin && \ - -f "$$(echo $${artifact} | cut -d. -f 1).elf" ]]; then \ - cp -f "$$(echo $${artifact} | cut -d. -f 1).elf" \ - $${run_dir}/$$(basename -s .bin $${artifact}).elf; \ - fi; \ - done; \ - fi; \ - done; \ - fi; \ - fi; \ - done; + cd ${proj_root} && ${sw_build_cmd} --build-seed=${build_seed} endif simulate: sw_build - @echo "[make]: simulate" + @echo -e "\n[make]: simulate" cd ${run_dir} && ${run_cmd} ${run_opts} post_run: simulate - @echo "[make]: post_run" + @echo -e "\n[make]: post_run" ifneq (${post_run_cmds},) cd ${run_dir} && ${post_run_cmds} endif run_result: post_run - @echo "[make]: run_result" + @echo -e "\n[make]: run_result" ####################### ## Load waves target ## @@ -204,19 +82,20 @@ debug_waves: ## coverage rated targets ## ############################ cov_unr_build: gen_sv_flist - @echo "[make]: cov_unr_build" + @echo -e "\n[make]: cov_unr_build" cd ${sv_flist_gen_dir} && ${cov_unr_build_cmd} ${cov_unr_build_opts} cov_unr_vcs: cov_unr_build - @echo "[make]: cov_unr" + @echo -e "\n[make]: cov_unr" cd ${sv_flist_gen_dir} && ${cov_unr_run_cmd} ${cov_unr_run_opts} cov_unr_xcelium: - @echo "[make]: cov_unr" + @echo -e "\n[make]: cov_unr" mkdir -p ${cov_unr_dir} cd ${cov_unr_dir} && ${cov_unr_run_cmd} ${cov_unr_run_opts} cov_unr_merge: + @echo -e "\n[make]: cov_unr_merge" cd ${cov_unr_dir} && ${job_prefix} ${cov_merge_cmd} -init ${cov_unr_dir}/jgproject/sessionLogs/session_0/unr_imc_coverage_merge.cmd ifeq (${SIMULATOR}, xcelium) @@ -227,17 +106,17 @@ endif # Merge coverage if there are multiple builds. cov_merge: - @echo "[make]: cov_merge" + @echo -e "\n[make]: cov_merge" ${job_prefix} ${cov_merge_cmd} ${cov_merge_opts} # Generate coverage reports. cov_report: - @echo "[make]: cov_report" + @echo -e "\n[make]: cov_report" ${cov_report_cmd} ${cov_report_opts} # Open coverage tool to review and create report or exclusion file. cov_analyze: - @echo "[make]: cov_analyze" + @echo -e "\n[make]: cov_analyze" ${cov_analyze_cmd} ${cov_analyze_opts} .PHONY: build \ diff --git a/hw/top_darjeeling/dv/env/chip_env_pkg.sv b/hw/top_darjeeling/dv/env/chip_env_pkg.sv index c47b2cce9d17a..b98e672d21ce7 100644 --- a/hw/top_darjeeling/dv/env/chip_env_pkg.sv +++ b/hw/top_darjeeling/dv/env/chip_env_pkg.sv @@ -102,8 +102,10 @@ package chip_env_pkg; SpiDeviceIngressMem } chip_mem_e; - // On OpenTitan, we deal with 4 types of SW - ROM, the main test, the OTBN test and the OTP image. + // On Darjeeling, we deal with 8 types of SW. // This basically puts these SW types into 'slots' that the external regression tool can set. + // Note: This enum must be consistent across tops. + // Note: If this enum is updated, then also update the file `build_sw_collateral_for_sim.py`. typedef enum { SwTypeRom = 0, // Ibex SW - first stage boot ROM. SwTypeTestSlotA = 1, // Ibex SW - test SW in (flash) slot A. diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson index 5d0a1b043ba0d..7f0555f153ab7 100644 --- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson +++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson @@ -325,22 +325,43 @@ run_opts: ["+accelerate_cold_power_up_time=3", "+accelerate_regulators_power_up_time=2"] } + { + // Format SW image names into output file names separated by commas to feed into + // +sw_images plusarg. Image names are just Bazel labels concatenated with an index + // and/or flags. + // The input is a space-seperated list in the variable 'sw_images'. + // The result is saved to the same variable 'sw_images'. + // + // For example, for an input of: + // [ + // "//sw/device/tests:uart_tx_rx_test:1", + // "//sw/device/lib/testing/test_rom:test_rom:0" + // ] + // + // ..then the result will be: + // + // "uart_tx_rx_test:1,test_rom:0". + // + name: format_sw_images + run_opts: [ + "+sw_build_device={sw_build_device}", + ''' + +sw_images={eval_cmd} \ + reformatted_sw_images=; \ + for image in {sw_images}; do \ + reformatted_sw_images="$reformatted_sw_images `echo $image | cut -d: -f2-`"; \ + done; \ + echo $reformatted_sw_images | sed -E 's/\s+/,/g' + ''' + ] + } + { + name: sw_test_mode_base + en_run_modes: ["format_sw_images"] + } { name: sw_test_mode_common - run_opts: ["+sw_build_device={sw_build_device}", - // Format SW image names (which are Bazel labels concatenated with an index - // and/or flags, see below) into output file names separated by commas to feed into - // +sw_images plusarg. For example, if the input list of SW images is - // ["//sw/device/tests:uart_tx_rx_test:1", - // "//sw/device/lib/testing/test_rom:test_rom:0"], then the output of this eval_cmd - // will be: "uart_tx_rx_test:1,test_rom:0". - '''+sw_images={eval_cmd} \ - reformatted_sw_images=; \ - for image in {sw_images}; do \ - reformatted_sw_images="$reformatted_sw_images `echo $image | cut -d: -f2-`"; \ - done; \ - echo $reformatted_sw_images | sed -E 's/\s+/,/g' '''] - en_run_modes: ["gen_otp_images_mode"] + en_run_modes: ["sw_test_mode_base", "gen_otp_images_mode"] } { name: sw_test_mode_test_rom diff --git a/hw/top_earlgrey/dv/env/chip_env_pkg.sv b/hw/top_earlgrey/dv/env/chip_env_pkg.sv index fe3ab5d6e501d..eba660d39ed27 100644 --- a/hw/top_earlgrey/dv/env/chip_env_pkg.sv +++ b/hw/top_earlgrey/dv/env/chip_env_pkg.sv @@ -100,8 +100,10 @@ package chip_env_pkg; Rom } chip_mem_e; - // On OpenTitan, we deal with 4 types of SW - ROM, the main test, the OTBN test and the OTP image. + // On Earlgrey, we deal with 6 types of SW. // This basically puts these SW types into 'slots' that the external regression tool can set. + // Note: This enum must be consistent across tops. + // Note: If this enum is updated, then also update the file `build_sw_collateral_for_sim.py`. typedef enum { SwTypeRom = 0, // Ibex SW - first stage boot ROM. SwTypeTestSlotA = 1, // Ibex SW - test SW in (flash) slot A. diff --git a/util/py/scripts/build_sw_collateral_for_sim.py b/util/py/scripts/build_sw_collateral_for_sim.py new file mode 100755 index 0000000000000..edaaf563f6042 --- /dev/null +++ b/util/py/scripts/build_sw_collateral_for_sim.py @@ -0,0 +1,548 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""Script that invokes Bazel to build and deploy software collateral for a simulation. + +MOTIVE +------ +Bazel is used to build device software for OpenTitan. +Many different software binaries may be run on or loaded into an OpenTitan +system in simulation, which Bazel Targets are used to define and build. + +Bazel rules can output multiple files, and the software collateral that +is actually needed by the simulator may be different depending on configuration, +such as if the binary is scrambled/unscrambled, .vmem or .bin format, etc. +Our rules are designed to build many possible pieces of collateral at once, all +with the same common root filename, but with different suffixes and file extensions +that differentiate their formats and uses. +To 'connect' a particular piece of software collateral with the correct +mechanism to load it into the sim, we append some extra metadata to the Bazel +Label. We refer to this extra data as 'flags'. The first flag is always the +integer 'index', which identifies which device memory system the image should be +loaded into (e.g. ROM, OTP, Flash, etc). The flag "signed" is used to set the SW +image extension correctly. The flag "test_in_rom" is used to indicate a test runs +directly out of ROM instead of flash. There are more possible flags documented below. + +A string for each sw image (root filename + index + flags) is also passed through +to the testbench, which re-parses it (in chip_env_cfg.sv) to extract the index and +flags which determines which piece of collateral should be loaded (by filename) +to which simulated memory model. + +ACTIONS +------- +- Loop through the list of 'sw_images' provided (label + flags) +- Build the Bazel Target for each label +- Use Bazel 'cquery' to locate the output files for the target +- Copy the output files to the simulation working directory + +DETAILS +------- +The arg `sw_images` is a space-separated list of images required for a test. +Each item in the list may have additional metadata / flags appended using +the delimiter ':'. + +Expected format: + + :::::... + ↑ ↑ + └──────label──────┘ + +e.g. + + //sw/device/silicon_creator/manuf/base/binaries:ft_personalize_sival:1:silicon_creator:signed + ↑ ↑ ↑ ↑ ↑ + └──────────────────────────────label────────────────────────────────┴─┴─────flag1─────┴flag2┘ + index + +If one delimiter is detected, then the full string is considered to be the +