1049 lines
38 KiB
C++
1049 lines
38 KiB
C++
|
|
// 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__
|