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