Global training solutions for engineers creating the world's electronics

SystemC Golden Reference Guide - Sample Code Fragments

The following examples of SystemC coding may be downloaded for use by readers of the Doulos SystemC Golden Reference Guide. You may freely use them in your projects subject to the Apache 2.0 License both privately and commercially. Please do not publish or re-distribute to others except by recommending they get a copy of our SystemVerilog Golden Reference Guide. If you wish to use some fragment in a presentation or article you are writing, please request permission from Doulos.

Click here to download the examples below as a zip file, or use the links below to jump to a particular example.

Click on the links under each section to run prepared examples in a live simulation environment using EDA Playground.  

Click here to use EDA Playground to run your own sample code.

Tutorial 1: Modules and Processes

A simple example showing hierarchical SystemC modules with SC_METHOD and SC_THREAD processes

Run this example in EDA Playground

#ifndef NAND2_H
#define NAND2_H

#include "systemc"

using namespace sc_core;

SC_MODULE(nand2)          // declare nand2 sc_module
{
  sc_in<bool> A, B;       // input signal ports
  sc_out<bool> F;         // output signal ports
  
  void do_nand2()         // a C++ function
  {  
    F.write( !(A.read() && B.read()) );
  }

  SC_CTOR(nand2)          // constructor for nand2
  {
    SC_METHOD(do_nand2);  // register do_nand2 with kernel
      sensitive << A << B;  // static sensitivity
  }
};

#endif
#ifndef EXOR2_H
#define EXOR2_H

#include "systemc"

using namespace sc_core;

#include "nand2.h"

SC_MODULE(exor2)
{
  sc_in<bool> A, B;
  sc_out<bool> F;
  
  nand2 n1, n2, n3, n4;

  sc_signal<bool> S1, S2, S3;
  
  SC_CTOR(exor2) : n1("N1"), n2("N2"), n3("N3"), n4("N4")
  {
    n1.A(A);
    n1.B(B);
    n1.F(S1);
    
    n2.A(A);
    n2.B(S1);
    n2.F(S2);
    
    n3(S1,B,S3);
    
    n4.A(S2);
    n4.B(S3);
    n4.F(F);    
  }
};

#endif
#ifndef STIM_H
#define STIM_H

#include "systemc"

using namespace sc_core;

SC_MODULE(stim)
{
  sc_out<bool> A, B;
  sc_in<bool> Clk;

  void StimGen()
  {
    A.write(false);
    B.write(false);
    wait();
    A.write(false);
    B.write(true);
    wait();
    A.write(true);
    B.write(false);
    wait();
    A.write(true);
    B.write(true);
    wait();
    sc_stop();
  }
  SC_CTOR(stim)
  {
    SC_THREAD(StimGen);
      sensitive << Clk.pos();
  }
};
#endif
#ifndef MON_H
#define MON_H

#include 
#include "systemc"
using namespace sc_core;

SC_MODULE(mon)
{
  sc_in<bool> A, B, F;
  sc_in<bool> Clk;

  void run()
  {
    std::cout << " Time A B F\n";
    while (1)
    {
      wait();
      std::cout << std::setw(5) << sc_time_stamp() << " " 
                << A << " " << B << " " << F << "\n";
    }
  }
  
  SC_CTOR(mon)
  {
    SC_THREAD(run);
      sensitive << Clk.pos();
  }
};
#endif
#include "systemc"
using namespace sc_core;

#include "stim.h"
#include "exor2.h"
#include "mon.h"

int sc_main(int argc, char* argv[])
{
  sc_signal<bool> ASig, BSig, FSig;
  sc_clock TestClk("TestClock", 10, SC_NS); 
  
  stim Stim1("Stimulus");
  Stim1.A(ASig);
  Stim1.B(BSig);
  Stim1.Clk(TestClk);
    
  exor2 DUT("exor2");
  DUT.A(ASig);
  DUT.B(BSig);
  DUT.F(FSig);

  mon Monitor1("Monitor");
  Monitor1.A(ASig);
  Monitor1.B(BSig);
  Monitor1.F(FSig);
  Monitor1.Clk(TestClk); 
  
  sc_start();  // run forever
  return 0;
}

 

Tutorial 3: Hierarchical Channels

A simple hierarchical channel with a register_port method

Run this example in EDA Playground

#ifndef STACK_IF_H
#define STACK_IF_H

#include "systemc"
using namespace sc_core;

class stack_write_if: virtual public sc_interface 
{
public:
  virtual bool nb_write(char) = 0;  // write a character
  virtual void reset() = 0;         // empty the stack
};

class stack_read_if: virtual public sc_interface 
{
public:
  virtual bool nb_read(char&) = 0;  // read a character
};

class stack_if: virtual public stack_write_if, virtual public stack_read_if  
{
};

#endif
#ifndef STACK_H
#define STACK_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

#include "stack_if.h"

// this class implements the virtual functions 
// in the interfaces

class stack 
  : public sc_module, public stack_if 
{
private:
  char data[20];
  int top;                 // top of stack 

public:
  // constructor
  stack(sc_module_name nm) : sc_module(nm), top(0) {}
    
  bool nb_write(char c)
  {
    if (top < 20)
    {
      data[top++] = c;
      return true;
    }
    return false;
  }

  void reset() 
  {
    top = 0;
  }

  bool nb_read(char& c) 
  {
    if (top > 0) 
    {
      c = data[--top];
      return true;
    }
    return false;
  }

  void register_port(sc_port_base& port_,
                            const char* if_typename_)
  {
    cout << "binding    " << port_.name() << " to "  
         << "interface: " << if_typename_ << endl;
  }
};

#endif
#ifndef PRODUCER_H
#define PRODUCER_H

#include "systemc"
using namespace sc_core;

#include "stack_if.h"

class producer : public sc_module 
{
public:
  sc_port<stack_write_if> out;
  sc_in<bool> Clock;
  
  void do_writes()
  {
    int i = 0;
    const char* TestString = "This Will Be Reversed";
    while (true) 
    {
      wait();             // for clock edge
      if (out->nb_write(TestString[i])) 
        std::cout << "Write " << TestString[i] << " at " 
                  << sc_time_stamp() << std::endl;
      i = (i+1) % 21;
    }
  } 
    
  SC_CTOR(producer)
  {
    SC_THREAD(do_writes);
      sensitive << Clock.pos();
  }
};

#endif
#ifndef CONSUMER_H
#define CONSUMER_H

#include "systemc"
using namespace sc_core;

#include "stack_if.h"

class consumer : public sc_module 
{
public:
  sc_port<stack_read_if> in;
  sc_in<bool> Clock;
  
  void do_reads()
  {
    char ch;
    while (true) 
    {
      wait();             // for clock edge
      if (in->nb_read(ch)) 
        std::cout << "Read  " << ch << " at " 
                  << sc_time_stamp() << std::endl;
    }
  } 
    
  SC_CTOR(consumer)
  {
    SC_THREAD(do_reads);
      sensitive << Clock.pos();
  }
};

#endif
#include "systemc"
#include "producer.h"
#include "consumer.h"
#include "stack.h"
using namespace sc_core;

int sc_main(int argc, char* argv[]) 
{
  sc_clock ClkFast("ClkFast", 100, SC_NS);
  sc_clock ClkSlow("ClkSlow", 50, SC_NS);
  
  stack Stack1("S1");

  producer P1("P1");
  P1.out(Stack1);
  P1.Clock(ClkFast);
  
  consumer C1("C1");
  C1.in(Stack1);
  C1.Clock(ClkSlow);

  sc_start(5000, SC_NS);
  
  return 0;
}

 

Tutorial 4: Primitive Channels and The Kernel - Non-determinism

Shows non-deterministic execution order

Run this example in EDA Playground

#ifndef NONDET_H
#define NONDET_H

#include <systemc>
using namespace sc_core;
using std::cout;
using std::endl;

SC_MODULE(nondet) 
{
  sc_in<bool> Trig;

  int SharedVariable;

  // The execution order of the following two processes
  // is non-deterministic
  
  void proc_1() 
  {
    SharedVariable = 1;    
    cout << SharedVariable << endl;
  }

  void proc_2()
  {
    SharedVariable = 2;
    cout << SharedVariable << endl;
  }

  SC_CTOR(nondet)
  {
    SC_THREAD(proc_1);
      sensitive << Trig.pos();
    SC_THREAD(proc_2);
      sensitive << Trig.pos();
  }
};

#endif
#include "nondet.h"

int sc_main(int argc, char* argv[])
{
  sc_clock clock; // Default period is 10ns
  
  nondet instance("instance");
  instance.Trig.bind(clock);
  
  sc_start(20, SC_NS);
  
  return 0;
}

 

Tutorial 4: Primitive Channels and The Kernel - FIFO

A user-defined primitive channel with its own interfaces accessed through ports

Run this example in EDA Playground

#ifndef FIFO_IF_H
#define FIFO_IF_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

class fifo_out_if :  virtual public sc_interface 
{
public:
  virtual void write(char) = 0;          // blocking write
  virtual int num_free() const = 0;      // free entries
protected:
  fifo_out_if() 
  {
  };
private: 
  fifo_out_if (const fifo_out_if&);      // disable copy
  fifo_out_if& operator= (const fifo_out_if&); // disable
};


class fifo_in_if :  virtual public sc_interface
{
public:
  virtual void read(char&) = 0;          // blocking read
  virtual char read() = 0;
  virtual int num_available() const = 0; // available
                                         // entries
protected:
  fifo_in_if() 
  {
  };
private:
  fifo_in_if(const fifo_in_if&);            // disable copy
  fifo_in_if& operator= (const fifo_in_if&); // disable =
};

#endif
#ifndef FIFO_H
#define FIFO_H

#include "fifo_if.h"

class fifo 
: public sc_prim_channel, public fifo_out_if,  
  public fifo_in_if 
{
protected:
  int size;                 // size
  char* buf;                // fifo buffer
  int free;                 // free space
  int ri;                   // read index
  int wi;                   // write index
  int num_readable, num_read, num_written;
  
  sc_event data_read_event;
  sc_event data_written_event;
   
public:
  // constructor
  explicit fifo(int _size = 16)
  : sc_prim_channel(sc_gen_unique_name("myfifo"))
  {
    size = _size;
    buf = new char[size];
    reset();
  }

  ~fifo()                   //destructor
  {
    delete [] buf;
  }

  int num_available() const
  {
    return num_readable - num_read;
  }
    
  int num_free() const
  {
    return size - num_readable - num_written;
  }

  void write(char c)        // blocking write
  {
    if (num_free() == 0) 
      wait(data_read_event);
    num_written++;
    buf[wi] = c;
    wi = (wi + 1) % size;
    free--;
    request_update();
  }

  void reset() 
  {
    free = size;
    ri = 0;
    wi = 0;
  }

  void read(char& c)        // blocking read
  {
    if (num_available() == 0) 
      wait(data_written_event);
    num_read++;
    c = buf[ri];
    ri = (ri + 1) % size;
    free++;
    request_update();
  }

  char read()                // shortcut read function 
  {
    char c;
    read(c);
    return c;
  }

  void update()              // updates the state of the channel
  {
    if (num_read > 0)
      data_read_event.notify(SC_ZERO_TIME);
    if (num_written > 0) 
      data_written_event.notify(SC_ZERO_TIME);
    num_readable = size - free;
    num_read = 0;
    num_written = 0;
  }
};

#endif
#ifndef PRODUCER_H
#define PRODUCER_H

#include "fifo_if.h"

SC_MODULE(producer)
{
  sc_in<bool> Clock;
  sc_port<fifo_out_if> out;
    
  SC_CTOR(producer)
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    std::string text = "Message";
    for (int i = 0; i < text.size(); i++)
    {
      wait(Clock.posedge_event());
      out->write(text[i]);
    }
  }
};

#endif
#ifndef CONSUMER_H
#define CONSUMER_H

#include "fifo_if.h"

SC_MODULE(consumer)
{
  sc_in<bool> Clock;
  sc_port<fifo_in_if> in;
    
  SC_CTOR(consumer)
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    while (true)
    {
      wait(Clock.posedge_event());
      cout << in->read() << endl;
    }
  }
};

#endif
#include "producer.h"
#include "consumer.h"
#include "fifo.h"

int sc_main(int argc, char* argv[]) 
{
  sc_clock ClkFast("ClkFast", 1, SC_NS);
  sc_clock ClkSlow("ClkSlow", 500, SC_NS);
  
  fifo fifo1;
  
  producer P1("P1");
  P1.out  .bind(fifo1);
  P1.Clock.bind(ClkFast);
  
  consumer C1("C1");
  C1.in   .bind(fifo1);
  C1.Clock.bind(ClkSlow);
  
  sc_start(5000, SC_NS);
  
  return 0;
}

 

Channel with Ports and Exports

Shows SystemC interface, channel, ports, and exports. The channel is accessed 1) standalone 2) through port-to-channel bindings 3) through port-to-export bindings

Run this example in EDA Playground

#ifndef QUEUE_IF_H
#define QUEUE_IF_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;


class queue_write_if : virtual public sc_core::sc_interface
{
public:
  virtual void write(char c) = 0; 
};


class queue_read_if : virtual public sc_core::sc_interface
{
public:
  virtual char read() = 0; 
};


class queue_if : public queue_write_if,
                 public queue_read_if   {};

#endif
#ifndef QUEUE_H
#define QUEUE_H

#include "queue_if.h"

class Queue : public queue_if, public sc_object
{
public:
  Queue(const char* nm, int _sz)
  : sc_object(nm), sz(_sz)
  { data = new char[sz]; w = r = n = 0; }

  void write(char c);
  char read();

private:
  char* data;
  int sz, w, r, n;
  sc_event read_event, write_event;
};


void Queue::write(char c)
{
  if (n < sz) {
    n++;
    data[w++] = c;
    if (w == sz) w = 0;
  }
  else
  {
    sc_core::wait(read_event);
    write(c);
  }
  write_event.notify();
}

char Queue::read()
{
  char c = 0;
  if (n > 0) {
    n--;
    c = data[r++];
    if (r == sz) r = 0;
  }
  else
  {
    sc_core::wait(write_event);
    c = read();
  }
  read_event.notify();
  return c;
}

#endif
#ifndef TEST_H
#define TEST_H

#include "queue.h"

SC_MODULE(Test)
{
  // Standalone instantiation of queue channel
  // accessed without any ports or exports
  
  Queue queue;

  SC_CTOR(Test) : queue("queue", 100) { SC_THREAD(T); }

  void T() {
    std::string txt = "Hallo World.";
    
    cout << "Test program: ";

    for (int i = 0; i < txt.size(); i++)
      queue.write(txt[i]);

    for (int i = 0; i < txt.size(); i++)
      cout << queue.read();
    cout << endl;
  }
}; 

#endif
#ifndef PRODUCER_H
#define PRODUCER_H

#include "queue.h"

class Producer : public sc_module 
{
public:
  sc_port<queue_write_if> out;

  void do_writes();
  
  sc_time m_delay;
  
  // A user-defined constructor argument delays executing of the producer

  Producer(sc_module_name name, const sc_time& delay)
    : sc_module(name), m_delay(delay)
  {
    SC_HAS_PROCESS(Producer);
    SC_THREAD(do_writes);
  }
};

void Producer::do_writes()
{
  wait(m_delay);

  cout << "Producer running at time " << sc_time_stamp() << endl;
  std::string txt = "Hallo World.";
  for (int i = 0; i < txt.size(); i++)
  {
    out->write(txt[i]);
  }
} 

#endif
#ifndef CONSUMER_H
#define CONSUMER_H

#include "queue.h"

class Consumer : public sc_module 
{
public:
  sc_port<queue_read_if> in;

  void do_reads();
  
  SC_CTOR(Consumer)
  {
    SC_THREAD(do_reads);
  }
};

void Consumer::do_reads()
{
  for (;;)
  {
    wait(SC_ZERO_TIME); 
    cout << in->read() << endl;
  }
} 

#endif
#ifndef TOP2_H
#define TOP2_H

#include "producer.h"
#include "consumer.h"

SC_MODULE(Top2)
{
  Queue queue;
  Producer *producer;
  Consumer *consumer;

  SC_CTOR(Top2)
    : queue("queue", 100)
  {
    // Bind exports of both producer and consumer to queue channel
     
    producer = new Producer("producer", sc_time(100, SC_NS));
    producer->out.bind(queue);

    consumer = new Consumer("consumer");
    consumer->in.bind(queue);
  }
};

#endif
#ifndef CONTAINER_H
#define CONTAINER_H

#include "queue.h"

class Container : public sc_module
{
public:
  
  // The queue channel is encapsulated within a container class,
  // which makes the queue methods available through two exports
  
  sc_export<queue_write_if>  producer_export;
  sc_export<queue_read_if>   consumer_export;
  
  Queue queue;

  Container(sc_module_name name)
    : sc_module(name), queue("queue", 100)
  {
    // Bind both exports to the local queue channel

    producer_export.bind(queue);
    consumer_export.bind(queue);
  }
};

#endif
#ifndef TOP3_H
#define TOP3_H

#include "producer.h"
#include "consumer.h"
#include "container.h"

SC_MODULE(Top3)
{
  Producer  *producer;
  Consumer  *consumer;
  Container *container;

  SC_CTOR(Top3)
  {
    container = new Container("container");
    
    // Bind exports of both producer and consumer to exports of container 

    producer = new Producer("producer", sc_time(200, SC_NS));
    producer->out.bind( container->producer_export );

    consumer = new Consumer("consumer");
    consumer->in.bind ( container->consumer_export );
  }
};

#endif
#include "test.h"
#include "top2.h"
#include "top3.h"

int sc_main(int argc, char* argv[])
{
  // Three top-level modules:
  // the first a test program for the queue channel
  Test test_inst("test_inst");
  
  // the second shows port-to-channel binding
  Top2 top2_inst("top2_inst");
  
  // the third shows export-to-export binding
  Top3 top3_inst("top3_inst");
  
  sc_start();
  return 0;
}

 

Bus with Multiple Masters and Slaves

A hierarchical channel representing a cycle-accurate bus model with multiple masters and slaves

Run this example in EDA Playground

#ifndef DEF_H
#define DEF_H

#include "systemc"
using namespace sc_core;
using namespace sc_dt;
using std::cout;
using std::endl;

namespace BusT
{
  enum MEM_STATUS
  {
    ALL_READ_DONE,
    READ_DONE,
    WRITE_DONE
  };

  typedef sc_uint<8> addressT;
  typedef sc_uint<8> dataT;

  const addressT MemStatusReg = 0xFF;

  //conversion functions for MEM_STATUS/dataT
  inline MEM_STATUS to_mem_status(const dataT& d) {
    return static_cast(ALL_READ_DONE + d.to_int());
  }

  inline dataT to_dataT(const MEM_STATUS& m) {
    return dataT(m);
  }

  const char MSGTYPE[] = "DOULOS_BUS/";

};

#endif
#ifndef MASTER_IF_H
#define MASTER_IF_H

#include "defs.h"

class master_if : virtual public sc_interface
{
  public:
    virtual void write(BusT::addressT address, BusT::dataT  data, int id) = 0;
    virtual void read (BusT::addressT address, BusT::dataT &data, int id) = 0;
};

#endif
#ifndef SLAVE_IF_H
#define SLAVE_IF_H

#include "defs.h"

class slave_if : virtual public sc_interface
{
  public:
    virtual void slave_write(BusT::addressT address, BusT::dataT  data) = 0;
    virtual void slave_read (BusT::addressT address, BusT::dataT &data) = 0;
    virtual void get_map(unsigned int &start, unsigned int &size)  = 0;
};

#endif
#ifndef BUS_H
#define BUS_H

#include "master_if.h"
#include "slave_if.h"

struct reqT
{
  bool request;
  sc_event proceed;
  //default constructor
  reqT():request(false) {}
};

// Hierarchical channel

class Bus : public sc_module, public master_if
{
  public:
    // clock port - bus will operate on negative edge
    sc_in<bool> clock;

    // a port to connect slave(s)
    sc_port<slave_if> slave_port;

    SC_HAS_PROCESS(Bus);

    // constructor
    Bus(sc_module_name name)
    : sc_module(name)
    , n_masters(0)
    , request_array(0)
    , request_count(0)
    , mem_status(0)
    {
      SC_THREAD(control_bus);
      sensitive << clock.neg();
    }

    // destructor
    ~Bus() {
      if (request_array) delete [] request_array;
      if (request_count) delete [] request_count;
      if (mem_status)    delete [] mem_status;
    }

    // bus public methods
    void write(BusT::addressT address, BusT::dataT  data, int id);
    void read (BusT::addressT address, BusT::dataT &data, int id);

  private:
    // data fields
    unsigned n_masters;
    reqT* request_array;
    int*  request_count;
    unsigned int *starts, *sizes;
    bool* mem_status;

    // private methods
    void control_bus();
    int  find_interface(BusT::addressT address);
    void end_of_elaboration();
    void register_port(sc_port_base &port_, const char* if_typename_);
    BusT::dataT get_mem_status(int id);
    void set_mem_status(BusT::MEM_STATUS, int id=0);
    int n_pending();

};


using namespace BusT;

void Bus::write(BusT::addressT address, BusT::dataT data, int id)
{
  request_array[id].request = true;     // set request
  wait(request_array[id].proceed);      // wait to proceed

  if (address == MemStatusReg)
  {
    set_mem_status(to_mem_status(data),id);

    //cout << "Master " << id << " has set mem status reg to " << data
    //     << " at " << sc_time_stamp() << endl;
  }
  else
  {
    int s = find_interface(address);         // address decoding

    // slave write
    slave_port[s]->slave_write(address - starts[s], data);

    cout << "Master " << id << " has written "
      << "to address " << address << " data "
      << data << " at " << sc_time_stamp() << endl;
  }

  request_array[id].request = false;    // clear request

}

void Bus::read(BusT::addressT address, BusT::dataT &data, int id)
{
  request_array[id].request = true;     // set request
  wait(request_array[id].proceed);      // wait to proceed

  if (address == MemStatusReg)
  {
    data = get_mem_status(id);
    if (n_pending() <= 1) set_mem_status(ALL_READ_DONE);

    //cout << "Master " << id << " has got " << data
    //     << " from mem status reg at " << sc_time_stamp() << endl;
  }
  else
  {
    int s = find_interface(address);         // address decoding

    // slave read
    slave_port[s]->slave_read(address- starts[s], data);

    cout << "Master " << id << " has read "
      << "from address " << address << " data "
      << data << " at " << sc_time_stamp() << endl;
  }
  request_array[id].request = false;    // clear request

}

void Bus::control_bus()
{
  int highest, max_request_count;

                                        // clear counters
  for (unsigned i = 0; i < n_masters; i++)
    request_count[i] = 0;

  for (;;) {
    wait();

    for (unsigned i = 0; i < n_masters; i++) {
      if (request_array[i].request == true)
        request_count[i]++;
    }

    highest = -1;
    max_request_count = 0;
    for (unsigned i = 0; i < n_masters; i++ ) {
      if (request_count[i] >= max_request_count) {
        max_request_count = request_count[i];
        highest = i;
      }
    }

    // If highest is > -1, then give the bus to the master with a
    // request pending for the greatest number of clock cycles
    // Reset the pending request count for that master.
    if (highest > -1) {
      request_array[highest].proceed.notify();
      request_count[highest] = 0;
    }
  }
}

int Bus::find_interface(BusT::addressT address)
{
  // check address range of each slave in turn
  for (int i = 0; i < slave_port.size(); i++)
    if (address >= starts[i] && address < (starts[i] + sizes[i]))
      return i;
  SC_REPORT_ERROR(BusT::MSGTYPE, "address out-of-range");
  return 1;
}

void Bus::end_of_elaboration()
{
  // create the request array
  request_array = new reqT[n_masters];
  request_count = new int[n_masters];

  // create the array for MemStatusReg
  mem_status = new bool[n_masters];
                                        // clear mem_status
  for (unsigned i = 0; i < n_masters; i++)
    mem_status[i] = false;

  const int n = slave_port.size();
  starts = new unsigned[n];
  sizes  = new unsigned[n];
  bool ok = true;
  for (int i = 0; i < n; i++) {
    slave_port[i]->get_map(starts[i], sizes[i]);

    // Check for overlapping address ranges
    for (int j = 0; j < i; j++)
      if (starts[i] < (starts[j] + sizes[j]) &&
      starts[j] < (starts[i] + sizes[i])) {
        cout << "Error: overlapping regions in Bus address map: "
        << starts[i] << ".." << starts[i] + sizes[i] - 1 << " and "
        << starts[j] << ".." << starts[j] + sizes[j] - 1 << endl;
      ok = false;
    }
  }
  sc_assert(ok);
}

// counts how many masters are bound to the bus channel
void Bus::register_port(sc_port_base &port_, const char* if_typename_)
{
  std::string nm(if_typename_);
  if ( nm == typeid(master_if).name() )
    n_masters++;
}

BusT::dataT Bus::get_mem_status(int id)
{
  //return 1 if flag set
  if (mem_status[id])
    return 1;
  else
    return 0;
}

void Bus::set_mem_status(MEM_STATUS status, int id)
{
  switch (status) {
    case ALL_READ_DONE:
      for (unsigned i=0; i < n_masters; i++) mem_status[i] = false;
      break;
    case READ_DONE:
      mem_status[id] = false;
      break;
    case WRITE_DONE:
      for (unsigned i=0; i < n_masters; i++) mem_status[i] = true;
  }
}

int Bus::n_pending()
{
  int count=0;
  for (unsigned i=0; i < n_masters; i++)
    if (mem_status[i]) count++;
  return count;
}

#endif
#ifndef SOURCE_H
#define SOURCE_H

#include "master_if.h"

class Source: public sc_module
{
  public:

    sc_in<bool>         clock;
    sc_port<master_if>  system_bus;

    SC_HAS_PROCESS(Source);

    // constructor
    Source( sc_module_name _name, unsigned _src, unsigned _num_items, int _id)
    : sc_module(_name), src(_src), num_items(_num_items), id(_id) {
      SC_THREAD(do_source);
      sensitive << clock.pos();
    }

    // method declarations
    void do_source();

  private:
    const unsigned src, num_items;
    const int id;
};


using namespace BusT;

void Source::do_source()
{

  BusT::dataT wrdata = 0, rddata = 0;
  const int EOD = 255;

  for (;;) {
    // write random numbers between 0 and EOD-1 to the bus
    for (unsigned int i = 0; i < num_items; i++) {
      wait();
      wrdata = rand() % EOD;
      system_bus->write(src + i, wrdata, id);
    }

    // write to memory status register
    wait();
    system_bus->write(MemStatusReg, to_dataT(WRITE_DONE), id);

    // wait for data to be processed, memory status register cleared
    for (;;) {
      wait();
      system_bus->read(MemStatusReg, rddata, id);
      if (!rddata) break;
    }
  }
}

#endif
#ifndef PROC_H
#define PROC_H

#include "master_if.h"

// Processor to read data values from the bus
// and write them back to some output locations

class Proc: public sc_module
{
  public:

    sc_in<bool>         clock;
    sc_port<master_if>  system_bus;

    SC_HAS_PROCESS(Proc);

    // Constructor
    Proc(sc_module_name _nm, unsigned _src, unsigned _dest, unsigned _num_items, int _id)
    : sc_module(_nm),  src(_src), dest(_dest), num_items(_num_items), id(_id) {
      SC_THREAD(do_proc);
      sensitive << clock.pos();
      sc_assert (_src < _dest);
    }

    // Method declarations
    void do_proc();

  private:
    const unsigned int src, dest, num_items;
    const int id;
};


using namespace BusT;

void Proc::do_proc()
{
  BusT::dataT data = 0;

  for (;;) {
    // Scan the address range looking memory status register to clear

    do {
      wait();
      system_bus->read(MemStatusReg, data, id);
    } while (!data);

    // Copy all values which have the LSB set (odd values) up to the block
    // of memory starting at address dest

    unsigned int copyto = dest;
    for (unsigned int i = src; i < num_items;  i++) {
      wait();
      system_bus->read(i, data, id);
      if (data[0] == 1) {
        wait();
        system_bus->write(copyto++, data, id);
      }
    }

    // Finally, clear the memory status register for this master
    wait();
    system_bus->write(MemStatusReg, to_dataT(READ_DONE), id);

  }
}

#endif
#ifndef MEM_H
#define MEM_H

#include "slave_if.h"

class Mem : public sc_module, public slave_if
{
  public:
    Mem(sc_module_name name, unsigned int start, unsigned int size, sc_trace_file *trace_file)
    : sc_module(name),
      start(start),
      size(size),
      trace_file(trace_file)
    {
      if (size < 1) {
        cout << "Mem size must be at least 1" << endl;
        sc_assert(false);
      }
      init();
    }

    // interface methods
    void slave_write(BusT::addressT address, BusT::dataT  data);
    void slave_read (BusT::addressT address, BusT::dataT &data);
    void get_map(unsigned int &start, unsigned int &size);

    // support methods
    void status();
    void dump(unsigned int start_addr = 0 , unsigned int end_addr = ~0);
    void add_trace(unsigned int address);
    void init();

  private:
    // data items
    unsigned int start;
    unsigned int size;
    sc_trace_file *trace_file;
    BusT::dataT *buf;
};


void Mem::slave_write(BusT::addressT address, BusT::dataT data)
{
  if ((address < 0) || (address >= size)) {
    SC_REPORT_ERROR(BusT::MSGTYPE, "write address out of range ");
  }
  else {
    buf[address] = data;
  }
}

void Mem::slave_read(BusT::addressT address, BusT::dataT &data)
{
  if ((address < 0) || (address >= size)) {
    SC_REPORT_ERROR(BusT::MSGTYPE, "read address out of range ");
  }
  else {
    data = buf[address];
  }
}

void Mem::get_map(unsigned int &mem_start, unsigned int &mem_size)
{
  // return memory address map
  mem_start = start;
  mem_size  = size;
}

// -------------------------------------------------------
// Initialisation routine

void Mem::init()
{
  buf = new BusT::dataT[size];
  for (unsigned int i = 0; i < size; i++)
    buf[i] = 0;
}

// -------------------------------------------------------
// Debug routines

void Mem::status()
{
  cout << "Slave status" << endl;
  cout << "------------" << endl;
  cout << name() << " ";
  cout << " Size = " << size;
  cout << " Start Address = " << start;
  cout << endl << endl;
}

void Mem::dump(unsigned int start_addr, unsigned int end_addr)
{
  if (start_addr < 0) start_addr = 0;
  if (end_addr >= (size - 1)) end_addr = size - 1;

  cout << name() << " contents:" << endl;
  cout << "--------------" << endl;
  for (unsigned int i = start_addr; i <= end_addr; i++) {
    cout << start+i << " " << buf[i] << endl;
  }
  cout << endl;
}

void Mem::add_trace(unsigned int address)
{
  std::string nm( name() );
  nm = nm + "(" + sc_uint<32>(address+start).to_string().c_str() + ")";

  if ((address >= 0) && (address < size) )
    sc_trace(trace_file, buf[address], nm.c_str());
  else
    SC_REPORT_WARNING(BusT::MSGTYPE, "address to trace out of range");
}

#endif
#ifndef TOP_H
#define TOP_H

#include "bus.h"
#include "source.h"
#include "proc.h"
#include "mem.h"

SC_MODULE(Top)
{
  sc_clock clock;

  sc_trace_file *trace;

  // Module Instantiations
  Mem ram0;

  // Add another ram here and initialize it to start 32, size 32

  // Bus
  Bus bus1;

  // Data source ID 0
  Source source0;

  // Processor, ID = 1
  // destination address starts at 16
  Proc proc1;

  SC_CTOR(Top)
  : clock( "clock", 100, SC_NS),
    trace(sc_create_vcd_trace_file("bus_system")),

    // Memory ram0, start address 0 , size 32
    ram0("ram0", 0, 32, trace),

    // Bus
    bus1("bus1"),

    // Data source ID 0
    source0("source0", 0, 12, 0),

    // Processor, ID = 1
    // Destination address starts at 16
    proc1("proc1", 0, 16, 12, 1)
  {

    bus1.clock(clock);
    bus1.slave_port(ram0);

    source0.clock(clock);
    source0.system_bus(bus1);

    proc1.clock(clock);
    proc1.system_bus(bus1);

    // Trace external  pins
    sc_trace(trace, clock,    "clock");
    // Trace ram memory locations
    int i;
    for (i = 0; i < 32; i++)
      ram0.add_trace(i);

    // Display ram status
    ram0.status();

    // Dump ram contents
    ram0.dump();

  }

  void end_of_simulation() {
    // Dump slave contents
    ram0.dump();
  }

  ~Top() {
    sc_close_vcd_trace_file(trace);
  }

};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top1("top1");

  sc_start(25000, SC_NS);
  sc_stop(); // triggers end_of_simulation() callback

  return 0;
}

 

Specialized Port with Event Finder

Shows a SystemC interface and channel with default_event and register_port methods and specialized ports that define event finders

Run this example in EDA Playground

#ifndef ZQ_IF_H
#define ZQ_IF_H

// A SystemC interface for a zero-length queue
// separated into a write interface and a read interface
// such that register_port() can be used for checking
// the number of writers and readers

#include "systemc"
using namespace sc_core;

template<class T>
class zq_write_if: virtual public sc_interface
{
public:
  virtual void write(T) = 0;
  virtual const sc_event& read_event()  const = 0;
};


template<class T>
class zq_read_if: virtual public sc_interface
{
public:
  virtual void read(T&) = 0;
  virtual const sc_event& write_event() const = 0;
};


template<class T>
class zq_if: virtual public zq_write_if<T>, virtual public zq_read_if<T>
{
public:
  virtual const sc_event& default_event() const = 0;
};

#endif
#ifndef ZQ_CHANNEL_H
#define ZQ_CHANNEL_H

#include "zq_if.h"

// A SystemC channel that implements the zero-length queue interface

template<class T>
class zq_channel: public zq_if<T>, public sc_object
{
public:
  zq_channel(const char* name)
    : sc_object(name), m_written(false), m_writer(0), m_reader(0) {}
  
  virtual void write(T arg)
  {  
    m_value   = arg;
    m_written = true;
    m_write_event.notify();
    sc_core::wait(m_read_event);
    m_written = false;
  }
  
  virtual void read(T& arg)
  {
    if (!m_written)
      sc_core::wait(m_write_event);
    arg = m_value;
    m_read_event.notify();
    sc_core::wait(SC_ZERO_TIME);
  }
  
  virtual const sc_event& write_event() const
  {
    return m_write_event;
  }
  
  virtual const sc_event& read_event() const
  {
    return m_write_event;
  }
  
  virtual const sc_event& default_event() const
  {
    return m_read_event;
  }

  // register_port checks there is no more than one reader or writer
  
  void register_port( sc_port_base& port_ , 
                      const char* if_typename_)
  {
    if(std::string(if_typename_) == typeid(zq_write_if<T>).name())
    {
      if( m_writer != 0 )
        SC_REPORT_ERROR("zq_channel", "More than one writer");
      m_writer = &port_;
    }
    if(std::string(if_typename_) == typeid(zq_read_if<T>).name())
    {
      if( m_reader != 0 )
        SC_REPORT_ERROR("zq_channel", "More than one reader");
      m_reader = &port_;
    }
  }
  
  void end_of_elaboration()
  {
    if( m_writer == 0 || m_reader == 0 )
        SC_REPORT_ERROR("zq_channel", "Must have exactly one writer and one reader");
  }
  
private:  
  T    m_value;
  bool m_written;
  
  sc_event m_write_event;
  sc_event m_read_event;

  sc_port_base *m_writer;
  sc_port_base *m_reader;
};

#endif
#ifndef ZQ_PORT_H
#define ZQ_PORT_H

#include "zq_if.h"

// Specialized read and write ports, each with their own event finder

template<class T>
class zq_write_port: public sc_port<zq_write_if<T> >
{
public:
 
  sc_event_finder& find_read_event() const { 
    return
    *new sc_event_finder_t<zq_write_if<T> >( *this , &zq_write_if<T>::read_event );
  }
};


template<class T>
class zq_read_port: public sc_port<zq_read_if<T> > 
{
public:
  
  sc_event_finder& find_write_event() const { 
    return
    *new sc_event_finder_t<zq_read_if<T> >( *this , &zq_read_if<T>::write_event );
  }
};

#endif
#ifndef PRODUCER_H
#define PRODUCER_H

#include "zq_port.h"

template<class T>
class Producer: public sc_module
{
public:
  
  zq_write_port<int> out;
  
  SC_CTOR(Producer)
  {
    SC_THREAD(run);
    SC_METHOD(monitor);
      sensitive << out.find_read_event();
  }
  
  void run()
  {
    const sc_time tick(10, SC_NS);
    wait(tick);
    out->write(1);
    
    wait(tick);
    wait(tick);
    out->write(2);

    wait(tick);
    out->write(3);
    
    wait(tick);
    out->write(4);

    out->write(5);
}
  
  void monitor()
  {
    std::cout << "Producer sees read_event at " << sc_time_stamp() << std::endl;
  }
};

#endif
#ifndef CONSUMER_H
#define CONSUMER_H

#include "zq_port.h"
using std::cout;
using std::endl;

template<class T>
class Consumer: public sc_module
{
public:
  
  zq_read_port<int> in;
  
  SC_CTOR(Consumer)
  {
    SC_THREAD(run);
    SC_METHOD(monitor);
      sensitive << in.find_write_event();
  }
  
  void run()
  {
    const sc_time tick(10, SC_NS);
    int data;

    wait(tick);
    wait(tick);
    in->read(data);
    cout << "data = " << data << " at " << sc_time_stamp() << endl;

    wait(tick);
    in->read(data);
    cout << "data = " << data << " at " << sc_time_stamp() << endl;

    wait(tick);
    wait(tick);
    in->read(data);
    cout << "data = " << data << " at " << sc_time_stamp() << endl;

    in->read(data);
    cout << "data = " << data << " at " << sc_time_stamp() << endl;

    wait(tick);
    wait(tick);
    in->read(data);
    cout << "data = " << data << " at " << sc_time_stamp() << endl;
}

  void monitor()
  {
    cout << "Consumer sees write_event at " << sc_time_stamp() << endl;
  }
};

#endif
#ifndef TOP_H
#define TOP_H

#include "systemc"
using namespace sc_core;

#include "producer.h"
#include "consumer.h"
#include "zq_channel.h"

class Top: public sc_module
{
public:
  Producer<int> *m_producer;
  Consumer<int> *m_consumer;
  
  zq_channel<int> m_channel;
  
  SC_CTOR(Top)
    : m_channel("m_channel")
  {
    m_producer = new Producer<int>("m_producer");
    m_producer->out.bind(m_channel);
      
    m_consumer = new Consumer<int>("m_consumer");
    m_consumer->in.bind(m_channel);
  }
  
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  return 0;
}

 

next_trigger with Event List Object

Uses a SystemC interface, channel, multiport, and sc_vector to show the next_trigger method being used with an explicit event list object

Run this example in EDA Playground

#ifndef INTERFACE_H
#define INTERFACE_H

// A simple SystemC interface

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

class i_f: virtual public sc_interface
{
public:
  virtual void write(int) = 0;
  virtual void read(int&) = 0;
  virtual bool written() = 0;
};

#endif
#ifndef CHANNEL_H
#define CHANNEL_H

#include "interface.h"

// A simple SystemC channel that implements the
// default_event method of class sc_interface

class channel: public i_f, public sc_object
{
public:
  channel(const char* name): sc_object(name) {}
  
  int  m_value;
  bool m_written;
  
  sc_event m_write_event;

  const sc_event& default_event() const
  {
    return m_write_event;
  }
  
  virtual void write(int arg)
  {
    m_value = arg;
    m_written = true;
    m_write_event.notify();
  }
  
  virtual void read(int& arg)
  {
    arg = m_value;
    m_written = false;
  }
  
  virtual bool written()
  {
    return m_written;
  }

};

#endif
#ifndef MODULE_H
#define MODULE_H

#include "interface.h"

SC_MODULE(module)
{
  sc_port<i_f, 0, SC_ZERO_OR_MORE_BOUND> p;
  
  SC_CTOR(module)
    : p("p")
  {
    SC_METHOD(method);
  }
  
  sc_event_or_list all_events() const
  {
    sc_event_or_list list;
    for (int i = 0; i < p.size(); i++)
      list = list | p[i]->default_event();
    return list;
  }
  
  // An explicit event list object
  sc_event_or_list event_list;
  
  void method()
  {
    // The method is made sensitive to the default event of
    // all the channels that are bound to the multiport p
    
    event_list = all_events();
    next_trigger( event_list );
    
    for (int i = 0; i < p.size(); i++)
    {
      if ( p[i]->written() )
      {
        int value;
        p[i]->read(value);
        cout << "Input " << i << ", value = " << value << endl;
      }
    }
  }

};

#endif
#ifndef TOP_H
#define TOP_H

#include "module.h"
#include "channel.h"

SC_MODULE(top)
{
  module *inst;
  
  sc_vector<channel> channels;
  
  // The number of channels to be bound to the multiport
  const int N = 4; 
  
  SC_CTOR(top)
    : channels("channels")
  {
    channels.init(N);
    inst = new module("inst");
      
    // Bind the multiport p to N channels
    for (int i = 0; i < channels.size(); i++)
      inst->p.bind(channels[i]);
      
    SC_THREAD(run);
  }
  
  void run()
  {
    // Test program to exercise the target module
    
    for (int i = 0; i < channels.size(); i++)
    {
      wait(10, SC_NS);
      channels[i].write(100 + i);
    }
    for (int i = 0; i < channels.size(); i++)
    {
      wait(10, SC_NS);
      channels[N-i-1].write(100 + N-i-1);
    }
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  top top_inst("top_inst");
  sc_start();
  return 0;
}

 

sc_clock

Shows an sc_clock object being constructed explicitly with the clock waveform defined by the constructor arguments

Run this example in EDA Playground

#ifndef CHILD_H
#define CHILD_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

SC_MODULE(child)
{
  sc_in<bool> clk;
  
  SC_CTOR(child)
  {
    SC_THREAD(run);           // Process sensitive to clock
      sensitive << clk.pos(); // Event finder gets event from clock
  }
  
  void run() {
    while (true) {
      wait();
      cout << "child::run awoke at " << sc_time_stamp() << endl; 
    }
  }
};

#endif
#ifndef MODULE_H
#define MODULE_H

#include "child.h"

SC_MODULE(module)
{
  sc_export<sc_signal_in_if<bool> > clock_export;

  const sc_time  period;      // Clock parameters
  const double   duty;
  const sc_time  start_at;
  const bool     pos;
  sc_clock clock;             // Clock instance

  SC_CTOR(module)
  : period  (10, SC_NS),      // Initialize members
    duty    (0.5),
    start_at(200, SC_NS),
    pos     (true),
    clock   ("clock", period, duty, start_at, pos)
  {
    clock_export.bind(clock); // Export the clock
    inst = new child("inst");
    inst->clk(clock);         // Connect clock to child port
    SC_THREAD(run);           // Process sensitive to clock
      sensitive << clock.posedge_event();
  }
  
  child* inst;
  
  void run() {
    while (true) {
      wait();
      cout << "module::run awoke at " << sc_time_stamp() << endl; 
    }
  }
};

#endif
#include "module.h"

int sc_main(int argc, char* argv[])
{
  module inst("module");
  sc_start(250, SC_NS);
  return 0;
}

 

SC_CTHREAD

Shows several clocked thread processes, one of which has a synchronous reset

Run this example in EDA Playground

#ifndef INTERLEAVE_H
#define INTERLEAVE_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Interleave)
{
  sc_in<bool> clock, reset, d0, d1;
  sc_out<bool> q; 

  SC_CTOR(Interleave)
  {
    SC_CTHREAD(run, clock.pos());
      reset_signal_is(reset, true);
  }

  void run()
  {
    q = 0;  // Reset
    wait();
    q = d0; // Initialize after reset
    while (true) {
      wait();
      q = d1;
      wait();
      q = d0;
    }
  }
};

#endif
#ifndef STIMULUS_H
#define STIMULUS_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Stimulus)
{
  sc_in<bool> clock;
  sc_out<bool> reset, d0, d1;

  SC_CTOR(Stimulus)
  {
    SC_CTHREAD(run, clock.neg());
  }

  void run()
  {
    reset = 1;
    d0 = 0;
    d1 = 1;
    wait();
    reset = 0;
    while (true) {
      wait();
    }
  }
};

#endif
#ifndef MONITOR_H
#define MONITOR_H

#include "systemc"
#include "iomanip"
using namespace sc_core;
using std::cout;
using std::endl;

SC_MODULE(Monitor)
{
  sc_in<bool> clock, reset, d0, d1, q;

  SC_CTOR(Monitor)
  {
    SC_CTHREAD(run, clock.neg());
  }

  void run()
  {
    cout << "reset d0 d1  q" << endl;
    wait();
    while (true) {
      wait();
      cout << "    " << reset << "  " << d0 << "  " 
           << d1 << "  " << q << endl;
    }
  }
};

#endif
#include "stimulus.h"
#include "interleave.h"
#include "monitor.h"

int sc_main(int argc, char* argv[])
{
  sc_clock clock("clock");
  sc_signal<bool> reset, d0, d1, q;
  
  Stimulus stimulus("stimulus");
  stimulus.clock.bind(clock);
  stimulus.reset.bind(reset);
  stimulus.d0.bind(d0);
  stimulus.d1.bind(d1);
  
  Interleave interleave("interleave");
  interleave.clock.bind(clock);
  interleave.reset.bind(reset);
  interleave.d0.bind(d0);
  interleave.d1.bind(d1);
  interleave.q.bind(q);
  
  Monitor monitor("monitor");
  monitor.clock.bind(clock);
  monitor.reset.bind(reset);
  monitor.d0.bind(d0);
  monitor.d1.bind(d1);
  monitor.q.bind(q);

  sc_start(10, SC_NS);
  return 0;
}

 

sc_event

Uses an immediate event notification to synchronize two thread processes

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

SC_MODULE(Test)
{
  int data;
  
  sc_event e;

  SC_CTOR(Test)
  {
    SC_THREAD(producer);
    SC_THREAD(consumer);
  }
  
  void producer()
  {
    wait(1, SC_NS);
    for (data = 0; data < 10; data++) {
      e.notify();
      wait(1, SC_NS);
    }
  }
  
  void consumer()
  {
    for (;;) {
      wait(e);
      cout << "Received " << data << endl;
    }
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  return 0;
}

 

sc_event_queue

Simple example of an event queue

Run this example in EDA Playground

#ifndef SOURCE_H
#define SOURCE_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Source)
{
  // A port is not mandatory – just an example
  sc_port<sc_event_queue_if> eqport;

  SC_CTOR(Source) {
    SC_THREAD(run);
  }

  void run() {
    while(true) {
      // Queue a burst of 4 events then wait for them to occur
      for (int i = 0; i < 3; i++)
        eqport->notify( sc_time(i * 10, SC_NS) );
      wait(100, SC_NS);
    }
  }
};

#endif
#ifndef TOP_H
#define TOP_H

#include "source.h"

SC_MODULE(Top)
{
  sc_event_queue eq;
  Source* source;

  SC_CTOR(Top)
  {
    source = new Source("source");
    source->eqport(eq);
    SC_METHOD(run);
      dont_initialize();
      sensitive << eq;
  }
  void run() // Called once per event on the queue
  {
    std::cout << "Event at " << sc_time_stamp() << std::endl;
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start(500, SC_NS);
  return 0;
}

 

sc_export

Shows a SystemC interface and channel with examples of port-to-export binding (module Parent) and export-to-channel binding (module Child2)

Run this example in EDA Playground

#ifndef CHILDREN_H
#define CHILDREN_H

#include "systemc"
using namespace sc_core;

class IF: virtual public sc_interface
{
public:
  virtual void method() = 0;
};

class Channel: public IF, public sc_object
{
public:
  Channel(): sc_object(sc_gen_unique_name("channel")) {}
  virtual void method() {}
};

SC_MODULE(Child1)
{
  sc_port<IF> port;
  SC_CTOR(Child1) {
    SC_THREAD(T);
  }
  void T() {
    port->method();          // Interface Method Call
  }
};

SC_MODULE(Child2)
{
  sc_export<IF> xport;
  Channel chan;              // Local channel

  SC_CTOR(Child2) {
    xport.bind(chan);        // Bind export to channel
  }
};

#endif
#ifndef PARENT_H
#define PARENT_H

#include "children.h"

SC_MODULE(Parent)
{
  Child1* child1;
  Child2* child2;

  SC_CTOR(Parent) {
    child1 = new Child1("child1");
    child2 = new Child2("child2");

    // Top level port-to-export binding
    // Method bind() is equivalent to operator()
    child1->port.bind( child2->xport );
  }
};

#endif
#include "parent.h"

int sc_main(int argc, char* argv[])
{
  Parent parent_inst("parent_inst");
  sc_start();
  return 0;
}

 

sc_fifo

Shows an sc_fifo<int> channel accessed through sc_fifo_in and sc_fifo_out ports

Run this example in EDA Playground

#ifndef PRODUCER_H
#define PRODUCER_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Producer)
{
  sc_fifo_out<int> fifo_out;
  
  SC_CTOR(Producer)
    : fifo_out("fifo_out")
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    int data = 1;
    for (int i = 0; i < 4; i++)
      fifo_out->write(data++);
  }
};

#endif
#ifndef CONSUMER_H
#define CONSUMER_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

SC_MODULE(Consumer)
{
  sc_fifo_in<int> fifo_in;;
  
  SC_CTOR(Consumer)
    : fifo_in("fifo_in")
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    wait(10, SC_NS);
    for (int i = 0; i < 4; i++)
    {
      int data;
      data = fifo_in->read();
      cout << "Consumer received data = " << data << endl;
    }
  }
};

#endif
#ifndef TOP_H
#define TOP_H

#include "producer.h"
#include "consumer.h"

SC_MODULE(Top)
{
  Producer *producer;
  Consumer *consumer;

  sc_fifo<int> fifo;
  
  SC_CTOR(Top)
    : fifo("fifo", 5)
  {
    producer = new Producer("producer");
    producer->fifo_out.bind(fifo);

    consumer = new Consumer("consumer");
    consumer->fifo_in.bind(fifo);
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start();
  return 0;
}

 

SC_HAS_PROCESS

Shows the SC_HAS_PROCESS macro used both outside the constructor (module counter) and inside the constructor (module Top)

Run this example in EDA Playground

#ifndef COUNTER_H
#define COUNTER_H

#include "systemc"
using namespace sc_core;

SC_MODULE(counter) {
  sc_out<int> count;
  sc_in<bool> clock;
  counter(sc_module_name _name, int _size) 
  : sc_module(_name), m_size(_size) {
    SC_THREAD(run);
      sensitive << clock.pos();
  }
  void run();
  SC_HAS_PROCESS(counter);
  int m_size;
};

// Parameterized counter counts up from 0 to m_size-1

void counter::run()
{
  count.write(0);
  wait();
  while (true)
  {
    count.write( (count.read() + 1) % m_size );
    wait();
  }
}

#endif
#ifndef TOP_H
#define TOP_H

#include "counter.h"

SC_MODULE(Top)
{
  counter *m_counter;

  sc_signal<int> count;
  sc_clock       clock;

  Top(sc_module_name _name) 
    : sc_module(_name), count("count"), clock("clock")
  {
    // Instantiate parameterized counter module
    m_counter = new counter("m_counter", 16);
    m_counter->clock.bind(clock);
    m_counter->count.bind(count);

    // SC_HAS_PROCESS can be placed in a module class or its constructor
      
    SC_HAS_PROCESS(Top);

    SC_THREAD(run);
      sensitive << clock.posedge_event();
  }
  
  void run()
  {
    wait();
    while (true)
    {
      wait();
      std::cout << "count = " << count.read() << std::endl;
    }
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start(20, SC_NS);
  return 0;
}

 

sc_int

Example showing basic operations on the sc_int data type

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Test)
{
  SC_CTOR(Test)
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    // A regular C++ int
    int i;
    i = 0x1234;
    sc_assert( i == 0x1234 );

    // An 8-bit sc_int
    sc_dt::sc_int<8> i8;
    
    // sc_int<8> value truncated to 8 bits
    i8 = 0x1234;
    sc_assert( i8 == 0x34 );

    // Assignment from int to sc_int<8>
    i8 = i;
    sc_assert( i8 == 0x34 );

    // Assignment from sc_int<8> to int
    i = i8;
    sc_assert( i == 0x34 );

    // Picking one bit from an sc_int
    sc_assert( i8[0] == 0 );
    
    // Picking a range of bits from an sc_int
    sc_assert( i8.range(7,4) == 0x3 );

    // Initialization to an int value
    sc_dt::sc_int<16> i16(1);
    sc_assert( i16 == 0x0001 );

    // Addition of two sc_ints
    sc_assert( i8 + i16 == 0x0035 );

    // Concatenation of two sc_ints
    sc_assert( concat(i8,i16) == 0x340001 );
    
    // Conversion to string
    sc_assert( i8.to_string(sc_dt::SC_BIN) == "0b00110100" );
    sc_assert( i8.to_string(sc_dt::SC_OCT) == "0o064" );
    sc_assert( i8.to_string(sc_dt::SC_DEC) == "52" );
    sc_assert( i8.to_string(sc_dt::SC_HEX) == "0x34" );
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  
  std::cout << "Reached the end of sc_main" << std::endl;
  return 0;
}

 

sc_logic and sc_lv

Example showing basic operations on the sc_logic and sc_lv data types

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Test)
{
  SC_CTOR(Test)
  {
    SC_THREAD(run);
  }
  
  void run()
  {
    sc_dt::sc_logic a;
    sc_dt::sc_logic b;
    sc_dt::sc_logic c;
    sc_dt::sc_logic d;
    
    // Implicit conversions from char to sc_logic
    a = '0';
    b = '1';
    c = 'X';
    d = 'Z';
    
    // Built-in constants
    sc_assert( a == sc_dt::SC_LOGIC_0 );
    sc_assert( b == sc_dt::SC_LOGIC_1 );
    sc_assert( c == sc_dt::SC_LOGIC_X );
    sc_assert( d == sc_dt::SC_LOGIC_Z );

    // Operators
    sc_assert( (a & b) == sc_dt::SC_LOGIC_0 );
    sc_assert( (a | b) == '1' );
    
    // Logic vectors
    sc_dt::sc_lv<8> p;
    sc_dt::sc_lv<8> q;
    
    // Implicit conversion from string to sc_lv
    p = "111101XZ";
    sc_assert( p[7] == '1' ); // Bit select
    sc_assert( p[6] == '1' );
    sc_assert( p[5] == '1' );
    sc_assert( p[4] == '1' );
    sc_assert( p[3] == '0' );
    sc_assert( p[2] == '1' );
    sc_assert( p[1] == 'X' );
    sc_assert( p[0] == 'Z' );
      
    // Implicit conversion from string to sc_lv
    q = "0X34";
    sc_assert( q == "00110100" );
    
    // Bitwise operations
    sc_assert( (p & q) == "00110100" ); // AND
    sc_assert( (p | q) == "111101XX" ); // OR
    sc_assert( ~p      == "000010XX" ); // NOT
    
    sc_assert( p.range(3,0) == "01XZ" ); // Part select
    sc_assert( p.range(0,3) == "ZX10" ); // Reverse bits
    
    sc_assert( concat(p,q) == "111101XZ00110100" ); // Concatentation

    // Conversion to string
    sc_assert( q.to_string() == "00110100" );

    // Conversion to numerical representation
    sc_assert( q.to_string(sc_dt::SC_BIN) == "0b000110100" );
    sc_assert( q.to_string(sc_dt::SC_OCT) == "0o064" );
    sc_assert( q.to_string(sc_dt::SC_DEC) == "0d52" );
    sc_assert( q.to_string(sc_dt::SC_HEX) == "0x034" );

    // Conversion to int
    int i = q.to_int();
    sc_assert( i == 52 );
    
    // Various methods
    sc_assert( p.is_01() == 0 );
    sc_assert( q.is_01() == 1 );
    sc_assert( q.length() == 8 );
    sc_assert( q.reverse() == "00101100" );
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  
  std::cout << "Reached the end of sc_main" << std::endl;
  return 0;
}

 

sc_pause and sc_get_status

Shows calls to sc_pause and sc_get_status

Run this example in EDA Playground

#ifndef TOP_H
#define TOP_H

#include "systemc"
using namespace sc_core;

class Top: public sc_module
{
public:
  Top(sc_module_name name)
  {
    SC_HAS_PROCESS(Top);
    SC_THREAD(run);
  }
  
  void end_of_elaboration()
  {
    sc_assert( sc_get_status() == SC_END_OF_ELABORATION );
  }
  
  void run()
  {
    sc_assert( sc_get_status() == SC_RUNNING );

    wait(100, SC_NS);
    sc_pause();
    
    wait(10, SC_NS);
    sc_pause();

    wait(90, SC_NS);
    sc_stop();
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_assert( sc_get_status() == SC_ELABORATION );

  sc_start();
  
  sc_assert( sc_get_status() == SC_PAUSED );
  sc_assert( sc_time_stamp() == sc_time(100, SC_NS) );

  sc_start();
  
  sc_assert( sc_get_status() == SC_PAUSED );
  sc_assert( sc_time_stamp() == sc_time(110, SC_NS) );

  sc_start();
  
  sc_assert( sc_get_status() == SC_STOPPED );
  sc_assert( sc_time_stamp() == sc_time(200, SC_NS) );
  
  std::cout << "Reached the end of sc_main" << std::endl;
}

 

sc_report_handler

Shows the use of the methods set_verbosity_level, set_actions, set_log_file_name, and stop_after of class sc_report_handler, and an example of catching the exception thrown by an error report

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;

SC_MODULE(Test)
{
  SC_CTOR(Test)
  {
    SC_THREAD(run);
    
    // By default, an error report throws an exception
    try {
      SC_REPORT_ERROR("/EDAplayground", "The first ERROR report");
    }
    catch (sc_exception& e) {
      SC_REPORT_INFO("/EDAplayground", "Caught exception from first ERROR report");
    }
    
    // Suppress info messages with a verbosity above SC_HIGH (default is SC_MEDIUM)
    sc_report_handler::set_verbosity_level(SC_HIGH);

    // Save all warning reports to a log file irrespective of their message type
    sc_report_handler::set_actions(SC_WARNING, SC_DISPLAY | SC_LOG);
    sc_report_handler::set_log_file_name("warnings.txt");

    // Suppress the default error actions, which are SC_LOG|SC_CACHE_REPORT|SC_THROW
    sc_report_handler::set_actions("/EDAplayground", SC_ERROR, SC_DO_NOTHING);
    
    // Set the error action to write the message to standard output
    sc_report_handler::set_actions("/EDAplayground", SC_ERROR, SC_DISPLAY);
    
    // Call sc_stop() after the fourth error report
    sc_report_handler::stop_after(SC_ERROR, 4);
    
    // Suppress the default fatal actions, which include SC_ABORT 
    sc_report_handler::set_actions("/EDAplayground", SC_FATAL, SC_DO_NOTHING);
  }
  
  void run()
  {
    wait(1, SC_NS);
    
    SC_REPORT_INFO     ("/EDAplayground", "This is an INFO report");
    SC_REPORT_INFO_VERB("/EDAplayground", "This is a LOW INFO report", SC_LOW);
    SC_REPORT_INFO_VERB("/EDAplayground", "This is a HIGH INFO report", SC_HIGH);
    SC_REPORT_INFO_VERB("/EDAplayground", "This is a DEBUG INFO report", SC_DEBUG);

    SC_REPORT_WARNING  ("/EDAplayground", "This is a WARNING report");
    SC_REPORT_ERROR    ("/EDAplayground", "This is an ERROR report");
    SC_REPORT_FATAL    ("/EDAplayground", "This is a FATAL report");
    
    for (int i = 0; i < 10; i++)
    {
      wait(1, SC_NS);
      SC_REPORT_ERROR  ("/EDAplayground", "This is another ERROR report");
    }
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  return 0;
}

 

sc_set_time_resolution

An example of setting the simulation time resolution

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;


SC_MODULE(Test)
{
  SC_CTOR(Test)
  {
    SC_THREAD(thread);
  }
  
  void thread()
  {
    const sc_time period(10, SC_NS);
    
    for (;;)
    {
      wait(period);
      cout << "time   = " << sc_time_stamp()  << endl;
      cout << "deltas = " << sc_delta_count() << endl;
    }
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  sc_set_time_resolution(100, SC_PS);
  
  Test test("test");
  sc_start(100, SC_NS);
  return 0;
}

 

sc_signal Writer Policy

Shows the effect of the sc_signal writer policies SC_ONE_WRITER and SC_MANY_WRITERS

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#include "systemc"
using namespace sc_core;

struct Test: sc_module
{
  // The default is SC_ONE_WRITER, the signal is only ever allowed one writer

  sc_signal<int> sig1; 

  
  // This signal is allowed to have many writers provided the writes occur 
  // in different delta cycles

  sc_signal<int, SC_MANY_WRITERS> sig_many;


  Test(sc_module_name n)
    : sig1("sig1"), sig_many("sig_many")
  {
    SC_HAS_PROCESS(Test);
    SC_THREAD(proc1);
    SC_THREAD(proc2);

    // Avoid aborting simulation on the first error
    sc_report_handler::set_actions(SC_ERROR, SC_DO_NOTHING);
    sc_report_handler::set_actions(SC_ERROR, SC_DISPLAY);
  }
  
  void proc1()
  {
    sig1.write(1);     // Okay
    wait(1, SC_NS);
    sig_many.write(3); // Okay
    wait(1, SC_NS);
    sig_many.write(4); // Error at 2ns
  }

  void proc2()
  {
    sig_many.write(2); // Okay
    wait(1, SC_NS);
    sig1.write(4);     // Error at 1ns
    wait(1, SC_NS);
    sig_many.write(6); // Error at 2ns
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  return 0;
}

 

sc_spawn

Shows sc_spawn being used to create two concurrent thread processes, each of which has one argument

Run this example in EDA Playground

#ifndef TOP_H
#define TOP_H

#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <systemc>
using namespace sc_core;

SC_MODULE(Top)
{
  SC_CTOR(Top)
  {
    // Thread process f1 takes a reference argument
    
    sc_spawn(        sc_bind(&Top::f1, this, sc_ref (n)), "f1");

    // Thread process f2 takes a const reference argument
    
    sc_spawn(&final, sc_bind(&Top::f2, this, sc_cref(n)), "f2");
  }
  
  int n, final;
  
  void f1(int &arg)
  {
    for (arg = 0; arg < 10; arg++)
      wait(10, SC_NS);
  }
  
  int f2(const int &arg)
  {
    wait(5, SC_NS);
    for (int i = 0; i < 10; i++)
    {
      wait(10, SC_NS);
      std::cout << "arg = " << arg << "\n";
    }
    return arg;
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start();
  return 0;
}

 

sc_spawn_options

Shows the use of sc_spawn_options to spawn a method process with static sensitivity

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#define SC_INCLUDE_DYNAMIC_PROCESSES

#include "systemc"
using namespace sc_core;

struct Test: sc_module
{
  SC_CTOR(Test)
  {
    // Spawn a method process sensitive to 'a' and 'b'

    sc_spawn_options opt;
      opt.spawn_method();
      opt.set_sensitivity( &a );
      opt.set_sensitivity( &b );
      opt.dont_initialize();
    
    sc_spawn( sc_bind( &Test::method, this), "method", &opt );
    
    // Spawn a thread process with a single pass-by-value argument
    
    sc_spawn( sc_bind( &Test::thread, this, 5), "thread");
  }

  sc_signal<int> a, b;

  void method()
  {
    std::cout << "a = " << a << ", b = " << b
              << " at " << sc_time_stamp() << std:: endl;
  }
  
  void thread(int count)
  {
    int n = 0;
    for (int i = 0; i < count; i++)
    {
      wait(1, SC_NS);
      a.write(++n);
      wait(1, SC_NS);
      b.write(++n);
    }
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst");
  sc_start();
  return 0;
}

 

sc_vector

Shows a vector-of-ports, a vector-of-signals, and vector-to-vector binding

Run this example in EDA Playground

#ifndef CHILD_H
#define CHILD_H

#include "systemc"
using namespace sc_core;

struct Child: sc_module
{
  sc_vector< sc_in<int> > port_vec; // Vector of ports

  Child(sc_module_name n) : port_vec("port_vec", 4)
  { 
    SC_HAS_PROCESS(Child);
    SC_METHOD(run);
      sensitive << port_vec[0] << port_vec[1] << port_vec[2] << port_vec[3];
      dont_initialize();
  }
  
  void run()
  {
    for (int i = 0; i < port_vec.size(); i++)
       std::cout << " " << port_vec[i];

    std::cout << " at " << sc_time_stamp() << std::endl;
  }
};

#endif
#ifndef TOP_H
#define TOP_H

#include "child.h"

struct Top: sc_module 
{
  sc_vector< sc_signal<int> > sig_vec; // Vector of signals

  Child* c;

  Top(sc_module_name n) : sig_vec("sig_vec", 4)
  {
    c = new Child("c");
    c->port_vec.bind(sig_vec); // Bind vector to vector
    
    SC_HAS_PROCESS(Top);
    SC_THREAD(run);
  }
  
  void run()
  {
    for (int base = 0; base < 4; base++)
    {
      for (int i = 0; i < sig_vec.size(); i++)
         sig_vec[i].write(base * sig_vec.size() + i);
      wait(1, SC_NS);
    }
  } 
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start();
  return 0;
}

 

sc_vector Partial Binding

Shows the partial binding of an sc_vector and the use of sc_assemble_vector to assemble a vector-of-exports from a vector-of-modules where each module has one export

Run this example in EDA Playground

#ifndef CHANNEL_H
#define CHANNEL_H

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;


class i_f: virtual public sc_interface
{
public:
  virtual void print(const sc_object& from) const = 0;
};


class channel: virtual public i_f, public sc_object
{
public:
  channel(const char* name) : sc_object(name) {}

  virtual void print(const sc_object& from) const
  {
    cout << name() << " print called from " << from.name() << endl;
  }
};

#endif
#ifndef MODULES_H
#define MODULES_H

#include "channel.h"

typedef sc_vector< sc_port<i_f> > port_type;

SC_MODULE(M) {
  port_type ports;
  SC_CTOR(M)
  {
    ports.init(8);
    SC_THREAD(run);
  }
  
  void run()
  {
    for (int i = 0; i < ports.size(); i++)
    {
      ports[i]->print(*this);
    }
  }
};


class Target: public sc_module, public i_f
{
public:
  sc_export<i_f> xport;

  SC_CTOR(Target)
    : xport("xport") 
  {
    xport.bind(*this);
  }

  virtual void print(const sc_object& from) const
  {
    cout << name() << " print called from " << from.name() << endl;
  }
};


SC_MODULE(Top) {
  M *m1, *m2;
  sc_vector<channel> vec1;
  sc_vector<channel> vec2;
  sc_vector<Target> targets;

  SC_CTOR(Top) : vec1("vec1"), vec2("vec2") {
    vec1.init(4);
    vec2.init(4);

    m1 = new M("m1");
    m2 = new M("m2");

    port_type::iterator it;

// Bind elements 0 to 3 of ports to vec1
    
    it = m1->ports.bind(vec1);
    
// Bind elements 4 to 7 of ports to vec2
    
    it = m1->ports.bind(vec2.begin(), vec2.end(), it);

    targets.init(8);
    
// Bind elements 0 to 7 of ports to xport in target 0 to 7
    
    m2->ports.bind(
              sc_assemble_vector(targets, &Target::xport));
  }
};

#endif
#include "modules.h"

int sc_main(int argc, char* argv[])
{
  Top top_inst("top_inst");
  sc_start();
  return 0;
}

 

suspend and resume

Shows an abstract task scheduler implemented using the methods suspend and resume of class sc_process_handle

Run this example in EDA Playground

#ifndef TEST_H
#define TEST_H

#define SC_INCLUDE_DYNAMIC_PROCESSES

#include "systemc"
using namespace sc_core;
using std::cout;
using std::endl;

struct Test: sc_module
{
  const int n;
  const sc_time timeslot;
  
  sc_process_handle* task_handle;

  // User-defined constructor argument n_threads

  Test(sc_module_name name, const int n_threads)
    : n(n_threads), timeslot(1, SC_US)
  {
    SC_HAS_PROCESS(Test);
    SC_THREAD(scheduler);

    // Spawn a set of thread processes
    task_handle = new sc_process_handle[n];
    for (int i = 0; i < n; i++)
      task_handle[i] = sc_spawn(sc_bind(&Test::task, this , i));
  }
  
  void scheduler()
  {
    for (int i = 0; i < n; i++)
      task_handle[i].suspend();
    
    while (true)
    {
      int count_dead_processes = 0;

      // Switch between the running tasks
      for (int i = 0; i < n; i++)
      {
        if ( task_handle[i].terminated() )
          count_dead_processes++;
        else
        {
          task_handle[i].resume();
          wait(timeslot);
          task_handle[i].suspend();
        }
      }

      // Quit once all the spawned processes are dead
      if (count_dead_processes == n)
      {
        SC_REPORT_INFO("/EDAplayground", "Scheduler has nothing left to do");
        break;
      }
    }
  }

  void task(int number)
  {
    for (int i = 0; i < 6; i++)
    {
      cout << "Thread " << number << " busy at " << sc_time_stamp() << endl;

      sc_time busy_for;
      busy_for = sc_time(rand() % 800, SC_NS);
      wait(busy_for);

      cout << "Thread " << number << " done at " << sc_time_stamp() << endl;
    }
  }
};

#endif
#include "test.h"

int sc_main(int argc, char* argv[])
{
  Test test_inst("test_inst", 4);
  sc_start();
  return 0;
}

 

TLM-2.0 Tutorial Example 1

Shows the generic payload, sockets, and blocking transport interface. Shows the responsibilities of the initiator and target with respect to the generic payload

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 2  16-June-2008 - updated for TLM-2.0

// Getting Started with TLM-2.0, Tutorial Example 1

// For a full description, see https://www.doulos.com/knowhow/systemc/tlm2

// Shows the generic payload, sockets, and blocking transport interface.
// Shows the responsibilities of initiator and target with respect to the generic payload.
// Has only dummy implementations of the direct memory and debug transaction interfaces.
// Does not show the non-blocking transport interface.
#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_CTOR(Initiator)
  : socket("socket")  // Construct and name socket
  {
    SC_THREAD(thread_process);
  }

  void thread_process()
  {
    // TLM-2 generic payload transaction, reused across calls to b_transport
    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 = 32; i < 96; i += 4)
    {

      tlm::tlm_command cmd = static_cast(rand() % 2);
      if (cmd == tlm::TLM_WRITE_COMMAND) data = 0xFF000000 | i;

      // Initialize 8 out of the 10 attributes, byte_enable_length and extensions being unused
      trans->set_command( cmd );
      trans->set_address( i );
      trans->set_data_ptr( reinterpret_cast(&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

      socket->b_transport( *trans, delay );  // Blocking transport call

      // Initiator obliged to check response status and delay
      if ( trans->is_response_error() )
        SC_REPORT_ERROR("TLM-2", "Response error from b_transport");

      cout << "trans = { " << (cmd ? 'W' : 'R') << ", " << hex << i
           << " } , data = " << hex << data << " at time " << sc_time_stamp()
           << " delay = " << delay << endl;

      // Realize the delay annotated onto the transport call
      wait(delay);
    }
  }

  // Internal data buffer used by initiator with generic payload
  int data;
};

#endif
#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 };

  SC_CTOR(Memory)
  : socket("socket")
  {
    // Register callback for incoming b_transport interface method call
    socket.register_b_transport(this, &Memory::b_transport);

    // Initialize memory with random data
    for (int i = 0; i < SIZE; i++)
      mem[i] = 0xAA000000 | (rand() % 256);
  }

  // TLM-2 blocking transport method
  virtual void 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 DMI hint and extensions
    // Using the SystemC report handler is an acceptable way of signalling an error

    if (adr >= sc_dt::uint64(SIZE) || byt != 0 || len > 4 || wid < len)
      SC_REPORT_ERROR("TLM-2", "Target does not support given generic payload transaction");

    // 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);

    // Obliged to set response status to indicate successful completion
    trans.set_response_status( tlm::TLM_OK_RESPONSE );
  }

  int mem[SIZE];
};

#endif
#ifndef TOP_H
#define TOP_H

#include "initiator.h"
#include "target.h"

SC_MODULE(Top)
{
  Initiator *initiator;
  Memory    *memory;

  SC_CTOR(Top)
  {
    // Instantiate components
    initiator = new Initiator("initiator");
    memory    = new Memory   ("memory");

    // One initiator is bound directly to one target with no intervening bus

    // Bind initiator socket to target socket
    initiator->socket.bind( memory->socket );
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  return 0;
}

 

TLM-2.0 Tutorial Example 2

Shows the direct memory interfaces and the DMI hint. Shows the debug transaction interface. Shows the proper use of the response status

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 2  18-June-2008 - updated for TLM-2.0
// Version 3   3-July-2008 - bug fix: call dmi_data.init()
// Version 4  12-Jan-2009  - fix bug in transport_dbg
// Version 5  26-Sep-2009  - fix bug with set_end_address

// Getting Started with TLM-2.0, Tutorial Example 2

// For a full description, see https://www.doulos.com/knowhow/systemc/tlm2

// Shows the direct memory interfaces and the DMI hint.
// Shows the debug transaction interface
// Shows the proper use of response status

// Define the following macro to invoke an error response from the target
// #define INJECT_ERROR
#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_CTOR(Initiator)
  : 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 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 = 0; i < 128; i += 4)
    {
      int data;
      tlm::tlm_command cmd = static_cast(rand() % 2);
      if (cmd == tlm::TLM_WRITE_COMMAND) data = 0xFF000000 | i;

      // *********************************************
      // Use DMI if it is available, reusing same transaction object
      // *********************************************

      if (dmi_ptr_valid)
      {
        // 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, 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, &data, 4);
          wait( dmi_data.get_write_latency() );
        }

        cout << "DMI   = { " << (cmd ? 'W' : 'R') << ", " << hex << i
             << " } , data = " << hex << data << " at time " << sc_time_stamp() << endl;
      }
      else
      {
        trans->set_command( cmd );
        trans->set_address( i );
        trans->set_data_ptr( reinterpret_cast(&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-user transaction object for DMI
          dmi_data.init();
          dmi_ptr_valid = socket->get_direct_mem_ptr( *trans, dmi_data );
        }

        cout << "trans = { " << (cmd ? 'W' : 'R') << ", " << hex << i
             << " } , data = " << hex << data << " at time " << sc_time_stamp()
             << " delay = " << delay << endl;
      }
    }

    // *********************************************
    // Use debug transaction interface to dump memory contents, reusing same transaction object
    // *********************************************

    trans->set_address(0);
    trans->set_read();
    trans->set_data_length(128);

    unsigned char* data = new unsigned char[128];
    trans->set_data_ptr(data);

    unsigned int n_bytes = socket->transport_dbg( *trans );

    for (unsigned int i = 0; i < n_bytes; i += 4)
    {
      cout << "mem[" << i << "] = "
           << *(reinterpret_cast( &data[i] )) << endl;
    }
  }

  // *********************************************
  // TLM-2 backward DMI method
  // *********************************************

  virtual 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
#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_CTOR(Memory)
  : 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 | (rand() % 256);

    SC_THREAD(invalidation_process);
  }

  // TLM-2 blocking transport method
  virtual void 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 >= sc_dt::uint64(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;
    }

    // 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);

    // Illustrates that b_transport may block
    wait(delay);

    // Reset timing annotation after waiting
    delay = SC_ZERO_TIME;

    // *********************************************
    // 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 );
  }

  // *********************************************
  // TLM-2 forward DMI method
  // *********************************************

  virtual bool 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( &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;
  }

  void invalidation_process()
  {
    // Invalidate DMI pointers periodically
    for (int i = 0; i < 4; i++)
    {
      wait(LATENCY*8);
      socket->invalidate_direct_mem_ptr(0, SIZE-1);
    }
  }

  // *********************************************
  // TLM-2 debug transport method
  // *********************************************

  virtual unsigned int 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;
  }

  int mem[SIZE];
};

#endif
#ifndef TOP_H
#define TOP_H

#include "initiator.h"
#include "target.h"

SC_MODULE(Top)
{
  Initiator *initiator;
  Memory    *memory;

  SC_CTOR(Top)
  {
    // Instantiate components
    initiator = new Initiator("initiator");
    memory    = new Memory   ("memory");

    // One initiator is bound directly to one target with no intervening bus

    // Bind initiator socket to target socket
    initiator->socket.bind(memory->socket);
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  return 0;
}

 

TLM-2.0 Tutorial Example 3

Shows a router modeled as an interconnect component between the initiator and the target. The router decodes the address to select a target, and masks the address in the transaction. Shows the router passing transport, DMI and debug transactions along forward and backward paths and doing address translation in both directions

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 2 - fix warnings that only showed up using g++
// Version 3  18-June-2008 - updated for TLM-2.0
// Version 4  12-Jan-2009  - fix bug in transport_dbg
// Version 5  26-Sep-2009  - fix bug with set_end_address


// Getting Started with TLM-2.0, Tutorial Example 3

// For a full description, see https://www.doulos.com/knowhow/systemc/tlm2

// Shows a router modeled as an interconnect component between the initiator and the target
// The router decodes the address to select a target, and masks the address in the transaction
// Shows the router passing transport, DMI and debug transactions along forward and backward paths
// and doing address translation in both directions


// Define the following macro to invoke an error response from the target
// #define INJECT_ERROR
#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_CTOR(Initiator)
  : 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 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(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() );
        }

        cout << "DMI   = { " << (cmd ? 'W' : 'R') << ", " << hex << i
             << " } , data = " << hex << data << " at time " << sc_time_stamp() << endl;
      }
      else
      {
        trans->set_command( cmd );
        trans->set_address( i );
        trans->set_data_ptr( reinterpret_cast(&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 );
        }

        cout << "trans = { " << (cmd ? 'W' : 'R') << ", " << hex << i
             << " } , data = " << hex << data << " at time " << sc_time_stamp() << endl;
      }
    }

    // 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)
    {
      cout << "mem[" << (A + i) << "] = "
           << *(reinterpret_cast( &data[i] )) << endl;
    }

    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)
    {
      cout << "mem[" << (A + i) << "] = "
           << *(reinterpret_cast( &data[i] )) << endl;
    }
  }

  // TLM-2 backward DMI method
  virtual 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
#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
  // *********************************************

  tlm_utils::simple_initiator_socket_tagged<Router>* initiator_socket[N_TARGETS];

  SC_CTOR(Router)
  : target_socket("target_socket")
  {
    // 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);
      initiator_socket[i] = new tlm_utils::simple_initiator_socket_tagged<Router>(txt);

      // *********************************************
      // 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( (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
#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_CTOR(Memory)
  : 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 blocking transport method
  virtual void 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 );
  }

  // TLM-2 forward DMI method
  virtual bool 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( &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;
  }

  // TLM-2 debug transaction method
  virtual unsigned int 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;
  }

  int mem[SIZE];
  static unsigned int mem_nr;
};

unsigned int Memory::mem_nr = 0;

#endif
#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
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  return 0;
}

 

TLM-2.0 Example 4

Shows the non-blocking transport interface with the generic payload and simple sockets

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 2  19-June-2008 - updated for TLM-2.0


// Getting Started with TLM-2.0, Example 4

// Shows the non-blocking transport interface with the generic payload and simple sockets
// Shows nb_transport used with the forward and backward paths
// Both components are able to accept transactions on the return path,
// although neither component actually uses the return path (TLM_UPDATED)

// Shows the Approximately Timed coding style
// Models processing delay of initiator, latency of target, and request and response accept delays
// Uses payload event queues to manage both timing annotations and internal delays

// Shows the BEGIN_REQ exclusion rule at the initiator and BEGIN_RESP exclusion rule at the target
// In this example, the target allows two pipelined transactions in-flight

// Shows an explicit memory manager and reference counting

// No use of temporal decoupling, DMI or debug transport
// Nominal use of the blocking transport interface just to show the simple socket b/nb adapter
#ifndef UTILITIES_H
#define UTILITIES_H

#include "systemc"
using namespace sc_core;
using namespace sc_dt;
using namespace std;

#include "tlm.h"

static ofstream fout("output.txt");

// **************************************************************************************
// User-defined memory manager, which maintains a pool of transactions
// **************************************************************************************

class mm: public tlm::tlm_mm_interface
{
  typedef tlm::tlm_generic_payload gp_t;

public:
  mm() : free_list(0), empties(0)
  #ifdef DEBUG
  , count(0)
  #endif
  {}

  gp_t* allocate();
  void  free(gp_t* trans);

private:
  struct access
  {
    gp_t* trans;
    access* next;
    access* prev;
  };

  access* free_list;
  access* empties;

  #ifdef DEBUG
  int     count;
  #endif
};

mm::gp_t* mm::allocate()
{
  #ifdef DEBUG
    fout << "----------------------------- Called allocate(), #trans = " << ++count << endl;
  #endif
  gp_t* ptr;
  if (free_list)
  {
    ptr = free_list->trans;
    empties = free_list;
    free_list = free_list->next;
  }
  else
  {
    ptr = new gp_t(this);
  }
  return ptr;
}

void mm::free(gp_t* trans)
{
  #ifdef DEBUG
    fout << "----------------------------- Called free(), #trans = " << --count << endl;
  #endif
  if (!empties)
  {
    empties = new access;
    empties->next = free_list;
    empties->prev = 0;
    if (free_list)
      free_list->prev = empties;
  }
  free_list = empties;
  free_list->trans = trans;
  empties = free_list->prev;
}

// Generate a random delay (with power-law distribution) to aid testing and stress the protocol
int rand_ps()
{
  int n = rand() % 100;
  n = n * n * n;
  return n / 100;
}

#endif
#ifndef INITIATOR_H
#define INITIATOR_H

#include "utilities.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/peq_with_cb_and_phase.h"


// **************************************************************************************
// Initiator module generating multiple pipelined 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_CTOR(Initiator)
  : socket("socket")  // Construct and name socket
  , request_in_progress(0)
  , m_peq(this, &Initiator::peq_cb)
  {
    // Register callbacks for incoming interface method calls
    socket.register_nb_transport_bw(this, &Initiator::nb_transport_bw);

    SC_THREAD(thread_process);
  }

  void thread_process()
  {
    tlm::tlm_generic_payload* trans;
    tlm::tlm_phase phase;
    sc_time delay;

    // Generate a sequence of random transactions
    for (int i = 0; i < 1000; i++)
    {
      int adr = rand();
      tlm::tlm_command cmd = static_cast(rand() % 2);
      if (cmd == tlm::TLM_WRITE_COMMAND) data[i % 16] = rand();

      // Grab a new transaction from the memory manager
      trans = m_mm.allocate();
      trans->acquire();

      // Set all attributes except byte_enable_length and extensions (unused)
      trans->set_command( cmd );
      trans->set_address( adr );
      trans->set_data_ptr( reinterpret_cast(&data[i % 16]) );
      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

      // Initiator must honor BEGIN_REQ/END_REQ exclusion rule
      if (request_in_progress)
        wait(end_request_event);
      request_in_progress = trans;
      phase = tlm::BEGIN_REQ;

      // Timing annotation models processing time of initiator prior to call
      delay = sc_time(rand_ps(), SC_PS);

      fout << hex << adr << " new, cmd=" << (cmd ? 'W' : 'R')
           << ", data=" << hex << data[i % 16] << " at time " << sc_time_stamp() << endl;

      // Non-blocking transport call on the forward path
      tlm::tlm_sync_enum status;
      status = socket->nb_transport_fw( *trans, phase, delay );

      // Check value returned from nb_transport_fw
      if (status == tlm::TLM_UPDATED)
      {
        // The timing annotation must be honored
        m_peq.notify( *trans, phase, delay );
      }
      else if (status == tlm::TLM_COMPLETED)
      {
        // The completion of the transaction necessarily ends the BEGIN_REQ phase
        request_in_progress = 0;

        // The target has terminated the transaction
        check_transaction( *trans );
      }
      wait( sc_time(rand_ps(), SC_PS) );
    }

    wait(100, SC_NS);

    // Allocate a transaction for one final, nominal call to b_transport
    trans = m_mm.allocate();
    trans->acquire();
    trans->set_command( tlm::TLM_WRITE_COMMAND );
    trans->set_address( 0 );
    trans->set_data_ptr( reinterpret_cast(&data[0]) );
    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

    delay = sc_time(rand_ps(), SC_PS);

    fout << "Calling b_transport at " << sc_time_stamp() << " with delay = " << delay << endl;

    // Call b_transport to demonstrate the b/nb conversion by the simple_target_socket
    socket->b_transport( *trans, delay );
    check_transaction( *trans );
  }

  // TLM-2 backward non-blocking transport method

  virtual tlm::tlm_sync_enum nb_transport_bw( tlm::tlm_generic_payload& trans,
                                              tlm::tlm_phase& phase, sc_time& delay )
  {
    // The timing annotation must be honored
    m_peq.notify( trans, phase, delay );
    return tlm::TLM_ACCEPTED;
  }

  // Payload event queue callback to handle transactions from target
  // Transaction could have arrived through return path or backward path

  void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase)
  {
    #ifdef DEBUG
      if (phase == tlm::END_REQ)
        fout << hex << trans.get_address() << " END_REQ at " << sc_time_stamp() << endl;
      else if (phase == tlm::BEGIN_RESP)
        fout << hex << trans.get_address() << " BEGIN_RESP at " << sc_time_stamp() << endl;
    #endif

    if (phase == tlm::END_REQ || (&trans == request_in_progress && phase == tlm::BEGIN_RESP))
    {
      // The end of the BEGIN_REQ phase
      request_in_progress = 0;
      end_request_event.notify();
    }
    else if (phase == tlm::BEGIN_REQ || phase == tlm::END_RESP)
      SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by initiator");

    if (phase == tlm::BEGIN_RESP)
    {
      check_transaction( trans );

      // Send final phase transition to target
      tlm::tlm_phase fw_phase = tlm::END_RESP;
      sc_time delay = sc_time(rand_ps(), SC_PS);
      socket->nb_transport_fw( trans, fw_phase, delay );
      // Ignore return value
    }
  }

  // Called on receiving BEGIN_RESP or TLM_COMPLETED
  void check_transaction(tlm::tlm_generic_payload& trans)
  {
    if ( trans.is_response_error() )
    {
      char txt[100];
      sprintf(txt, "Transaction returned with error, response status = %s",
                   trans.get_response_string().c_str());
      SC_REPORT_ERROR("TLM-2", txt);
    }

    tlm::tlm_command cmd = trans.get_command();
    sc_dt::uint64    adr = trans.get_address();
    int*             ptr = reinterpret_cast<int*>( trans.get_data_ptr() );

    fout<< hex << adr << " check, cmd=" << (cmd ? 'W' : 'R')
         << ", data=" << hex << *ptr << " at time " << sc_time_stamp() << endl;

    // Allow the memory manager to free the transaction object
    trans.release();
  }

  mm   m_mm;
  int  data[16];
  tlm::tlm_generic_payload* request_in_progress;
  sc_event end_request_event;
  tlm_utils::peq_with_cb_and_phase<Initiator> m_peq;
};

#endif
</int*>
#ifndef TARGET_H
#define TARGET_H

// Needed for the simple_target_socket
#define SC_INCLUDE_DYNAMIC_PROCESSES

#include "utilities.h"
#include "tlm_utils/simple_target_socket.h"


// **************************************************************************************
// Target module able to handle two pipelined transactions
// **************************************************************************************

DECLARE_EXTENDED_PHASE(internal_ph);

struct Target: sc_module
{
  // TLM-2 socket, defaults to 32-bits wide, base protocol
  tlm_utils::simple_target_socket<Target> socket;

  SC_CTOR(Target)
  : socket("socket")
  , n_trans(0)
  , response_in_progress(false)
  , next_response_pending(0)
  , end_req_pending(0)
  , m_peq(this, &Target::peq_cb)
  {
    // Register callbacks for incoming interface method calls
    socket.register_nb_transport_fw(this, &Target::nb_transport_fw);
  }

  // TLM-2 non-blocking transport method

  virtual tlm::tlm_sync_enum nb_transport_fw( tlm::tlm_generic_payload& trans,
                                              tlm::tlm_phase& phase, sc_time& delay )
  {
    sc_dt::uint64    adr = trans.get_address();
    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 the transaction attributes for unsupported features
    // and to generate the appropriate error response
    if (byt != 0) {
      trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
      return tlm::TLM_COMPLETED;
    }
    if (len > 4 || wid < len) {
      trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
      return tlm::TLM_COMPLETED;
    }

    // Now queue the transaction until the annotated time has elapsed
    m_peq.notify( trans, phase, delay);
    return tlm::TLM_ACCEPTED;
  }

  void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase)
  {
    tlm::tlm_sync_enum status;
    sc_time delay;

    switch (phase) {
    case tlm::BEGIN_REQ:

      #ifdef DEBUG
        fout << hex << trans.get_address() << " BEGIN_REQ at " << sc_time_stamp() << endl;
      #endif

      // Increment the transaction reference count
      trans.acquire();

      // Put back-pressure on initiator by deferring END_REQ until pipeline is clear
      if (n_trans == 2)
        end_req_pending = &trans;
      else
      {
        status = send_end_req(trans);
        if (status == tlm::TLM_COMPLETED) // It is questionable whether this is valid
          break;
      }

      break;

    case tlm::END_RESP:
      // On receiving END_RESP, the target can release the transaction
      // and allow other pending transactions to proceed

      #ifdef DEBUG
        fout << hex << trans.get_address() << " END_RESP at " << sc_time_stamp() << endl;
      #endif

      if (!response_in_progress)
        SC_REPORT_FATAL("TLM-2", "Illegal transaction phase END_RESP received by target");

      trans.release();
      n_trans--;

      // Target itself is now clear to issue the next BEGIN_RESP
      response_in_progress = false;
      if (next_response_pending)
      {
        send_response( *next_response_pending );
        next_response_pending = 0;
      }

      // ... and to unblock the initiator by issuing END_REQ
      if (end_req_pending)
      {
        status = send_end_req( *end_req_pending );
        end_req_pending = 0;
      }

      break;

    case tlm::END_REQ:
    case tlm::BEGIN_RESP:
      SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by target");
      break;

    default:
      if (phase == internal_ph)
      {
        // Execute the read or write commands

        tlm::tlm_command cmd = trans.get_command();
        sc_dt::uint64    adr = trans.get_address();
        unsigned char*   ptr = trans.get_data_ptr();
        unsigned int     len = trans.get_data_length();

        if ( cmd == tlm::TLM_READ_COMMAND )
        {
          *reinterpret_cast<int*>(ptr) = rand();
          fout << hex << adr << " Execute READ, data = " << *reinterpret_cast<int*>(ptr) << endl;
        }
        else if ( cmd == tlm::TLM_WRITE_COMMAND )
          fout << hex << adr << " Execute WRITE, data = " << *reinterpret_cast<int*>(ptr) << endl;

        trans.set_response_status( tlm::TLM_OK_RESPONSE );

        // Target must honor BEGIN_RESP/END_RESP exclusion rule
        // i.e. must not send BEGIN_RESP until receiving previous END_RESP or BEGIN_REQ
        if (response_in_progress)
        {
          // Target allows only two transactions in-flight
          if (next_response_pending)
            SC_REPORT_FATAL("TLM-2", "Attempt to have two pending responses in target");
          next_response_pending = &trans;
        }
        else
          send_response(trans);
        break;
      }
    }
  }

  tlm::tlm_sync_enum send_end_req(tlm::tlm_generic_payload& trans)
  {
    tlm::tlm_sync_enum status;
    tlm::tlm_phase bw_phase;
    tlm::tlm_phase int_phase = internal_ph;
    sc_time delay;

    // Queue the acceptance and the response with the appropriate latency
    bw_phase = tlm::END_REQ;
    delay = sc_time(rand_ps(), SC_PS); // Accept delay
    status = socket->nb_transport_bw( trans, bw_phase, delay );
    if (status == tlm::TLM_COMPLETED)
    {
      // Transaction aborted by the initiator
      // (TLM_UPDATED cannot occur at this point in the base protocol, so need not be checked)
      trans.release();
      return status;
    }

    // Queue internal event to mark beginning of response
    delay = delay + sc_time(rand_ps(), SC_PS); // Latency
    m_peq.notify( trans, int_phase, delay );
    n_trans++;

    return status;
  }

  void send_response(tlm::tlm_generic_payload& trans)
  {
    tlm::tlm_sync_enum status;
    tlm::tlm_phase bw_phase;
    sc_time delay;

    response_in_progress = true;
    bw_phase = tlm::BEGIN_RESP;
    delay = SC_ZERO_TIME;
    status = socket->nb_transport_bw( trans, bw_phase, delay );

    if (status == tlm::TLM_UPDATED)
    {
      // The timing annotation must be honored
      m_peq.notify( trans, bw_phase, delay);
    }
    else if (status == tlm::TLM_COMPLETED)
    {
      // The initiator has terminated the transaction
      trans.release();
      n_trans--;
      response_in_progress = false;
    }
  }

  int   n_trans;
  bool  response_in_progress;
  tlm::tlm_generic_payload*  next_response_pending;
  tlm::tlm_generic_payload*  end_req_pending;
  tlm_utils::peq_with_cb_and_phase<Target> m_peq;
};

#endif
</int*></int*></int*>
#ifndef TOP_H
#define TOP_H

#include "initiator.h"
#include "target.h"

SC_MODULE(Top)
{
  Initiator *initiator;
  Target    *target;

  SC_CTOR(Top)
  {
    // Instantiate components
    initiator = new Initiator("initiator");
    target    = new Target   ("target");

    // One initiator is bound directly to one target with no intervening bus

    // Bind initiator socket to target socket
    initiator->socket.bind(target->socket);
  }
};

#endif
#define DEBUG

#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  
  cout << "\n***** Messages have been written to file output.txt                    *****\n";
  cout << "***** Select 'Download files after run' to read file in EDA Playground *****\n\n";

  return 0;
}

 

TLM-2.0 Example 5

Shows two loosely-timed initiators both of which use the quantum keeper to keep track of temporal decoupling

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 1, 26-June-2008
// Version 2,  3-July-2008 - fix bug: call dmi_data.init()
// Version 3  12-Jan-2009  - fix bug in transport_dbg
// Version 4  26-Sep-2009  - fix bug with set_end_address


// Getting Started with TLM-2.0, Example 5

// Shows two loosely-timed initiators both with temporal decoupling and quantum keeper

// Shows a bus with multiple initiators and multiple targets (four memories)
// Routes transactions to target and back using address decoding built into the bus
// Uses tagged interfaces and sockets to implement multiple fw/bw interfaces in a single module
// Propagates DMI calls on both forward and backward paths,
// with 'invalidate' being broadcast to every initiator

// Shows transaction pooling using a memory manager
#ifndef UTILITIES_H
#define UTILITIES_H

#include "systemc"
using namespace sc_core;
using namespace sc_dt;
using namespace std;

#include "tlm.h"

// **************************************************************************************
// User-defined memory manager, which maintains a pool of transactions
// **************************************************************************************

class mm: public tlm::tlm_mm_interface
{
  typedef tlm::tlm_generic_payload gp_t;

public:
  mm() : free_list(0), empties(0) {}

  gp_t* allocate();
  void  free(gp_t* trans);

private:
  struct access
  {
    gp_t* trans;
    access* next;
    access* prev;
  };

  access* free_list;
  access* empties;

};

mm::gp_t* mm::allocate()
{
  gp_t* ptr;
  if (free_list)
  {
    ptr = free_list->trans;
    empties = free_list;
    free_list = free_list->next;
  }
  else
  {
    ptr = new gp_t(this);
  }
  return ptr;
}

void mm::free(gp_t* trans)
{
  if (!empties)
  {
    empties = new access;
    empties->next = free_list;
    empties->prev = 0;
    if (free_list)
      free_list->prev = empties;
  }
  free_list = empties;
  free_list->trans = trans;
  empties = free_list->prev;
}

#endif
#ifndef INITIATOR1_H
#define INITIATOR1_H

#include "utilities.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/tlm_quantumkeeper.h"


// *****************************************************************************************
// Initiator1 writes to all 4 memories, and demonstrates DMI and debug transport
// Does not use an explicit memory manager
// *****************************************************************************************

const int RUN_LENGTH = 256;

struct Initiator1: sc_module
{
  tlm_utils::simple_initiator_socket<Initiator1> socket;

  SC_CTOR(Initiator1) : socket("socket"), dmi_ptr_valid(false)
  {
    socket.register_invalidate_direct_mem_ptr(this, &Initiator1::invalidate_direct_mem_ptr);

    SC_THREAD(thread_process);

    // *************************************************************************
    // All initiators use a quantum of 1us, that is, they synchronize themselves
    // to simulation time every 1us using the quantum keeper
    // *************************************************************************

    m_qk.set_global_quantum( sc_time(1, SC_US) );
    m_qk.reset();
  }

  void thread_process() {
    // Use debug transaction interface to dump entire memory contents
    dump();

    tlm::tlm_generic_payload* trans = new tlm::tlm_generic_payload;
    sc_time delay;

    for (int i = 0; i < RUN_LENGTH; i += 4)
    {
      data = i;
      delay = m_qk.get_local_time();

      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
        assert( dmi_data.is_write_allowed() );
        memcpy(dmi_data.get_dmi_ptr() + i - dmi_data.get_start_address(), &data, 4);

        // Accumulate memory latency into local time
        delay += dmi_data.get_write_latency();

        cout << "WRITE/DMI addr = " << hex << i << ", data = " << data
             << " at " << sc_time_stamp() << " delay = " << delay << "\n";
      }
      else
      {
        // No DMI, so use blocking transport interface
        trans->set_command( tlm::TLM_WRITE_COMMAND );
        trans->set_address( i );
        trans->set_data_ptr( reinterpret_cast(&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

        socket->b_transport( *trans, delay );

        cout << "WRITE     addr = " << hex << i << ", data = " << data
             << " at " << sc_time_stamp() << " delay = " << delay << "\n";

        // Initiator obliged to check response status
        if (trans->is_response_error())
          SC_REPORT_ERROR("TLM-2", trans->get_response_string().c_str());

        if ( trans->is_dmi_allowed() )
        {
          dmi_data.init();
          dmi_ptr_valid = socket->get_direct_mem_ptr( *trans, dmi_data );
        }
      }

      // Accumulate local time and synchronize when quantum is reached
      m_qk.set( delay );
      m_qk.inc( sc_time(100, SC_NS) ); // Model time used for additional processing
      if (m_qk.need_sync()) m_qk.sync();
    }

    // Use debug transaction interface to dump entire memory contents
    dump();
  }


  virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, sc_dt::uint64 end_range)
  {
    cout << "INVALIDATE DMI (" << start_range << ".." << end_range
         << ") for Initiator1 at " << sc_time_stamp() << "\n";

    // Ignore range and invalidate all DMI pointers regardless
    dmi_ptr_valid = false;
  }

  void dump()
  {
  unsigned char buffer[64];
    // Use debug transaction interface to dump memory contents

    cout << "\nDump memories at time " << sc_time_stamp() << "\n";

    for (unsigned int k = 0; k < 4; k++)
    {
      tlm::tlm_generic_payload dbg;
      sc_dt::uint64 A = 64 * k;
      dbg.set_address(A);
      dbg.set_read();
      dbg.set_data_length(64);
      dbg.set_data_ptr(buffer);

      unsigned int n_bytes = socket->transport_dbg( dbg );

      for (unsigned int i = 0; i < n_bytes; i += 4)
      {
        cout << "mem[" << hex << (A + i) << "] = "
             << *(reinterpret_cast( &buffer[i] )) << endl;
      }
    }
    cout << "\n";
  }

  int data; // Internal data buffer used by initiator with generic payload
  tlm_utils::tlm_quantumkeeper m_qk; // Quantum keeper for temporal decoupling
  bool dmi_ptr_valid;
  tlm::tlm_dmi dmi_data;
};

#endif
#ifndef INITIATOR2_H
#define INITIATOR2_H

#include "utilities.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/tlm_quantumkeeper.h"

// *****************************************************************************************
// Initiator2 reads from all 4 memories, but does not use DMI or debug transport
// Uses an explicit memory manager and transaction pool
// *****************************************************************************************

struct Initiator2: sc_module
{
  tlm_utils::simple_initiator_socket<Initiator2> socket;

  SC_CTOR(Initiator2) : socket("socket")
  {
    // No callback methods registered with socket

    SC_THREAD(thread_process);
  }

  void thread_process()
  {
    tlm::tlm_generic_payload* trans;
    sc_time delay;

    // Reset the local quantum keeper
    m_qk.reset();
    wait(1, SC_US);

    for (int i = 0; i < RUN_LENGTH; i += 4)
    {
      // Grab a new transaction from the memory manager
      trans = m_mm.allocate();
      trans->acquire();

      data = i;

      trans->set_command( tlm::TLM_READ_COMMAND );
      trans->set_address( i );
      trans->set_data_ptr( reinterpret_cast(&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

      delay = m_qk.get_local_time();

      socket->b_transport( *trans, delay );

      // Initiator obliged to check response status
      if (trans->is_response_error())
        SC_REPORT_ERROR("TLM-2", trans->get_response_string().c_str());
      if (data != i)
        SC_REPORT_ERROR("TLM-2", "Mismatch in initiator when reading back data");

      cout << "READ     addr = " << hex << i << ", data = " << data
           << " at " << sc_time_stamp() << " delay = " << delay << "\n";
      trans->release();

      // Accumulate local time and synchronize when quantum is reached
      m_qk.set( delay );
      m_qk.inc( sc_time(100, SC_NS) );// Model time used for additional processing
      if (m_qk.need_sync()) m_qk.sync();
    }
  }

  int data; // Internal data buffer used by initiator with generic payload

  mm   m_mm;                         // Memory manager
  tlm_utils::tlm_quantumkeeper m_qk; // Quantum keeper for temporal decoupling
};

#endif
#ifndef BUS_H
#define BUS_H

#include "utilities.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"

// ************************************************************************************
// Bus model supports multiple initiators and multiple targets
// Supports b_ and nb_ transport interfaces, although only b_transport is actually used
// It does no arbitration, but routes all transactions from initiators without blocking
// It uses a simple built-in routing algorithm
// ************************************************************************************

template<unsigned int N_INITIATORS, unsigned int N_TARGETS>
struct Bus: sc_module
{
  // Tagged sockets allow incoming transactions to be identified
  tlm_utils::simple_target_socket_tagged<Bus>*    targ_socket[N_INITIATORS];
  tlm_utils::simple_initiator_socket_tagged<Bus>* init_socket[N_TARGETS];

  SC_CTOR(Bus)
  {
    for (unsigned int i = 0; i < N_INITIATORS; i++)
    {
      char txt[20];
      sprintf(txt, "targ_socket_%d", i);
      targ_socket[i] = new tlm_utils::simple_target_socket_tagged<Bus>(txt);

      targ_socket[i]->register_nb_transport_fw(   this, &Bus::nb_transport_fw, i);
      targ_socket[i]->register_b_transport(       this, &Bus::b_transport, i);
      targ_socket[i]->register_get_direct_mem_ptr(this, &Bus::get_direct_mem_ptr, i);
      targ_socket[i]->register_transport_dbg(     this, &Bus::transport_dbg, i);
    }
    for (unsigned int i = 0; i < N_TARGETS; i++)
    {
      char txt[20];
      sprintf(txt, "init_socket_%d", i);
      init_socket[i] = new tlm_utils::simple_initiator_socket_tagged<Bus>(txt);

      init_socket[i]->register_nb_transport_bw(          this, &Bus::nb_transport_bw, i);
      init_socket[i]->register_invalidate_direct_mem_ptr(this, &Bus::invalidate_direct_mem_ptr, i);
    }
  }


  // Tagged non-blocking transport forward method
  virtual tlm::tlm_sync_enum nb_transport_fw(int id,
      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay)
  {
    if (id < N_INITIATORS)
    {
      // Forward path
      m_id_map[ &trans ] = id;

      sc_dt::uint64 address = trans.get_address();
      sc_dt::uint64 masked_address;
      unsigned int target_nr = decode_address( address, masked_address);

      if (target_nr < N_TARGETS)
      {
        // Modify address within transaction
        trans.set_address( masked_address );

        // Forward transaction to appropriate target
        tlm::tlm_sync_enum status = (*init_socket[target_nr])->nb_transport_fw(trans, phase, delay);

        if (status == tlm::TLM_COMPLETED)
          // Put back original address
          trans.set_address( address );
        return status;
      }
      else
        return tlm::TLM_COMPLETED;
    }
    else
    {
      SC_REPORT_FATAL("TLM-2", "Invalid tagged socket id in bus");
      return tlm::TLM_COMPLETED;
    }
  }

  // Tagged non-blocking transport backward method
  virtual tlm::tlm_sync_enum nb_transport_bw(int id,
      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay)
  {
    if (id < N_TARGETS)
    {
      // Backward path

      // Replace original address
      sc_dt::uint64 address = trans.get_address();
      trans.set_address( compose_address( id, address ) );

      return ( *(targ_socket[ m_id_map[ &trans ] ]) )->nb_transport_bw(trans, phase, delay);
    }
    else
    {
      SC_REPORT_FATAL("TLM-2", "Invalid tagged socket id in bus");
      return tlm::TLM_COMPLETED;
    }
  }

  // Tagged TLM-2 blocking transport method
  virtual void b_transport( int id, tlm::tlm_generic_payload& trans, sc_time& delay )
  {
    if (id < N_INITIATORS)
    {
      // Forward path
      sc_dt::uint64 address = trans.get_address();
      sc_dt::uint64 masked_address;
      unsigned int target_nr = decode_address( address, masked_address);

      if (target_nr < N_TARGETS)
      {
        // Modify address within transaction
        trans.set_address( masked_address );

        // Forward transaction to appropriate target
        (*init_socket[target_nr])->b_transport(trans, delay);

        // Replace original address
        trans.set_address( address );
      }
    }
    else
      SC_REPORT_FATAL("TLM-2", "Invalid tagged socket id in bus");
  }

  // Tagged TLM-2 forward DMI method
  virtual bool get_direct_mem_ptr(int id,
                                  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 );
    if (target_nr >= N_TARGETS)
      return false;

    trans.set_address( masked_address );

    bool status = ( *init_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;
  }


  // Tagged debug transaction method
  virtual unsigned int transport_dbg(int id, tlm::tlm_generic_payload& trans)
  {
    sc_dt::uint64 masked_address;
    unsigned int target_nr = decode_address( trans.get_address(), masked_address );
    if (target_nr >= N_TARGETS)
      return 0;
    trans.set_address( masked_address );

    // Forward debug transaction to appropriate target
    return ( *init_socket[target_nr] )->transport_dbg( trans );
  }


  // 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 );

    // Propagate call backward to all initiators
    for (unsigned int i = 0; i < N_INITIATORS; i++)
      (*targ_socket[i])->invalidate_direct_mem_ptr(bw_start_range, bw_end_range);
  }

  // Simple fixed address decoding
  inline unsigned int decode_address( sc_dt::uint64 address, sc_dt::uint64& masked_address )
  {
    unsigned int target_nr = static_cast( (address >> 6) & 0x3 );
    masked_address = address & 0x3F;
    return target_nr;
  }

  inline sc_dt::uint64 compose_address( unsigned int target_nr, sc_dt::uint64 address)
  {
    return (target_nr << 6) | (address & 0x3F);
  }

  std::map <tlm::tlm_generic_payload*, unsigned="" int=""> m_id_map;
};

#endif
</tlm::tlm_generic_payload*,>
#ifndef TARGET_H
#define TARGET_H

// *****************************************************************************************
// Target memory implements b_transport, DMI and debug
// *****************************************************************************************

struct Memory: sc_module
{
  tlm_utils::simple_target_socket<Memory> socket;

  enum { SIZE = 64 };
  const sc_time LATENCY;

  SC_CTOR(Memory)
  : socket("socket"), LATENCY(10, SC_NS)
  {
    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);

    // Each instance is given identifiable contents to help debug
    ++mem_nr;

    SC_THREAD(invalidation_process);
  }

  virtual void 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();

    if (adr > sc_dt::uint64(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;
    }

    if (trans.get_command() == tlm::TLM_READ_COMMAND)
      memcpy(ptr, &mem[adr], len);
    else if (cmd == tlm::TLM_WRITE_COMMAND)
      memcpy(&mem[adr], ptr, len);

    // Use temporal decoupling: add memory latency to delay argument
    delay += LATENCY;

    trans.set_dmi_allowed(true);
    trans.set_response_status( tlm::TLM_OK_RESPONSE );
  }


  // TLM-2 DMI method
  virtual bool 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( &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;
  }


  // TLM-2 debug transaction method
  virtual unsigned int 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;
  }

  void invalidation_process()
  {
    // Invalidate DMI pointers once just as an example of routing a call back to initiators
    wait(3, SC_US);
    socket->invalidate_direct_mem_ptr(0, SIZE-1);
  }

  int mem[SIZE];
  static unsigned int mem_nr;  // Unique memory number to help debug
};

unsigned int Memory::mem_nr = 0;

#endif
#ifndef TOP_H
#define TOP_H

#include "initiator1.h"
#include "initiator2.h"
#include "bus.h"
#include "target.h"

// *****************************************************************************************
// Top-level module instantiates 2 initiators, a bus, and 4 memories
// *****************************************************************************************

SC_MODULE(Top)
{
  Initiator1* init1;
  Initiator2* init2;
  Bus<2,4>*   bus;
  Memory*     memory[4];

  SC_CTOR(Top)
  {
    init1 = new Initiator1("init1");
    init2 = new Initiator2("init2");
    bus   = new Bus<2,4>  ("bus");

    init1->socket.bind( *(bus->targ_socket[0]) );
    init2->socket.bind( *(bus->targ_socket[1]) );

    for (int i = 0; i < 4; i++)
    {
      char txt[20];
      sprintf(txt, "memory_%d", i);
      memory[i] = new Memory(txt);

      ( *(bus->init_socket[i]) ).bind( memory[i]->socket );
    }
  }
};


#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();
  return 0;
}

 

TLM-2.0 Example 6

Shows the use of multi-sockets in an interconnect component

Run this example in EDA Playground

//----------------------------------------------------------------------
//  Copyright (c) 2007-2008 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.
//----------------------------------------------------------------------

// Version 1, 26-June-2008
// Version 2,  7-July-2008  Remove N_INITIATORS, N_TARGETS template parameters from Bus
// Version 3   8-March-2010 Replaced target end_req_pending pointer with a queue

// Getting Started with TLM-2.0, Example 6

// Shows the use of multi-sockets in an interconnect component,
// that is, multi_passthrough_initiator_socket and multi_passthrough_target_socket

// This example combines the AT initiator and target from example 4 with the bus from
// example 5, modified to use multi-sockets instead of tagged sockets
// Uses the forward and backward non-blocking transport interfaces of the bus interconnect
#ifndef UTILITIES_H
#define UTILITIES_H

#include "systemc"
using namespace sc_core;
using namespace sc_dt;
using namespace std;

#include "tlm.h"

static ofstream fout("output.txt");

// **************************************************************************************
// User-defined memory manager, which maintains a pool of transactions
// **************************************************************************************

class mm: public tlm::tlm_mm_interface
{
  typedef tlm::tlm_generic_payload gp_t;

public:
  mm() : free_list(0), empties(0) {}

  gp_t* allocate();
  void  free(gp_t* trans);

private:
  struct access
  {
    gp_t* trans;
    access* next;
    access* prev;
  };

  access* free_list;
  access* empties;

};

mm::gp_t* mm::allocate()
{
  gp_t* ptr;
  if (free_list)
  {
    ptr = free_list->trans;
    empties = free_list;
    free_list = free_list->next;
  }
  else
  {
    ptr = new gp_t(this);
  }
  return ptr;
}

void mm::free(gp_t* trans)
{
  if (!empties)
  {
    empties = new access;
    empties->next = free_list;
    empties->prev = 0;
    if (free_list)
      free_list->prev = empties;
  }
  free_list = empties;
  free_list->trans = trans;
  empties = free_list->prev;
}


// Generate a random delay (with power-law distribution) to aid testing and stress the protocol
int rand_ps()
{
  int n = rand() % 100;
  n = n * n * n;
  return n / 100;
}

#endif
#ifndef INITIATOR_H
#define INITIATOR_H

#include "utilities.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/peq_with_cb_and_phase.h"


// **************************************************************************************
// Initiator module generating multiple pipelined 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_CTOR(Initiator)
  : socket("socket")  // Construct and name socket
  , request_in_progress(0)
  , m_peq(this, &Initiator::peq_cb)
  {
    // Register callbacks for incoming interface method calls
    socket.register_nb_transport_bw(this, &Initiator::nb_transport_bw);

    SC_THREAD(thread_process);
  }

  void thread_process()
  {
    tlm::tlm_generic_payload* trans;
    tlm::tlm_phase phase;
    sc_time delay;

    // Generate a sequence of random transactions
    for (int i = 0; i < 1000; i++)
    {
      int adr = rand();
      tlm::tlm_command cmd = static_cast(rand() % 2);
      if (cmd == tlm::TLM_WRITE_COMMAND) data[i % 16] = rand();

      // Grab a new transaction from the memory manager
      trans = m_mm.allocate();
      trans->acquire();

      // Set all attributes except byte_enable_length and extensions (unused)
      trans->set_command( cmd );
      trans->set_address( adr );
      trans->set_data_ptr( reinterpret_cast(&data[i % 16]) );
      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

      // Initiator must honor BEGIN_REQ/END_REQ exclusion rule
      if (request_in_progress)
        wait(end_request_event);
      request_in_progress = trans;
      phase = tlm::BEGIN_REQ;

      // Timing annotation models processing time of initiator prior to call
      delay = sc_time(rand_ps(), SC_PS);

      fout << hex << adr << " " << name() << " new, cmd=" << (cmd ? 'W' : 'R')
           << ", data=" << hex << data[i % 16] << " at time " << sc_time_stamp() << endl;

      // Non-blocking transport call on the forward path
      tlm::tlm_sync_enum status;
      status = socket->nb_transport_fw( *trans, phase, delay );

      // Check value returned from nb_transport_fw
      if (status == tlm::TLM_UPDATED)
      {
        // The timing annotation must be honored
        m_peq.notify( *trans, phase, delay );
      }
      else if (status == tlm::TLM_COMPLETED)
      {
        // The completion of the transaction necessarily ends the BEGIN_REQ phase
        request_in_progress = 0;

        // The target has terminated the transaction
        check_transaction( *trans );
      }
      wait( sc_time(rand_ps(), SC_PS) );
    }

    wait(100, SC_NS);

    // Allocate a transaction for one final, nominal call to b_transport
    trans = m_mm.allocate();
    trans->acquire();
    trans->set_command( tlm::TLM_WRITE_COMMAND );
    trans->set_address( 0 );
    trans->set_data_ptr( reinterpret_cast(&data[0]) );
    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

    delay = sc_time(rand_ps(), SC_PS);

    fout << "Calling b_transport at " << sc_time_stamp() << " with delay = " << delay << endl;

    // Call b_transport to demonstrate the b/nb conversion by the simple_target_socket
    socket->b_transport( *trans, delay );
    check_transaction( *trans );
  }

  // TLM-2 backward non-blocking transport method

  virtual tlm::tlm_sync_enum nb_transport_bw( tlm::tlm_generic_payload& trans,
                                              tlm::tlm_phase& phase, sc_time& delay )
  {
    // The timing annotation must be honored
    m_peq.notify( trans, phase, delay );
    return tlm::TLM_ACCEPTED;
  }

  // Payload event queue callback to handle transactions from target
  // Transaction could have arrived through return path or backward path

  void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase)
  {
    #ifdef DEBUG
      if (phase == tlm::END_REQ)
        fout << hex << trans.get_address() << " " << name() << " END_REQ at " << sc_time_stamp() << endl;
      else if (phase == tlm::BEGIN_RESP)
        fout << hex << trans.get_address() << " " << name() << " BEGIN_RESP at " << sc_time_stamp() << endl;
    #endif

    if (phase == tlm::END_REQ || (&trans == request_in_progress && phase == tlm::BEGIN_RESP))
    {
      // The end of the BEGIN_REQ phase
      request_in_progress = 0;
      end_request_event.notify();
    }
    else if (phase == tlm::BEGIN_REQ || phase == tlm::END_RESP)
      SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by initiator");

    if (phase == tlm::BEGIN_RESP)
    {
      check_transaction( trans );

      // Send final phase transition to target
      tlm::tlm_phase fw_phase = tlm::END_RESP;
      sc_time delay = sc_time(rand_ps(), SC_PS);
      socket->nb_transport_fw( trans, fw_phase, delay );
      // Ignore return value
    }
  }

  // Called on receiving BEGIN_RESP or TLM_COMPLETED
  void check_transaction(tlm::tlm_generic_payload& trans)
  {
    if ( trans.is_response_error() )
    {
      char txt[100];
      sprintf(txt, "Transaction returned with error, response status = %s",
                   trans.get_response_string().c_str());
      SC_REPORT_ERROR("TLM-2", txt);
    }

    tlm::tlm_command cmd = trans.get_command();
    sc_dt::uint64    adr = trans.get_address();
    int*             ptr = reinterpret_cast<int*>( trans.get_data_ptr() );

    fout << hex << adr << " " << name() << " check, cmd=" << (cmd ? 'W' : 'R')
         << ", data=" << hex << *ptr << " at time " << sc_time_stamp() << endl;

    // Allow the memory manager to free the transaction object
    trans.release();
  }

  mm   m_mm;
  int  data[16];
  tlm::tlm_generic_payload* request_in_progress;
  sc_event end_request_event;
  tlm_utils::peq_with_cb_and_phase<Initiator> m_peq;
};

#endif
</int*>
#ifndef BUS_H
#define BUS_H

#include "utilities.h"
#include "tlm_utils/multi_passthrough_initiator_socket.h"
#include "tlm_utils/multi_passthrough_target_socket.h"


// ************************************************************************************
// Bus model supports multiple initiators and multiple targets
// Supports b_ and nb_ transport interfaces, DMI and debug
// It does no arbitration, but routes all transactions from initiators without blocking
// It uses a simple built-in routing algorithm
// ************************************************************************************

struct Bus: sc_module
{
  // ***********************************************************
  // Each multi-socket can be bound to multiple sockets
  // No need for an array-of-sockets
  // ***********************************************************

  tlm_utils::multi_passthrough_target_socket<Bus>    targ_socket;
  tlm_utils::multi_passthrough_initiator_socket<Bus> init_socket;

  SC_CTOR(Bus)
  : targ_socket("targ_socket"), init_socket("init_socket")
  {
    targ_socket.register_nb_transport_fw(   this, &Bus::nb_transport_fw);
    targ_socket.register_b_transport(       this, &Bus::b_transport);
    targ_socket.register_get_direct_mem_ptr(this, &Bus::get_direct_mem_ptr);
    targ_socket.register_transport_dbg(     this, &Bus::transport_dbg);

    init_socket.register_nb_transport_bw(          this, &Bus::nb_transport_bw);
    init_socket.register_invalidate_direct_mem_ptr(this, &Bus::invalidate_direct_mem_ptr);
  }


  // Tagged non-blocking transport forward method
  virtual tlm::tlm_sync_enum nb_transport_fw(int id,
      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay)
  {
    assert (id < targ_socket.size());

    // Forward path
    m_id_map[ &trans ] = id;

    sc_dt::uint64 address = trans.get_address();
    sc_dt::uint64 masked_address;
    unsigned int target_nr = decode_address( address, masked_address);

    if (target_nr < init_socket.size())
    {
      // Modify address within transaction
      trans.set_address( masked_address );

      // Forward transaction to appropriate target
      tlm::tlm_sync_enum status = init_socket[target_nr]->nb_transport_fw(trans, phase, delay);

      if (status == tlm::TLM_COMPLETED)
        // Put back original address
        trans.set_address( address );
      return status;
    }
    else
      return tlm::TLM_COMPLETED;
  }

  // Tagged non-blocking transport backward method
  virtual tlm::tlm_sync_enum nb_transport_bw(int id,
      tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay)
  {
    assert (id < init_socket.size());

    // Backward path

    // Replace original address
    sc_dt::uint64 address = trans.get_address();
    trans.set_address( compose_address( id, address ) );

    return targ_socket[ m_id_map[ &trans ] ]->nb_transport_bw(trans, phase, delay);
  }

  // Tagged TLM-2 blocking transport method
  virtual void b_transport( int id, tlm::tlm_generic_payload& trans, sc_time& delay )
  {
    assert (id < targ_socket.size());

    // Forward path
    sc_dt::uint64 address = trans.get_address();
    sc_dt::uint64 masked_address;
    unsigned int target_nr = decode_address( address, masked_address);

    if (target_nr < init_socket.size())
    {
      // Modify address within transaction
      trans.set_address( masked_address );

      // Forward transaction to appropriate target
      init_socket[target_nr]->b_transport(trans, delay);

      // Replace original address
      trans.set_address( address );
    }
  }

  // Tagged TLM-2 forward DMI method
  virtual bool get_direct_mem_ptr(int id,
                                  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 );
    if (target_nr >= init_socket.size())
      return false;

    trans.set_address( masked_address );

    bool status = init_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;
  }


  // Tagged debug transaction method
  virtual unsigned int transport_dbg(int id, tlm::tlm_generic_payload& trans)
  {
    sc_dt::uint64 masked_address;
    unsigned int target_nr = decode_address( trans.get_address(), masked_address );
    if (target_nr >= init_socket.size())
      return 0;
    trans.set_address( masked_address );

    // Forward debug transaction to appropriate target
    return init_socket[target_nr]->transport_dbg( trans );
  }


  // 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 );

    // Propagate call backward to all initiators
    for (unsigned int i = 0; i < targ_socket.size(); i++)
      targ_socket[i]->invalidate_direct_mem_ptr(bw_start_range, bw_end_range);
  }

  // Simple fixed address decoding
  // In this example, for clarity, the address is passed through unmodified to the target
  inline unsigned int decode_address( sc_dt::uint64 address, sc_dt::uint64& masked_address )
  {
    unsigned int target_nr = static_cast( address & 0x3 );
    masked_address = address;
    return target_nr;
  }

  inline sc_dt::uint64 compose_address( unsigned int target_nr, sc_dt::uint64 address)
  {
    return address;
  }

  std::map <tlm::tlm_generic_payload*, unsigned="" int=""> m_id_map;
};

#endif
</tlm::tlm_generic_payload*,>
#ifndef TARGET_H
#define TARGET_H

#include "utilities.h"
#include "tlm_utils/simple_target_socket.h"
#include 

// **************************************************************************************
// Target module able to handle two pipelined transactions
// **************************************************************************************

DECLARE_EXTENDED_PHASE(internal_ph);

struct Target: sc_module
{
  // TLM-2 socket, defaults to 32-bits wide, base protocol
  tlm_utils::simple_target_socket<Target> socket;

  SC_CTOR(Target)
  : socket("socket")
  , n_trans(0)
  , response_in_progress(false)
  , next_response_pending(0)
  , end_req_pending()
  , m_peq(this, &Target::peq_cb)
  {
    // Register callbacks for incoming interface method calls
    socket.register_nb_transport_fw(this, &Target::nb_transport_fw);
  }

  // TLM-2 non-blocking transport method

  virtual tlm::tlm_sync_enum nb_transport_fw( tlm::tlm_generic_payload& trans,
                                              tlm::tlm_phase& phase, sc_time& delay )
  {
    sc_dt::uint64    adr = trans.get_address();
    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 the transaction attributes for unsupported features
    // and to generate the appropriate error response
    if (byt != 0) {
      trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
      return tlm::TLM_COMPLETED;
    }
    if (len > 4 || wid < len) {
      trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
      return tlm::TLM_COMPLETED;
    }

    // Now queue the transaction until the annotated time has elapsed
    m_peq.notify( trans, phase, delay);
    return tlm::TLM_ACCEPTED;
  }

  void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase)
  {
    tlm::tlm_sync_enum status;
    sc_time delay;

    switch (phase) {
    case tlm::BEGIN_REQ:

      #ifdef DEBUG
        fout << hex << trans.get_address() << " " << name() << " BEGIN_REQ at " << sc_time_stamp() << endl;
      #endif

      // Increment the transaction reference count
      trans.acquire();

      // Put back-pressure on initiator by deferring END_REQ until pipeline is clear
    // (initiator is blocked until pending END_REQ comes out of queue)
      if (n_trans == 2)
        end_req_pending.push(&trans);
      else
      {
        status = send_end_req(trans);
        if (status == tlm::TLM_COMPLETED) // It is questionable whether this is valid
          break;
      }

      break;

    case tlm::END_RESP:
      // On receiving END_RESP, the target can release the transaction
      // and allow other pending transactions to proceed

      #ifdef DEBUG
        fout << hex << trans.get_address() << " " << name() << " END_RESP at " << sc_time_stamp() << endl;
      #endif

      if (!response_in_progress)
        SC_REPORT_FATAL("TLM-2", "Illegal transaction phase END_RESP received by target");

      trans.release();
      n_trans--;

      // Target itself is now clear to issue the next BEGIN_RESP
      response_in_progress = false;
      if (next_response_pending)
      {
        send_response( *next_response_pending );
        next_response_pending = 0;
      }

      // ... and to unblock the initiator by issuing END_REQ
      if (!end_req_pending.empty())
      {
        status = send_end_req( *end_req_pending.front() );
        end_req_pending.pop();
      }

      break;

    case tlm::END_REQ:
    case tlm::BEGIN_RESP:
      SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by target");
      break;

    default:
      if (phase == internal_ph)
      {
        // Execute the read or write commands

        tlm::tlm_command cmd = trans.get_command();
        sc_dt::uint64    adr = trans.get_address();
        unsigned char*   ptr = trans.get_data_ptr();
        unsigned int     len = trans.get_data_length();

        if ( cmd == tlm::TLM_READ_COMMAND )
        {
          *reinterpret_cast<int*>(ptr) = rand();
          fout << hex << adr << " " << name() << " Execute READ, target = " << name()
               << " data = " << *reinterpret_cast<int*>(ptr) << endl;
        }
        else if ( cmd == tlm::TLM_WRITE_COMMAND )
          fout << hex << adr << " " << name() << " Execute WRITE, target = " << name()
               << " data = " << *reinterpret_cast<int*>(ptr) << endl;

        trans.set_response_status( tlm::TLM_OK_RESPONSE );

        // Target must honor BEGIN_RESP/END_RESP exclusion rule
        // i.e. must not send BEGIN_RESP until receiving previous END_RESP or BEGIN_REQ
        if (response_in_progress)
        {
          // Target allows only two transactions in-flight
          if (next_response_pending)
            SC_REPORT_FATAL("TLM-2", "Attempt to have two pending responses in target");
          next_response_pending = &trans;
        }
        else
          send_response(trans);
        break;
      }
    }
  }

  tlm::tlm_sync_enum send_end_req(tlm::tlm_generic_payload& trans)
  {
    tlm::tlm_sync_enum status;
    tlm::tlm_phase bw_phase;
    tlm::tlm_phase int_phase = internal_ph;
    sc_time delay;

    // Queue the acceptance and the response with the appropriate latency
    bw_phase = tlm::END_REQ;
    delay = sc_time(rand_ps(), SC_PS); // Accept delay
    status = socket->nb_transport_bw( trans, bw_phase, delay );
    if (status == tlm::TLM_COMPLETED)
    {
      // Transaction aborted by the initiator
      // (TLM_UPDATED cannot occur at this point in the base protocol, so need not be checked)
      trans.release();
      return status;
    }

    // Queue internal event to mark beginning of response
    delay = delay + sc_time(rand_ps(), SC_PS); // Latency
    m_peq.notify( trans, int_phase, delay );
    n_trans++;

    return status;
  }

  void send_response(tlm::tlm_generic_payload& trans)
  {
    tlm::tlm_sync_enum status;
    tlm::tlm_phase bw_phase;
    sc_time delay;

    response_in_progress = true;
    bw_phase = tlm::BEGIN_RESP;
    delay = SC_ZERO_TIME;
    status = socket->nb_transport_bw( trans, bw_phase, delay );

    if (status == tlm::TLM_UPDATED)
    {
      // The timing annotation must be honored
      m_peq.notify( trans, bw_phase, delay);
    }
    else if (status == tlm::TLM_COMPLETED)
    {
      // The initiator has terminated the transaction
      trans.release();
      n_trans--;
      response_in_progress = false;
    }
  }

  int   n_trans;
  bool  response_in_progress;
  tlm::tlm_generic_payload*  next_response_pending;
  std::queue<tlm::tlm_generic_payload*>  end_req_pending;
  tlm_utils::peq_with_cb_and_phase<Target> m_peq;
};

#endif
</tlm::tlm_generic_payload*></int*></int*></int*>
#ifndef TOP_H
#define TOP_H

#include "initiator.h"
#include "bus.h"
#include "target.h"

// *****************************************************************************************
// Top-level module instantiates 4 initiators, a bus, and 4 targets
// *****************************************************************************************

SC_MODULE(Top)
{
  Initiator* init[4];
  Bus*       bus;
  Target*    target[4];

  SC_CTOR(Top)
  {
    bus   = new Bus("bus");

    // ***************************************************************************
    // bus->init_socket and bus->targ_socket are multi-sockets, each bound 4 times
    // ***************************************************************************

    for (int i = 0; i < 4; i++)
    {
      char txt[20];
      sprintf(txt, "init_%d", i);
      init[i] = new Initiator(txt);
      init[i]->socket.bind( bus->targ_socket );
    }

    for (int i = 0; i < 4; i++)
    {
      char txt[20];
      sprintf(txt, "target_%d", i);
      target[i] = new Target(txt);

      bus->init_socket.bind( target[i]->socket );
    }
  }
};

#endif
#include "top.h"

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();

  cout << "\n***** Messages have been written to file output.txt                    *****\n";
  cout << "***** Select 'Download files after run' to read file in EDA Playground *****\n\n";

  return 0;
}