| |
| // 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 <iomanip> |
| |
| #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<Initiator> 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<unsigned char*>(&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<tlm::tlm_command>(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<unsigned char*>(&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<int*>( 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<Initiator> m_peq; |
| sc_time previous_time; |
| }; |
| |
| |
| // Dumb interconnect that simply routes transactions through |
| |
| struct Interconnect: sc_module |
| { |
| tlm_utils::multi_passthrough_target_socket<Interconnect, 32> targ_socket; |
| tlm_utils::multi_passthrough_initiator_socket<Interconnect, 32> 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<route_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<Target> 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<int*>(ptr) = -int(adr); |
| } |
| else if ( cmd == tlm::TLM_WRITE_COMMAND ) |
| { |
| sc_assert( *reinterpret_cast<unsigned int*>(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; |
| } |
| |