2 SystemC Library Requirements 4 Connecting SystemC and Simics Models
SystemC* Library  / 

3 Running a SystemC Model in Simics

The simplest case of running a SystemC model in Simics is to run it without any connections to models written in other languages, a so called SystemC-only model. When working with the proof of concept implementation of SystemC provided by Accellera it is common to build an executable by providing an implementation of sc_main that assembles the model hierarchy, starts the simulation, and performs any cleanup actions. Because of the dynamic nature of Simics the model will be a Simics module instead of a standalone application. This allows Simics to instantiate one or more copies of the model at run-time using the normal Simics configuration mechanisms. To achieve this the sc_main function is replaced by a Simics configuration object. The purpose of this object is the same as sc_main: create the model hierarchy, provide a way to start the simulation, and perform any cleanup. The difference is that now the object hierarchy is created when the Simics object is instantiated and the cleanup is done when the Simics object is destroyed. Simulation control is also handled by Simics, using standard Simics commands such as continue and stop.

3.1 Standalone application

Here a standalone application refers to an executable that runs in its own process without Simics. Typically, sc_main is implemented as the entry point to the application.

Running a SystemC model in Simics does not affect the model's behavior and this fact can be leveraged when integrating models with Simics. It is recommended to first verify that the functionality is as expected by running the model as a standalone application before integrating with Simics. The verification is typically done using a SystemC test bench, which may be reused later as a Simics model test.

To get started with a SystemC-only model in Simics it is best to begin with a simple example. Create a new SystemC-only model in your project by issuing the command: ./bin/project-setup --sc-only-device empty-device-sc-only

This command copies a SystemC-only model skeleton from the installation into the module directory of the project with the name empty-device-sc-only.

The empty-device-sc-only module contains a very simple SystemC device, in three source files: sc-device.h, sc-device.cc, and sc-main.cc. Compiled together, these three files contain all the relevant code to run the simulation standalone, i.e., as a separate application outside of Simics. The Makefile for this standalone application is not provided but is easy to write since the only dependency for this module is the SystemC kernel. The entire source code is shown below.

#ifndef EMPTY_DEVICE_SC_ONLY_SC_DEVICE_H
#define EMPTY_DEVICE_SC_ONLY_SC_DEVICE_H

#include <systemc>

SC_MODULE(DeviceModel) {
  public:
    SC_CTOR(DeviceModel) : count_(10) {
        SC_THREAD(run_test);
    }

    void set_count(int count) { count_ = count; }
    int get_count() const { return count_; }

 private:
    void run_test();

    int count_;
};

sc_core::sc_module *setup(int argc, char *argv[]);
void teardown(sc_core::sc_module *top_level);

#endif  // EMPTY_DEVICE_SC_ONLY_SC_DEVICE_H

#include "sc-device.h"

namespace {
const char *const TAG = "intel/empty-device-sc-only/info";
}

void DeviceModel::run_test() {
    while (--count_ >= 0) {
        SC_REPORT_INFO(TAG, "Looping...");
        wait(sc_core::sc_time(500, sc_core::SC_NS));
    }
}

sc_core::sc_module *setup(int argc, char *argv[]) {
    DeviceModel *top = new DeviceModel("top");
    if (argc > 1) {
        int count = atoi(argv[1]);
        top->set_count(count);
    }

    return top;
}

void teardown(sc_core::sc_module *top_level) {
    delete top_level;
}

#include "sc-device.h"

int sc_main(int argc, char *argv[]) {
    sc_core::sc_module *top_level = setup(argc, argv);
    // coverity[fun_call_w_exception]
    sc_core::sc_start();
    teardown(top_level);

    return 0;
}

This device has a counter which counts down every 500 nano seconds. When it counts down, a message as shown below is printed to the standard output.

  Info: intel/empty-device-sc-only/info: Looping...

By default, the above message is printed 10 times since counter is initialized to 10 in the constructor. This value can be modified by passing an integer argument when the application is executed.

Now that we have verified the functionality of this simple SystemC device, let's move on to next section on how to run it in Simics. The same behavior will be observed when it runs in Simics.

3.2 Running in Simics

To create a Simics module instead of a standalone application, the file sc-main.cc is replaced with a similar entry-point that defines a Simics class instead of creating an application.

#include <simics/systemc/sc_factory.h>
#include "sc-device.h"

#define CLASS_NAME "empty_device_sc_only"
#define CLASS_DESC "example SystemC device"
#define CLASS_DOC  "The <class>" CLASS_NAME "</class>" \
    " class runs a SystemC-only device in Simics."

simics::systemc::RegisterModel model(CLASS_NAME, CLASS_DESC, CLASS_DOC,
                                     setup, teardown);

The example above may need some explanation. It registers a new configuration class with Simics. The name of the class is empty_device_sc_only and the documentation and short description are provided by the CLASS_DOC and CLASS_DESC constants. The class is registered by creating an instance of the simics::systemc::RegisterModel in the file scope, that is as a global or static variable, or in a unnamed namespace. When the module is loaded into Simics, all objects defined in file scope will be constructed and the model object will take care of registering the empty_device_sc_only class with Simics. Simics will now know how to instantiate this class, which in turn will be responsible for instantiating the rest of the SystemC subsystem, as defined by the provided setup function. When the configuration object is destroyed, the registered teardown function, if any, will be called with the return value of setup. The return value from setup and the argument to teardown can be a pointer to any type, the only restriction is that they have to match. It is also possible to have a void return value from setup, in which case teardown does not take any arguments.

The device can be built by running make empty-device-sc-only in the top-level project directory. Refer to the Model Builder User's Guide for more details.

No binaries are shipped from the Simics package. Therefore, these binaries must be built before any device modules can be constructed. See 8 for more details.

To test the device in Simics it is necessary to instantiate it. This is easily done from the Simics CLI, by calling the SIM_create_object function via the Python API:

simics> @SIM_create_object('empty_device_sc_only', 'dev')
<the empty_device_sc_only 'dev'>

To see what objects exist in the simulation, the list-objects command can be used:

simics> list-objects -show-port-objects substr = dev
┌──────────────────────────┬────────────────────────────────────────┐
│          Object          │                 Class                  │
├──────────────────────────┼────────────────────────────────────────┤
│dev                       │<empty_device_sc_only>                  │
│dev.cci_global            │<empty_device_sc_only_cci_global>       │
│dev.engine                │<co-execute>                            │
│dev.engine.vtime          │<vtime>                                 │
│dev.engine.vtime.cycles   │<cycle-counter>                         │
│dev.engine.vtime.ps       │<ps-clock>                              │
│dev.sc_event_all_dynamic  │<ScEventObjectAllDynamic>               │
│dev.sc_process_all_dynamic│<sc_method_process_all_dynamic>         │
│dev.top                   │<empty_device_sc_only_sc_module>        │
│dev.top.run_test          │<empty_device_sc_only_sc_thread_process>│
│dev.vtime                 │<vtime>                                 │
│dev.vtime.cycles          │<cycle-counter>                         │
│dev.vtime.ps              │<ps-clock>                              │
└──────────────────────────┴────────────────────────────────────────┘

In this example dev is the Simics configuration object that wraps the SystemC subsystem, dev.top is the SystemC device returned by setup, and dev.top.run_test is the thread process in dev.top that will call SC_REPORT_INFO and print the "Looping..." output shown in the example output below.

The dev.engine, dev.vtime and their sub-objects are Simics helper objects and can be ignored.

Before running the simulation, turn up the log-level to 2 or higher in order to show reports of INFO type and normal verbosity:

simics> dev.log-level 2
simics> continue
[dev info] Looping... @ 0 s of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 500 ns of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 1 us of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 1500 ns of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 2 us of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 2500 ns of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 3 us of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 3500 ns of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 4 us of intel/empty-device-sc-only/info in sc-device.cc:14
[dev info] Looping... @ 4500 ns of intel/empty-device-sc-only/info in sc-device.cc:14
running> stop
simics> 
2 SystemC Library Requirements 4 Connecting SystemC and Simics Models