KisTA_logo
Kista infrastructure for SystemC simulation and Time Analysis

User Guidelines: Transfer of Complex User Data Structures
back
kth_logo



Using KisTA:

    The communication facilities of the KisTA library enable the tranfer of complex data structures. There are two basic techniques to do so:

- Transfer a reference to the complex user structure
- Transfer the whole user structure

Transfer by reference:

For instance, lets assume the following user defined structure "my_type":

struct my_type {
       unsigned int scal_coeff;
       float coeffs[9];
       char filter_type;
};

Then, in order to use the transfer of a reference, the user can declare the following declaration:

fifo_buffer<my_type *>   my_buffer("my_buffer");

This is a fast way to transfer data among tasks. However, the user must mind that, as it can happen in a real system, this way to transfer data among concurrent tasks is to formal effects equivalent to a shared memory mechanism so the model can suffer races depending on the timing, mapping, scheduling, etc. The user should mind the life times of the objects, and more precisely, of the actual values associated to those object, whose reference is trasferred.

Transfer the whole structure:

A "safe" in the sense of functionally deterministic, but also more time costly alternative is to transfer the whole structure. In order to do that, the user can do the following:

class my_type {
public:
       my_type(unsigned int _scal_coeff_var, float *_coeffs, char _filter_type );
       my_type(const my_type& other); // copy constructor

       my_type& operator=(const my_type& other); // assignement operator

       unsigned int scal_coeff;
       float coeffs[9];
       char filter_type;

};


ostream& operator<<(ostream& os, my_type msg);

Notice that the user has to write the type as a class, and in a case like this, to describe a copy constructor to describe how the copy of the data is done, The implementation is ommited in this guideline, but notive that it is required to make a copy of the 9 coefficients, and not simply a copy of the array pointer. It is then also convenient to describe the assignement operator. Finally, the user has to provide at least the "<<" operator to enable its usage with the "fifo_buffer" KisTA communication facility, which relies on the SystemC sc_fifo channel.

Now, the user can make the following declaration:

fifo_buffer<my_type>   my_buffer("my_buffer");

As can be seen, this can be cumbersome and very time wasting. KisTA provides the "message_t" facility which alleviates this task. It has the following declaration:

class message_t {
    friend ostream& operator<<(ostream& os, message_t msg);
public:
    message_t();
    message_t(void *datap_var, size_t size_var); // pointer to the data and size
   
    // copy constructor
    message_t(const message_t& other);
   
    // Asignation
    message_t& operator =(const message_t& other);
   
    operator void* () {
        return (void *)this->datap;
    }
   
private:   
    char *datap;     // pointer to the data
    size_t host_size; // size of the message used to transfer the data by native host
};

By relying on this class, the user can just declare the "my_type" structure as in the transfer by reference case. The the user can write something lik this:


fifo_buffer<message_t>   comm1("comm1");

This declaration is independent on the data type. So, to transfer an object "data" of type "my_type", you can do the following:

my_type      data;
message_t    msg_data(&data,sizeof(data));
comm1.write(msg_data);

For the reading, the user can do the following:

my_type      *recv_data;
message_t    msg_recv_data;

comm1.read(msg_recv_data);
recv_data = (my_type *)(void *)msg_recv_data;

Notice that, at the receiving side there will be a local copy of the received data in the "msg_recv_data" object, and that it is sufficient to handle a pointer to the "my_type" type to access the data, which will be valid as long as the "msg_recv_data" is not step over by a new access to "comm1.read".

There are two more important advantages that this class can offer to the user.

First, the message_t class enables a ftransfer by cope where the data type transferred can be dynamically changed. Thats is, it is a kind of dynamically polimorphic channel which can transfer different data types by copy during the simulation.

Second, it allows to hook complex user data structures, defined in the user code to a KisTA-XML description of the system, This enables an enhanced and powerful usage of KisTA as a "black-box" tool. This is illustrated in the following section.

Using KisTA-XML:

If the user uses the "message_t" to transfer comples user data structures, then the user code can be hooked to the KisTA-XML description. Specifically, assuming the previous declaration of the "msg_data", the user, instead of having declared the fifo_buffer through the fifo_buffer<message_t> class template,  the user will make the following channel declaration in the XML KisTA system description:

<application name="A1">
        ...
    <comm_synch name="comm1" src="Tsrc" dest="Tdest" comm_type="fifo_buffer" comm_data_type="message" token_size="27"/>
        ...
</application>

Notice that the value of the comm_data_type is now "message". Notice also that still the "token_size" does not need to be linked to the size that the user struct is taking in memory to perform the "copy-based transfer" (provided through the sizeof() statement). KisTA takes that size only in case the user does not specify the "token_size" attribute. Othewise, the value of the "token_size" attribute is the one taken for the calculation of the communication penalties associated to the transfer of the token.


Notice that this do not requires the user to remove calls to the set_functional_failure() function within the tasks code. They are simply ignored and the validations makes no effect.