Started from SystemC-Quickstart
This commit is contained in:
18
components/components.h
Normal file
18
components/components.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* components.h
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_H_
|
||||
#define COMPONENTS_H_
|
||||
|
||||
//#include "tx_example_mods.h"
|
||||
#include <systemc.h>
|
||||
#include "logging.h"
|
||||
#include "initiator.h"
|
||||
#include "router.h"
|
||||
#include "target.h"
|
||||
|
||||
#endif /* COMPONENTS_H_ */
|
136
components/initiator.cpp
Normal file
136
components/initiator.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* initiator.cpp
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#include "initiator.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
Initiator::Initiator(::sc_core::sc_module_name nm)
|
||||
: socket("socket") // Construct and name socket
|
||||
, dmi_ptr_valid(false)
|
||||
{
|
||||
// Register callbacks for incoming interface method calls
|
||||
socket.register_invalidate_direct_mem_ptr(this, &Initiator::invalidate_direct_mem_ptr);
|
||||
|
||||
SC_THREAD(thread_process);
|
||||
}
|
||||
|
||||
void Initiator::thread_process() {
|
||||
{
|
||||
// TLM-2 generic payload transaction, reused across calls to b_transport, DMI and debug
|
||||
tlm::tlm_generic_payload* trans = new tlm::tlm_generic_payload;
|
||||
sc_time delay = sc_time(10, SC_NS);
|
||||
|
||||
// Generate a random sequence of reads and writes
|
||||
for (int i = 256-64; i < 256+64; i += 4)
|
||||
{
|
||||
int data;
|
||||
tlm::tlm_command cmd = static_cast<tlm::tlm_command>(rand() % 2);
|
||||
if (cmd == tlm::TLM_WRITE_COMMAND) data = 0xFF000000 | i;
|
||||
|
||||
// Use DMI if it is available
|
||||
if (dmi_ptr_valid && sc_dt::uint64(i) >= dmi_data.get_start_address()
|
||||
&& sc_dt::uint64(i) <= dmi_data.get_end_address())
|
||||
{
|
||||
// Bypass transport interface and use direct memory interface
|
||||
// Implement target latency
|
||||
if ( cmd == tlm::TLM_READ_COMMAND )
|
||||
{
|
||||
assert( dmi_data.is_read_allowed() );
|
||||
memcpy(&data, dmi_data.get_dmi_ptr() + i - dmi_data.get_start_address(), 4);
|
||||
wait( dmi_data.get_read_latency() );
|
||||
}
|
||||
else if ( cmd == tlm::TLM_WRITE_COMMAND )
|
||||
{
|
||||
assert( dmi_data.is_write_allowed() );
|
||||
memcpy(dmi_data.get_dmi_ptr() + i - dmi_data.get_start_address(), &data, 4);
|
||||
wait( dmi_data.get_write_latency() );
|
||||
}
|
||||
|
||||
LOG_INFO << "DMI = { " << (cmd ? 'W' : 'R') << ", " << hex << i
|
||||
<< " } , data = " << hex << data << " at time " << sc_time_stamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
trans->set_command( cmd );
|
||||
trans->set_address( i );
|
||||
trans->set_data_ptr( reinterpret_cast<unsigned char*>(&data) );
|
||||
trans->set_data_length( 4 );
|
||||
trans->set_streaming_width( 4 ); // = data_length to indicate no streaming
|
||||
trans->set_byte_enable_ptr( 0 ); // 0 indicates unused
|
||||
trans->set_dmi_allowed( false ); // Mandatory initial value
|
||||
trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE ); // Mandatory initial value
|
||||
|
||||
|
||||
#ifdef INJECT_ERROR
|
||||
if (i > 90) trans->set_streaming_width(2);
|
||||
#endif
|
||||
|
||||
// Other fields default: byte enable = 0, streaming width = 0, DMI_hint = false, no extensions
|
||||
|
||||
socket->b_transport( *trans, delay ); // Blocking transport call
|
||||
|
||||
// Initiator obliged to check response status
|
||||
if ( trans->is_response_error() )
|
||||
{
|
||||
// Print response string
|
||||
char txt[100];
|
||||
sprintf(txt, "Error from b_transport, response status = %s",
|
||||
trans->get_response_string().c_str());
|
||||
SC_REPORT_ERROR("TLM-2", txt);
|
||||
|
||||
}
|
||||
|
||||
// Check DMI hint
|
||||
if ( trans->is_dmi_allowed() )
|
||||
{
|
||||
// *********************************************
|
||||
// Re-use transaction object for DMI. Reset the address because it could
|
||||
// have been modified by the interconnect on the previous transport call
|
||||
// *********************************************
|
||||
|
||||
trans->set_address( i );
|
||||
dmi_ptr_valid = socket->get_direct_mem_ptr( *trans, dmi_data );
|
||||
}
|
||||
|
||||
LOG_INFO << "trans = { " << (cmd ? 'W' : 'R') << ", " << hex << i
|
||||
<< " } , data = " << hex << data << " at time " << sc_time_stamp();
|
||||
}
|
||||
}
|
||||
|
||||
// Use debug transaction interface to dump memory contents, reusing same transaction object
|
||||
sc_dt::uint64 A = 128;
|
||||
trans->set_address(A);
|
||||
trans->set_read();
|
||||
trans->set_data_length(256);
|
||||
|
||||
unsigned char* data = new unsigned char[256];
|
||||
trans->set_data_ptr(data);
|
||||
|
||||
unsigned int n_bytes = socket->transport_dbg( *trans );
|
||||
|
||||
for (unsigned int i = 0; i < n_bytes; i += 4)
|
||||
{
|
||||
LOG_INFO << "mem[" << (A + i) << "] = "
|
||||
<< *(reinterpret_cast<unsigned int*>( &data[i] ));
|
||||
}
|
||||
|
||||
A = 256;
|
||||
trans->set_address(A);
|
||||
trans->set_data_length(128);
|
||||
|
||||
n_bytes = socket->transport_dbg( *trans );
|
||||
|
||||
for (unsigned int i = 0; i < n_bytes; i += 4)
|
||||
{
|
||||
LOG_INFO << "mem[" << (A + i) << "] = "
|
||||
<< *(reinterpret_cast<unsigned int*>( &data[i] ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
36
components/initiator.h
Normal file
36
components/initiator.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef INITIATOR_H
|
||||
#define INITIATOR_H
|
||||
|
||||
#include "systemc"
|
||||
using namespace sc_core;
|
||||
using namespace sc_dt;
|
||||
using namespace std;
|
||||
|
||||
#include "tlm.h"
|
||||
#include "tlm_utils/simple_initiator_socket.h"
|
||||
|
||||
|
||||
// Initiator module generating generic payload transactions
|
||||
|
||||
struct Initiator: sc_module
|
||||
{
|
||||
// TLM-2 socket, defaults to 32-bits wide, base protocol
|
||||
tlm_utils::simple_initiator_socket<Initiator> socket;
|
||||
|
||||
SC_HAS_PROCESS(Initiator);
|
||||
|
||||
Initiator( ::sc_core::sc_module_name );
|
||||
|
||||
void thread_process();
|
||||
|
||||
// TLM-2 backward DMI method
|
||||
void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, sc_dt::uint64 end_range){
|
||||
// Ignore range and invalidate all DMI pointers regardless
|
||||
dmi_ptr_valid = false;
|
||||
}
|
||||
|
||||
bool dmi_ptr_valid;
|
||||
tlm::tlm_dmi dmi_data;
|
||||
};
|
||||
|
||||
#endif
|
115
components/logging.cpp
Normal file
115
components/logging.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* logging.cpp
|
||||
*
|
||||
* Created on: 24.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
|
||||
#include "logging.h"
|
||||
#include <systemc>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace sc_core;
|
||||
|
||||
enum log_level {NONE, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE};
|
||||
|
||||
namespace {
|
||||
|
||||
static std::deque<std::string> msg_buf;
|
||||
|
||||
inline log_level verbosity2log(int verb) {
|
||||
if (verb >= sc_core::SC_FULL) return TRACE;
|
||||
if (verb >= sc_core::SC_HIGH) return DEBUG;
|
||||
return INFO;
|
||||
}
|
||||
|
||||
std::string time2string(const sc_core::sc_time &t) {
|
||||
const std::array<const char *, 6> time_units{"fs", "ps", "ns", "us", "ms", "s "};
|
||||
const std::array<uint64_t, 6> multiplier{1ULL,
|
||||
1000ULL,
|
||||
1000ULL * 1000,
|
||||
1000ULL * 1000 * 1000,
|
||||
1000ULL * 1000 * 1000 * 1000,
|
||||
1000ULL * 1000 * 1000 * 1000 * 1000};
|
||||
std::ostringstream oss;
|
||||
const sc_core::sc_time_tuple tt{t};
|
||||
const auto val = tt.value();
|
||||
if (!val) {
|
||||
oss << "0 s";
|
||||
} else {
|
||||
const unsigned scale = tt.unit();
|
||||
const auto fs_val = val * multiplier[scale];
|
||||
for (int j = multiplier.size() - 1; j >= scale; --j) {
|
||||
if (fs_val > multiplier[j]) {
|
||||
const auto i = val / multiplier[j - scale];
|
||||
const auto f = val % multiplier[j - scale];
|
||||
oss << i << '.' << std::setw(3 * (j - scale)) << std::setfill('0') << std::left << f << ' '
|
||||
<< time_units[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
const std::string compose_message(const sc_report &rep) {
|
||||
std::stringstream os;
|
||||
os << "[" << std::setw(20) << time2string(sc_core::sc_time_stamp()) << "] ";
|
||||
if (rep.get_id() >= 0)
|
||||
os << "("
|
||||
<< "IWEF"[rep.get_severity()] << rep.get_id() << ") ";
|
||||
os << rep.get_msg_type();
|
||||
if (*rep.get_msg()) os << ": " << rep.get_msg();
|
||||
if (rep.get_severity() > SC_INFO) {
|
||||
std::array<char, 16> line_number_str;
|
||||
os << " [FILE:" << rep.get_file_name() << ":" << rep.get_line_number() << "]";
|
||||
sc_simcontext *simc = sc_get_curr_simcontext();
|
||||
if (simc && sc_is_running()) {
|
||||
const char *proc_name = rep.get_process_name();
|
||||
if (proc_name) os << "[PROCESS:" << proc_name << "]";
|
||||
}
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void report_handler(const sc_report &rep, const sc_actions &actions) {
|
||||
std::array<const log_level, 4> map = {{INFO, WARNING, ERROR, FATAL}};
|
||||
if (actions & SC_DISPLAY) {
|
||||
auto level = rep.get_severity() > sc_core::SC_INFO ? map[rep.get_severity()] : verbosity2log(rep.get_verbosity());
|
||||
msg_buf.push_back(compose_message(rep));
|
||||
}
|
||||
if (actions & SC_STOP) sc_stop();
|
||||
if (actions & SC_ABORT) abort();
|
||||
if (actions & SC_THROW) throw rep;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_output(){
|
||||
return !msg_buf.empty();
|
||||
}
|
||||
|
||||
std::string get_output(){
|
||||
std::string ret = msg_buf.front();
|
||||
msg_buf.pop_front();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_logging(unsigned level) {
|
||||
const std::array<int, 8> verbosity = {SC_NONE, // Logging::NONE
|
||||
SC_LOW, // Logging::FATAL
|
||||
SC_LOW, // Logging::ERROR
|
||||
SC_LOW, // Logging::WARNING
|
||||
SC_MEDIUM, // Logging::INFO
|
||||
SC_HIGH, // logging::DEBUG
|
||||
SC_FULL, // logging::TRACE
|
||||
SC_DEBUG}; // logging::TRACE+1
|
||||
sc_report_handler::set_verbosity_level(verbosity[level]);
|
||||
sc_report_handler::set_handler(report_handler);
|
||||
}
|
||||
|
||||
|
||||
|
42
components/logging.h
Normal file
42
components/logging.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* logging.h
|
||||
*
|
||||
* Created on: 24.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#ifndef LOGGING_H_
|
||||
#define LOGGING_H_
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <systemc.h>
|
||||
|
||||
bool has_output();
|
||||
std::string get_output();
|
||||
void init_logging(unsigned level);
|
||||
|
||||
class Log {
|
||||
public:
|
||||
Log(const char* file, int line):messageLevel(sc_core::SC_INFO), file(file), line(line){};
|
||||
Log(const Log&) = delete;
|
||||
Log& operator =(const Log&) = delete;
|
||||
virtual ~Log(){
|
||||
::sc_core::sc_report_handler::report(messageLevel, "", os.str().c_str(), file, line );
|
||||
}
|
||||
std::ostringstream& Get(sc_core::sc_severity level = sc_core::SC_INFO){
|
||||
messageLevel = level;
|
||||
return os;
|
||||
}
|
||||
protected:
|
||||
std::ostringstream os;
|
||||
sc_core::sc_severity messageLevel;
|
||||
const char* file;
|
||||
int line;
|
||||
};
|
||||
|
||||
#define LOG(level) Log(__FILE__, __LINE__).Get(level)
|
||||
#define LOG_INFO Log(__FILE__, __LINE__).Get(sc_core::SC_INFO)
|
||||
#define LOG_WARN Log(__FILE__, __LINE__).Get(sc_core::SC_WARNING)
|
||||
#define LOG_ERR Log(__FILE__, __LINE__).Get(sc_core::SC_ERROR)
|
||||
#endif /* LOGGING_H_ */
|
133
components/router.h
Normal file
133
components/router.h
Normal file
@ -0,0 +1,133 @@
|
||||
#ifndef ROUTER_H
|
||||
#define ROUTER_H
|
||||
|
||||
#include "systemc"
|
||||
using namespace sc_core;
|
||||
using namespace sc_dt;
|
||||
using namespace std;
|
||||
|
||||
#include "tlm.h"
|
||||
#include "tlm_utils/simple_initiator_socket.h"
|
||||
#include "tlm_utils/simple_target_socket.h"
|
||||
|
||||
|
||||
// *********************************************
|
||||
// Generic payload blocking transport router
|
||||
// *********************************************
|
||||
|
||||
template<unsigned int N_TARGETS>
|
||||
struct Router: sc_module
|
||||
{
|
||||
// TLM-2 socket, defaults to 32-bits wide, base protocol
|
||||
tlm_utils::simple_target_socket<Router> target_socket;
|
||||
|
||||
// *********************************************
|
||||
// Use tagged sockets to be able to distinguish incoming backward path calls
|
||||
// *********************************************
|
||||
|
||||
sc_core::sc_vector<tlm_utils::simple_initiator_socket_tagged<Router>> initiator_socket;
|
||||
|
||||
SC_CTOR(Router)
|
||||
: target_socket("target_socket")
|
||||
, initiator_socket("socket", N_TARGETS)
|
||||
{
|
||||
// Register callbacks for incoming interface method calls
|
||||
target_socket.register_b_transport( this, &Router::b_transport);
|
||||
target_socket.register_get_direct_mem_ptr(this, &Router::get_direct_mem_ptr);
|
||||
target_socket.register_transport_dbg( this, &Router::transport_dbg);
|
||||
|
||||
for (unsigned int i = 0; i < N_TARGETS; i++)
|
||||
{
|
||||
char txt[20];
|
||||
sprintf(txt, "socket_%d", i);
|
||||
|
||||
// *********************************************
|
||||
// Register callbacks for incoming interface method calls, including tags
|
||||
// *********************************************
|
||||
initiator_socket[i].register_invalidate_direct_mem_ptr(this, &Router::invalidate_direct_mem_ptr, i);
|
||||
}
|
||||
}
|
||||
|
||||
// ****************
|
||||
// FORWARD PATH
|
||||
// ****************
|
||||
|
||||
// TLM-2 blocking transport method
|
||||
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay )
|
||||
{
|
||||
sc_dt::uint64 address = trans.get_address();
|
||||
sc_dt::uint64 masked_address;
|
||||
unsigned int target_nr = decode_address( address, masked_address);
|
||||
|
||||
// Modify address within transaction
|
||||
trans.set_address( masked_address );
|
||||
|
||||
// Forward transaction to appropriate target
|
||||
initiator_socket[target_nr]->b_transport( trans, delay );
|
||||
}
|
||||
|
||||
// TLM-2 forward DMI method
|
||||
virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
|
||||
tlm::tlm_dmi& dmi_data)
|
||||
{
|
||||
sc_dt::uint64 masked_address;
|
||||
unsigned int target_nr = decode_address( trans.get_address(), masked_address );
|
||||
trans.set_address( masked_address );
|
||||
|
||||
bool status = initiator_socket[target_nr]->get_direct_mem_ptr( trans, dmi_data );
|
||||
|
||||
// Calculate DMI address of target in system address space
|
||||
dmi_data.set_start_address( compose_address( target_nr, dmi_data.get_start_address() ));
|
||||
dmi_data.set_end_address ( compose_address( target_nr, dmi_data.get_end_address() ));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// TLM-2 debug transaction method
|
||||
virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans)
|
||||
{
|
||||
sc_dt::uint64 masked_address;
|
||||
unsigned int target_nr = decode_address( trans.get_address(), masked_address );
|
||||
trans.set_address( masked_address );
|
||||
|
||||
// Forward debug transaction to appropriate target
|
||||
return initiator_socket[target_nr]->transport_dbg( trans );
|
||||
}
|
||||
|
||||
// ****************
|
||||
// BACKWARD PATH
|
||||
// ****************
|
||||
|
||||
// **************************
|
||||
// Tagged backward DMI method
|
||||
// **************************
|
||||
|
||||
virtual void invalidate_direct_mem_ptr(int id,
|
||||
sc_dt::uint64 start_range,
|
||||
sc_dt::uint64 end_range)
|
||||
{
|
||||
// Reconstruct address range in system memory map
|
||||
sc_dt::uint64 bw_start_range = compose_address( id, start_range );
|
||||
sc_dt::uint64 bw_end_range = compose_address( id, end_range );
|
||||
target_socket->invalidate_direct_mem_ptr(bw_start_range, bw_end_range);
|
||||
}
|
||||
|
||||
// ****************
|
||||
// ROUTER INTERNALS
|
||||
// ****************
|
||||
|
||||
// Simple fixed address decoding
|
||||
inline unsigned int decode_address( sc_dt::uint64 address, sc_dt::uint64& masked_address )
|
||||
{
|
||||
unsigned int target_nr = static_cast<unsigned int>( (address >> 8) & 0x3 );
|
||||
masked_address = address & 0xFF;
|
||||
return target_nr;
|
||||
}
|
||||
|
||||
inline sc_dt::uint64 compose_address( unsigned int target_nr, sc_dt::uint64 address)
|
||||
{
|
||||
return (target_nr << 8) | (address & 0xFF);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
18
components/router_example.cpp
Normal file
18
components/router_example.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* router_example.cpp
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
|
||||
#include "top.h"
|
||||
|
||||
int sc_main(int argc, char* argv[])
|
||||
{
|
||||
Top top("top");
|
||||
sc_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
102
components/target.cpp
Normal file
102
components/target.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* target.cpp
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#include "target.h"
|
||||
|
||||
void Memory::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay) {
|
||||
tlm::tlm_command cmd = trans.get_command();
|
||||
sc_dt::uint64 adr = trans.get_address() / 4;
|
||||
unsigned char* ptr = trans.get_data_ptr();
|
||||
unsigned int len = trans.get_data_length();
|
||||
unsigned char* byt = trans.get_byte_enable_ptr();
|
||||
unsigned int wid = trans.get_streaming_width();
|
||||
|
||||
// Obliged to check address range and check for unsupported features,
|
||||
// i.e. byte enables, streaming, and bursts
|
||||
// Can ignore extensions
|
||||
|
||||
// Generate the appropriate error response
|
||||
if (adr >= SIZE) {
|
||||
trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
|
||||
return;
|
||||
}
|
||||
if (byt != 0) {
|
||||
trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
|
||||
return;
|
||||
}
|
||||
if (len > 4 || wid < len) {
|
||||
trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
|
||||
return;
|
||||
}
|
||||
|
||||
wait(delay);
|
||||
delay = SC_ZERO_TIME;
|
||||
|
||||
// Obliged to implement read and write commands
|
||||
if ( cmd == tlm::TLM_READ_COMMAND )
|
||||
memcpy(ptr, &mem[adr], len);
|
||||
else if ( cmd == tlm::TLM_WRITE_COMMAND )
|
||||
memcpy(&mem[adr], ptr, len);
|
||||
|
||||
// Set DMI hint to indicated that DMI is supported
|
||||
trans.set_dmi_allowed(true);
|
||||
|
||||
// Obliged to set response status to indicate successful completion
|
||||
trans.set_response_status( tlm::TLM_OK_RESPONSE );
|
||||
}
|
||||
|
||||
bool Memory::get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
|
||||
tlm::tlm_dmi& dmi_data)
|
||||
{
|
||||
// Permit read and write access
|
||||
dmi_data.allow_read_write();
|
||||
|
||||
// Set other details of DMI region
|
||||
dmi_data.set_dmi_ptr( reinterpret_cast<unsigned char*>( &mem[0] ) );
|
||||
dmi_data.set_start_address( 0 );
|
||||
dmi_data.set_end_address( SIZE*4-1 );
|
||||
dmi_data.set_read_latency( LATENCY );
|
||||
dmi_data.set_write_latency( LATENCY );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Memory::Memory(sc_core::sc_module_name nm)
|
||||
: socket("socket"), LATENCY(10, SC_NS)
|
||||
{
|
||||
// Register callbacks for incoming interface method calls
|
||||
socket.register_b_transport( this, &Memory::b_transport);
|
||||
socket.register_get_direct_mem_ptr(this, &Memory::get_direct_mem_ptr);
|
||||
socket.register_transport_dbg( this, &Memory::transport_dbg);
|
||||
|
||||
// Initialize memory with random data
|
||||
for (int i = 0; i < SIZE; i++)
|
||||
mem[i] = 0xAA000000 | (mem_nr << 20) | (rand() % 256);
|
||||
|
||||
++mem_nr;
|
||||
}
|
||||
|
||||
// TLM-2 debug transaction method
|
||||
unsigned int Memory::transport_dbg(tlm::tlm_generic_payload& trans)
|
||||
{
|
||||
tlm::tlm_command cmd = trans.get_command();
|
||||
sc_dt::uint64 adr = trans.get_address() / 4;
|
||||
unsigned char* ptr = trans.get_data_ptr();
|
||||
unsigned int len = trans.get_data_length();
|
||||
|
||||
// Calculate the number of bytes to be actually copied
|
||||
unsigned int num_bytes = (len < (SIZE - adr) * 4) ? len : (SIZE - adr) * 4;
|
||||
|
||||
if ( cmd == tlm::TLM_READ_COMMAND )
|
||||
memcpy(ptr, &mem[adr], num_bytes);
|
||||
else if ( cmd == tlm::TLM_WRITE_COMMAND )
|
||||
memcpy(&mem[adr], ptr, num_bytes);
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
|
46
components/target.h
Normal file
46
components/target.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef TARGET_H
|
||||
#define TARGET_H
|
||||
|
||||
// Needed for the simple_target_socket
|
||||
#define SC_INCLUDE_DYNAMIC_PROCESSES
|
||||
|
||||
#include "systemc"
|
||||
using namespace sc_core;
|
||||
using namespace sc_dt;
|
||||
using namespace std;
|
||||
|
||||
#include "tlm.h"
|
||||
#include "tlm_utils/simple_target_socket.h"
|
||||
|
||||
|
||||
// Target module representing a simple memory
|
||||
|
||||
struct Memory: sc_module
|
||||
{
|
||||
// TLM-2 socket, defaults to 32-bits wide, base protocol
|
||||
tlm_utils::simple_target_socket<Memory> socket;
|
||||
|
||||
enum { SIZE = 256 };
|
||||
const sc_time LATENCY;
|
||||
|
||||
SC_HAS_PROCESS(Memory);
|
||||
|
||||
Memory(sc_core::sc_module_name nm);
|
||||
|
||||
protected:
|
||||
// TLM-2 blocking transport method
|
||||
void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay );
|
||||
|
||||
// TLM-2 forward DMI method
|
||||
bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
|
||||
tlm::tlm_dmi& dmi_data);
|
||||
// TLM-2 debug transaction method
|
||||
unsigned int transport_dbg(tlm::tlm_generic_payload& trans);
|
||||
|
||||
int mem[SIZE];
|
||||
static unsigned int mem_nr;
|
||||
};
|
||||
|
||||
unsigned int Memory::mem_nr = 0;
|
||||
|
||||
#endif
|
33
components/top.h
Normal file
33
components/top.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef TOP_H
|
||||
#define TOP_H
|
||||
|
||||
#include "initiator.h"
|
||||
#include "target.h"
|
||||
#include "router.h"
|
||||
|
||||
SC_MODULE(Top)
|
||||
{
|
||||
Initiator* initiator;
|
||||
Router<4>* router;
|
||||
Memory* memory[4];
|
||||
|
||||
SC_CTOR(Top)
|
||||
{
|
||||
// Instantiate components
|
||||
initiator = new Initiator("initiator");
|
||||
router = new Router<4>("router");
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char txt[20];
|
||||
sprintf(txt, "memory_%d", i);
|
||||
memory[i] = new Memory(txt);
|
||||
}
|
||||
|
||||
// Bind sockets
|
||||
initiator->socket.bind( router->target_socket );
|
||||
for (int i = 0; i < 4; i++)
|
||||
router->initiator_socket[i].bind( memory[i]->socket );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
44
components/tx_example.cpp
Normal file
44
components/tx_example.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
// -*- C++ -*- <this line is for emacs to recognize it as C++ code>
|
||||
/*****************************************************************************
|
||||
|
||||
Licensed to Accellera Systems Initiative Inc. (Accellera)
|
||||
under one or more contributor license agreements. See the
|
||||
NOTICE file distributed with this work for additional
|
||||
information regarding copyright ownership. Accellera licenses
|
||||
this file to you 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.
|
||||
|
||||
*****************************************************************************/
|
||||
// this code compiles and runs with our latest prototype for this specification
|
||||
|
||||
#include "txtop.h"
|
||||
|
||||
int sc_main (int argc , char *argv[])
|
||||
{
|
||||
scv_startup();
|
||||
|
||||
scv_tr_text_init();
|
||||
scv_tr_db db("transaction_example.txlog");
|
||||
scv_tr_db::set_default_db(&db);
|
||||
|
||||
tx_top top("top");
|
||||
|
||||
// Accellera SystemC >=2.2 got picky about multiple drivers.
|
||||
// Disable check for bus simulation.
|
||||
sc_report_handler::set_actions(SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, SC_DO_NOTHING);
|
||||
// run the simulation
|
||||
sc_start(1.0, SC_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
140
components/tx_example_mods.cpp
Normal file
140
components/tx_example_mods.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
// -*- C++ -*- <this line is for emacs to recognize it as C++ code>
|
||||
/*****************************************************************************
|
||||
|
||||
Licensed to Accellera Systems Initiative Inc. (Accellera)
|
||||
under one or more contributor license agreements. See the
|
||||
NOTICE file distributed with this work for additional
|
||||
information regarding copyright ownership. Accellera licenses
|
||||
this file to you 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.
|
||||
|
||||
*****************************************************************************/
|
||||
// this code compiles and runs with our latest prototype for this specification
|
||||
|
||||
#include "tx_example_mods.h"
|
||||
|
||||
rw_task_if::data_t rw_pipelined_transactor::read(const rw_task_if::addr_t*
|
||||
addr) {
|
||||
addr_phase.lock();
|
||||
scv_tr_handle h = read_gen.begin_transaction(*addr);
|
||||
|
||||
scv_tr_handle h1 = addr_gen.begin_transaction(*addr,"addr_phase",h);
|
||||
wait(clk->posedge_event());
|
||||
bus_addr = *addr;
|
||||
addr_req = 1;
|
||||
wait(addr_ack->posedge_event());
|
||||
wait(clk->negedge_event());
|
||||
addr_req = 0;
|
||||
wait(addr_ack->negedge_event());
|
||||
addr_gen.end_transaction(h1);
|
||||
addr_phase.unlock();
|
||||
|
||||
data_phase.lock();
|
||||
scv_tr_handle h2 = data_gen.begin_transaction("data_phase",h);
|
||||
wait(data_rdy->posedge_event());
|
||||
data_t data = bus_data.read();
|
||||
wait(data_rdy->negedge_event());
|
||||
data_gen.end_transaction(h2);
|
||||
read_gen.end_transaction(h,data);
|
||||
data_phase.unlock();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void rw_pipelined_transactor::write(const write_t * req) {
|
||||
scv_tr_handle h = write_gen.begin_transaction(req->addr);
|
||||
// ...
|
||||
write_gen.end_transaction(h,req->data);
|
||||
}
|
||||
|
||||
void test::main() {
|
||||
// simple sequential tests
|
||||
for (int i=0; i<3; i++) {
|
||||
rw_task_if::addr_t addr = i;
|
||||
rw_task_if::data_t data = transactor->read(&addr);
|
||||
cout << "at time " << sc_time_stamp() << ": ";
|
||||
cout << "received data : " << data << endl;
|
||||
}
|
||||
|
||||
scv_smart_ptr<rw_task_if::addr_t> addr;
|
||||
for (int i=0; i<3; i++) {
|
||||
|
||||
addr->next();
|
||||
rw_task_if::data_t data = transactor->read( addr->get_instance() );
|
||||
cout << "data for address " << *addr << " is " << data << endl;
|
||||
}
|
||||
|
||||
scv_smart_ptr<rw_task_if::write_t> write;
|
||||
for (int i=0; i<3; i++) {
|
||||
write->next();
|
||||
transactor->write( write->get_instance() );
|
||||
cout << "send data : " << write->data << endl;
|
||||
}
|
||||
|
||||
scv_smart_ptr<int> data;
|
||||
scv_bag<int> distribution;
|
||||
distribution.push(1,40);
|
||||
distribution.push(2,60);
|
||||
data->set_mode(distribution);
|
||||
for (int i=0;i<3; i++) { data->next(); process(data); }
|
||||
}
|
||||
|
||||
void design::addr_phase() {
|
||||
while (1) {
|
||||
while (addr_req.read() != 1) {
|
||||
wait(addr_req->value_changed_event());
|
||||
}
|
||||
sc_uint<8> _addr = bus_addr.read();
|
||||
bool _rw = rw.read();
|
||||
|
||||
int cycle = rand() % 10 + 1;
|
||||
while (cycle-- > 0) {
|
||||
wait(clk->posedge_event());
|
||||
}
|
||||
|
||||
addr_ack = 1;
|
||||
wait(clk->posedge_event());
|
||||
addr_ack = 0;
|
||||
|
||||
outstandingAddresses.push_back(_addr);
|
||||
outstandingType.push_back(_rw);
|
||||
cout << "at time " << sc_time_stamp() << ": ";
|
||||
cout << "received request for memory address " << _addr << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void design::data_phase() {
|
||||
while (1) {
|
||||
while (outstandingAddresses.empty()) {
|
||||
wait(clk->posedge_event());
|
||||
}
|
||||
int cycle = rand() % 10 + 1;
|
||||
while (cycle-- > 0) {
|
||||
wait(clk->posedge_event());
|
||||
}
|
||||
if (outstandingType.front() == 0) {
|
||||
cout << "reading memory address " << outstandingAddresses.front()
|
||||
<< " with value " << memory[outstandingAddresses.front().to_ulong()] << endl;
|
||||
bus_data = memory[outstandingAddresses.front().to_ulong()];
|
||||
data_rdy = 1;
|
||||
wait(clk->posedge_event());
|
||||
data_rdy = 0;
|
||||
|
||||
} else {
|
||||
cout << "not implemented yet" << endl;
|
||||
}
|
||||
outstandingAddresses.pop_front();
|
||||
outstandingType.pop_front();
|
||||
}
|
||||
}
|
||||
|
136
components/tx_example_mods.h
Normal file
136
components/tx_example_mods.h
Normal file
@ -0,0 +1,136 @@
|
||||
// -*- C++ -*- <this line is for emacs to recognize it as C++ code>
|
||||
/*****************************************************************************
|
||||
|
||||
Licensed to Accellera Systems Initiative Inc. (Accellera)
|
||||
under one or more contributor license agreements. See the
|
||||
NOTICE file distributed with this work for additional
|
||||
information regarding copyright ownership. Accellera licenses
|
||||
this file to you 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.
|
||||
|
||||
*****************************************************************************/
|
||||
// this code compiles and runs with our latest prototype for this specification
|
||||
|
||||
#include <scv.h>
|
||||
#include <systemc.h>
|
||||
|
||||
// hack to fake a true fifo_mutex
|
||||
#define fifo_mutex sc_mutex
|
||||
|
||||
const unsigned ram_size = 256;
|
||||
|
||||
class rw_task_if : virtual public sc_interface {
|
||||
public:
|
||||
typedef sc_uint<8> addr_t;
|
||||
typedef sc_uint<8> data_t;
|
||||
struct write_t {
|
||||
addr_t addr;
|
||||
data_t data;
|
||||
};
|
||||
|
||||
virtual data_t read(const addr_t*) = 0;
|
||||
virtual void write(const write_t*) = 0;
|
||||
};
|
||||
|
||||
SCV_EXTENSIONS(rw_task_if::write_t) {
|
||||
public:
|
||||
scv_extensions<rw_task_if::addr_t> addr;
|
||||
scv_extensions<rw_task_if::data_t> data;
|
||||
SCV_EXTENSIONS_CTOR(rw_task_if::write_t) {
|
||||
SCV_FIELD(addr);
|
||||
SCV_FIELD(data);
|
||||
}
|
||||
};
|
||||
|
||||
class pipelined_bus_ports : public sc_module {
|
||||
public:
|
||||
sc_in< bool > clk;
|
||||
sc_inout< bool > rw;
|
||||
sc_inout< bool > addr_req;
|
||||
sc_inout< bool > addr_ack;
|
||||
sc_inout< sc_uint<8> > bus_addr;
|
||||
sc_inout< bool > data_rdy;
|
||||
sc_inout< sc_uint<8> > bus_data;
|
||||
|
||||
SC_CTOR(pipelined_bus_ports)
|
||||
: clk("clk"), rw("rw"),
|
||||
addr_req("addr_req"),
|
||||
addr_ack("addr_ack"), bus_addr("bus_addr"),
|
||||
data_rdy("data_rdy"), bus_data("bus_data") {}
|
||||
};
|
||||
|
||||
class rw_pipelined_transactor
|
||||
: public rw_task_if,
|
||||
public pipelined_bus_ports {
|
||||
|
||||
fifo_mutex addr_phase;
|
||||
fifo_mutex data_phase;
|
||||
|
||||
scv_tr_stream pipelined_stream;
|
||||
scv_tr_stream addr_stream;
|
||||
scv_tr_stream data_stream;
|
||||
scv_tr_generator<sc_uint<8>, sc_uint<8> > read_gen;
|
||||
scv_tr_generator<sc_uint<8>, sc_uint<8> > write_gen;
|
||||
scv_tr_generator<sc_uint<8> > addr_gen;
|
||||
scv_tr_generator<sc_uint<8> > data_gen;
|
||||
|
||||
public:
|
||||
rw_pipelined_transactor(sc_module_name nm) :
|
||||
pipelined_bus_ports(nm),
|
||||
addr_phase("addr_phase"),
|
||||
data_phase("data_phase"),
|
||||
pipelined_stream("pipelined_stream", "transactor"),
|
||||
addr_stream("addr_stream", "transactor"),
|
||||
data_stream("data_stream", "transactor"),
|
||||
read_gen("read",pipelined_stream,"addr","data"),
|
||||
write_gen("write",pipelined_stream,"addr","data"),
|
||||
addr_gen("addr",addr_stream,"addr"),
|
||||
data_gen("data",data_stream,"data")
|
||||
{}
|
||||
virtual data_t read(const addr_t* p_addr);
|
||||
virtual void write(const write_t * req);
|
||||
};
|
||||
class test : public sc_module {
|
||||
public:
|
||||
sc_port< rw_task_if > transactor;
|
||||
SC_CTOR(test) {
|
||||
SC_THREAD(main);
|
||||
}
|
||||
void main();
|
||||
};
|
||||
|
||||
class write_constraint : virtual public scv_constraint_base {
|
||||
public:
|
||||
scv_smart_ptr<rw_task_if::write_t> write;
|
||||
SCV_CONSTRAINT_CTOR(write_constraint) {
|
||||
SCV_CONSTRAINT( write->addr() <= ram_size );
|
||||
SCV_CONSTRAINT( write->addr() != write->data() );
|
||||
}
|
||||
};
|
||||
|
||||
inline void process(scv_smart_ptr<int> data) {}
|
||||
class design : public pipelined_bus_ports {
|
||||
std::list< sc_uint<8> > outstandingAddresses;
|
||||
std::list< bool > outstandingType;
|
||||
sc_uint<8> memory[ram_size];
|
||||
|
||||
public:
|
||||
SC_HAS_PROCESS(design);
|
||||
design(sc_module_name nm) : pipelined_bus_ports(nm) {
|
||||
for (unsigned i=0; i<ram_size; ++i) { memory[i] = i; }
|
||||
SC_THREAD(addr_phase);
|
||||
SC_THREAD(data_phase);
|
||||
}
|
||||
void addr_phase();
|
||||
void data_phase();
|
||||
};
|
49
components/txtop.cpp
Normal file
49
components/txtop.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* top1.cpp
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#include "txtop.h"
|
||||
using namespace sc_core;
|
||||
|
||||
tx_top::tx_top(sc_core::sc_module_name nm)
|
||||
: sc_module(nm)
|
||||
, clk("clk", 20.0, SC_NS, 0.5 ,0.0, SC_NS, true)
|
||||
, rw("rw")
|
||||
, addr_req("addr_reg")
|
||||
, addr_ack("addr_ack")
|
||||
, bus_addr("bus_addr")
|
||||
, data_rdy("data_rdy")
|
||||
, bus_data("bus_data")
|
||||
, t("t")
|
||||
, tr("tr")
|
||||
, duv("duv")
|
||||
{
|
||||
// TODO Auto-generated constructor stub
|
||||
// connect them up
|
||||
t.transactor(tr);
|
||||
|
||||
tr.clk(clk);
|
||||
tr.rw(rw);
|
||||
tr.addr_req(addr_req);
|
||||
tr.addr_ack(addr_ack);
|
||||
tr.bus_addr(bus_addr);
|
||||
tr.data_rdy(data_rdy);
|
||||
tr.bus_data(bus_data);
|
||||
|
||||
duv.clk(clk);
|
||||
duv.rw(rw);
|
||||
duv.addr_req(addr_req);
|
||||
duv.addr_ack(addr_ack);
|
||||
duv.bus_addr(bus_addr);
|
||||
duv.data_rdy(data_rdy);
|
||||
duv.bus_data(bus_data);
|
||||
|
||||
}
|
||||
|
||||
tx_top::~tx_top() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
31
components/txtop.h
Normal file
31
components/txtop.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* top1.h
|
||||
*
|
||||
* Created on: 02.12.2018
|
||||
* Author: eyck
|
||||
*/
|
||||
|
||||
#ifndef TXTOP_H_
|
||||
#define TXTOP_H_
|
||||
|
||||
#include "tx_example_mods.h"
|
||||
#include <systemc>
|
||||
|
||||
class tx_top: public sc_core::sc_module {
|
||||
public:
|
||||
tx_top(sc_core::sc_module_name nm);
|
||||
virtual ~tx_top();
|
||||
sc_core::sc_clock clk;
|
||||
sc_core::sc_signal< bool > rw;
|
||||
sc_core::sc_signal< bool > addr_req;
|
||||
sc_core::sc_signal< bool > addr_ack;
|
||||
sc_core::sc_signal< sc_dt::sc_uint<8> > bus_addr;
|
||||
sc_core::sc_signal< bool > data_rdy;
|
||||
sc_core::sc_signal< sc_dt::sc_uint<8> > bus_data;
|
||||
test t;
|
||||
rw_pipelined_transactor tr;
|
||||
design duv;
|
||||
|
||||
};
|
||||
|
||||
#endif /* TXTOP_H_ */
|
Reference in New Issue
Block a user