aboutsummaryrefslogtreecommitdiff
path: root/sc_export2.cc
blob: adf6e31372768cc3d774bd3a1ca7879a69fbcccf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <systemc>
#include "utils.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 and sc_export are then bound to connect the driver and the
// implementer.
//
// 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));
      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 bindd
  // 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;
}