aboutsummaryrefslogtreecommitdiff
path: root/src/sc_export2.cc
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2023-08-27 21:18:53 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2023-08-27 21:18:53 +0200
commitf396fab26611d6107e223a6a3f41c9dff3e2ee9e (patch)
treed1b22fd265f883404d7e484b7f7c460367a136be /src/sc_export2.cc
parent7a0f884c6b11db6a59cfafce7b53158ad59a365e (diff)
downloadsysc-playground-f396fab26611d6107e223a6a3f41c9dff3e2ee9e.tar.gz
sysc-playground-f396fab26611d6107e223a6a3f41c9dff3e2ee9e.zip
move sources into src/ subdir
Diffstat (limited to 'src/sc_export2.cc')
-rw-r--r--src/sc_export2.cc108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/sc_export2.cc b/src/sc_export2.cc
new file mode 100644
index 0000000..c5905bc
--- /dev/null
+++ b/src/sc_export2.cc
@@ -0,0 +1,108 @@
+#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 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;
+}