Simics CMake Reference Manual

The Simics CMake package provides functions and macros for building Simics modules and light-weight add-on packages.

The design goal of the Simics CMake package is to not reinvent functionality or wrap it when the standard CMake API can be used directly.

See Using CMake in Simics for detailed usage, BKMs and current limitations.

1 Basic Usage

To load the Simics CMake package into the current CMake project, use the find_package() command:

    find_package(Simics REQUIRED)

or

    find_package(Simics REQUIRED CONFIG NO_DEFAULT_PATH PATHS ${SIMICS_BASE}/cmake)

The former usage requires that CMAKE_PREFIX_PATH has been set to ${SIMICS_BASE}/cmake and the later usage explicitly provides the path (not trusting any default paths).

The Simics.cmake file contains the bulk of the implementation and provides these utility functions:

2 Functions

2.1 simics_global_cmake_properties()

NOTE: This function is invoked automatically by find_package(Simics); listed for reference only.

Sets up the global properties required by Simics such as:

2.2 simics_project_setup()

NOTE: This function is invoked automatically by find_package(Simics); listed for reference only.

The main setup function that configures the build-tree for building Simics modules with CMake. It detects and sets up the Simics project required by the build. The following modes are supported:

  1. User provides the path to a Simics project via the SIMICS_PROJECT_DIR variable, and the CMake project gets associated with this Simics project. This mode overrides/takes precedence over (2), so it does not matter if user runs from within a Simics project or not.

  2. Simics project exists at the root of the CMake project, pointed to by the CMAKE_CURRENT_SOURCE_DIR variable. This is the traditional Simics setup, and is automatically detected.

  3. Simics.cmake will implicitly create a Simics project in the root of the build tree, pointed to by the CMAKE_CURRENT_BINARY_DIR variable, if a path is not provided (1) and project could not be detected (2). This requires that Simics.cmake is found within a Simics Base package, which is the common case.

For all cases 1-3 the Simics modules are emitted into the ${SIMICS_HOST_LIB_DIR} folder as expected by Simics, e.g. <project>/linux64/lib. This can be overridden by providing the standard CMAKE_LIBRARY_OUTPUT_DIRECTORY cache variable, in which case the modules are emitted to this directory instead.

The simics_project_setup() function also sets up a few targets and variables.

2.3 generate_dml_compile_commands_json()

If put at the end of the top-level CMakeLists.txt (after having added all the Simics modules), and the CMAKE_EXPORT_COMPILE_COMMANDS option has been enabled, it will generate a dml_compile_commands.json file in the build directory. This file can be used by the VSCode DLS (DML Language Server) plugin.

Please note that, for practical reasons, the -- and - prefixes of each DMLC flag have been stripped prior to adding them to the .json file.

2.4 simics_add_module()

Adds a Simics module to the project using the specified source files.

Module sources can be a mix of C, C++, DML and Python files.

Please note that even though it is possible to provide classes, components and interfaces using a single module this should be avoided. The traditional way to model classes, components and interfaces is to wrap them in separate modules; as this makes it easier to locate and re-use the components and interfaces later on. Typically interfaces are used between two classes provided by two different modules; so it makes sense to keep the interface as a separate entity for testing. Similarly components provide a higher level abstraction to creating Simics configurations, and while it might seem useful to provide the component as part of the module that provides the class(es) it's usually better to provide the component as a separate entity.

Providing multiple interfaces or components or classes within the same module is not a problem if it makes sense to do so.

Multiple calls to simics_add_module() can be made from within the same CMakeLists.txt file. Each call defines a new module.

2.4.1 Usage

    simics_add_module(<module-name>
        {CLASSES | COMPONENTS | INTERFACES} <name> ...
        [SOURCES <file> ...]
        [IFACE_FILES <file> ...]
        [SIMICS_API <5|6|latest>]
        [INIT_LOCAL]
    )

2.4.2 Arguments

2.4.3 Example usages (from our sample modules)

    simics_add_module(sample-device-cpp
        CLASSES sample_device_cpp
        SOURCES sample-device.cc
        INIT_LOCAL)
    simics_add_module(sample-device-c
        CLASSES sample-device-c
        SOURCES sample-device.c
        INIT_LOCAL)
    simics_add_module(sample-device-dml
        CLASSES sample_device_dml
        SOURCES sample-device-dml.dml)
    simics_add_module(sample-device-mixed
        CLASSES sample_device_mixed
        SOURCES sample-device-mixed.dml sample-device-mixed.c
        INIT_LOCAL)
    simics_add_module(sample-device-python
        CLASSES sample_device_python
        SOURCES sample_device_python.py)
    simics_add_module(sample-interface
        INTERFACES sample
        IFACE_FILES sample-interface.h)
    simics_add_module(sample-comp
        COMPONENTS sample_comp
        SOURCES sample_comp.py)

2.5 simics_add_sanitizers()

Annotates a module/target to use sanitizers.

Sanitizers can be built into a module by passing compiler and linker flags to the build. The following flags are currently supported:

In addition, the RPATH must also be set to locate the sanitizer libs from within the corresponding toolchain used to build the module:

The simics_add_sanitizers() function adds these compiler and linker flags to the target module based on the values of the following options:

2.6 simics_add_test()

Adds a Simics test to the project using the specified test name or file, so that the test can be run through CTest; see examples below.

The simics_add_test() function wraps the standard CMake add_test() function. User can add tests by name (i.e. s-<name>.py) or file (i.e. s-name.py) and the function then automatically adds the current module's folder name as namespace so the tests can easily be filtered by CTest.

The tests are run through Simics in batch-mode.

2.6.1 Usage

    simics_add_test(<test-name | test-file>
        [CWD <path>]
        [SYS_PATH <path> ...]
        [ENV <VAR=value> ...]
        [NAMESPACE <namespace>]
        [NAME <name>]        
        [PYTEST]        
    )

2.6.2 Arguments

2.6.3 Example usages

    simics_add_test(sanity)
    function(cmn_add_tests class module)
      set(cwd ${CMAKE_CURRENT_FUNCTION_LIST_DIR})
      file(GLOB tests CONFIGURE_DEPENDS "${cwd}/s-*.py")
      foreach(test ${tests})
        set(env "CMN_CLASS=${class};CMN_MODULE=${module}")
        simics_add_test(${test} CWD ${cwd} ENV ${env})
      endforeach()
    endfunction()
    set(sample ${SIMICS_REPO_ROOT}/src/devices/sample-device-python)
    simics_add_test(sys-path SYS_PATH "${sample};..")
    simics_add_test(py-test.py PYTEST NAME py-test-renamed.py)

2.6.4 Example invocations of CTest

$ ctest --preset release -j20 -R sample-device
Test project /disk1/ah/simics-pr/bt/release
    Start 127: sample-device-c++::reg
    Start 132: sample-device-python::sample-python
    Start 128: sample-device-c++::sanity
    Start 131: sample-device-mixed::sample-mixed
    Start 130: sample-device-dml::sample-device-dml
    Start 126: sample-device-c++::multi
    Start 129: sample-device-c::sample-c
1/7 Test #129: sample-device-c::sample-c ..............   Passed    0.47 sec
2/7 Test #130: sample-device-dml::sample-device-dml ...   Passed    0.48 sec
3/7 Test #131: sample-device-mixed::sample-mixed ......   Passed    0.48 sec
4/7 Test #126: sample-device-c++::multi ...............   Passed    0.48 sec
5/7 Test #128: sample-device-c++::sanity ..............   Passed    0.49 sec
6/7 Test #132: sample-device-python::sample-python ....   Passed    0.50 sec
7/7 Test #127: sample-device-c++::reg .................   Passed    0.54 sec

100% tests passed, 0 tests failed out of 7

Total Test time (real) =   0.56 sec

2.7 simics_test_suite_begin() and simics_test_suite_end()

The simics_test_suite_begin() and simics_test_suite_end() macros marks the beginning and end of a Simics test suite. All tests added by calls to simics_add_test() between these two calls will be added to the same suite.

A suite will automatically create a scratch clean-up test fixture and add this fixture as a requirement to all the tests. This will guarantee that clean-up will run before any tests will run, even when the user runs just a subset of the tests.

This is useful, and a requirement, for tests creating files such as checkpoints within the scratch folder.

2.7.1 Usage

    simics_test_suite_begin()
    simics_test_suite_end()

2.7.2 Arguments

There are currently no arguments.

2.7.3 Example usages

    simics_test_suite_begin()
        simics_add_test(checkpoint-create)
        simics_add_test(checkpoint-load)
    simics_test_suite_end()

2.8 simics_copy_python_files()

Copy files from another module or CMake target into the current module's simmod directory. These files should not be added to the module's SOURCES parameter.

2.8.1 Usage

    simics_copy_python_files(<module>
        FROM <other-target>
        FILES <file> ...
    )

2.8.2 Arguments

2.9 simics_find_and_add_modules()

Locates modules in the current project, from Simics Base and any associated add-on packages and then adds them to the current project by invoking add_subdirectory(... EXCLUDE_FROM_ALL). This allows local modules to add external modules as taget link dependencies. Because these external modules are excluded from the 'all' build target they are not built by default; but can be built if explicitly provided as targets.

This means that unlike the old GNU Make driven Simics project, the user can build modules in a project directly without first copying them into the modules/ sub-directory.

By default, simics_find_and_add_modules() will search both the local project, in Simics Base and all add-on packages; but the user can opt-out of this and only search in the project's modules/* by passing IN_PROJECT as argument. If passing IN_PACKAGES the function will only search in the packages and not in the project.

2.10 simics_package()

Creates a Simics package info file in the 'packageinfo' sub-directory to make the package selectable from another project using the Simics addon manager.

Please note that package number and version must be handled by the caller, the function is just blindly adding the provided info as metadata to the package info file.

2.10.1 Usage

    simics_package(
        NAME <simics-package-name>
        NUMBER <simics-package-number>
        VERSION <simics-package-version>
        [TYPE {base|addon}]
        [BUILD_ID <build-id-number>]
        [BUILD_ID_NAMESPACE <build-id-namespace-name>]
    )

2.10.2 Arguments

2.10.3 Example usage

    simics_package(NAME "My Simics Package" NUMBER 4200 VERSION 6.0.0)

2.11 simics_add_dead_methods_check()

Annotates a module/target to run a post build command that checks if the module contains any dead DML methods.

For the command run, the CMake cache variable USE_DML_DEAD_METHODS_CHECK must be set during configuration of the project.

The command by default only checks for dead methods in DML files that is part of the current module and resides in the same folder where the module is defined.

2.11.1 Usage

    simics_add_dead_methods_check(<target>
        [EXTRA_SOURCES <path(s)>]
    )

2.11.2 Arguments

2.11.3 Example usage

simics_add_dead_methods_check(sample-pcie-device
    EXTRA_SOURCES $<TARGET_PROPERTY:common-code-target,SOURCE_DIR>
)

3 Targets

The Simics CMake package provides the following IMPORTED INTERFACE targets:

and these utility targets:

and these internal targets (please ignore, listed for completeness only):

The Simics::Simics is the basic Simics library and all CMake Simics modules depend on this target. Simics::DML and Simics::Python are automatically added as dependencies for .dml and .py files. User must explicitly add Simics::C++ as target dependency to use the Simics C++ API. The Simics::C++v1 is provided as legacy support for older models. Non-modules (e.g. STATIC libraries) that invokes the Simics API must add a target link dependency on Simics::includes.

If B is a Simics module and module A depends on headers from B, then A should add a target link dependency on B::includes (or the aliases ::headers or ::imports). A cannot depend directly on B because B is of library-type MODULE which prohibits it from being linked directly to other libraries.

The simics-modules target builds all registered modules, while list-simics-modules simply prints all modules to stdout.

The simics-asan is just a convenience layer for launching Simics with LD_PRELOAD and ASAN_OPTIONS set. See 'Sanitization with ASAN' in 'Using CMake in Simics' for details on using ASAN on Simics modules.

4 Variables

4.1 Cached (can be set by user after configuration)

4.2 Internal (should not be set by user)

NOTE: This list may or may not be up-to-date with Simics.cmake

5 Tips and tricks

5.1 C/C++ modules

Unlike DML modules, the C and C++ modules must define the init_local() entrypoint for the Simics module:

    extern "C" void init_local() {
        ...
    }

See C++ Device API v2 for details on using the C++ API.

Add a target dependency on Simics::C++ to build and link with Simics C++ API library. Add target dependency on Simics::C++v1 for legacy v1 support.

5.2 DML modules

Use SIMICS_DMLC_FLAGS property to set extra flags for DML compiler. The property can be set globally, per directory, per module and per DML source file.

Since there is no generator expression for source file properties (as of CMake 3.28); the source file property is extracted during configuration phase and thus must be set before invoking simics_add_module()

NOTE: Multiple flags must be separated by semi-colon

5.3 Simics API

Use the SIMICS_API directory property to change the default API used by all targets in the current directory and all sub-directories. The module property can be used to override the default directory property.

    set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY SIMICS_API 5)

The API version for a module is resolved in the following order:

Please note that only simics_add_module supports setting SIMICS_API to latest. If set to latest the current Simics major version of the associated Simics Base is used.

5.4 Simics tests

To alter how tests interact with Simics you can use the standard set_tests_properties() function with one of the standard test properties. See cmake.org:properties on tests for more details.

For example; this is how to toggle that a test is always failing:

set_tests_properties(bp-manager::bm-commands PROPERTIES WILL_FAIL TRUE)

And this is how to introduce test dependencies:

set_tests_properties(ich10-lan-v2::lan-checkpoint2
  PROPERTIES DEPENDS ich10-lan-v2::lan-checkpoint)

NOTE: If you update the environment the ENVIRONMENT_MODIFICATION property must be used and not the ENVIRONMENT property. The later will overwrite the variables set by the simics_add_test() function.

Tagging tests with labels is also done through properties, for example:

set_tests_properties(ich10-lan-v2::lan-checkpoint2
  PROPERTIES LABELS "ich10;lan")