tlm2_base_protocol_checker.h revision 12027
1
2// Filename: tlm2_base_protocol_checker.h
3
4//----------------------------------------------------------------------
5//  Copyright (c) 2008-2013 by Doulos Ltd.
6//
7//  Licensed under the Apache License, Version 2.0 (the "License");
8//  you may not use this file except in compliance with the License.
9//  You may obtain a copy of the License at
10//
11//  http://www.apache.org/licenses/LICENSE-2.0
12//
13//  Unless required by applicable law or agreed to in writing, software
14//  distributed under the License is distributed on an "AS IS" BASIS,
15//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16//  See the License for the specific language governing permissions and
17//  limitations under the License.
18//----------------------------------------------------------------------
19
20// Author: John Aynsley, Doulos
21
22// Version  1, 11 July 2008
23// Version  2, 16 July 2008  Only generate ref_count > 1 warning from 1st checker of path
24// Version  3, 17 July 2008  Support compilation under SystemC 2.1.v1
25// Version  4, 12 Aug  2008  Add header #include <map>
26// Version  5, 08 Sep  2008  Fix bugs in message text
27// Version  6, 01 Aug  2010  Update messages to refer to OSCI TLM-2.0 LRM of July 2009
28// Version  7, 25 Oct  2011  Minor bug fix for certain compilers: replace u_char with uchar_t
29// Version  8, 02 Nov  2011  Support the endianness conversion functions by excluding the
30//                           tlm_endian_context extension from the protocol checks
31// Version  9, 17 Aug  2012  Fix LRM reference on line 805 (should be 8.2.11 a) [NOT YET RELEASED]
32// Version 10,  3 Jan  2013  Updated messages to refer to IEEE Std 1666-2011, the combined SystemC + TLM-2.0 LRM
33//                           Added checks related to the generic payload option attribute
34// Version 11, 14 Mar  2016  Fix minor bug - start_phase should be a copy, not a reference
35
36// TLM-2.0 Base Protocol Compliance Checker
37
38/*
39Instantiate this checker module in-line between initiator and target, initiator and interconnect,
40or interconnect and target by binding the target_socket and initiator_socket
41Binding two checkers either side of an interconnect component, or interleaving a series of
42checkers with interconnect components, will enable some deeper checks as against having just
43a single checker
44
45For example
46
47  Initiator *initiator;
48  Bus       *bus;
49  Memory    *memory;
50  ...
51  initiator->socket.bind(bus->target_socket);
52  bus->initiator_socket.bind(memory->socket);
53
54might become
55
56  tlm_utils::tlm2_base_protocol_checker<32> *checker1;
57  tlm_utils::tlm2_base_protocol_checker<32> *checker2;
58  ...
59  initiator->socket.bind(checker1->target_socket);
60  checker1->initiator_socket.bind(bus->target_socket);
61  bus->initiator_socket.bind(checker2->target_socket);
62  checker2->initiator_socket.bind(memory->socket);
63
64
65GENERAL FEATURES OF THE BASE PROTOCOL CHECKER
66
67The checks are relatively expensive, hence by default the number of checks is limited.
68A maximum number can be set explicitly by calling set_num_checks(max)
69Checking can be deactivated at any time by calling set_num_checks(0)
70All checkers decrement a single global count, because having some checkers running and
71others not can cause bogus violation reports
72It is not permitted to turn checks on by calling set_num_checks() once checking has been
73deactivated, because this could cause bogus violation reports
74
75The DMI and debug checks are unaffected by the num_checks count (because they are cheap)
76
77The error messages contain User Manual references
78
79The checker is designed to be used with a transaction pool: otherwise it could consume
80a lot of memory. The checker keeps a local copy of each transaction object
81Failures are reported with a severity of SC_ERROR. The actions may be overridden by calling:
82   sc_report_handler::set_actions("tlm2_protocol_checker", ...);
83
84SPECIFIC CHECKS
85
86nb_transport: phase sequence BEGIN_REQ -> END_REQ -> BEGIN_RESP -> END_RESP
87Must not have two outstanding requests or responses (exclusion rules)
88Must not have decreasing timing annotations on calls to or returns from nb_transport_fw/bw
89Phase extensions permitted and ignored
90Must not call b_transport during nb_transport phase sequence and vice-versa
91
92nb_transport: memory manager must be set
93nb_transport: reference count must be non-zero
94First checker in BEGIN_REQ path should see ref_count == 1 (warning only)
95An interconnect component that sets a memory manager should also clear it
96An interconnect component that sets extensions with no memory manager should also clear them
97(Does not bother with these memory manager checks for DMI and debug)
98
99Transaction object must be properly initialized
100Many generic payload attributes must not be modified during the transaction lifetime
101Transaction object must not be re-allocated for a new transaction while still in use
102DMI descriptor must be properly initialized
103Debug transaction must be properly initialized
104Debug byte count must be less than data_length
105
106Checks that require multiple checkers to be instantiated along a transaction path:
107The BEGIN_RESP path must be the reverse of the BEGIN_REQ path
108Transaction object must not be sent with BEGIN_REQ while participating in a previous response
109Interconnect component must not set response status attribute to TLM_OK_RESPONSE
110Interconnect component must not modify data array on the response path
111
112Generic payload option attribute (IEEE Std 1666-2011, SystemC 2.3.x)
113gp_option must be properly initialized and only used for DMI and debug transport
114When gp_option is used, other gp attributes must be initalized and used as per the transport interfaces
115*/
116
117
118// ******************** PREAMBLE ********************
119
120
121#ifndef __tlm2_base_protocol_checker__
122#define __tlm2_base_protocol_checker__
123
124#include "systemc"
125using std::cout;
126using std::endl;
127using std::dec;
128using std::hex;
129
130#include "tlm.h"
131#include <sstream>
132#include <map>
133
134
135namespace tlm_utils {
136
137
138// Number of checks remaining
139const  sc_dt::uint64 default_num_checks = 100000;
140static sc_dt::uint64 num_checks = default_num_checks;
141
142
143// Types used when building a trace of the transaction path
144typedef unsigned char uchar_t;
145typedef std::deque<sc_core::sc_module*> deque_t;
146
147struct path_t {
148  path_t () { response_in_progress = false; ok_response = false; resp_data_ptr = 0; }
149
150  bool      response_in_progress;
151  bool      ok_response;
152  deque_t   path;
153  uchar_t*  resp_data_ptr;  // Copy of data on response path
154};
155
156// Global variable used for checks involving multiple checkers along a transaction path
157static std::map<tlm::tlm_generic_payload*, path_t> shared_map;
158
159
160// ******************** CLASS DEFINITION ********************
161
162
163template <unsigned int  BUSWIDTH = 32>
164class tlm2_base_protocol_checker
165
166: public sc_core::sc_module
167, public tlm::tlm_fw_transport_if<tlm::tlm_base_protocol_types>
168, public tlm::tlm_bw_transport_if<tlm::tlm_base_protocol_types>
169{
170public:
171
172  // Instantiate and bind checker inline between an existing pair of initiator and target sockets
173
174  tlm::tlm_target_socket   <BUSWIDTH, tlm::tlm_base_protocol_types, 1> target_socket;
175  tlm::tlm_initiator_socket<BUSWIDTH, tlm::tlm_base_protocol_types, 1> initiator_socket;
176
177  SC_CTOR(tlm2_base_protocol_checker)
178  : m_request_in_progress(0), m_response_in_progress(0)
179  {
180    target_socket   .bind( *this );
181    initiator_socket.bind( *this );
182  }
183
184
185  // Access methods for num_checks count
186
187  static void set_num_checks(sc_dt::uint64 n) {
188    if (num_checks == 0)
189      SC_REPORT_FATAL("tlm2_protocol_checker", "Method set_num_checks called after checking has stopped due to maximum number of checks being reached");
190    num_checks = n;
191  }
192
193  static sc_dt::uint64 get_num_checks() { return num_checks; }
194
195
196  // TLM-2.0 interface methods for initiator and target sockets, instrumented with checks
197
198  virtual tlm::tlm_sync_enum nb_transport_fw(
199    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
200  {
201    tlm::tlm_phase start_phase = phase;
202
203    if (num_checks)
204      nb_transport_fw_pre_checks( trans, phase, delay );
205
206    tlm::tlm_sync_enum status;
207    status = initiator_socket->nb_transport_fw( trans, phase, delay );
208
209    if (num_checks)
210      nb_transport_fw_post_checks( trans, start_phase, phase, delay, status );
211
212    return status;
213  }
214
215  virtual tlm::tlm_sync_enum nb_transport_bw(
216    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
217  {
218    if (num_checks)
219      nb_transport_bw_pre_checks( trans, phase, delay );
220
221    tlm::tlm_sync_enum status;
222    status = target_socket->nb_transport_bw( trans, phase, delay );
223
224    if (num_checks)
225      nb_transport_bw_post_checks( trans, phase, delay, status );
226
227    return status;
228  }
229
230  virtual void b_transport( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay )
231  {
232    if (num_checks)
233      b_transport_pre_checks( trans, delay );
234
235    initiator_socket->b_transport( trans, delay );
236
237    if (num_checks)
238      b_transport_post_checks( trans, delay );
239  }
240
241  virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
242                                  tlm::tlm_dmi&  dmi_data)
243  {
244    get_direct_mem_ptr_pre_checks( trans, dmi_data );
245
246    bool status;
247    status = initiator_socket->get_direct_mem_ptr( trans, dmi_data );
248
249    get_direct_mem_ptr_post_checks( trans, dmi_data );
250    return status;
251  }
252
253  virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
254                                         sc_dt::uint64 end_range)
255  {
256    target_socket->invalidate_direct_mem_ptr(start_range, end_range);
257  }
258
259  virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans)
260  {
261    transport_dbg_pre_checks( trans );
262
263    unsigned int count;
264    count = initiator_socket->transport_dbg( trans );
265
266    transport_dbg_post_checks( trans, count );
267    return count;
268  }
269
270
271private:
272  void b_transport_pre_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
273
274  void b_transport_post_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
275
276  void nb_transport_fw_pre_checks(
277      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay);
278
279  void nb_transport_fw_post_checks(
280      tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase,
281      sc_core::sc_time& delay, tlm::tlm_sync_enum status);
282
283  void nb_transport_bw_pre_checks(
284      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay);
285
286  void nb_transport_bw_post_checks(
287      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
288      tlm::tlm_sync_enum status);
289
290  void nb_transport_response_checks(
291      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
292      const char* txt2, const char* txt3, const char* txt4);
293
294  void check_initial_state(       tlm::tlm_generic_payload& trans, const char* txt2 );
295  void check_trans_not_modified(  tlm::tlm_generic_payload& trans, const char* txt2 );
296  void check_response_path(       tlm::tlm_generic_payload& trans, const char* txt2 );
297  void remember_gp_option(        tlm::tlm_generic_payload& trans );
298
299  void get_direct_mem_ptr_pre_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data );
300
301  void get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data );
302
303  void transport_dbg_pre_checks( tlm::tlm_generic_payload& trans );
304
305  void transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count );
306
307  void tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning = false  );
308
309private:
310
311  struct state_t {
312    state_t() { b_call = 0; ph = tlm::UNINITIALIZED_PHASE; gp = 0; }
313
314    bool                      has_mm;
315    unsigned int              b_call;    // Number of b_transport calls in progress
316    tlm::tlm_phase            ph;
317    sc_core::sc_time          time;      // Current time + annotated delay
318    tlm::tlm_generic_payload* gp;        // Points to new data and byte enable buffers
319    uchar_t*                  data_ptr;  // Stores original pointers
320    uchar_t*                  byte_enable_ptr;
321  };
322
323  // Transaction state for the specific hop where this checker is inlined
324  std::map<tlm::tlm_generic_payload*, state_t> m_map;
325
326  // Flags for exclusion rules
327  tlm::tlm_generic_payload* m_request_in_progress;
328  tlm::tlm_generic_payload* m_response_in_progress;
329
330  std::ostringstream txt;
331
332};
333
334
335
336// ******************** MEMBER FUNCTION DEFINITIONS ********************
337
338
339#define BOILERPLATE \
340template <unsigned int BUSWIDTH> \
341void tlm2_base_protocol_checker<BUSWIDTH>::
342
343
344BOILERPLATE
345b_transport_pre_checks(
346    tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
347{
348  ++ m_map[&trans].b_call;
349
350  if ( trans.has_mm() && trans.get_ref_count() == 0)
351  {
352    txt << "Transaction passed to b_transport with memory manager and reference count of 0";
353    tlm2error(trans, "14.5 t)");
354  }
355  check_initial_state(trans, "b_transport");
356
357#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
358  if (sc_core::sc_get_current_process_handle().proc_kind() == sc_core::SC_METHOD_PROC_)
359  {
360    txt << "b_transport called from method process";
361    tlm2error(trans, "11.1.1.4 b)");
362  }
363#endif
364
365  if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4)
366  {
367    txt << "b_transport called during a sequence of nb_transport calls";
368    tlm2error(trans, "15.2.10 c)");
369  }
370}
371
372
373BOILERPLATE
374b_transport_post_checks(
375    tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
376{
377  check_response_path(trans, "b_transport");
378  check_trans_not_modified(trans, "b_transport");
379  -- m_map[&trans].b_call;
380}
381
382
383BOILERPLATE
384nb_transport_fw_pre_checks(
385    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
386{
387  if ( !trans.has_mm() )
388  {
389    txt << "Transaction passed to nb_transport_fw with no memory manager set";
390    tlm2error(trans, "14.5 i)");
391  }
392  if ( trans.get_ref_count() == 0)
393  {
394    txt << "Transaction passed to nb_transport_fw with reference count of 0";
395    tlm2error(trans, "14.5 t)");
396  }
397
398  switch (phase)
399  {
400    case tlm::BEGIN_REQ:
401      check_initial_state(trans, "nb_transport_fw");
402
403      if (m_map[&trans].ph > 0 &&  m_map[&trans].ph < 4) // END_RESP -> BEGIN_REQ is legal
404      {
405        txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw";
406        tlm2error(trans, "15.2.4");
407      }
408
409      if (m_request_in_progress)
410      {
411        txt << "Transaction violates BEGIN_REQ exclusion rule, detected in nb_transport_fw";
412        tlm2error(trans, "15.2.6 e)");
413      }
414      m_request_in_progress = &trans;
415
416      if (m_map[&trans].b_call)
417      {
418        txt << "nb_transport_fw called during a b_transport call";
419        tlm2error(trans, "15.2.10 c)");
420      }
421      break;
422
423    case tlm::END_REQ:
424    case tlm::BEGIN_RESP:
425    case tlm::UNINITIALIZED_PHASE:
426      txt << "Phase " << phase << " sent on forward path, detected in nb_transport_fw";
427      tlm2error(trans, " 15.2.3 c)");
428      break;
429
430    case tlm::END_RESP:
431      if (m_map[&trans].ph != tlm::BEGIN_RESP)
432      {
433        txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw";
434        tlm2error(trans, "15.2.4");
435      }
436      m_response_in_progress = 0;
437      break;
438  }
439
440  if (phase < 5)  // Ignore extended phases
441    m_map[&trans].ph = phase;
442
443  if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
444  {
445    txt << "nb_transport_fw called with decreasing timing annotation:"
446        << " delay = " << delay
447        << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
448    tlm2error(trans, "15.2.7 c)");
449  }
450  m_map[&trans].time = sc_core::sc_time_stamp() + delay;
451}
452
453
454BOILERPLATE
455nb_transport_fw_post_checks(
456    tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase,
457    sc_core::sc_time& delay, tlm::tlm_sync_enum status)
458{
459  if (status == tlm::TLM_UPDATED)
460  {
461    nb_transport_response_checks(
462        trans, phase, delay, "(forward) return", "Return from nb_transport_fw", "nb_transport_fw");
463  }
464  else if (status == tlm::TLM_COMPLETED)
465  {
466    if (start_phase == tlm::BEGIN_REQ)
467      check_response_path(trans, "nb_transport_fw");
468    m_request_in_progress = 0;
469    m_map[&trans].ph = tlm::UNINITIALIZED_PHASE;
470  }
471
472  // Transaction object should not be re-allocated, even during the END_RESP phase
473  //if (phase != tlm::END_RESP)
474  {
475    std::ostringstream txt;
476    txt << "nb_transport_fw, phase = " << phase;
477    check_trans_not_modified(trans, txt.str().c_str());
478  }
479}
480
481
482BOILERPLATE
483nb_transport_bw_pre_checks(
484    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
485{
486  if ( !trans.has_mm() )
487  {
488    txt << "Transaction passed to nb_transport_bw with no memory manager set";
489    tlm2error(trans, "14.5 i)");
490  }
491  if ( trans.get_ref_count() == 0)
492  {
493    txt << "Transaction passed to nb_transport_bw with reference count of 0";
494    tlm2error(trans, "14.5 t)");
495  }
496  nb_transport_response_checks(
497      trans, phase, delay, "backward", "nb_transport_bw called", "nb_transport_bw");
498}
499
500
501BOILERPLATE
502nb_transport_bw_post_checks(
503    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
504    tlm::tlm_sync_enum status)
505{
506  if (status == tlm::TLM_UPDATED)
507  {
508    switch (phase)
509    {
510      case tlm::BEGIN_REQ:
511        txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw";
512        tlm2error(trans, "15.2.4");
513        break;
514
515      case tlm::END_REQ:
516      case tlm::BEGIN_RESP:
517      case tlm::UNINITIALIZED_PHASE:
518        txt << "Phase " << phase << " sent on (backward) return path, detected in nb_transport_bw";
519        tlm2error(trans, "15.2.3 c)");
520        break;
521
522      case tlm::END_RESP:
523        if (m_map[&trans].ph != tlm::BEGIN_RESP)
524        {
525          txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw";
526          tlm2error(trans, "15.2.4");
527        }
528
529        m_response_in_progress = 0;
530        break;
531    }
532
533    if (phase < 5)  // Ignore extended phases
534      m_map[&trans].ph = phase;
535
536    if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
537    {
538      txt << "Return from nb_transport_bw with decreasing timing annotation:"
539          << " delay = " << delay
540          << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
541      tlm2error(trans, "15.2.7 c)");
542    }
543    m_map[&trans].time = sc_core::sc_time_stamp() + delay;
544  }
545  else if (status == tlm::TLM_COMPLETED)
546  {
547    m_response_in_progress = 0;
548    m_map[&trans].ph = tlm::UNINITIALIZED_PHASE;
549  }
550
551  // Transaction object should not be re-allocated, even during the END_RESP phase
552  //if (phase != tlm::END_RESP)
553  {
554    std::ostringstream txt;
555    txt << "nb_transport_bw, phase = " << phase;
556    check_trans_not_modified(trans, txt.str().c_str());
557  }
558}
559
560
561BOILERPLATE
562nb_transport_response_checks(
563    tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
564    const char* txt2, const char* txt3, const char* txt4)
565{
566  if (trans.is_response_ok())
567    if (shared_map[&trans].response_in_progress && !shared_map[&trans].ok_response)
568    {
569      txt << "Interconnect component sets response status attribute to TLM_OK_RESPONSE"
570          << ", detected in " << txt4;
571      tlm2error(trans, "14.7");
572
573    }
574
575  switch (phase)
576  {
577    case tlm::BEGIN_REQ:
578    case tlm::END_RESP:
579    case tlm::UNINITIALIZED_PHASE:
580      txt << "Phase " << phase << " sent on " << txt2 << " path"
581          << ", detected in " << txt4;
582      tlm2error(trans, "15.2.3 c)");
583      break;
584
585    case tlm::END_REQ:
586      if (m_map[&trans].ph != tlm::BEGIN_REQ)
587      {
588        txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path"
589            << ", detected in " << txt4;
590        tlm2error(trans, "15.2.4");
591      }
592
593      m_request_in_progress = 0;
594      break;
595
596    case tlm::BEGIN_RESP:
597      if (m_map[&trans].ph != tlm::BEGIN_REQ && m_map[&trans].ph != tlm::END_REQ)
598      {
599        txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path"
600            << ", detected in " << txt4;
601        tlm2error(trans, "15.2.4");
602      }
603
604      if (&trans == m_request_in_progress)
605        m_request_in_progress = 0;
606
607      if (m_response_in_progress)
608      {
609        txt << "Transaction violates BEGIN_RESP exclusion rule"
610            << ", detected in " << txt4;
611        tlm2error(trans, "15.2.6 f)");
612      }
613      m_response_in_progress = &trans;
614
615      check_response_path(trans, txt4);
616      break;
617  }
618
619  if (phase < 5)  // Ignore extended phases
620    m_map[&trans].ph = phase;
621
622  if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
623  {
624    txt << txt3 << " with decreasing timing annotation:"
625        << " delay = " << delay
626        << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
627    tlm2error(trans, "15.2.7 c)");
628  }
629  m_map[&trans].time = sc_core::sc_time_stamp() + delay;
630}
631
632
633BOILERPLATE
634check_initial_state(
635    tlm::tlm_generic_payload& trans, const char* txt2 )
636{
637  if (num_checks > 0)
638  {
639    --num_checks;
640    if (num_checks == 0)
641      SC_REPORT_INFO("tlm2_protocol_checker", "Checkers deactivated after executing the set number of checks");
642  }
643
644  if ( trans.has_mm() && trans.get_ref_count() > 1 && shared_map[&trans].path.empty() )
645  {
646    txt << "New transaction passed to " << txt2 << " with reference count = "
647        << trans.get_ref_count();
648    tlm2error(trans, "14.5 t)", true);
649  }
650  if (trans.get_data_ptr() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND)
651  {
652    txt << "Transaction not properly initialized: data_ptr == 0, detected in " << txt2;
653    tlm2error(trans, "14.11 e)");
654  }
655  if (trans.get_data_length() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND)
656  {
657    txt << "Transaction not properly initialized: data_langth == 0, detected in " << txt2;
658    tlm2error(trans, "14.12 d)");
659  }
660  if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0)
661  {
662    txt << "Transaction not properly initialized: "
663        << "byte_enable_ptr != 0 and byte_enable_length == 0, detected in " << txt2;
664    tlm2error(trans, "14.14 f)");
665  }
666  if (trans.get_streaming_width() == 0)
667  {
668    txt << "Transaction not properly initialized: streaming_width == 0, detected in " << txt2;
669    tlm2error(trans, "14.15 f)");
670  }
671  if (trans.is_dmi_allowed())
672  {
673    txt << "Transaction not properly initialized: dmi_allowed == true, detected in " << txt2;
674    tlm2error(trans, "14.16");
675  }
676  if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
677  {
678    txt << "Transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE, detected in " << txt2;
679    tlm2error(trans, "14.17 e)");
680  }
681  if (trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
682  {
683    txt << "Transaction not properly initialized: gp_option != TLM_MIN_PAYLOAD, detected in " << txt2;
684    tlm2error(trans, "14.8 g)");
685  }
686
687  // Setup clones of transaction and buffers in map
688  tlm::tlm_generic_payload* gp = m_map[&trans].gp;
689  if (gp == 0)
690    gp = new tlm::tlm_generic_payload;  // Memory leak: transactions are never cleared from map
691  else
692  {
693    delete [] gp->get_data_ptr();
694    gp->free_all_extensions();
695  }
696  gp->set_data_ptr( new uchar_t[trans.get_data_length()] );
697  m_map[&trans].data_ptr = trans.get_data_ptr();
698
699  if (gp->get_byte_enable_ptr())
700    delete [] gp->get_byte_enable_ptr();
701  if (trans.get_byte_enable_ptr())
702    gp->set_byte_enable_ptr( new uchar_t[trans.get_byte_enable_length()] );
703  else
704    gp->set_byte_enable_ptr(0);
705  m_map[&trans].byte_enable_ptr = trans.get_byte_enable_ptr();
706
707  gp->deep_copy_from(trans);
708  m_map[&trans].gp = gp;
709  m_map[&trans].time = sc_core::SC_ZERO_TIME;
710  m_map[&trans].has_mm = trans.has_mm();
711
712  // Store request path checker sequence
713  if (shared_map[&trans].resp_data_ptr)
714  {
715    delete [] shared_map[&trans].resp_data_ptr;
716    shared_map[&trans].resp_data_ptr = 0;
717  }
718  if (shared_map[&trans].response_in_progress)
719  {
720    txt << "Transaction object sent with BEGIN_REQ while still being used on a previous response path, detected in " << txt2;
721    tlm2error(trans, "14.5 x)");
722  }
723  shared_map[&trans].ok_response = false;
724  shared_map[&trans].path.push_back(this);
725}
726
727
728BOILERPLATE
729remember_gp_option(
730    tlm::tlm_generic_payload& trans)
731{
732  // Setup clone of transaction in map in order to check gp_option only
733  tlm::tlm_generic_payload* gp = m_map[&trans].gp;
734  if (gp == 0)
735    gp = new tlm::tlm_generic_payload;  // Memory leak: transactions are never cleared from map
736  gp->set_gp_option( trans.get_gp_option() );
737  m_map[&trans].gp = gp;
738}
739
740
741BOILERPLATE
742check_trans_not_modified(
743    tlm::tlm_generic_payload& trans, const char* txt2 )
744{
745  tlm::tlm_generic_payload* init = m_map[&trans].gp;
746
747  if (trans.get_command() != init->get_command())
748  {
749    txt << "Command attribute modified during transaction lifetime, detected in " << txt2;
750    tlm2error(trans, "14.7");
751  }
752  if (trans.get_data_ptr() != m_map[&trans].data_ptr)
753  {
754    txt << "Data pointer attribute modified during transaction lifetime, detected in " << txt2;
755    tlm2error(trans, "14.7");
756  }
757  if (trans.get_data_length() != init->get_data_length())
758  {
759    txt << "Data length attribute modified during transaction lifetime, detected in " << txt2;
760    tlm2error(trans, "14.7");
761  }
762  if (trans.get_command() == tlm::TLM_WRITE_COMMAND)
763    for (unsigned int i = 0; i < init->get_data_length(); i++)
764      if (trans.get_data_ptr()[i] != init->get_data_ptr()[i])
765      {
766        txt << "Data array modified during transaction lifetime, detected in " << txt2;
767        tlm2error(trans, "14.7");
768      }
769  if (trans.get_byte_enable_ptr() != m_map[&trans].byte_enable_ptr)
770  {
771    txt << "Byte enable pointer attribute modified during transaction lifetime, detected in " << txt2;
772    tlm2error(trans, "14.7");
773  }
774  if (trans.get_byte_enable_length() != init->get_byte_enable_length())
775  {
776    txt << "Byte enable length attribute modified during transaction lifetime, detected in " << txt2;
777    tlm2error(trans, "14.7");
778  }
779  if (trans.get_byte_enable_ptr())
780    for (unsigned int i = 0; i < init->get_byte_enable_length(); i++)
781      if (trans.get_byte_enable_ptr()[i] != init->get_byte_enable_ptr()[i])
782      {
783        txt << "Byte enable array modified during transaction lifetime, detected in " << txt2;
784        tlm2error(trans, "14.7");
785      }
786  if (trans.get_streaming_width() != init->get_streaming_width())
787  {
788    txt << "Streaming width attribute modified during transaction lifetime, detected in " << txt2;
789    tlm2error(trans, "14.7");
790  }
791  if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
792  {
793    txt << "Generic payload option attribute modified during transaction lifetime, detected in " << txt2;
794    tlm2error(trans, "14.8 g)");
795  }
796  if ( !m_map[&trans].has_mm )
797  {
798    if (trans.has_mm())
799    {
800      txt << "Interconnect component sets a memory manager, but does not clear it on return, detected in " << txt2;
801      tlm2error(trans, "14.5 aa)");
802    }
803
804    for (unsigned int i = 0; i < tlm::max_num_extensions(); i++)
805      // Exclude tlm_endian_context extension from the check because it is not cloned in m_map
806      if (i != tlm::tlm_endian_context::ID)
807        if (trans.get_extension(i))
808          if ( !m_map[&trans].gp->get_extension(i) )
809          {
810            txt << "Extension set (index = " << i << ") without also being deleted in the absence of a memory manager, detected in " << txt2;
811            tlm2error(trans, "14.5 aa)");
812          }
813  }
814
815  uchar_t* resp_data_ptr = shared_map[&trans].resp_data_ptr;
816  if (resp_data_ptr)
817    for (unsigned int i = 0; i < trans.get_data_length(); i++)
818      if (trans.get_data_ptr()[i] != resp_data_ptr[i])
819      {
820        txt << "Transaction data array modified in interconnect component on response path, detected in " << txt2;
821        tlm2error(trans, "14.7");
822      }
823}
824
825
826BOILERPLATE
827check_response_path(
828    tlm::tlm_generic_payload& trans, const char* txt2 )
829{
830  if ( !shared_map[&trans].path.empty() )
831  {
832    if ( this != shared_map[&trans].path.back() )
833    {
834      txt << "BEGIN_RESP path is not the reverse of the BEGIN_REQ path.";
835      txt << "\nBEGIN_REQ path includes these checkers: -> ";
836      deque_t path = shared_map[&trans].path;
837      for (deque_t::iterator i = path.begin(); i < path.end(); i++)
838        txt << (*i)->name() << " -> ";
839      txt << "\nDetected in " << txt2;
840      tlm2error(trans, "15.2.11 a)");
841    }
842    shared_map[&trans].path.pop_back();
843    shared_map[&trans].response_in_progress = !shared_map[&trans].path.empty();
844    shared_map[&trans].ok_response = trans.is_response_ok();
845
846    // Create a copy of the data array for comparison on the response path
847    if ( !shared_map[&trans].resp_data_ptr )
848    {
849      shared_map[&trans].resp_data_ptr = new uchar_t[trans.get_data_length()];
850      memcpy(shared_map[&trans].resp_data_ptr, trans.get_data_ptr(), trans.get_data_length());
851    }
852  }
853}
854
855
856BOILERPLATE
857get_direct_mem_ptr_pre_checks(
858    tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data )
859{
860  remember_gp_option(trans);
861
862  if (dmi_data.get_dmi_ptr() != 0)
863  {
864    txt << "DMI descriptor not properly initialized: dmi_ptr != 0";
865    tlm2error(trans, "11.2.5 f)");
866  }
867  if (!dmi_data.is_none_allowed())
868  {
869    txt << "DMI descriptor not properly initialized: granted_access != DMI_ACCESS_NONE";
870    tlm2error(trans, "11.2.5 a)");
871  }
872  if (dmi_data.get_start_address() != 0)
873  {
874    txt << "DMI descriptor not properly initialized: start_address != 0";
875    tlm2error(trans, "11.2.5 u)");
876  }
877  if (dmi_data.get_end_address() != (sc_dt::uint64)(-1))
878  {
879    txt << "DMI descriptor not properly initialized: end_address != 0";
880    tlm2error(trans, "11.2.5 u)");
881  }
882  if (dmi_data.get_read_latency() != sc_core::SC_ZERO_TIME)
883  {
884    txt << "DMI descriptor not properly initialized: read_latency != SC_ZERO_TIME";
885    tlm2error(trans, "11.2.5 ac)");
886  }
887  if (dmi_data.get_write_latency() != sc_core::SC_ZERO_TIME)
888  {
889    txt << "DMI descriptor not properly initialized: write_latency != SC_ZERO_TIME";
890    tlm2error(trans, "11.2.5 ac)");
891  }
892
893  if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD)
894  {
895    /*
896    if (trans.is_dmi_allowed())  // Would be rather brutal to flag dmi_allowed as an arror for a DMI transaction!
897    {
898      txt << "DMI transaction not properly initialized: dmi_allowed == true";
899      tlm2error(trans, "14.8 e) & 14.16");
900    }
901	*/
902    if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
903    {
904      txt << "DMI transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE";
905      tlm2error(trans, "14.8 e) & 14.17 e)");
906    }
907  }
908  else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED)
909  {
910    txt << "DMI transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED";
911    tlm2error(trans, "14.8 c) & e) & j)");
912  }
913}
914
915
916BOILERPLATE
917get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data )
918{
919  tlm::tlm_generic_payload* init = m_map[&trans].gp;
920
921  if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
922  {
923    txt << "DMI transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime";
924    tlm2error(trans, "14.8 h)");
925  }
926  else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD)
927  {
928    txt << "DMI transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD";
929    tlm2error(trans, "14.8 j)");
930  }
931}
932
933
934BOILERPLATE
935transport_dbg_pre_checks( tlm::tlm_generic_payload& trans )
936{
937  remember_gp_option(trans);
938
939  if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0)
940  {
941    txt << "Debug transaction has data_ptr == 0";
942    tlm2error(trans, "11.3.4 l)");
943  }
944
945  if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD)
946  {
947    if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0)
948    {
949      txt << "Debug transaction not properly initialized: "
950          << "byte_enable_ptr != 0 and byte_enable_length == 0";
951      tlm2error(trans, "14.8 f) & 14.14 f)");
952    }
953    if (trans.get_streaming_width() == 0)
954    {
955      txt << "Debug transaction not properly initialized: streaming_width == 0";
956      tlm2error(trans, "14.8 f) & 14.15 f)");
957    }
958    if (trans.is_dmi_allowed())
959    {
960      txt << "Debug transaction not properly initialized: dmi_allowed == true";
961      tlm2error(trans, "14.8 f) & 14.16");
962    }
963    if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
964    {
965      txt << "Debug transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE";
966      tlm2error(trans, "14.8 f) & 14.17 e)");
967    }
968  }
969  else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED)
970  {
971    txt << "Debug transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED";
972    tlm2error(trans, "14.8 c) & f) & l)");
973  }}
974
975
976BOILERPLATE
977transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count )
978{
979  tlm::tlm_generic_payload* init = m_map[&trans].gp;
980
981  if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0)
982  {
983    txt << "Debug transaction has data_ptr == 0";
984    tlm2error(trans, "11.3.4 l)");
985  }
986  if (count > trans.get_data_length())
987  {
988    txt << "Count returned from transport_dbg is greater than data_length";
989    tlm2error(trans, "11.3.4 s)");
990  }
991
992  if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
993  {
994    txt << "Debug transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime";
995    tlm2error(trans, "14.8 h)");
996  }
997  else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD)
998  {
999    txt << "Debug transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD";
1000    tlm2error(trans, "14.8 l)");
1001  }}
1002
1003
1004BOILERPLATE
1005tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning )
1006{
1007  txt << "\n\nRefer to IEEE Std 1666-2011, clause " << ref;
1008  txt << "\n\nChecker instance: " << this->name();
1009  txt << "\n\nTransaction details:";
1010  txt << "\n  has_mm             = " << dec << trans.has_mm() << " (bool)";
1011  txt << "\n  ref_count          = " << dec << trans.get_ref_count() << " (int)";
1012  txt << "\n\n  gp_option          = " <<
1013      (trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD  ? "TLM_MIN_PAYLOAD"
1014	  :trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD ? "TLM_FULL_PAYLOAD"
1015	                                                  : "TLM_FULL_PAYLOAD_ACCEPTED");
1016  txt << "\n  command            = " <<
1017     (trans.get_command() == tlm::TLM_READ_COMMAND  ? "TLM_READ_COMMAND"
1018     :trans.get_command() == tlm::TLM_WRITE_COMMAND ? "TLM_WRITE_COMMAND"
1019                                                    : "TLM_IGNORE_COMMAND");
1020  txt << "\n  address            = " << hex << trans.get_address() << " (hex)";
1021  txt << "\n  data_ptr           = " << hex
1022      << reinterpret_cast<int*>(trans.get_data_ptr()) << " (hex)";
1023  txt << "\n  data_length        = " << hex << trans.get_data_length() << " (hex)";
1024  txt << "\n  streaming_width    = " << hex << trans.get_streaming_width() << " (hex)";
1025  txt << "\n  byte_enable_ptr    = " << hex
1026      << reinterpret_cast<int*>(trans.get_byte_enable_ptr()) << " (hex)";
1027  txt << "\n  byte_enable_length = " << hex << trans.get_byte_enable_length() << " (hex)";
1028  txt << "\n  dmi_allowed        = " << dec << trans.is_dmi_allowed() << " (bool)";
1029  txt << "\n  response_status    = " << trans.get_response_string();
1030
1031  bool extensions_present = false;
1032  for (unsigned int i = 0; i < tlm::max_num_extensions(); i++)
1033  {
1034    tlm::tlm_extension_base* ext = trans.get_extension(i);
1035    if (ext)
1036    {
1037      if (!extensions_present)
1038        txt << "\n\n  extensions:";
1039      txt << "\n    index = " << i << "   type = " << typeid(*ext).name();
1040      extensions_present = true;
1041    }
1042  }
1043
1044  txt << "\n\n";
1045  if (warning)
1046    SC_REPORT_WARNING("tlm2_protocol_checker", txt.str().c_str());
1047  else
1048    SC_REPORT_ERROR("tlm2_protocol_checker", txt.str().c_str());
1049}
1050
1051
1052
1053} // namespace tlm_utils
1054
1055#endif // __tlm2_base_protocol_checker__
1056