Vamos con el primer ejemplo…
Initiator
Empezamos con los includes de las cabeceras necesarias
#include
using namespace sc_core;
#include "tlm.h"
#include "tlm_utils/simple_initiator_socket.h"
Primero incluimos systemc (previsible, eh?) y luego tlm.h (también previsible).
Por último incluimos el socket mas sencillo de todos para este ejemplo.
A continuación declaramos la classe Initiator
class Initiator: sc_module
{
public:
tlm_utils::simple_initiator_socket initiator_socket;
SC_HAS_PROCESS(Initiator);
Initiator(sc_module_name name_);
private:
void initiator_thread();
};
Aquí hay cosas más interesantes: primero definimos la clase Initiator como un hijo de sc_module. A continuación definimos un socket de tipo simple_initiator, le llamamos initiator_socket (soy muy original para los nombres). A este template (los usaremos a menudo), se le pasa como parámetro la propia clase que lo contiene.
Luego declaramos el constructor usando la macro SC_HAS_PROCESS().
Por último definimos un método que será un SC_THREAD llamado initiator_thread (original, eh?).
...
double data;
tlm::tlm_generic_payload transaction;
sc_time delay = SC_ZERO_TIME;
...
Dentro del SC_THREAD declaramos una variable transaction y una variable sc_time para anotar los tiempos (serán 0, pero hace falta declararlos). También la variable double data que seran los datos a enviar entre los módulos.
transaction.set_data_length( sizeof(data) );
transaction.set_streaming_width( sizeof(data) );
transaction.set_byte_enable_ptr(0);
transaction.set_dmi_allowed(false);
transaction.set_response_status( tlm::TLM_INCOMPLETE_RESPONSE );
transaction.set_data_ptr( reinterpret_cast(&data) );
Llenamos los campos de la transacción, creo que cada campo se explica por si solo, y sólo vale la pena comentar las últimas lineas. Declaramos la transacción como TLM_INCOMPLETE_RESPONSE (así lo marca el estandard).
Luego preparamos los datos, para eso actualizamos el apuntador de la transacción a nuestros datos y lo hacemos con un “cast” en versión C++ (puedes consultar este link donde lo explica bastante bien). Fíjate que la transacción sólo mueve el puntero a los datos, no los datos en sí.
transaction.set_command( tlm::TLM_WRITE_COMMAND );
transaction.set_address( i );
initiator_socket-&>b_transport( transaction, delay);
En estas tres linias se pone el comando de la transacción que sea una escritura, la dirección donde acceder, y se realiza (por fin!) la comunicación con la función b_transport. A esta función se le pasa la transacción (transaction) y el tiempo de esta transacción (en este modo sera 0).
if (transaction.get_response_status() == tlm::TLM_OK_RESPONSE)
A continuación deberíamos comprobar si el Target ha respondido con un TLM_OK_RESPONSE a la transacción.
Acto seguido hacemos lo mismo pero para el caso de lectura (cambiando el comando por TLM_READ_COMMAND, claro).
Target
Vamos ahora a por el código del Target
#include "tlm_utils/simple_target_socket.h"
...
tlm_utils::simple_target_socket target_socket;
A diferencia del Target, aquí incluimos este header para incluir el socket para el target y declaramos una variable con el socket mismo (pasándole como parámetro la clase que lo contiene).
Veamos ahora el código del constructor:
Target::Target(sc_module_name name_):
sc_module(name_),
target_socket("target_socket")
{
target_socket.register_b_transport(this, &Target::b_transport);
}
Nada extraño hasta la última línea, donde registramos la función que implementa la función b_transport (puede tener cualquier nombre).
Y a continuación la función en sí:
void Target::b_transport( tlm::tlm_generic_payload& transaction, sc_time& delay )
{
tlm::tlm_command command = transaction.get_command();
sc_dt::uint64 address = transaction.get_address();
unsigned char* data_ptr = transaction.get_data_ptr();
unsigned int length = transaction.get_data_length();
unsigned char* byte_enable_ptr = transaction.get_byte_enable_ptr();
unsigned int width = transaction.get_streaming_width();
double *my_data_ptr;
if (command == tlm::TLM_WRITE_COMMAND) {
data[address] = *(reinterpret_cast( data_ptr ) );
std::cout << "Target Write: " << sc_time_stamp() << ". Data: " << data[address] << std::endl;
} else {
my_data_ptr = reinterpret_cast(data_ptr);
*my_data_ptr = data[address];
std::cout << "Target Read: " << sc_time_stamp() << ". Data " << data[address] << std::endl;
}
transaction.set_response_status(tlm::TLM_OK_RESPONSE );
return;
}
Primero recogemos todos los distintos parámetros de la transacción, y luego según la transacción sea de escritura o lectura, hacemos la copia de datos pertinente.
Fíjate de nuevo que la transacción sólo contiene un puntero, y hay que acceder a los datos del puntero con un "cast".
Por último, ponemos el estatus de la transacción como un OK (TLM_OK_RESPONSE) y volvemos de la función.
Y hasta aquí todo el código necesario para un modelado Untimed. Sencillo verdad?
Puedes bajarte todo el código en este enlace
1 Trackback
[…] pues el ejemplo del modo untimed, que con eso casi casi lo tenemos ya […]