nb2b_adapter.cpp revision 12855:588919e0e4aa
1
2// Unit test for nb2b adapter in simple_target_socket, PEQ, and instance-specific extensions
3// Checks for bug in original version of simple_target_socket
4
5#include <iomanip>
6
7#define SC_INCLUDE_DYNAMIC_PROCESSES
8
9#include "systemc"
10using namespace sc_core;
11using namespace sc_dt;
12using namespace std;
13
14#include "tlm.h"
15#include "tlm_utils/simple_initiator_socket.h"
16#include "tlm_utils/simple_target_socket.h"
17#include "tlm_utils/multi_passthrough_initiator_socket.h"
18#include "tlm_utils/multi_passthrough_target_socket.h"
19#include "tlm_utils/peq_with_cb_and_phase.h"
20#include "tlm_utils/instance_specific_extensions.h"
21
22#include "mm.h"
23
24
25int rand_ps()
26{
27  int n = rand() % 100;
28  n = n * n * n;
29  return n / 100;
30}
31
32
33struct Initiator: sc_module
34{
35  tlm_utils::simple_initiator_socket<Initiator> socket;
36
37  SC_CTOR(Initiator)
38  : socket("socket")
39  , request_in_progress(0)
40  , m_peq(this, &Initiator::peq_cb)
41  {
42    socket.register_nb_transport_bw(this, &Initiator::nb_transport_bw);
43
44    SC_THREAD(thread_process);
45  }
46
47  void thread_process()
48  {
49    tlm::tlm_generic_payload* trans;
50    tlm::tlm_phase phase;
51    sc_time delay;
52
53    trans = m_mm.allocate();
54    trans->acquire();
55
56    int adr = 0;
57    data[0] = adr;
58
59    trans->set_command( tlm::TLM_WRITE_COMMAND );
60    trans->set_address( adr );
61    trans->set_data_ptr( reinterpret_cast<unsigned char*>(&data[0]) );
62    trans->set_data_length( 4 );
63    trans->set_streaming_width( 4 );
64    trans->set_byte_enable_ptr( 0 );
65    trans->set_dmi_allowed( false );
66    trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE );
67
68    socket->b_transport( *trans, delay );
69
70    trans->release();
71
72    for (int i = 0; i < 5000; i++)
73    {
74      int adr = rand();
75      tlm::tlm_command cmd = static_cast<tlm::tlm_command>(rand() % 2);
76      if (cmd == tlm::TLM_WRITE_COMMAND) data[i % 16] = adr;
77
78      // Grab a new transaction from the memory manager
79      trans = m_mm.allocate();
80      trans->acquire();
81
82      trans->set_command( cmd );
83      trans->set_address( adr );
84      trans->set_data_ptr( reinterpret_cast<unsigned char*>(&data[i % 16]) );
85      trans->set_data_length( 4 );
86      trans->set_streaming_width( 4 );
87      trans->set_byte_enable_ptr( 0 );
88      trans->set_dmi_allowed( false );
89      trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE );
90
91      if (request_in_progress)
92        wait(end_request_event);
93      request_in_progress = trans;
94      phase = tlm::BEGIN_REQ;
95
96      delay = sc_time(rand_ps(), SC_PS);
97
98      tlm::tlm_sync_enum status;
99      status = socket->nb_transport_fw( *trans, phase, delay );
100      previous_time = sc_time_stamp() + delay;
101
102      if (status == tlm::TLM_UPDATED)
103      {
104        m_peq.notify( *trans, phase, delay );
105      }
106      else if (status == tlm::TLM_COMPLETED)
107      {
108        request_in_progress = 0;
109
110        check_transaction( *trans );
111
112        trans->release();
113      }
114      wait( sc_time(rand_ps(), SC_PS) );
115    }
116  }
117
118  virtual tlm::tlm_sync_enum nb_transport_bw( tlm::tlm_generic_payload& trans,
119                                              tlm::tlm_phase& phase, sc_time& delay )
120  {
121    if (sc_time_stamp() + delay < previous_time)
122      SC_REPORT_FATAL("TLM-2", "nb_transport_bw called with decreasing timing annotation");
123    previous_time = sc_time_stamp() + delay;
124
125    m_peq.notify( trans, phase, delay );
126    return tlm::TLM_ACCEPTED;
127  }
128
129  void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase)
130  {
131    if (phase == tlm::END_REQ || (&trans == request_in_progress && phase == tlm::BEGIN_RESP))
132    {
133      request_in_progress = 0;
134      end_request_event.notify();
135    }
136    else if (phase == tlm::BEGIN_REQ || phase == tlm::END_RESP)
137      SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by initiator");
138
139    if (phase == tlm::BEGIN_RESP)
140    {
141      check_transaction( trans );
142
143      tlm::tlm_phase fw_phase = tlm::END_RESP;
144      sc_time delay = sc_time(rand_ps(), SC_PS);
145      socket->nb_transport_fw( trans, fw_phase, delay );
146      previous_time = sc_time_stamp() + delay;
147
148      trans.release();
149    }
150  }
151
152  void check_transaction(tlm::tlm_generic_payload& trans)
153  {
154    if ( trans.is_response_error() )
155    {
156      char txt[100];
157      sprintf(txt, "Transaction returned with error, response status = %s",
158                   trans.get_response_string().c_str());
159      SC_REPORT_ERROR("TLM-2", txt);
160    }
161
162    tlm::tlm_command cmd = trans.get_command();
163    sc_dt::uint64    adr = trans.get_address();
164    int*             ptr = reinterpret_cast<int*>( trans.get_data_ptr() );
165
166    if (cmd == tlm::TLM_READ_COMMAND)
167      sc_assert( *ptr == -int(adr) );
168  }
169
170  mm   m_mm;
171  int  data[16];
172  tlm::tlm_generic_payload* request_in_progress;
173  sc_event end_request_event;
174  tlm_utils::peq_with_cb_and_phase<Initiator> m_peq;
175  sc_time previous_time;
176};
177
178
179// Dumb interconnect that simply routes transactions through
180
181struct Interconnect: sc_module
182{
183  tlm_utils::multi_passthrough_target_socket<Interconnect, 32>    targ_socket;
184  tlm_utils::multi_passthrough_initiator_socket<Interconnect, 32> init_socket;
185
186  Interconnect(sc_module_name _name, unsigned int _offset)
187  : sc_module(_name)
188  , targ_socket("targ_socket")
189  , init_socket("init_socket")
190  , offset(_offset)
191  {
192    targ_socket.register_b_transport              (this, &Interconnect::b_transport);
193    targ_socket.register_nb_transport_fw          (this, &Interconnect::nb_transport_fw);
194    targ_socket.register_get_direct_mem_ptr       (this, &Interconnect::get_direct_mem_ptr);
195    targ_socket.register_transport_dbg            (this, &Interconnect::transport_dbg);
196    init_socket.register_nb_transport_bw          (this, &Interconnect::nb_transport_bw);
197    init_socket.register_invalidate_direct_mem_ptr(this, &Interconnect::invalidate_direct_mem_ptr);
198  }
199
200  void end_of_elaboration()
201  {
202    if ( targ_socket.size() != init_socket.size() )
203      SC_REPORT_ERROR("TLM-2", "#initiators != #targets in Interconnect");
204  }
205
206  virtual void b_transport( int id, tlm::tlm_generic_payload& trans, sc_time& delay )
207  {
208    unsigned int target = (id + offset) % init_socket.size(); // Route-through
209
210    init_socket[target]->b_transport( trans, delay );
211  }
212
213
214  struct route_extension: tlm_utils::instance_specific_extension<route_extension>
215  {
216    int id;
217  };
218
219  tlm_utils::instance_specific_extension_accessor accessor;
220
221
222  virtual tlm::tlm_sync_enum nb_transport_fw( int id, tlm::tlm_generic_payload& trans,
223                                              tlm::tlm_phase& phase, sc_time& delay )
224  {
225    route_extension* ext = 0;
226    if (phase == tlm::BEGIN_REQ)
227    {
228      ext = new route_extension;
229      ext->id = id;
230      accessor(trans).set_extension(ext);
231    }
232
233    unsigned int target = (id + offset) % init_socket.size(); // Route-through
234
235    tlm::tlm_sync_enum status;
236    status = init_socket[target]->nb_transport_fw( trans, phase, delay );
237
238    if (status == tlm::TLM_COMPLETED)
239    {
240      accessor(trans).clear_extension(ext);
241      delete ext;
242    }
243
244    return status;
245  }
246
247  virtual bool get_direct_mem_ptr( int id, tlm::tlm_generic_payload& trans,
248                                           tlm::tlm_dmi& dmi_data)
249  {
250    unsigned int target = (id + offset) % init_socket.size(); // Route-through
251
252    bool status = init_socket[target]->get_direct_mem_ptr( trans, dmi_data );
253
254    return status;
255  }
256
257  virtual unsigned int transport_dbg( int id, tlm::tlm_generic_payload& trans )
258  {
259    unsigned int target = (id + offset) % init_socket.size(); // Route-through
260
261    return init_socket[target]->transport_dbg( trans );
262  }
263
264
265  virtual tlm::tlm_sync_enum nb_transport_bw( int id, tlm::tlm_generic_payload& trans,
266                                              tlm::tlm_phase& phase, sc_time& delay )
267  {
268    route_extension* ext = 0;
269    accessor(trans).get_extension(ext);
270    sc_assert(ext);
271
272    tlm::tlm_sync_enum status;
273    status = targ_socket[ ext->id ]->nb_transport_bw( trans, phase, delay );
274
275    if (status == tlm::TLM_COMPLETED)
276    {
277      accessor(trans).clear_extension(ext);
278      delete ext;
279    }
280
281    return status;
282  }
283
284  virtual void invalidate_direct_mem_ptr( int id, sc_dt::uint64 start_range,
285                                                  sc_dt::uint64 end_range )
286  {
287    for (unsigned int i = 0; i < targ_socket.size(); i++)
288      targ_socket[i]->invalidate_direct_mem_ptr(start_range, end_range);
289  }
290
291  unsigned int offset;
292};
293
294
295struct Target: sc_module
296{
297  tlm_utils::simple_target_socket<Target> socket;
298
299  SC_CTOR(Target)
300  : socket("socket")
301  {
302    socket.register_b_transport    (this, &Target::b_transport);
303  }
304
305  virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay )
306  {
307    execute_transaction(trans);
308  }
309
310
311  void execute_transaction(tlm::tlm_generic_payload& trans)
312  {
313    tlm::tlm_command cmd = trans.get_command();
314    sc_dt::uint64    adr = trans.get_address();
315    unsigned char*   ptr = trans.get_data_ptr();
316    unsigned int     len = trans.get_data_length();
317    unsigned char*   byt = trans.get_byte_enable_ptr();
318    unsigned int     wid = trans.get_streaming_width();
319
320    if (byt != 0) {
321      trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
322      return;
323    }
324    if (len > 4 || wid < len) {
325      trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
326      return;
327    }
328
329    if ( cmd == tlm::TLM_READ_COMMAND )
330    {
331      *reinterpret_cast<int*>(ptr) = -int(adr);
332    }
333    else if ( cmd == tlm::TLM_WRITE_COMMAND )
334    {
335      sc_assert( *reinterpret_cast<unsigned int*>(ptr) == adr );
336    }
337
338    trans.set_response_status( tlm::TLM_OK_RESPONSE );
339  }
340
341};
342
343
344SC_MODULE(Top)
345{
346  Initiator    *initiator1;
347  Initiator    *initiator2;
348  Interconnect *interconnect;
349  Target       *target1;
350  Target       *target2;
351
352  SC_CTOR(Top)
353  {
354    initiator1   = new Initiator   ("initiator1");
355    initiator2   = new Initiator   ("initiator2");
356    interconnect = new Interconnect("interconnect", 1);
357    target1      = new Target      ("target1");
358    target2      = new Target      ("target2");
359
360    initiator1->socket.bind(interconnect->targ_socket);
361    initiator2->socket.bind(interconnect->targ_socket);
362    interconnect->init_socket.bind(target1->socket);
363    interconnect->init_socket.bind(target2->socket);
364  }
365};
366
367
368int sc_main(int argc, char* argv[])
369{
370  cout << "Unit test for nb2b adapter, PEQ, and instance-specific extensions. Should remain silent\n";
371
372  Top top("top");
373  sc_start();
374  return 0;
375}
376
377