4 Connecting SystemC and Simics Models 6 Execution of SystemC Models in the Simics Simulator
SystemC* Library  / 

5 Overview of SystemC Features

This section outlines some of the Simics features that are available when running SystemC models in Simics.

In Simics the SystemC scheduler runs under the control of the Simics framework, meaning that simulation can be started and stopped using the Simics GUI or normal Simics commands such as continue or stop.

All SystemC objects, derived from sc_object class, are visible in Simics as configuration objects. This means that it is possible to interact with the objects from the Simics CLI.

If some SystemC object is not visible in Simics, check that all the following conditions apply:

  1. The name complies to the recommendation of IEEE 1666-2011 5.17 which is also a requirement for naming Simics objects. Non-compliant names are transformed and the Simics object created under Adapter.renamed using the corresponding hierarchy. Invalid characters are escaped with _0x and replaced with the ASCII value in hex. E.g: a.b.test[A] becomes renamed.a.b.test_0x5B_A_0x5D_

  2. The object is not dynamic, as stated in section 5.3.2.1;

Use help SystemC to get a list of all supported SystemC CLI commands. And use help on selected command to get detail information.

Typical operations available on SystemC objects are enabling tracing or breakpoints on sockets and signals. In addition, normal Simics commands work as expected. For example, to find all SystemC ports the command list-objects can be used with sc_port as the type argument.

simics> list-objects -show-port-objects iface=sc_port
┌─────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────┐
│                       Object                        │                                       Class                                        │
├─────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────┤
│lt.dut.m_at_and_lt_target_1.memory_socket_1_port_0   │<lt_example_sc_port>                                                                │
│lt.dut.m_bus.simple_initiator_socket_tagged_0        │<lt_example_tlm_initiator_socket_dut_m_bus_simple_initiator_socket_tagged_0>        │
│lt.dut.m_bus.simple_initiator_socket_tagged_1        │<lt_example_tlm_initiator_socket_dut_m_bus_simple_initiator_socket_tagged_1>        │
│lt.dut.m_bus.simple_target_socket_tagged_0_port_0    │<lt_example_sc_port>                                                                │
│lt.dut.m_bus.simple_target_socket_tagged_1_port_0    │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_1.m_initiator.initiator_socket    │<lt_example_tlm_initiator_socket_dut_m_initiator_1_m_initiator_initiator_socket>    │
│lt.dut.m_initiator_1.m_initiator.initiator_socket_opt│<lt_example_tlm_initiator_socket_dut_m_initiator_1_m_initiator_initiator_socket_opt>│
│lt.dut.m_initiator_1.m_initiator.port_0              │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_1.m_initiator.port_1              │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_1.m_traffic_gen.port_0            │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_1.m_traffic_gen.port_1            │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_1.top_initiator_socket            │<lt_example_tlm_initiator_socket_dut_m_initiator_1_top_initiator_socket>            │
│lt.dut.m_initiator_2.m_initiator.initiator_socket    │<lt_example_tlm_initiator_socket_dut_m_initiator_2_m_initiator_initiator_socket>    │
│lt.dut.m_initiator_2.m_initiator.initiator_socket_opt│<lt_example_tlm_initiator_socket_dut_m_initiator_2_m_initiator_initiator_socket_opt>│
│lt.dut.m_initiator_2.m_initiator.port_0              │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_2.m_initiator.port_1              │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_2.m_traffic_gen.port_0            │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_2.m_traffic_gen.port_1            │<lt_example_sc_port>                                                                │
│lt.dut.m_initiator_2.top_initiator_socket            │<lt_example_tlm_initiator_socket_dut_m_initiator_2_top_initiator_socket>            │
│lt.dut.m_lt_target_2.memory_socket_2_opt_port_0      │<lt_example_sc_port>                                                                │
│lt.dut.m_lt_target_2.memory_socket_2_port_0          │<lt_example_sc_port>                                                                │
└─────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────┘

SystemC signals and TLM2 sockets can be traced or have breakpoints attached to them. Please refer to section 5.3 for more information about trace and break on SystemC objects.

For example, a transaction break-point can be set on a target socket as shown below.

simics> lt.dut.m_lt_target_2.memory_socket_2.break-sc
simics> c
[lt.dut.m_lt_target_2.memory_socket_2 break-b-in] write sz:4 addr:0x0 data:0xefffffff
[lt.dut.m_lt_target_2.memory_socket_2 break-b-out] write sz:4 addr:0x0 data:0xefffffff status:TLM_OK_RESPONSE
[lt.dut.m_lt_target_2.memory_socket_2 break-b-in] write sz:4 addr:0x0 data:0xefffffff
[lt.dut.m_lt_target_2.memory_socket_2 break-b-out] write sz:4 addr:0x0 data:0xefffffff status:TLM_OK_RESPONSE
simics> 

In order to reduce the message length, Simics shortens common SystemC terminology. For instance, in the example above "b_in" refers to an inbound blocking transaction. The table below summarizes all the abbreviations.

SimicsSystemC
bblocking
nbnon-blocking
ininbound
outoutbound
fwforward
bwbackwards

When a new socket type/protocol is used, it is necessary to register the type/protocol with the awareness framework. If it is not registered the commands trace-sc and break-sc will not be available for the new socket. As an example, for a socket that uses width 64 and protocol "MyProtocol", it is necessary to register the socket calling the function registerSocketType<64, MyProtocol>().

Tracing and breaking has certain limitations. Please refer to chapter 7.

5.1 Logging

SystemC Library implements report handler which forwards SystemC reports to Simics according to sc_report actions. Since SystemC does not have the concept of objects attached to log messages, all messages will be printed on the top-level Adapter object in Simics. It is possible to control SystemC logging level using log-level command on the Adapter object. Also, all the normal Simics logging commands, such as log-setup, log-type, work.

Simics performs a mapping from SystemC log concepts such as verbosity and severity into Simics concepts. The sc_report actions work as defined by IEEE-1666 standard. The most important mapping is the one from the SystemC verbosity to the Simics log-level, as shown in the table below:

Simics log-levelSystemC verbosity
1verbosity < SC_MEDIUM
2SC_MEDIUM ≤ verbosity < SC_HIGH
3SC_HIGH ≤ verbosity < SC_DEBUG
4SC_DEBUG ≤ verbosity

Log messages map to the info category in Simics, unless the severity is greater than SC_WARNING, in which case the error category is used. It is also possible to emit unimplemented and spec-violation log categories by ending the message type with "unimplemented" or "unimpl", or "spec-violation" or "spec-viol", respectively. However, a severity greater than SC_WARNING will always generate an error log.

simics> lt.log-level level = 3
[lt] Changing log level: 1 -> 3
simics> bp.log.break object = lt
simics> c
[lt info] 0 s - traffic_generator_thread
      Initiator: 101 Starting Traffic @ 0 s of traffic_generator.cpp in traffic_generator.cpp:114
[lt info] 0 s - traffic_generator_thread
      Initiator: 102 Starting Traffic @ 0 s of traffic_generator.cpp in traffic_generator.cpp:114
[lt info] 0 s - initiator_thread
      Initiator: 101 b_transport(GP, 0 s) @ 0 s of lt_initiator.cpp in lt_initiator.cpp:124
[lt info] 0 s - print
      ID: 201 COMMAND: WRITE Length: 04
      Addr: 0x0000000000000000 Data: 0x00000000 @ 0 s of memory.cpp in report.cpp:133
[lt info] 0 s - b_transport
      Target: 201 returned delay of 0 s + 20 ns + 60 ns = 80 ns @ 0 s of at_target_1_phase.cpp in at_target_1_phase.cpp:111
[lt info] 0 s - initiator_thread
      Initiator: 101 b_transport returned delay = 80 ns @ 0 s of lt_initiator.cpp in lt_initiator.cpp:145
[lt info] 0 s - initiator_thread
      Initiator: 102 b_transport(GP, 0 s) @ 0 s of lt_initiator.cpp in lt_initiator.cpp:124
[lt info] 0 s - print
      ID: 201 COMMAND: WRITE Length: 04
      Addr: 0x0000000000000000 Data: 0x00000000 @ 0 s of memory.cpp in report.cpp:133
[lt info] 0 s - b_transport
      Target: 201 returned delay of 0 s + 20 ns + 60 ns = 80 ns @ 0 s of at_target_1_phase.cpp in at_target_1_phase.cpp:111
[lt info] 0 s - initiator_thread
      Initiator: 102 b_transport returned delay = 80 ns @ 0 s of lt_initiator.cpp in lt_initiator.cpp:145
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
[lt] Breakpoint 1: lt log message of type <all>
simics> 

SystemC Library allows breaking simulation on SystemC log messages, which is enabled by bp.log.break command. It should be noted that Simics does not break immediately on the first log message, but rather after several log messages have been printed. This is because a "break" in Simics corresponds to stopping virtual time. All the log messages above were printed at the same virtual time, and thus there is no way to stop until all messages have been printed and virtual time is ready to move on. To understand what is going on between the various log messages, source level debugging is required.

5.1.1 Redirecting Custom API Logs

Logs using sc_report are automatically integrated with the Simics logging system. However, since sc_report has some issues of its own it is common that SystemC developers have their own logging API or resort to using plain std::cout. While the use of standard streams for logging is highly discouraged this sections explains how to redirect these logs to Simics.

If a custom log API is being used, it is often possible to enable it to support multiple front ends. One simple approach is to specialize it to work in Simics using the standard set of SIM_log* functions, as described in the API Reference Manual. If the log API works with streams Simics provides a convenience class called LogStream that can be used. The convenience class has the following signature:

 template<log_type_t Type = Sim_Log_Info,
         unsigned Level = 1,
         int Groups = 0>
class LogStream : public std::ostream {
  public:
    explicit LogStream(ConfObjectRef log_obj)

 ... 

It is possible to use multiple LogStream objects to map different types of output to different Simics log-types, log-levels, and log-groups. The streams can be used wherever the logging API outputs to a std::ostream object and the log will automatically be turned into a Simics log message with the appropriate type, level, and group.

The LogStream can also be used to redirect standard streams, for example std::cout. This is done by replacing the stream's streambuf with the streambuf of the Simics LogStream as is shown in the example below.

#include <simics/systemc/awareness/log.h>

class CoutRedirect : public simics::ConfObject {
  public:
    explicit CoutRedirect(simics::ConfObjectRef o)
        : simics::ConfObject(o),
          simLog_(o) {
        std::cout.rdbuf(simLog_.rdbuf());
    }

    static void init_class(simics::ConfClass *cls);

  private:
    void print_a_log() const {
        std::cout << "Print a simple message to std::cout" << std::flush;
    }

    simics::LogStream<Sim_Log_Info, 2> simLog_;

};

5.2 SystemC Signal Read and Write

SystemC Library has support for read and write of SystemC signals sc_signal, sc_in, sc_out, and sc_inout. A signal's value can be read by invoking the Simics interface sc_signal_read on the signal object.

There is a second Simics interface, sc_signal_write, which is used to write a value to the signal object. Based on the type of signal, either the sc_signal_read, sc_signal_write, or both interfaces are implemented by the object.

When writing a value to the signal object, the value will be updated during the next SystemC cycle. From the command line, simply invoke c 1 will update the signal object's value.

SystemC signals use a template parameter that specifies the underlying value-type of the signal. This allows to use arbitrary classes as value-types. The current implementation directly supports the following value-types:

To support SystemC signals with a value-type not listed above, simics::systemc::ScSignalAccessTemplate needs to be implemented.

To activate support for the additional type, the template class needs to be instantiated. This is required to register the support for the new value-type in the infrastructure. One possibility is to declare it as a member variable of the Adapter.

Below is an example that shows the required steps to support signals with a value-type of sc_dt::sc_bigint<1024>.

 class BigInt1024Access
    : public simics::systemc::ScSignalAccessTemplate<sc_dt::sc_bigint<1024> > {
  public:
    bool attrToValueT(const attr_value_t *attr,
                      sc_dt::sc_bigint<1024> *value) const {
        const char *str = NULL;
        if (!attrToValue(attr, &str))
            return false;

        *value = str;
        return true;
    }

    attr_value_t valueToAttrT(const sc_dt::sc_bigint<1024> &value) const {
        std::string str = value.to_string();
        return valueToAttr(str.c_str());
    }
};


class Adapter : public scl::Adapter
[...]
    BigInt1024Access big_int_1024_access_;
[...]

The function attrToValueT implements the transformation from a Simics attr_value_t to the signal's value-type. If the transformation can not be performed, false must be returned, otherwise true.

The template class provides attrToValue functions to transform from attr_value_t to:

The function valueToAttrT is used to transform from the SystemC signal value-type to a attr_value_t. The template class provides transformation for the the same types as listed above.

5.3 SystemC Library Tools

SystemC Library comes with a variety of tools that use the Simics instrumentation framework and interact with the SystemC objects. For generic information on how Simics instrumentation framework works please refer to the chapter in Analyzer User's Guide

5.3.1 Supported SystemC Objects

The following table shows the different kinds of SystemC objects and when they interact with the SystemC Library tools:

SystemC objectEvent
sc_eventan event is notified
SC_METHODa method process is triggered
SC_THREADa thread process is triggered or resumed
tlm_initiator_socketa TLM initiator socket method is called
tlm_target_socketa TLM target socket method is called
sc_signala signal's value is changed
sc_in/sc_out/sc_inouta port's value is changed

5.3.2 Using the Tools

There are two different ways to use the tools and their provided functionality. The user could use the Simics commands registered on the SystemC objects. These commands are tool-specific both in terms of functionality and usage. They automatically create an instrumentation tool for internal use by the commands. The user should avoid using this tool directly.

When using trace and break tools, existing DMI tables are automatically invalidated and the DMI hint is suppressed. When all tools have been removed, DMI hint is no longer suppressed - allowing the initiator to build up a new DMI table if supported.

In the following example, by invoking the command trace-sc on the SystemC target socket, an internal instrumentation tool is created and connected to the target socket. It traces the invocation of any socket method.

simics> simple.simple_device.target_socket.trace-sc
Created simple.internal.sc_trace_tool (connected to 1 provider)

For advanced instrumentation tasks, the user should use the Simics instrumentation framework directly. This makes it possible to use more tool features. It is, for example, possible to create a tool that only traces a certain event or method invocation. Other tools with different filtering options can of course be instantiated and operated in parallel.

The commands bound to the SystemC objects are covered in the tool specific sections below. The remainder of this section shows how the tools provided with SystemC Library can be used.

In the following example, a new trace tool is created that traces all supported SystemC objects. To avoid mixing different C++ ABIs, the standard SystemC tools are built together with the adapter as part of building the corresponding Simics module. The default name of the tool if not provided is prefixed with the module's name. For readability it is recommended to create the tool as a sub-object of the adapter instead, as shown by the example below, whenever that makes sense.

When using trace and break tools, existing DMI tables are automatically invalidated and the DMI hint is suppressed. When all tools have been removed, DMI hint is no longer suppressed - allowing the initiator to build up a new DMI table if supported.

simics> simple.new-sc-trace-tool -connect-all name=simple.trace_tool
[simple.gasket_simple_device_target_socket.initiator_socket trace-invalidate-dmi-in] start_addr:0x0 end_addr:0xffffffffffffffff
Created simple.trace_tool (connected to 7 providers)

The newly created tool can now be disabled, enabled and, if it is of no use anymore, removed. Additional SystemC objects can also be connected and disconnected. All this information is available by invoking the command help on the tool.

simics> help simple.trace_tool

To trace TLM sockets only, the user can instantiate a filter that only matches sockets and add it to the tool. The filter can be removed later to trace all supported objects again. There are four kinds of filters: signal, event, process and socket. All this information is available by invoking help on the new-systemc-filter command.

simics> new-systemc-filter socket
Created SystemC filter sc_filter0
simics> simple.trace_tool.add-filter sc_filter0
simics> simple.trace_tool.remove-filter sc_filter0

When connecting the tool to objects of socket kind the functions argument can be used to select which TLM2 functions to act on. The functions argument correspond to the methods in the tlm_bw_transport_if and tlm_fw_transport_if interfaces. For each interface method there exists a pair of pre and post functions. The pre function is used to instrument the transaction before it is sent through the socket. The post function is used to instrument the transaction after the invocation of the interface method has been performed.

The functions argument is optional. If omitted, all interface methods are handled by the tool. The argument is ignored and has no effect for connections to other kind of objects.

A summary of all tools and their supported functions can be shown by running:

simics> help topic = Instrumentation

5.3.2.1 Dynamic SystemC Objects

SystemC allows to create and delete processes and events dynamically during simulation. Due to their dynamic nature, these objects do not have dedicated Simics configuration objects. Instead, they are grouped together and have one shared object for each type that can be used by the tools.

<adapter>.sc_event_all_dynamic can be used to connect tools to all dynamic sc_event objects.

<adapter>.sc_process_all_dynamic can be used to connect tools to all dynamic SC_METHOD or SC_THREAD objects.

The commands trace-sc, untrace-sc, break-sc, and unbreak-sc can be used on these two Simics configuration objects.

5.3.3 sc_trace_tool

The sc_trace_tool can be used for tracing any event listed in section 5.3.1.

To enable or disable tracing of these events, trace-sc or untrace-sc needs to be invoked on the corresponding SystemC object. See help <sc_provider_controller>.trace-sc and help <sc_provider_controller>.untrace-sc for more information about the commands. Section 5 contains an example of how to enable tracing on a TLM2 socket.

There is also a set of commands that makes it possible to trace and untrace on all objects of a certain kind. These commands are located in the corresponding adapter. The following commands trace-sc-event-all, trace-sc-signal-all, trace-sc-process-all, and trace-sc-socket-all are available for enabling instrumentation and the following commands untrace-sc-event-all, untrace-sc-signal-all, untrace-sc-process-all, and untrace-sc-socket-all are available for disabling instrumentation.

5.3.4 sc_break_tool

The sc_break_tool is a tool that can be used to stop the simulation on any event listed in section 5.3.1.

To enable or disable break on these events, break-sc or unbreak-sc has to be invoked on the corresponding SystemC object. See help <sc_provider_controller>.break-sc and help <sc_provider_controller>.unbreak-sc for more information about the commands. Section 5 contains an example of how to enable tracing on a TLM2 socket; the break tools works just like trace.

There is also a set of commands that makes it possible to break and unbreak on all objects of a certain kind. These commands are located in the corresponding adapter. The following commands break-sc-event-all, break-sc-signal-all, break-sc-process-all, and break-sc-socket-all are available for enabling instrumentation and the following commands unbreak-sc-event-all, unbreak-sc-signal-all, unbreak-sc-process-all, and unbreak-sc-socket-all are available for disabling instrumentation.

5.3.5 sc_transaction_tracker_tool

To simplify debugging of complex SystemC models where multiple TLM transactions are sent between multiple sockets, Simics has a capability to track the path a transaction went through as well as to save the history of the transaction changes which happened when the transaction travelled along the path. This capability is provided by the transaction tracker tool which is based on Simics instrumentation framework.

To track a transaction the tool connects to TLM sockets of the SystemC model and observes all the transactions which are sent between the sockets. The tool adds special TLM extension to each transaction passed through a socket, if the transaction does not have the extension added earlier, and uses the extension to save the transaction history. A new entry, consisting of current transaction attributes and hierarchical name of the socket, is appended to the history when the transaction is passed through the socket. Currently, the only transaction attribute which is saved in the entry is transaction address.

To create and connect the tool to all sockets of the SystemC model one should use adapter's track-transactions-all command. Corresponding untrack-transactions-all command disconnects the tool from all the sockets. One may use track-transactions command of a selected socket to connect the socket to the tool, and untrack-transactions command to disconnect it. Also it is possible to use instrumentation tool commands, such as add-instrumentation or remove-instrumentation, to control connection of the tool to all or selected sockets. Please refer to corresponding help commands or the Instrumentation chapter in the Model Builder User's Guide.

To view the transaction history user should enable GDB pretty printing for TLM transactions. The pretty printer output can be seen in GDB console.

5.3.6 sc_protocol_checker_tool

The sc_protocol_checker_tool is a tool for validation of TLM2 transactions. It checks the transactions sent through sockets and detect those which are not in compliance with the "OSCI TLM-2.0 USER MANUAL".

The checker is using the Doulos TLM-2.0 Base Protocol Checker of an older version. The checker will be upgraded to a more recent version checking against the IEEE Std 1666-2011 specification.

The sc_protocol_checker_tool supports tlm_initiator_socket and tlm_target_socket as stated in section 5.3.1. This tool does not have commands registered on the SystemC objects.

The protocol checker keeps track of the transaction state before the transaction is sent through a socket and after it returns. It checks all TLM socket methods for possible TLM2 transaction specification violations.

As an example, <adapter>.new-sc-protocol-checker-tool -connect-all can be used to check all sockets in a simulation.

5.3.7 sc_vcd_trace_tool

The sc_vcd_trace_tool generates files based on the VCD format.

As an example, the command <adapter>.new-sc-vcd-trace-tool file = myfile.vcd -connect-all would generate a file in VCD format containing changes for any SystemC object listed in section 5.3.1 with the exception of type SC_THREAD and SC_METHOD.

During generation, two files are created. One contains the header section with variable definitions and the second file contains the value change section. When Simics ends or the tool is deleted, these two files are merged together into a single file. If this merge can not be performed automatically, it should be sufficient to append the file with the value change section on to the header file. For instance: cat myfile.vcd.tail >> myfile.vcd

5.4 SystemC Profiling

To aid users in developing well-performing models, Simics offers two ways of profiling SystemC processes. These features encompass a process profiler, that helps users identify performance-heavy processes, and memory profiling, that compiles memory statistics of memory-intensive processes. Details on how to enable and use these features are presented below.

5.4.1 Process Profiler

The process profiler feature measures wall clock time execution of method- and thread-processes. The feature is disabled by default, but can be enabled on adapters by using the enable-process-profiler command. The feature can be disabled again by using the command disable-process-profiler.

The profiling results can be obtained by using the adapter command process-profiler-results. This command will show the number of processes of each type and the number of calls and total time spent executing them.

If using the status command on a process, more detailed information will be shown. This information will consist of minimum time, maximum time, total time executing and number of calls to the process. For thread processes the number of calls will represent the number of times we yielded to that process.

The clear-process-profiler-results command clears accumulated profiling results. All process profiler data including the aggregated execution time is cleared.

5.4.2 Memory Profiling

Memory profiling is enabled and disabled for each adapter by using the enable-memory-profiler and disable-memory-profiler commands. Memory profiling is disabled by default.

When memory profiling is enabled, Simics will record allocations and deallocations made in underlying processes. Please note that memory profiling has to be enabled for each adapter you wish to profile.

Users may inspect the current memory usage of a process by using the status command on the corresponding object in the awareness object hierarchy. Additionally, the collective memory usage of processes in a module may be inspected by using the status command on that module.

Memory profiling has certain limitations listed in chapter 7.

5.5 Trigger SystemC Processes

SystemC Library supports unscheduled running of SystemC thread and SystemC method process objects. Two different options are available to run the processes. The first way is to locate the event object that the processes are sensitive to and invoke notify on the event object. This will cause all processes that are sensitive to the event object to be run during the next SystemC cycle. The second option is to locate the SystemC thread or process and invoke run on the object directly. The process will be run during the next SystemC cycle. This implies that SIM_continue() must be invoked before the process is run. From the command line, this corresponds to invoking c 1.

5.6 TLM Injection

Sockets, registered with the awareness framework, provide the Simics sc_tlm_fw_transport and sc_tlm_bw_transport interfaces. The interfaces correspond to the SystemC TLM2 transport interfaces and support injection. The following rules specify the mapping between the Simics interfaces and the SystemC transport interfaces.

For example, injecting a write transaction into a socket could be done as shown below.

simics> dut.top.simple_initiator_socket_0.trace-sc
Created dut.internal.sc_trace_tool (connected to 1 provider)
simics> @socket = dut.top.simple_initiator_socket_0.iface.sc_tlm_fw_transport
simics> @socket.b_transport({'gp.command' : 1, 'gp.data_ptr' : (0, 1, 2, 3)}, 0)
None
simics> c 1
[dut.top.simple_initiator_socket_0 trace-b-out] write sz:4 addr:0x0 data:0x03020100
[dut.top.simple_initiator_socket_0 trace-b-in] write sz:4 addr:0x0 data:0x03020100 status:TLM_OK_RESPONSE
simics> 

The transaction is injected into the socket by invoking b_transport of the sc_tlm_fw_transport interface. To avoid blocking Simics when the interface is called, the transaction is queued up and sent when simulation starts. This is only required for b_transport. All other functions of sc_tlm_fw_transport and sc_tlm_bw_transport execute their side-effects directly. The fields of the tlm_generic_payload are set by building the key prefixed with gp., followed by the name of the setter function. The key gp.command addresses the generic payload and invokes set_command on the generic payload. The functions expects a tlm_command enum type when invoked in SystemC. For argument passing, enums are passed as signed integer types. The value 0 reflects a read transaction and 1 corresponds to a write transaction. This is according to the tlm_command enum definition in the SystemC kernel.

To simplify matters, gp.data_ptr invokes set_data_ptr, set_data_length and set_streaming_width. The following guidelines should be followed when setting the value of the key-value pair in dictionaries.

Injecting a read transaction into a socket could be done as shown below.

simics> @socket.b_transport({'gp.command' : 0, 'gp.data_ptr' : (0,) * 4}, 0)
None
simics> c 1
[dut.top.simple_initiator_socket_0 trace-b-out] read sz:4 addr:0x0 data:0x00000000
[dut.top.simple_initiator_socket_0 trace-b-in] read sz:4 addr:0x0 data:0x03020100 status:TLM_OK_RESPONSE
simics> 

The length of the data to be read is specified by the Python tuple. Because the transaction is sent into the socket at a later point in time, the Simics instrumentation framework is used to display the actual data returned from the b_transport call.

To add an extension to the transaction, additional key-value pairs need to be set in the dictionary associated with the transaction argument. The table below lists the mapping between each namespace and the corresponding SystemC Library extension.

Namespace keyExtension
ethernet_commonEthernetCommonExtension
i2c_master_v2I2cMasterV2Extension
i2c_slave_v2I2cSlaveV2Extension
map_infoMapInfoExtension
pci_busPciBusExtension
pci_devicePciDeviceExtension
pci_expressPciExpressExtension
pci_upstream_operationPciUpstreamOperationExtension
serial_deviceSerialDeviceExtension

The key-value pairs for the extensions are specific. For details about the valid pairs, please refer to the implementation of the extension injector located in simics/systemc/iface/. One example that demonstrates the inject of a transaction with an EthernetCommonExtension set is shown below.

simics> @data = tuple(bytearray(b'ABCDEF'))
simics> @fraglist = [data[:4], data[4:]]
simics> @frame = {'len' : len(data), 'fraglist' : fraglist}
simics> @socket.b_transport({'ethernet_common.frame' : {'frame' : frame, 'crc_ok' : 1}}, 0)
None
simics> c 1
[dut.top.simple_initiator_socket_0 trace-b-out] ignore sz:0 addr:0x0
[dut.top.simple_initiator_socket_0 trace-b-in] ignore sz:0 addr:0x0 status:TLM_OK_RESPONSE

5.6.1 Writing an Injector for a Custom Extension

To support inject of extensions not provided by SystemC Library, simics::systemc::injection::InjectBase needs to be implemented.

To activate support for the additional injector, the template class needs to be instantiated. This is required to register the support for the new injector in the infrastructure. One possibility is to declare it as a member variable of the Adapter. Below is an example that shows the required steps to support a new extension.

 class CustomExtension : public tlm::tlm_extension<CustomExtension> {
  public:
    CustomExtension() : member_a_(0), member_b_(0),
                        member_d_(NULL), member_d_len_(0),
                        member_x_(0), member_y_(0) {}
    virtual tlm::tlm_extension_base *clone() const {
        return new CustomExtension(*static_cast<const CustomExtension *>(this));
    }
    virtual void copy_from(tlm::tlm_extension_base const &extension) {
        *this = static_cast<const CustomExtension &>(extension);
    }
    void set_member_b(uint8_t member) {
        member_b_ = member;
    }
    void set_xy(uint8_t x, int8_t y) {
        member_x_ = x;
        member_y_ = y;
    }
    virtual ~CustomExtension() {}
    uint8_t member_a_;
    uint8_t member_b_;
    std::vector<uint8_t> member_c_;
    const uint8_t *member_d_;
    uint64_t member_d_len_;

  private:
    uint8_t member_x_;
    int8_t member_y_;
};

#include <simics/systemc/injection/inject_base.h>  // NOLINT

template <typename TPAYLOAD>
class InjectCustomExtension
    : public simics::systemc::injection::InjectBase<TPAYLOAD> {
  public:
    ATTR_DICT_PARSER_NAMESPACE("custom.")

    virtual bool setValue(simics::systemc::injection::AttrDictParser *parser,
                          const std::string &key, attr_value_t *attr,
                          TPAYLOAD *gp) {
        if (key == "member_a") {
            CustomExtension *extension =
                this->template get_extension<CustomExtension>(gp);

            if (!parser->value(&extension->member_a_))
                return false;

            return true;
        }

        INJECT_SET_VALUE(set_member_b, uint8_t, CustomExtension);

        if (key == "set_xy") {
            simics::systemc::injection::AttrDictParser p = parser->init(attr);
            CustomExtension *extension =
                this->template get_extension<CustomExtension>(gp);
            uint8_t x = 0;
            if (!p.lookUp("x", &x))
                return false;

            int8_t y = 0;
            if (!p.lookUp("y", &y))
                return false;

            extension->set_xy(x, y);
            return true;
        }

        if (key == "member_c") {
            CustomExtension *extension =
                this->template get_extension<CustomExtension>(gp);

            if (!parser->value(&extension->member_c_))
                return false;

            return true;
        }

        if (key == "member_d") {
            CustomExtension *extension =
                this->template get_extension<CustomExtension>(gp);

            if (!SIM_attr_is_data(*attr)) {
                parser->reportError("member_d must be data");
                return false;
            }
            extension->member_d_ = SIM_attr_data(*attr);
            extension->member_d_len_ = SIM_attr_data_size(*attr);

            return true;
        }

        return false;
    }
};

static InjectCustomExtension<tlm::tlm_generic_payload> injector_;

The example shows different ways to set the members in the CustomerExtension. If the member in the extension is directly accessible, it could be set as shown for member_a. Data access is usually provided by setters and getters, set_member_b can be called by INJECT_SET_VALUE. If multiple arguments are required by the extension method, the arguments should be grouped in another dictionary or list as key set_xy shows. The key member_c demonstrates how a Python list can be loaded into a vector. Setting a data pointer is shown by key member_d. If a member in the extension cannot be properly setup, false must be returned. The failing key will be reported as an error and the inject call will be aborted.

The corresponding calls to setup the extension from Python could be as in the following example.

simics> @socket.b_transport({'custom.member_a' : 1}, 0)
simics> @socket.b_transport({'custom.set_member_b' : 2}, 0)
simics> @socket.b_transport({'custom.set_xy' : {'x' : 3, 'y' : 4}}, 0)
simics> @socket.b_transport({'custom.member_c' : [5, 6]}, 0)
simics> @socket.b_transport({'custom.member_d' : (7, 8, 9)}, 0)

5.7 Unconnected Ports

According to SystemC language standard, port instances defined by sc_port class cannot remain unbound at the end of elaboration, unless allowed by port policy. SystemC Library optionally permits using unbound ports. Such ports are then automatically bound to a dynamically allocated object implementing the corresponding interface. Access to an unbound port will log an unimplemented message in Simics, for example:

[device unimpl] Access to unbound port: device.simple_initiator_socket_0 @ 0 s
of intc/unimplemented in unconnected_base.h:28

The feature is disabled by default and can be enabled using allow_unconnected_ports attribute of the SystemC adapter. The list of supported unbound ports includes specialized sc_in, sc_out and sc_inout ports as well as socket ports which use the standard TLM-2.0 forward and backward transport interfaces.

4 Connecting SystemC and Simics Models 6 Execution of SystemC Models in the Simics Simulator