aboutsummaryrefslogblamecommitdiff
path: root/src/sc_export2.cc
blob: 42381d2d0e81c1379a68e828ebf99f861ab3229f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                  
                      







                                                                                


                                                                             
                                                                         

                                                                             
















































                                                                                
                                                     


















                                                                                
                                                                               





















                                                                                
#include <systemc>
#include "utils/log.h"

// This file gives an example on how to define an own interface and use that
// interface to communicate between two sc_modules.
//
// One module is the driver and accesses the interface via a sc_port and the
// other module provides an implementation of the interface and exposes it via a
// sc_export.
//
// The sc_port deref into the interface and therefore allows to directly call
// any interface method on the sc_port via the operator->().
//
// The sc_port and sc_export are then bound to connect the driver and the
// implementer. The sc_export must additionally be bound against an interface
// implementation.
//
// This example lays a foundation in understanding how TLM2 sockets work.

using sc_core::sc_export;
using sc_core::sc_interface;
using sc_core::sc_module;
using sc_core::sc_module_name;
using sc_core::sc_port;

// -- INTERFACE ----------------------------------------------------------------

struct msg_if : sc_interface {
  enum message {
    kHello,
    kPayload,
    kGoodbye,
  };

  virtual void send(message) = 0;
};

constexpr inline const char* to_str(msg_if::message msg) {
  switch (msg) {
    case msg_if::kHello:
      return "Hello";
    case msg_if::kPayload:
      return "Payload";
    case msg_if::kGoodbye:
      return "Goodbye";
  }
}

// -- SENDER -------------------------------------------------------------------

struct sender : public sc_module {
  SC_HAS_PROCESS(sender);

  explicit sender(sc_module_name nm) : sc_module(nm) {
    SC_METHOD(do_protocol);
  }

  // A sc_port instantiated with the custom interface. The sc_port is used by
  // this module to access the interface implementation.
  sc_port<msg_if> p_msg;

 private:
  void do_protocol() {
    const auto send = [this](msg_if::message msg) {
      LOGM("%s", to_str(msg));
      // sc_port derefs into msg_if via operator->().
      p_msg->send(msg);
    };

    // Send out some messages through the msg_if via the sc_port.
    send(msg_if::kHello);
    send(msg_if::kPayload);
    send(msg_if::kGoodbye);
  }
};

// -- RECEIVER -----------------------------------------------------------------

struct receiver : public sc_module, public msg_if {
  explicit receiver(sc_module_name nm) : sc_module(nm) {
    // Bind the msg_if implementation against sc_export.
    p_msg(*this);
  }

  // A sc_export instantiated with the custom interface. The sc_export can be
  // bound against a sc_port (with the same interface), which effectively binds
  // the implementation behind the sc_export against the sc_port.
  sc_export<msg_if> p_msg;

 private:
  virtual void send(msg_if::message msg) override {
    LOGM("%s", to_str(msg));
  }
};

// -- SC_MAIN ------------------------------------------------------------------

extern "C" int sc_main(int, char*[]) {
  sender s("sender");
  receiver r{"receiver"};

  // Bind the sc_port and sc_export, such that the sender will invoke the msg_if
  // implemented by the receiver.
  s.p_msg(r.p_msg);

  sc_core::sc_start();
  return 0;
}