blob: 3dbca9e49e2717d05d61f14ecdd2dfc5c419fb05 [file] [log] [blame]
// Filename: tlm2_base_protocol_checker.h
//----------------------------------------------------------------------
// Copyright (c) 2008-2013 by Doulos Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//----------------------------------------------------------------------
// Author: John Aynsley, Doulos
// Version 1, 11 July 2008
// Version 2, 16 July 2008 Only generate ref_count > 1 warning from 1st checker of path
// Version 3, 17 July 2008 Support compilation under SystemC 2.1.v1
// Version 4, 12 Aug 2008 Add header #include <map>
// Version 5, 08 Sep 2008 Fix bugs in message text
// Version 6, 01 Aug 2010 Update messages to refer to OSCI TLM-2.0 LRM of July 2009
// Version 7, 25 Oct 2011 Minor bug fix for certain compilers: replace u_char with uchar_t
// Version 8, 02 Nov 2011 Support the endianness conversion functions by excluding the
// tlm_endian_context extension from the protocol checks
// Version 9, 17 Aug 2012 Fix LRM reference on line 805 (should be 8.2.11 a) [NOT YET RELEASED]
// Version 10, 3 Jan 2013 Updated messages to refer to IEEE Std 1666-2011, the combined SystemC + TLM-2.0 LRM
// Added checks related to the generic payload option attribute
// Version 11, 14 Mar 2016 Fix minor bug - start_phase should be a copy, not a reference
// TLM-2.0 Base Protocol Compliance Checker
/*
Instantiate this checker module in-line between initiator and target, initiator and interconnect,
or interconnect and target by binding the target_socket and initiator_socket
Binding two checkers either side of an interconnect component, or interleaving a series of
checkers with interconnect components, will enable some deeper checks as against having just
a single checker
For example
Initiator *initiator;
Bus *bus;
Memory *memory;
...
initiator->socket.bind(bus->target_socket);
bus->initiator_socket.bind(memory->socket);
might become
tlm_utils::tlm2_base_protocol_checker<32> *checker1;
tlm_utils::tlm2_base_protocol_checker<32> *checker2;
...
initiator->socket.bind(checker1->target_socket);
checker1->initiator_socket.bind(bus->target_socket);
bus->initiator_socket.bind(checker2->target_socket);
checker2->initiator_socket.bind(memory->socket);
GENERAL FEATURES OF THE BASE PROTOCOL CHECKER
The checks are relatively expensive, hence by default the number of checks is limited.
A maximum number can be set explicitly by calling set_num_checks(max)
Checking can be deactivated at any time by calling set_num_checks(0)
All checkers decrement a single global count, because having some checkers running and
others not can cause bogus violation reports
It is not permitted to turn checks on by calling set_num_checks() once checking has been
deactivated, because this could cause bogus violation reports
The DMI and debug checks are unaffected by the num_checks count (because they are cheap)
The error messages contain User Manual references
The checker is designed to be used with a transaction pool: otherwise it could consume
a lot of memory. The checker keeps a local copy of each transaction object
Failures are reported with a severity of SC_ERROR. The actions may be overridden by calling:
sc_report_handler::set_actions("tlm2_protocol_checker", ...);
SPECIFIC CHECKS
nb_transport: phase sequence BEGIN_REQ -> END_REQ -> BEGIN_RESP -> END_RESP
Must not have two outstanding requests or responses (exclusion rules)
Must not have decreasing timing annotations on calls to or returns from nb_transport_fw/bw
Phase extensions permitted and ignored
Must not call b_transport during nb_transport phase sequence and vice-versa
nb_transport: memory manager must be set
nb_transport: reference count must be non-zero
First checker in BEGIN_REQ path should see ref_count == 1 (warning only)
An interconnect component that sets a memory manager should also clear it
An interconnect component that sets extensions with no memory manager should also clear them
(Does not bother with these memory manager checks for DMI and debug)
Transaction object must be properly initialized
Many generic payload attributes must not be modified during the transaction lifetime
Transaction object must not be re-allocated for a new transaction while still in use
DMI descriptor must be properly initialized
Debug transaction must be properly initialized
Debug byte count must be less than data_length
Checks that require multiple checkers to be instantiated along a transaction path:
The BEGIN_RESP path must be the reverse of the BEGIN_REQ path
Transaction object must not be sent with BEGIN_REQ while participating in a previous response
Interconnect component must not set response status attribute to TLM_OK_RESPONSE
Interconnect component must not modify data array on the response path
Generic payload option attribute (IEEE Std 1666-2011, SystemC 2.3.x)
gp_option must be properly initialized and only used for DMI and debug transport
When gp_option is used, other gp attributes must be initalized and used as per the transport interfaces
*/
// ******************** PREAMBLE ********************
#ifndef __tlm2_base_protocol_checker__
#define __tlm2_base_protocol_checker__
#include "systemc"
using std::cout;
using std::endl;
using std::dec;
using std::hex;
#include "tlm.h"
#include <sstream>
#include <map>
namespace tlm_utils {
// Number of checks remaining
const sc_dt::uint64 default_num_checks = 100000;
static sc_dt::uint64 num_checks = default_num_checks;
// Types used when building a trace of the transaction path
typedef unsigned char uchar_t;
typedef std::deque<sc_core::sc_module*> deque_t;
struct path_t {
path_t () { response_in_progress = false; ok_response = false; resp_data_ptr = 0; }
bool response_in_progress;
bool ok_response;
deque_t path;
uchar_t* resp_data_ptr; // Copy of data on response path
};
// Global variable used for checks involving multiple checkers along a transaction path
static std::map<tlm::tlm_generic_payload*, path_t> shared_map;
// ******************** CLASS DEFINITION ********************
template <unsigned int BUSWIDTH = 32>
class tlm2_base_protocol_checker
: public sc_core::sc_module
, public tlm::tlm_fw_transport_if<tlm::tlm_base_protocol_types>
, public tlm::tlm_bw_transport_if<tlm::tlm_base_protocol_types>
{
public:
// Instantiate and bind checker inline between an existing pair of initiator and target sockets
tlm::tlm_target_socket <BUSWIDTH, tlm::tlm_base_protocol_types, 1> target_socket;
tlm::tlm_initiator_socket<BUSWIDTH, tlm::tlm_base_protocol_types, 1> initiator_socket;
SC_CTOR(tlm2_base_protocol_checker)
: m_request_in_progress(0), m_response_in_progress(0)
{
target_socket .bind( *this );
initiator_socket.bind( *this );
}
// Access methods for num_checks count
static void set_num_checks(sc_dt::uint64 n) {
if (num_checks == 0)
SC_REPORT_FATAL("tlm2_protocol_checker", "Method set_num_checks called after checking has stopped due to maximum number of checks being reached");
num_checks = n;
}
static sc_dt::uint64 get_num_checks() { return num_checks; }
// TLM-2.0 interface methods for initiator and target sockets, instrumented with checks
virtual tlm::tlm_sync_enum nb_transport_fw(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
{
tlm::tlm_phase start_phase = phase;
if (num_checks)
nb_transport_fw_pre_checks( trans, phase, delay );
tlm::tlm_sync_enum status;
status = initiator_socket->nb_transport_fw( trans, phase, delay );
if (num_checks)
nb_transport_fw_post_checks( trans, start_phase, phase, delay, status );
return status;
}
virtual tlm::tlm_sync_enum nb_transport_bw(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
{
if (num_checks)
nb_transport_bw_pre_checks( trans, phase, delay );
tlm::tlm_sync_enum status;
status = target_socket->nb_transport_bw( trans, phase, delay );
if (num_checks)
nb_transport_bw_post_checks( trans, phase, delay, status );
return status;
}
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay )
{
if (num_checks)
b_transport_pre_checks( trans, delay );
initiator_socket->b_transport( trans, delay );
if (num_checks)
b_transport_post_checks( trans, delay );
}
virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
tlm::tlm_dmi& dmi_data)
{
get_direct_mem_ptr_pre_checks( trans, dmi_data );
bool status;
status = initiator_socket->get_direct_mem_ptr( trans, dmi_data );
get_direct_mem_ptr_post_checks( trans, dmi_data );
return status;
}
virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
sc_dt::uint64 end_range)
{
target_socket->invalidate_direct_mem_ptr(start_range, end_range);
}
virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans)
{
transport_dbg_pre_checks( trans );
unsigned int count;
count = initiator_socket->transport_dbg( trans );
transport_dbg_post_checks( trans, count );
return count;
}
private:
void b_transport_pre_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
void b_transport_post_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
void nb_transport_fw_pre_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay);
void nb_transport_fw_post_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase,
sc_core::sc_time& delay, tlm::tlm_sync_enum status);
void nb_transport_bw_pre_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay);
void nb_transport_bw_post_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
tlm::tlm_sync_enum status);
void nb_transport_response_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
const char* txt2, const char* txt3, const char* txt4);
void check_initial_state( tlm::tlm_generic_payload& trans, const char* txt2 );
void check_trans_not_modified( tlm::tlm_generic_payload& trans, const char* txt2 );
void check_response_path( tlm::tlm_generic_payload& trans, const char* txt2 );
void remember_gp_option( tlm::tlm_generic_payload& trans );
void get_direct_mem_ptr_pre_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data );
void get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data );
void transport_dbg_pre_checks( tlm::tlm_generic_payload& trans );
void transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count );
void tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning = false );
private:
struct state_t {
state_t() { b_call = 0; ph = tlm::UNINITIALIZED_PHASE; gp = 0; }
bool has_mm;
unsigned int b_call; // Number of b_transport calls in progress
tlm::tlm_phase ph;
sc_core::sc_time time; // Current time + annotated delay
tlm::tlm_generic_payload* gp; // Points to new data and byte enable buffers
uchar_t* data_ptr; // Stores original pointers
uchar_t* byte_enable_ptr;
};
// Transaction state for the specific hop where this checker is inlined
std::map<tlm::tlm_generic_payload*, state_t> m_map;
// Flags for exclusion rules
tlm::tlm_generic_payload* m_request_in_progress;
tlm::tlm_generic_payload* m_response_in_progress;
std::ostringstream txt;
};
// ******************** MEMBER FUNCTION DEFINITIONS ********************
#define BOILERPLATE \
template <unsigned int BUSWIDTH> \
void tlm2_base_protocol_checker<BUSWIDTH>::
BOILERPLATE
b_transport_pre_checks(
tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
{
++ m_map[&trans].b_call;
if ( trans.has_mm() && trans.get_ref_count() == 0)
{
txt << "Transaction passed to b_transport with memory manager and reference count of 0";
tlm2error(trans, "14.5 t)");
}
check_initial_state(trans, "b_transport");
#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714)
if (sc_core::sc_get_current_process_handle().proc_kind() == sc_core::SC_METHOD_PROC_)
{
txt << "b_transport called from method process";
tlm2error(trans, "11.1.1.4 b)");
}
#endif
if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4)
{
txt << "b_transport called during a sequence of nb_transport calls";
tlm2error(trans, "15.2.10 c)");
}
}
BOILERPLATE
b_transport_post_checks(
tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
{
check_response_path(trans, "b_transport");
check_trans_not_modified(trans, "b_transport");
-- m_map[&trans].b_call;
}
BOILERPLATE
nb_transport_fw_pre_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
{
if ( !trans.has_mm() )
{
txt << "Transaction passed to nb_transport_fw with no memory manager set";
tlm2error(trans, "14.5 i)");
}
if ( trans.get_ref_count() == 0)
{
txt << "Transaction passed to nb_transport_fw with reference count of 0";
tlm2error(trans, "14.5 t)");
}
switch (phase)
{
case tlm::BEGIN_REQ:
check_initial_state(trans, "nb_transport_fw");
if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4) // END_RESP -> BEGIN_REQ is legal
{
txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw";
tlm2error(trans, "15.2.4");
}
if (m_request_in_progress)
{
txt << "Transaction violates BEGIN_REQ exclusion rule, detected in nb_transport_fw";
tlm2error(trans, "15.2.6 e)");
}
m_request_in_progress = &trans;
if (m_map[&trans].b_call)
{
txt << "nb_transport_fw called during a b_transport call";
tlm2error(trans, "15.2.10 c)");
}
break;
case tlm::END_REQ:
case tlm::BEGIN_RESP:
case tlm::UNINITIALIZED_PHASE:
txt << "Phase " << phase << " sent on forward path, detected in nb_transport_fw";
tlm2error(trans, " 15.2.3 c)");
break;
case tlm::END_RESP:
if (m_map[&trans].ph != tlm::BEGIN_RESP)
{
txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw";
tlm2error(trans, "15.2.4");
}
m_response_in_progress = 0;
break;
}
if (phase < 5) // Ignore extended phases
m_map[&trans].ph = phase;
if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
{
txt << "nb_transport_fw called with decreasing timing annotation:"
<< " delay = " << delay
<< ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
tlm2error(trans, "15.2.7 c)");
}
m_map[&trans].time = sc_core::sc_time_stamp() + delay;
}
BOILERPLATE
nb_transport_fw_post_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase,
sc_core::sc_time& delay, tlm::tlm_sync_enum status)
{
if (status == tlm::TLM_UPDATED)
{
nb_transport_response_checks(
trans, phase, delay, "(forward) return", "Return from nb_transport_fw", "nb_transport_fw");
}
else if (status == tlm::TLM_COMPLETED)
{
if (start_phase == tlm::BEGIN_REQ)
check_response_path(trans, "nb_transport_fw");
m_request_in_progress = 0;
m_map[&trans].ph = tlm::UNINITIALIZED_PHASE;
}
// Transaction object should not be re-allocated, even during the END_RESP phase
//if (phase != tlm::END_RESP)
{
std::ostringstream txt;
txt << "nb_transport_fw, phase = " << phase;
check_trans_not_modified(trans, txt.str().c_str());
}
}
BOILERPLATE
nb_transport_bw_pre_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay)
{
if ( !trans.has_mm() )
{
txt << "Transaction passed to nb_transport_bw with no memory manager set";
tlm2error(trans, "14.5 i)");
}
if ( trans.get_ref_count() == 0)
{
txt << "Transaction passed to nb_transport_bw with reference count of 0";
tlm2error(trans, "14.5 t)");
}
nb_transport_response_checks(
trans, phase, delay, "backward", "nb_transport_bw called", "nb_transport_bw");
}
BOILERPLATE
nb_transport_bw_post_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
tlm::tlm_sync_enum status)
{
if (status == tlm::TLM_UPDATED)
{
switch (phase)
{
case tlm::BEGIN_REQ:
txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw";
tlm2error(trans, "15.2.4");
break;
case tlm::END_REQ:
case tlm::BEGIN_RESP:
case tlm::UNINITIALIZED_PHASE:
txt << "Phase " << phase << " sent on (backward) return path, detected in nb_transport_bw";
tlm2error(trans, "15.2.3 c)");
break;
case tlm::END_RESP:
if (m_map[&trans].ph != tlm::BEGIN_RESP)
{
txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw";
tlm2error(trans, "15.2.4");
}
m_response_in_progress = 0;
break;
}
if (phase < 5) // Ignore extended phases
m_map[&trans].ph = phase;
if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
{
txt << "Return from nb_transport_bw with decreasing timing annotation:"
<< " delay = " << delay
<< ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
tlm2error(trans, "15.2.7 c)");
}
m_map[&trans].time = sc_core::sc_time_stamp() + delay;
}
else if (status == tlm::TLM_COMPLETED)
{
m_response_in_progress = 0;
m_map[&trans].ph = tlm::UNINITIALIZED_PHASE;
}
// Transaction object should not be re-allocated, even during the END_RESP phase
//if (phase != tlm::END_RESP)
{
std::ostringstream txt;
txt << "nb_transport_bw, phase = " << phase;
check_trans_not_modified(trans, txt.str().c_str());
}
}
BOILERPLATE
nb_transport_response_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay,
const char* txt2, const char* txt3, const char* txt4)
{
if (trans.is_response_ok())
if (shared_map[&trans].response_in_progress && !shared_map[&trans].ok_response)
{
txt << "Interconnect component sets response status attribute to TLM_OK_RESPONSE"
<< ", detected in " << txt4;
tlm2error(trans, "14.7");
}
switch (phase)
{
case tlm::BEGIN_REQ:
case tlm::END_RESP:
case tlm::UNINITIALIZED_PHASE:
txt << "Phase " << phase << " sent on " << txt2 << " path"
<< ", detected in " << txt4;
tlm2error(trans, "15.2.3 c)");
break;
case tlm::END_REQ:
if (m_map[&trans].ph != tlm::BEGIN_REQ)
{
txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path"
<< ", detected in " << txt4;
tlm2error(trans, "15.2.4");
}
m_request_in_progress = 0;
break;
case tlm::BEGIN_RESP:
if (m_map[&trans].ph != tlm::BEGIN_REQ && m_map[&trans].ph != tlm::END_REQ)
{
txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path"
<< ", detected in " << txt4;
tlm2error(trans, "15.2.4");
}
if (&trans == m_request_in_progress)
m_request_in_progress = 0;
if (m_response_in_progress)
{
txt << "Transaction violates BEGIN_RESP exclusion rule"
<< ", detected in " << txt4;
tlm2error(trans, "15.2.6 f)");
}
m_response_in_progress = &trans;
check_response_path(trans, txt4);
break;
}
if (phase < 5) // Ignore extended phases
m_map[&trans].ph = phase;
if (sc_core::sc_time_stamp() + delay < m_map[&trans].time)
{
txt << txt3 << " with decreasing timing annotation:"
<< " delay = " << delay
<< ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time;
tlm2error(trans, "15.2.7 c)");
}
m_map[&trans].time = sc_core::sc_time_stamp() + delay;
}
BOILERPLATE
check_initial_state(
tlm::tlm_generic_payload& trans, const char* txt2 )
{
if (num_checks > 0)
{
--num_checks;
if (num_checks == 0)
SC_REPORT_INFO("tlm2_protocol_checker", "Checkers deactivated after executing the set number of checks");
}
if ( trans.has_mm() && trans.get_ref_count() > 1 && shared_map[&trans].path.empty() )
{
txt << "New transaction passed to " << txt2 << " with reference count = "
<< trans.get_ref_count();
tlm2error(trans, "14.5 t)", true);
}
if (trans.get_data_ptr() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND)
{
txt << "Transaction not properly initialized: data_ptr == 0, detected in " << txt2;
tlm2error(trans, "14.11 e)");
}
if (trans.get_data_length() == 0 && trans.get_command() != tlm::TLM_IGNORE_COMMAND)
{
txt << "Transaction not properly initialized: data_langth == 0, detected in " << txt2;
tlm2error(trans, "14.12 d)");
}
if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0)
{
txt << "Transaction not properly initialized: "
<< "byte_enable_ptr != 0 and byte_enable_length == 0, detected in " << txt2;
tlm2error(trans, "14.14 f)");
}
if (trans.get_streaming_width() == 0)
{
txt << "Transaction not properly initialized: streaming_width == 0, detected in " << txt2;
tlm2error(trans, "14.15 f)");
}
if (trans.is_dmi_allowed())
{
txt << "Transaction not properly initialized: dmi_allowed == true, detected in " << txt2;
tlm2error(trans, "14.16");
}
if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
{
txt << "Transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE, detected in " << txt2;
tlm2error(trans, "14.17 e)");
}
if (trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
{
txt << "Transaction not properly initialized: gp_option != TLM_MIN_PAYLOAD, detected in " << txt2;
tlm2error(trans, "14.8 g)");
}
// Setup clones of transaction and buffers in map
tlm::tlm_generic_payload* gp = m_map[&trans].gp;
if (gp == 0)
gp = new tlm::tlm_generic_payload; // Memory leak: transactions are never cleared from map
else
{
delete [] gp->get_data_ptr();
gp->free_all_extensions();
}
gp->set_data_ptr( new uchar_t[trans.get_data_length()] );
m_map[&trans].data_ptr = trans.get_data_ptr();
if (gp->get_byte_enable_ptr())
delete [] gp->get_byte_enable_ptr();
if (trans.get_byte_enable_ptr())
gp->set_byte_enable_ptr( new uchar_t[trans.get_byte_enable_length()] );
else
gp->set_byte_enable_ptr(0);
m_map[&trans].byte_enable_ptr = trans.get_byte_enable_ptr();
gp->deep_copy_from(trans);
m_map[&trans].gp = gp;
m_map[&trans].time = sc_core::SC_ZERO_TIME;
m_map[&trans].has_mm = trans.has_mm();
// Store request path checker sequence
if (shared_map[&trans].resp_data_ptr)
{
delete [] shared_map[&trans].resp_data_ptr;
shared_map[&trans].resp_data_ptr = 0;
}
if (shared_map[&trans].response_in_progress)
{
txt << "Transaction object sent with BEGIN_REQ while still being used on a previous response path, detected in " << txt2;
tlm2error(trans, "14.5 x)");
}
shared_map[&trans].ok_response = false;
shared_map[&trans].path.push_back(this);
}
BOILERPLATE
remember_gp_option(
tlm::tlm_generic_payload& trans)
{
// Setup clone of transaction in map in order to check gp_option only
tlm::tlm_generic_payload* gp = m_map[&trans].gp;
if (gp == 0)
gp = new tlm::tlm_generic_payload; // Memory leak: transactions are never cleared from map
gp->set_gp_option( trans.get_gp_option() );
m_map[&trans].gp = gp;
}
BOILERPLATE
check_trans_not_modified(
tlm::tlm_generic_payload& trans, const char* txt2 )
{
tlm::tlm_generic_payload* init = m_map[&trans].gp;
if (trans.get_command() != init->get_command())
{
txt << "Command attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_data_ptr() != m_map[&trans].data_ptr)
{
txt << "Data pointer attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_data_length() != init->get_data_length())
{
txt << "Data length attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_command() == tlm::TLM_WRITE_COMMAND)
for (unsigned int i = 0; i < init->get_data_length(); i++)
if (trans.get_data_ptr()[i] != init->get_data_ptr()[i])
{
txt << "Data array modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_byte_enable_ptr() != m_map[&trans].byte_enable_ptr)
{
txt << "Byte enable pointer attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_byte_enable_length() != init->get_byte_enable_length())
{
txt << "Byte enable length attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_byte_enable_ptr())
for (unsigned int i = 0; i < init->get_byte_enable_length(); i++)
if (trans.get_byte_enable_ptr()[i] != init->get_byte_enable_ptr()[i])
{
txt << "Byte enable array modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (trans.get_streaming_width() != init->get_streaming_width())
{
txt << "Streaming width attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.7");
}
if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
{
txt << "Generic payload option attribute modified during transaction lifetime, detected in " << txt2;
tlm2error(trans, "14.8 g)");
}
if ( !m_map[&trans].has_mm )
{
if (trans.has_mm())
{
txt << "Interconnect component sets a memory manager, but does not clear it on return, detected in " << txt2;
tlm2error(trans, "14.5 aa)");
}
for (unsigned int i = 0; i < tlm::max_num_extensions(); i++)
// Exclude tlm_endian_context extension from the check because it is not cloned in m_map
if (i != tlm::tlm_endian_context::ID)
if (trans.get_extension(i))
if ( !m_map[&trans].gp->get_extension(i) )
{
txt << "Extension set (index = " << i << ") without also being deleted in the absence of a memory manager, detected in " << txt2;
tlm2error(trans, "14.5 aa)");
}
}
uchar_t* resp_data_ptr = shared_map[&trans].resp_data_ptr;
if (resp_data_ptr)
for (unsigned int i = 0; i < trans.get_data_length(); i++)
if (trans.get_data_ptr()[i] != resp_data_ptr[i])
{
txt << "Transaction data array modified in interconnect component on response path, detected in " << txt2;
tlm2error(trans, "14.7");
}
}
BOILERPLATE
check_response_path(
tlm::tlm_generic_payload& trans, const char* txt2 )
{
if ( !shared_map[&trans].path.empty() )
{
if ( this != shared_map[&trans].path.back() )
{
txt << "BEGIN_RESP path is not the reverse of the BEGIN_REQ path.";
txt << "\nBEGIN_REQ path includes these checkers: -> ";
deque_t path = shared_map[&trans].path;
for (deque_t::iterator i = path.begin(); i < path.end(); i++)
txt << (*i)->name() << " -> ";
txt << "\nDetected in " << txt2;
tlm2error(trans, "15.2.11 a)");
}
shared_map[&trans].path.pop_back();
shared_map[&trans].response_in_progress = !shared_map[&trans].path.empty();
shared_map[&trans].ok_response = trans.is_response_ok();
// Create a copy of the data array for comparison on the response path
if ( !shared_map[&trans].resp_data_ptr )
{
shared_map[&trans].resp_data_ptr = new uchar_t[trans.get_data_length()];
memcpy(shared_map[&trans].resp_data_ptr, trans.get_data_ptr(), trans.get_data_length());
}
}
}
BOILERPLATE
get_direct_mem_ptr_pre_checks(
tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data )
{
remember_gp_option(trans);
if (dmi_data.get_dmi_ptr() != 0)
{
txt << "DMI descriptor not properly initialized: dmi_ptr != 0";
tlm2error(trans, "11.2.5 f)");
}
if (!dmi_data.is_none_allowed())
{
txt << "DMI descriptor not properly initialized: granted_access != DMI_ACCESS_NONE";
tlm2error(trans, "11.2.5 a)");
}
if (dmi_data.get_start_address() != 0)
{
txt << "DMI descriptor not properly initialized: start_address != 0";
tlm2error(trans, "11.2.5 u)");
}
if (dmi_data.get_end_address() != (sc_dt::uint64)(-1))
{
txt << "DMI descriptor not properly initialized: end_address != 0";
tlm2error(trans, "11.2.5 u)");
}
if (dmi_data.get_read_latency() != sc_core::SC_ZERO_TIME)
{
txt << "DMI descriptor not properly initialized: read_latency != SC_ZERO_TIME";
tlm2error(trans, "11.2.5 ac)");
}
if (dmi_data.get_write_latency() != sc_core::SC_ZERO_TIME)
{
txt << "DMI descriptor not properly initialized: write_latency != SC_ZERO_TIME";
tlm2error(trans, "11.2.5 ac)");
}
if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD)
{
/*
if (trans.is_dmi_allowed()) // Would be rather brutal to flag dmi_allowed as an arror for a DMI transaction!
{
txt << "DMI transaction not properly initialized: dmi_allowed == true";
tlm2error(trans, "14.8 e) & 14.16");
}
*/
if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
{
txt << "DMI transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE";
tlm2error(trans, "14.8 e) & 14.17 e)");
}
}
else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED)
{
txt << "DMI transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED";
tlm2error(trans, "14.8 c) & e) & j)");
}
}
BOILERPLATE
get_direct_mem_ptr_post_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data )
{
tlm::tlm_generic_payload* init = m_map[&trans].gp;
if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
{
txt << "DMI transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime";
tlm2error(trans, "14.8 h)");
}
else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD)
{
txt << "DMI transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD";
tlm2error(trans, "14.8 j)");
}
}
BOILERPLATE
transport_dbg_pre_checks( tlm::tlm_generic_payload& trans )
{
remember_gp_option(trans);
if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0)
{
txt << "Debug transaction has data_ptr == 0";
tlm2error(trans, "11.3.4 l)");
}
if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD)
{
if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0)
{
txt << "Debug transaction not properly initialized: "
<< "byte_enable_ptr != 0 and byte_enable_length == 0";
tlm2error(trans, "14.8 f) & 14.14 f)");
}
if (trans.get_streaming_width() == 0)
{
txt << "Debug transaction not properly initialized: streaming_width == 0";
tlm2error(trans, "14.8 f) & 14.15 f)");
}
if (trans.is_dmi_allowed())
{
txt << "Debug transaction not properly initialized: dmi_allowed == true";
tlm2error(trans, "14.8 f) & 14.16");
}
if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE)
{
txt << "Debug transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE";
tlm2error(trans, "14.8 f) & 14.17 e)");
}
}
else if (trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD_ACCEPTED)
{
txt << "Debug transaction not properly initialized: gp_option == TLM_FULL_PAYLOAD_ACCEPTED";
tlm2error(trans, "14.8 c) & f) & l)");
}}
BOILERPLATE
transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count )
{
tlm::tlm_generic_payload* init = m_map[&trans].gp;
if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0)
{
txt << "Debug transaction has data_ptr == 0";
tlm2error(trans, "11.3.4 l)");
}
if (count > trans.get_data_length())
{
txt << "Count returned from transport_dbg is greater than data_length";
tlm2error(trans, "11.3.4 s)");
}
if (init->get_gp_option() == tlm::TLM_MIN_PAYLOAD && trans.get_gp_option() != tlm::TLM_MIN_PAYLOAD)
{
txt << "Debug transaction gp_option attribute value TLM_MIN_PAYLOAD modified during transaction lifetime";
tlm2error(trans, "14.8 h)");
}
else if (init->get_gp_option() == tlm::TLM_FULL_PAYLOAD && trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD)
{
txt << "Debug transaction gp_option attribute value changed from TLM_FULL_PAYLOAD to TLM_MIN_PAYLOAD";
tlm2error(trans, "14.8 l)");
}}
BOILERPLATE
tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning )
{
txt << "\n\nRefer to IEEE Std 1666-2011, clause " << ref;
txt << "\n\nChecker instance: " << this->name();
txt << "\n\nTransaction details:";
txt << "\n has_mm = " << dec << trans.has_mm() << " (bool)";
txt << "\n ref_count = " << dec << trans.get_ref_count() << " (int)";
txt << "\n\n gp_option = " <<
(trans.get_gp_option() == tlm::TLM_MIN_PAYLOAD ? "TLM_MIN_PAYLOAD"
:trans.get_gp_option() == tlm::TLM_FULL_PAYLOAD ? "TLM_FULL_PAYLOAD"
: "TLM_FULL_PAYLOAD_ACCEPTED");
txt << "\n command = " <<
(trans.get_command() == tlm::TLM_READ_COMMAND ? "TLM_READ_COMMAND"
:trans.get_command() == tlm::TLM_WRITE_COMMAND ? "TLM_WRITE_COMMAND"
: "TLM_IGNORE_COMMAND");
txt << "\n address = " << hex << trans.get_address() << " (hex)";
txt << "\n data_ptr = " << hex
<< reinterpret_cast<int*>(trans.get_data_ptr()) << " (hex)";
txt << "\n data_length = " << hex << trans.get_data_length() << " (hex)";
txt << "\n streaming_width = " << hex << trans.get_streaming_width() << " (hex)";
txt << "\n byte_enable_ptr = " << hex
<< reinterpret_cast<int*>(trans.get_byte_enable_ptr()) << " (hex)";
txt << "\n byte_enable_length = " << hex << trans.get_byte_enable_length() << " (hex)";
txt << "\n dmi_allowed = " << dec << trans.is_dmi_allowed() << " (bool)";
txt << "\n response_status = " << trans.get_response_string();
bool extensions_present = false;
for (unsigned int i = 0; i < tlm::max_num_extensions(); i++)
{
tlm::tlm_extension_base* ext = trans.get_extension(i);
if (ext)
{
if (!extensions_present)
txt << "\n\n extensions:";
txt << "\n index = " << i << " type = " << typeid(*ext).name();
extensions_present = true;
}
}
txt << "\n\n";
if (warning)
SC_REPORT_WARNING("tlm2_protocol_checker", txt.str().c_str());
else
SC_REPORT_ERROR("tlm2_protocol_checker", txt.str().c_str());
}
} // namespace tlm_utils
#endif // __tlm2_base_protocol_checker__