aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2023-07-26 00:05:14 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2023-07-26 00:05:14 +0200
commit173622e9e82b7c2b1c6f8146056661b2a6577735 (patch)
tree4d14127163d9621e7462a8fc25444552b5a80137
downloadsysc-playground-173622e9e82b7c2b1c6f8146056661b2a6577735.tar.gz
sysc-playground-173622e9e82b7c2b1c6f8146056661b2a6577735.zip
add some initial playground examples + scripts to build
-rw-r--r--.clang-format7
-rw-r--r--.gitignore5
-rw-r--r--CMakeLists.txt13
-rw-r--r--Makefile56
-rw-r--r--browse.cc74
-rw-r--r--event.cc140
-rw-r--r--minimal_socket.cc122
-rw-r--r--sc_export.cc76
-rw-r--r--sc_export2.cc103
-rw-r--r--utils.h32
10 files changed, 628 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..3234cd9
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+---
+Language: Cpp
+BasedOnStyle: Chromium
+
+AllowShortFunctionsOnASingleLine: Empty
+AlignConsecutiveMacros:
+ Enabled: true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1bf9102
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.cache
+BUILD
+compile_commands.json
+INSTALL
+systemc
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..373f414
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.0)
+project(systemc_utils CXX)
+
+set (CMAKE_PREFIX_PATH $ENV{SYSTEMC_HOME})
+find_package(SystemCLanguage CONFIG REQUIRED)
+
+# Set CXX std based on SystemC configuration.
+set (CMAKE_CXX_STANDARD ${SystemC_CXX_STANDARD} CACHE STRING "C++ standard to build all targets.")
+
+foreach (X IN ITEMS event sc_export sc_export2 minimal_socket browse)
+ add_executable(${X} ${X}.cc)
+ target_link_libraries(${X} SystemC::systemc)
+endforeach()
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f22fbd0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,56 @@
+export CC = clang
+export CXX = clang++
+
+export SYSTEMC_HOME := $(PWD)/INSTALL
+
+SRCS = $(wildcard *.cc)
+BINS = $(SRCS:%.cc=BUILD/UTIL/%)
+
+# -- SYSTEMC UTILS -------------------------------------------------------------
+
+run: build
+ @for B in $(BINS); do \
+ echo "==> $$B"; \
+ $$B; \
+ done
+
+build: INSTALL/lib/libsystemc.a BUILD/UTIL $(SRCS)
+ ninja -C BUILD/UTIL
+
+BUILD/UTIL: CMakeLists.txt
+ cmake -B $@ -S . \
+ -G Ninja \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \
+ -DCMAKE_BUILD_TYPE=DEBUG \
+ #-DCMAKE_FIND_DEBUG_MODE=ON
+ #ninja -C $@ -t compdb > compile_commands.json
+
+# -- SYSTEMC -------------------------------------------------------------------
+
+INSTALL/lib/libsystemc.a: BUILD/SYSC
+ ninja -C BUILD/SYSC install
+
+BUILD/SYSC: systemc
+ cmake -B $@ -S systemc \
+ -G Ninja \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \
+ -DCMAKE_BUILD_TYPE=DEBUG \
+ -DCMAKE_INSTALL_PREFIX=$(PWD)/INSTALL \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCMAKE_CXX_STANDARD=14
+
+systemc:
+ git clone --depth=1 https://github.com/accellera-official/systemc.git
+
+# -- UTIL ----------------------------------------------------------------------
+
+fmt:
+ clang-format -i *.cc *.h
+
+# -- CLEAN ---------------------------------------------------------------------
+
+clean:
+ $(RM) -r BUILD/UTIL compile_commands.json
+
+distclean: clean
+ $(RM) -r BUILD INSTALL systemc
diff --git a/browse.cc b/browse.cc
new file mode 100644
index 0000000..afc132e
--- /dev/null
+++ b/browse.cc
@@ -0,0 +1,74 @@
+#include <systemc>
+#include "utils.h"
+
+using sc_core::sc_module;
+using sc_core::sc_module_name;
+using sc_core::sc_object;
+
+struct leaf_a : public sc_module {
+ explicit leaf_a(sc_module_name nm) : sc_module(nm) {}
+};
+
+struct leaf_b : public sc_module {
+ explicit leaf_b(sc_module_name nm) : sc_module(nm) {}
+};
+
+struct processor : public sc_module {
+ explicit processor(sc_module_name nm) : sc_module(nm), m_a("a"), m_b("b") {}
+
+ leaf_a m_a;
+ leaf_b m_b;
+};
+
+struct memory : public sc_module {
+ explicit memory(sc_module_name nm) : sc_module(nm), m_a("a") {}
+
+ leaf_a m_a;
+};
+
+struct soc : public sc_module {
+ explicit soc(sc_module_name nm)
+ : sc_module(nm),
+ m_mem0{"mem0"},
+ m_mem1{"mem1"},
+ m_mem2{"mem2"},
+ m_mem3{"mem3"},
+ m_proc0{"proc0"},
+ m_proc1{"proc1"} {}
+
+ memory m_mem0, m_mem1, m_mem2, m_mem3;
+ processor m_proc0, m_proc1;
+};
+
+// -- WALKER -------------------------------------------------------------------
+
+void walk(const std::vector<sc_object*>& objs, unsigned indent = 0) {
+ for (const auto* obj : objs) {
+ if (indent) {
+ printf("%*c", indent, ' ');
+ }
+ printf("BASENAME=" YELLOW "%s" RESET " NAME=" MAGENTA "%s" RESET
+ " HAS_PARENT=%d\n",
+ obj->basename(), obj->name(), obj->get_parent_object() != nullptr);
+
+ walk(obj->get_child_objects(), indent + 2);
+ }
+}
+
+// -- SC_MAIN ------------------------------------------------------------------
+
+extern "C" int sc_main(int, char*[]) {
+ soc s("soc");
+
+ walk(sc_core::sc_get_top_level_objects());
+
+ const auto* obj = sc_core::sc_find_object("soc.mem0");
+ if (obj != nullptr) {
+ puts(obj->name());
+ }
+
+ std::cout << sc_core::sc_hierarchical_name_exists("soc") << std::endl;
+ std::cout << sc_core::sc_hierarchical_name_exists(obj, "a") << std::endl;
+
+ return 0;
+}
diff --git a/event.cc b/event.cc
new file mode 100644
index 0000000..96750bc
--- /dev/null
+++ b/event.cc
@@ -0,0 +1,140 @@
+#include <systemc>
+#include "utils.h"
+
+using sc_core::sc_event;
+using sc_core::sc_module;
+using sc_core::sc_module_name;
+using sc_core::SC_NS;
+using sc_core::sc_start;
+using sc_core::sc_time;
+using sc_core::SC_ZERO_TIME;
+
+// -- MOD_METHOD ---------------------------------------------------------------
+
+struct mod_method : public sc_module {
+ SC_HAS_PROCESS(mod_method);
+
+ explicit mod_method(sc_module_name nm) : sc_module(nm) {
+ SC_METHOD(ev_method);
+ // STATIC sensitivity for ev_method, the METHOD will become RUNNABLE when
+ // the event is notified.
+ sensitive << m_method_event;
+ // DONT run method initially at time 0s, only after it is triggered.
+ // By default SC_METHODs are made RUNNABLE at time 0s.
+ dont_initialize();
+
+ // Helper to drive the method example.
+ SC_THREAD(run_method);
+ }
+
+ private:
+ sc_event m_method_event;
+
+ void ev_method() {
+ // No calls to wait() in METHODS.
+ // Methods are triggered by static sensitivity or dynamic sensitivity.
+
+ CLOGM(GREEN, "triggered by_static_event_not_next_trigger=%d timed_out=%d",
+ m_method_event.triggered(), timed_out());
+
+ if (m_method_event.triggered()) {
+ // Create DYNAMIC sensitivity list, either by time or event.
+ // The DYNAMIC sensitivity OVERRIDES the STATIC sensitivity, therefore if
+ // the event is not listed here, the method will only be triggered by the
+ // TIME value no matter if the EVENT (static sensitivity) is triggered.
+ next_trigger(5000, SC_NS, m_method_event);
+ }
+ }
+
+ void run_method() {
+ // Notify event in the future.
+ CLOGM(YELLOW, "TIMED NOTIFY");
+ m_method_event.notify(100, SC_NS);
+
+ wait(200, SC_NS);
+
+ // Notify event in the next delta cycle.
+ CLOGM(YELLOW, "DELTA NOTIFY");
+ m_method_event.notify(SC_ZERO_TIME);
+
+ wait(200, SC_NS);
+
+ // Notify event in the current delta cycle.
+ CLOGM(YELLOW, "IMMEDIATE NOTIFY");
+ m_method_event.notify();
+
+ wait(200, SC_NS);
+ }
+};
+
+// -- MOD_THREAD ---------------------------------------------------------------
+
+struct mod_thread : public sc_module {
+ SC_HAS_PROCESS(mod_thread);
+
+ explicit mod_thread(sc_module_name nm) : sc_module(nm) {
+ SC_THREAD(ev_thread);
+ // STATIC sensitivity for ev_thread.
+ sensitive << m_thread_event;
+
+ // Helper to drive the example and drive the simulator.
+ SC_THREAD(run_thread);
+ }
+
+ private:
+ sc_event m_thread_event;
+
+ void ev_thread() {
+ // WAIT on STATIC sensitivity event.
+ wait();
+
+ while (true) {
+ CLOGM(CYAN, "WAIT TIMED or EVENT");
+ // Create DYNAMIC sensitivity list, WAIT on either TIMEOUT or EVENT.
+ wait(sc_time(2000, SC_NS), m_thread_event);
+ CLOGM(CYAN, "done by_event_not_time=%d timed_out=%d",
+ m_thread_event.triggered(), timed_out());
+
+ CLOGM(CYAN, "WAIT STATIC SENSITIVITY");
+ // WAIT on static sensitivity trigger.
+ wait();
+ CLOGM(CYAN, "done");
+ }
+ }
+
+ void run_thread() {
+ // Some initial wait, to run at different time than the method example.
+ wait(10000, SC_NS);
+ m_thread_event.notify(SC_ZERO_TIME);
+
+ CLOGM(MAGENTA, "big TIMED WAIT -> should timeout thread");
+ wait(10000, SC_NS);
+
+ CLOGM(MAGENTA, "TIMED NOTIFY");
+ m_thread_event.notify(100, SC_NS);
+
+ wait(1000, SC_NS);
+
+ CLOGM(MAGENTA, "TIMED NOTIFY");
+ m_thread_event.notify(200, SC_NS);
+
+ wait(1000, SC_NS);
+
+ CLOGM(MAGENTA, "DELTA NOTIFY");
+ m_thread_event.notify(SC_ZERO_TIME);
+ }
+};
+
+// -- SC_MAIN ------------------------------------------------------------------
+
+extern "C" int sc_main(int, char*[]) {
+ // timed_out() is deprecated, but it is just used for visualization here.
+ sc_core::sc_report_handler::set_actions("/IEEE_Std_1666/deprecated",
+ sc_core::SC_DO_NOTHING);
+
+ mod_method method("method");
+ mod_thread thread("thread");
+
+ sc_start();
+ return 0;
+}
diff --git a/minimal_socket.cc b/minimal_socket.cc
new file mode 100644
index 0000000..7fe7f38
--- /dev/null
+++ b/minimal_socket.cc
@@ -0,0 +1,122 @@
+#include <systemc>
+#include "utils.h"
+
+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_NS;
+using sc_core::sc_port;
+
+// -- INTERFACES ---------------------------------------------------------------
+
+struct fwd_if : sc_interface {
+ virtual void fwd(const char* msg) = 0;
+};
+
+struct bwd_if : sc_interface {
+ virtual void bwd(const char* msg) = 0;
+};
+
+// -- INITIATOR SOCKET ---------------------------------------------------------
+
+// An initiator socket provides its owner access to a FWD_IF implementation. The
+// initiator socket itself must be bound against a BWD_IF implementation,
+// offering the BWD_IF implementation to the target socket that can be bound
+// against the initiator socket.
+//
+// Effectively the initiator socket is a sc_port / sc_export pair, using the
+// sc_port to access the FWD_IF and the sc_export to offer the BWD_IF
+// implementation.
+
+struct initiator_socket : sc_export<bwd_if> {
+ sc_port<fwd_if> p_fwd;
+
+ fwd_if* operator->() {
+ return p_fwd.operator->();
+ }
+};
+
+// -- TARGET SOCKET ------------------------------------------------------------
+
+// A target socket provides its owner access to a BWD_IF implementation. The
+// target socket itself must be bound against a FWD_IF implementation,
+// offering the FWD_IF implementation to the initiator socket that can be bound
+// against the target socket.
+//
+// Effectively the target socket is a sc_port / sc_export pair, using the
+// sc_port to access the BWD_IF and the sc_export to offer the FWD_IF
+// implementation.
+
+struct target_socket : sc_export<fwd_if> {
+ sc_port<bwd_if> p_bwd;
+
+ void bind(initiator_socket& init) {
+ // Bind the sc_port / sc_export pairs between the initiator and target
+ // socket.
+
+ // Initiator:sc_port -> Target:sc_export.
+ init.p_fwd(*this);
+
+ // Target:sc_port -> Initiator:sc_export.
+ p_bwd(init);
+ }
+
+ bwd_if* operator->() {
+ return p_bwd.operator->();
+ }
+};
+
+// -- INITIATOR ----------------------------------------------------------------
+
+struct initiator : public sc_module, public bwd_if {
+ SC_HAS_PROCESS(initiator);
+
+ initiator_socket p_sock;
+
+ explicit initiator(sc_module_name nm) : sc_module(std::move(nm)) {
+ p_sock(*this);
+ SC_THREAD(work);
+ }
+
+ private:
+ virtual void bwd(const char* msg) override {
+ LOGM("%s", msg);
+ }
+
+ void work() {
+ sc_core::wait(10, SC_NS);
+ p_sock->fwd("moose");
+ }
+};
+
+// -- TARGET -------------------------------------------------------------------
+
+struct target : public sc_module, public fwd_if {
+ SC_HAS_PROCESS(target);
+
+ target_socket p_sock;
+
+ explicit target(sc_module_name nm) : sc_module(std::move(nm)) {
+ p_sock(*this);
+ }
+
+ private:
+ virtual void fwd(const char* msg) override {
+ LOGM("%s", msg);
+ p_sock->bwd("meeh");
+ }
+};
+
+// -- SC_MAIN ------------------------------------------------------------------
+
+extern "C" int sc_main(int, char*[]) {
+ initiator I("initiator");
+ target T("target");
+
+ // Bind the initiator and target sockets.
+ T.p_sock.bind(I.p_sock);
+
+ sc_core::sc_start();
+ return 0;
+}
diff --git a/sc_export.cc b/sc_export.cc
new file mode 100644
index 0000000..7acca58
--- /dev/null
+++ b/sc_export.cc
@@ -0,0 +1,76 @@
+#include <systemc>
+#include "utils.h"
+
+using sc_core::sc_export;
+using sc_core::sc_in;
+using sc_core::sc_module;
+using sc_core::sc_module_name;
+using sc_core::SC_NS;
+using sc_core::sc_signal;
+using sc_core::sc_signal_inout_if;
+
+// -- CLOCK_GEN ----------------------------------------------------------------
+
+struct clock_gen : public sc_module {
+ SC_HAS_PROCESS(clock_gen);
+
+ explicit clock_gen(sc_module_name nm, unsigned period_ns = 20)
+ : sc_module(nm), m_half_period(period_ns / 2) {
+ assert(period_ns == m_half_period * 2);
+
+ // Bind sc_signal (clk) against export.
+ p_clk(m_clk);
+
+ SC_METHOD(tick);
+ }
+
+ // Export to expose sc_signal (clk) outside this module.
+ sc_export<sc_signal_inout_if<bool>> p_clk;
+
+ private:
+ // Implementation of sc_signal_inout_if.
+ sc_signal<bool> m_clk;
+
+ const unsigned m_half_period;
+
+ void tick() {
+ m_clk.write(!m_clk.read());
+ next_trigger(m_half_period, SC_NS);
+ }
+};
+
+// -- TICKTOCK -----------------------------------------------------------------
+
+struct ticktock : public sc_module {
+ SC_HAS_PROCESS(ticktock);
+
+ explicit ticktock(sc_module_name nm) : sc_module(nm) {
+ SC_METHOD(handle_clk);
+ sensitive << p_clk;
+ dont_initialize();
+ }
+
+ // In port, where sc_in is just a sc_port<sc_signal_in_if<bool>>.
+ sc_in<bool> p_clk;
+
+ private:
+ void handle_clk() {
+ if (p_clk.read()) {
+ LOGM("TICK");
+ } else {
+ LOGM("TOCK");
+ }
+ }
+};
+
+// -- SC_MAIN ------------------------------------------------------------------
+
+extern "C" int sc_main(int, char*[]) {
+ clock_gen clk("clk");
+ ticktock tiktok{"listen"};
+
+ tiktok.p_clk(clk.p_clk);
+
+ sc_start(200, SC_NS);
+ return 0;
+}
diff --git a/sc_export2.cc b/sc_export2.cc
new file mode 100644
index 0000000..adf6e31
--- /dev/null
+++ b/sc_export2.cc
@@ -0,0 +1,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;
+}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..5584027
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,32 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <cstdio>
+
+#define LOG(FMT, ...) \
+ do { \
+ std::fprintf(stderr, "%10s | %4lld:%2lld | %10s | " FMT "\n", \
+ sc_core::sc_time_stamp().to_string().c_str(), \
+ sc_core::sc_delta_count(), \
+ sc_core::sc_delta_count_at_current_time(), __FUNCTION__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define LOGM(FMT, ...) LOG("%12s | " FMT, name(), ##__VA_ARGS__)
+
+// -- COLOR LOGS ---------------------------------------------------------------
+
+#define RESET "\e[0m"
+#define BLACK "\e[0;30m"
+#define RED "\e[0;31m"
+#define GREEN "\e[0;32m"
+#define YELLOW "\e[0;33m"
+#define BLUE "\e[0;34m"
+#define MAGENTA "\e[0;35m"
+#define CYAN "\e[0;36m"
+#define WHITE "\e[0;37m"
+
+#define CLOG(COL, FMT, ...) LOG(COL FMT "\e[0m", ##__VA_ARGS__)
+#define CLOGM(COL, FMT, ...) LOGM(COL FMT "\e[0m", ##__VA_ARGS__)
+
+#endif