// Unit test for nb2b adapter in simple_target_socket, PEQ, and instance-specific extensions // Checks for bug in original version of simple_target_socket #include #define SC_INCLUDE_DYNAMIC_PROCESSES #include "systemc" using namespace sc_core; using namespace sc_dt; using namespace std; #include "tlm.h" #include "tlm_utils/simple_initiator_socket.h" #include "tlm_utils/simple_target_socket.h" #include "tlm_utils/multi_passthrough_initiator_socket.h" #include "tlm_utils/multi_passthrough_target_socket.h" #include "tlm_utils/peq_with_cb_and_phase.h" #include "tlm_utils/instance_specific_extensions.h" #include "mm.h" int rand_ps() { int n = rand() % 100; n = n * n * n; return n / 100; } struct Initiator: sc_module { tlm_utils::simple_initiator_socket socket; SC_CTOR(Initiator) : socket("socket") , request_in_progress(0) , m_peq(this, &Initiator::peq_cb) { socket.register_nb_transport_bw(this, &Initiator::nb_transport_bw); SC_THREAD(thread_process); } void thread_process() { tlm::tlm_generic_payload* trans; tlm::tlm_phase phase; sc_time delay; trans = m_mm.allocate(); trans->acquire(); int adr = 0; data[0] = adr; trans->set_command( tlm::TLM_WRITE_COMMAND ); trans->set_address( adr ); trans->set_data_ptr( reinterpret_cast(&data[0]) ); trans->set_data_length( 4 ); trans->set_streaming_width( 4 ); trans->set_byte_enable_ptr( 0 ); trans->set_dmi_allowed( false ); trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE ); socket->b_transport( *trans, delay ); trans->release(); for (int i = 0; i < 5000; i++) { int adr = rand(); tlm::tlm_command cmd = static_cast(rand() % 2); if (cmd == tlm::TLM_WRITE_COMMAND) data[i % 16] = adr; // Grab a new transaction from the memory manager trans = m_mm.allocate(); trans->acquire(); trans->set_command( cmd ); trans->set_address( adr ); trans->set_data_ptr( reinterpret_cast(&data[i % 16]) ); trans->set_data_length( 4 ); trans->set_streaming_width( 4 ); trans->set_byte_enable_ptr( 0 ); trans->set_dmi_allowed( false ); trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE ); if (request_in_progress) wait(end_request_event); request_in_progress = trans; phase = tlm::BEGIN_REQ; delay = sc_time(rand_ps(), SC_PS); tlm::tlm_sync_enum status; status = socket->nb_transport_fw( *trans, phase, delay ); previous_time = sc_time_stamp() + delay; if (status == tlm::TLM_UPDATED) { m_peq.notify( *trans, phase, delay ); } else if (status == tlm::TLM_COMPLETED) { request_in_progress = 0; check_transaction( *trans ); trans->release(); } wait( sc_time(rand_ps(), SC_PS) ); } } virtual tlm::tlm_sync_enum nb_transport_bw( tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay ) { if (sc_time_stamp() + delay < previous_time) SC_REPORT_FATAL("TLM-2", "nb_transport_bw called with decreasing timing annotation"); previous_time = sc_time_stamp() + delay; m_peq.notify( trans, phase, delay ); return tlm::TLM_ACCEPTED; } void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase) { if (phase == tlm::END_REQ || (&trans == request_in_progress && phase == tlm::BEGIN_RESP)) { request_in_progress = 0; end_request_event.notify(); } else if (phase == tlm::BEGIN_REQ || phase == tlm::END_RESP) SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by initiator"); if (phase == tlm::BEGIN_RESP) { check_transaction( trans ); tlm::tlm_phase fw_phase = tlm::END_RESP; sc_time delay = sc_time(rand_ps(), SC_PS); socket->nb_transport_fw( trans, fw_phase, delay ); previous_time = sc_time_stamp() + delay; trans.release(); } } void check_transaction(tlm::tlm_generic_payload& trans) { if ( trans.is_response_error() ) { char txt[100]; sprintf(txt, "Transaction returned with error, response status = %s", trans.get_response_string().c_str()); SC_REPORT_ERROR("TLM-2", txt); } tlm::tlm_command cmd = trans.get_command(); sc_dt::uint64 adr = trans.get_address(); int* ptr = reinterpret_cast( trans.get_data_ptr() ); if (cmd == tlm::TLM_READ_COMMAND) sc_assert( *ptr == -int(adr) ); } mm m_mm; int data[16]; tlm::tlm_generic_payload* request_in_progress; sc_event end_request_event; tlm_utils::peq_with_cb_and_phase m_peq; sc_time previous_time; }; // Dumb interconnect that simply routes transactions through struct Interconnect: sc_module { tlm_utils::multi_passthrough_target_socket targ_socket; tlm_utils::multi_passthrough_initiator_socket init_socket; Interconnect(sc_module_name _name, unsigned int _offset) : sc_module(_name) , targ_socket("targ_socket") , init_socket("init_socket") , offset(_offset) { targ_socket.register_b_transport (this, &Interconnect::b_transport); targ_socket.register_nb_transport_fw (this, &Interconnect::nb_transport_fw); targ_socket.register_get_direct_mem_ptr (this, &Interconnect::get_direct_mem_ptr); targ_socket.register_transport_dbg (this, &Interconnect::transport_dbg); init_socket.register_nb_transport_bw (this, &Interconnect::nb_transport_bw); init_socket.register_invalidate_direct_mem_ptr(this, &Interconnect::invalidate_direct_mem_ptr); } void end_of_elaboration() { if ( targ_socket.size() != init_socket.size() ) SC_REPORT_ERROR("TLM-2", "#initiators != #targets in Interconnect"); } virtual void b_transport( int id, tlm::tlm_generic_payload& trans, sc_time& delay ) { unsigned int target = (id + offset) % init_socket.size(); // Route-through init_socket[target]->b_transport( trans, delay ); } struct route_extension: tlm_utils::instance_specific_extension { int id; }; tlm_utils::instance_specific_extension_accessor accessor; virtual tlm::tlm_sync_enum nb_transport_fw( int id, tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay ) { route_extension* ext = 0; if (phase == tlm::BEGIN_REQ) { ext = new route_extension; ext->id = id; accessor(trans).set_extension(ext); } unsigned int target = (id + offset) % init_socket.size(); // Route-through tlm::tlm_sync_enum status; status = init_socket[target]->nb_transport_fw( trans, phase, delay ); if (status == tlm::TLM_COMPLETED) { accessor(trans).clear_extension(ext); delete ext; } return status; } virtual bool get_direct_mem_ptr( int id, tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data) { unsigned int target = (id + offset) % init_socket.size(); // Route-through bool status = init_socket[target]->get_direct_mem_ptr( trans, dmi_data ); return status; } virtual unsigned int transport_dbg( int id, tlm::tlm_generic_payload& trans ) { unsigned int target = (id + offset) % init_socket.size(); // Route-through return init_socket[target]->transport_dbg( trans ); } virtual tlm::tlm_sync_enum nb_transport_bw( int id, tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay ) { route_extension* ext = 0; accessor(trans).get_extension(ext); sc_assert(ext); tlm::tlm_sync_enum status; status = targ_socket[ ext->id ]->nb_transport_bw( trans, phase, delay ); if (status == tlm::TLM_COMPLETED) { accessor(trans).clear_extension(ext); delete ext; } return status; } virtual void invalidate_direct_mem_ptr( int id, sc_dt::uint64 start_range, sc_dt::uint64 end_range ) { for (unsigned int i = 0; i < targ_socket.size(); i++) targ_socket[i]->invalidate_direct_mem_ptr(start_range, end_range); } unsigned int offset; }; struct Target: sc_module { tlm_utils::simple_target_socket socket; SC_CTOR(Target) : socket("socket") { socket.register_b_transport (this, &Target::b_transport); } virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ) { execute_transaction(trans); } void execute_transaction(tlm::tlm_generic_payload& trans) { tlm::tlm_command cmd = trans.get_command(); sc_dt::uint64 adr = trans.get_address(); unsigned char* ptr = trans.get_data_ptr(); unsigned int len = trans.get_data_length(); unsigned char* byt = trans.get_byte_enable_ptr(); unsigned int wid = trans.get_streaming_width(); if (byt != 0) { trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE ); return; } if (len > 4 || wid < len) { trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE ); return; } if ( cmd == tlm::TLM_READ_COMMAND ) { *reinterpret_cast(ptr) = -int(adr); } else if ( cmd == tlm::TLM_WRITE_COMMAND ) { sc_assert( *reinterpret_cast(ptr) == adr ); } trans.set_response_status( tlm::TLM_OK_RESPONSE ); } }; SC_MODULE(Top) { Initiator *initiator1; Initiator *initiator2; Interconnect *interconnect; Target *target1; Target *target2; SC_CTOR(Top) { initiator1 = new Initiator ("initiator1"); initiator2 = new Initiator ("initiator2"); interconnect = new Interconnect("interconnect", 1); target1 = new Target ("target1"); target2 = new Target ("target2"); initiator1->socket.bind(interconnect->targ_socket); initiator2->socket.bind(interconnect->targ_socket); interconnect->init_socket.bind(target1->socket); interconnect->init_socket.bind(target2->socket); } }; int sc_main(int argc, char* argv[]) { cout << "Unit test for nb2b adapter, PEQ, and instance-specific extensions. Should remain silent\n"; Top top("top"); sc_start(); return 0; }