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 HINTS ${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 invoked and the CMAKE_EXPORT_COMPILE_COMMANDS option has been enabled, a build target that generates a dml_compile_commands.json file in the build directory will be added to the project. This file can be used by the Simics Modeling VS Code extension and its accompanying language server.

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> ...]
        [PYWRAP <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_systemc_module()

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

This works exactly like simics_add_module() and takes the same set of arguments in addition to a couple of extra SystemC-specific arguments. Only the SystemC arguments are listed below.

2.5.1 Usage

    simics_add_systemc_module(<module-name>
    ...
        [GASKET_OBJECTS]
        [CHECKPOINTING]
        [SC_FACTORY]
        [KERNEL <version>]
        [ISCTLM]
        [IOSF]
    )

2.5.2 Arguments

2.5.3 Example usages (from our sample modules)

    simics_add_systemc_module(sample-tlm2-dma-device
        CLASSES sample_tlm2_dma_device
        SOURCES simics-init.cc module_load.py
        INIT_LOCAL
        GASKET_OBJECTS)
    simics_add_systemc_module(sample-tlm2-checkpoint-device
        CLASSES sample_tlm2_checkpoint_device
        SOURCES simics-init.cc module_load.py
        INIT_LOCAL
        CHECKPOINTING)

2.6 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.7 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.7.1 Usage

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

2.7.2 Arguments

2.7.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.7.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.8 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.8.1 Usage

    simics_test_suite_begin()
    simics_test_suite_end()

2.8.2 Arguments

There are currently no arguments.

2.8.3 Example usages

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

2.9 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.9.1 Usage

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

2.9.2 Arguments

2.10 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.11 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.11.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.11.2 Arguments

2.11.3 Example usage

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

2.12 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.12.1 Usage

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

2.12.2 Arguments

2.12.3 Example usage

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

2.13 simics_add_documentation()

Adds a target that builds documentation using the Simics dodoc tool. The target is not added to the ALL target, unlike simics_add_module(), so an explicit call to add_dependencies is needed. The target name will be prefixed with PREFIX to avoid name clashes between the documentation target and module targets.

This function is just a wrapper around the dodoc tool distributed with Simics Base. Most likely larger projects will create their own wrapper, e.g.:

function(add_documentation name)
  cmake_parse_arguments(ARG "" "" "INPUT_DIRS;DEPENDS" ${ARGN})
  simics_add_documentation(${name}
    FLAGS --css simics.css ${ARG_INPUT_DIRS}
    DEPENDS ${ARG_DEPENDS})
  add_dependencies(simics-docs-all doc_${name})
endfunction()

See dodoc --help or the Simics documentation for more details.

If any files should be copied or generated the caller is responsible for writing the rules to do so using CMake’s add_custom_command(). The generated files must then be listed in the DEPENDS argument to make sure they are generated before dodoc runs.

By default dodoc will generate a dep-file containing all files that is part of the toc.json along with the toc.json itself. Thus it is not necessary to add these (existing) files to DEPENDS.

2.13.1 Usage

    simics_add_documentation(<name>
        [PREFIX <prefix>]        
        [OUTPUT <path>]
        [CSS <css> ...]
        [FLAGS <flag> ...]        
        [DEPENDS <path> ...]        
    )

2.13.2 Arguments

2.14 simics_copy_file()

Creates a custom command that copies a file from anywhere to anywhere.

2.14.1 Usage

    simics_copy_file(<from> [TO <path>])

2.14.2 Arguments

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 Read-only (should not be set by user, but can be used/read)

4.3 Internal (should not be used by user)

5 Tips and tricks

5.1 C/C++ modules

5.2 DML modules

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 Initialization order dependency

When a class attempts to reference another class within the same module, such as by initializing a port with init_as_subobj in DML or directly calling SIM_get_class(), careful attention must be paid to the initialization order.

In C/C++ and Python, the order can typically be managed within the init_local function. However, in DML, this is not possible, nor is it feasible when classes originate from different languages (i.e., with distinct entry points). The build system currently defines the initialization order as follows:

  1. Custom C interface wrappers (experimental feature, not used by Simics CMake)
  2. Python interface wrappers
    • Implicitly included through SOURCES as headers files with SIM_INTERFACE calls; or
    • Explicitly included through PYWRAP as headers files with custom types or functions to pythonwrap
  3. DML generated initialization functions, specified in SOURCES as .dml files
  4. Explicit init_local() function, included through SOURCES as source file when INIT_LOCAL is set.
  5. Code run from module_load.py

To some degree, dependencies can be managed by altering the sequence of files specified in SOURCES. For instance, if a.dml depends on a class defined in b.dml, rearranging the order from a.dml b.dml to b.dml a.dml will establish the correct dependency order and ensure proper functionality.

Classes from separate modules are automatically and correctly resolved by the Simics module loading system. Therefore, placing dependent classes in a different module can resolve issues with initialization order. In CMake, this is easily achieved by adding an additional simics_add_module() call within the same or a different CMakeLists.txt.

5.5 THREAD_SAFE

By default all modules should be thread safe in Simics. But if they are not they can be tagged using the SIMICS_MODULE_NOT_THREAD_SAFE directory property:

set_directory_properties(PROPERTIES SIMICS_MODULE_NOT_THREAD_SAFE TRUE)

This currently affects the --thread-safe flag passed to the module_id.py tool used to generate the module_id.c file linked into the Simics module. The setting will be part of the module metadata. When loading a module that is not thread-safe into Simics the disable-multimachine-accelerator command will be automatically invoked to disable multi-threading. The user can still enable multi-threading by explicitly invoking enable-multimachine-accelerator; so this annotation is just a convenience layer.

NOTE: Simics is phasing out the use of THREAD_SAFE, but currently there is no good replacement.

5.6 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.