systemc: Implement the sc_signal_rv channel and ports.

Change-Id: Id1a3fd2ded224bbe94a4a65e0acf34a3547aedcc
Reviewed-on: https://gem5-review.googlesource.com/c/12813
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
diff --git a/src/systemc/ext/channel/sc_in_rv.hh b/src/systemc/ext/channel/sc_in_rv.hh
index eca2c0c..278b958 100644
--- a/src/systemc/ext/channel/sc_in_rv.hh
+++ b/src/systemc/ext/channel/sc_in_rv.hh
@@ -30,7 +30,10 @@
 #ifndef __SYSTEMC_EXT_CHANNEL_SC_IN_RV_HH__
 #define __SYSTEMC_EXT_CHANNEL_SC_IN_RV_HH__
 
+#include <sstream>
+
 #include "sc_in.hh"
+#include "sc_signal_rv.hh"
 
 namespace sc_dt
 {
@@ -51,7 +54,18 @@
     explicit sc_in_rv(const char *name) : sc_in<sc_dt::sc_lv<W>>(name) {}
     virtual ~sc_in_rv() {};
 
-    virtual void end_of_elaboration() {}
+    virtual void
+    end_of_elaboration()
+    {
+        sc_in<sc_dt::sc_lv<W> >::end_of_elaboration();
+        if (!dynamic_cast<sc_signal_rv<W> *>(this->get_interface())) {
+            std::ostringstream ss;
+            ss << "port '" << this->name() << "' (" << this->kind() << ")";
+            SC_REPORT_ERROR(
+                    "(E117) resolved port not bound to resolved signal",
+                    ss.str().c_str());
+        }
+    }
 
     virtual const char *kind() const { return "sc_in_rv"; }
 
diff --git a/src/systemc/ext/channel/sc_inout_rv.hh b/src/systemc/ext/channel/sc_inout_rv.hh
index f231d93..5ff9d10 100644
--- a/src/systemc/ext/channel/sc_inout_rv.hh
+++ b/src/systemc/ext/channel/sc_inout_rv.hh
@@ -30,9 +30,12 @@
 #ifndef __SYSTEMC_EXT_CHANNEL_SC_INOUT_RV_HH__
 #define __SYSTEMC_EXT_CHANNEL_SC_INOUT_RV_HH__
 
+#include <sstream>
+
 #include "../core/sc_port.hh"
 #include "sc_signal_in_if.hh"
 #include "sc_signal_inout_if.hh"
+#include "sc_signal_rv.hh"
 #include "warn_unimpl.hh"
 
 namespace sc_dt
@@ -55,37 +58,48 @@
     virtual ~sc_inout_rv() {}
 
     sc_inout_rv<W> &
-    operator = (const sc_dt::sc_lv<W> &)
+    operator = (const sc_dt::sc_lv<W> &l)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(l);
         return *this;
     }
     sc_inout_rv<W> &
-    operator = (const sc_signal_in_if<sc_dt::sc_lv<W>> &)
+    operator = (const sc_signal_in_if<sc_dt::sc_lv<W>> &i)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(i.read());
         return *this;
     }
     sc_inout_rv<W> &
-    operator = (const sc_port<sc_signal_in_if<sc_dt::sc_lv<W>>, 1> &)
+    operator = (const sc_port<sc_signal_in_if<sc_dt::sc_lv<W>>, 1> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
     sc_inout_rv<W> &
-    operator = (const sc_port<sc_signal_inout_if<sc_dt::sc_lv<W>>, 1> &)
+    operator = (const sc_port<sc_signal_inout_if<sc_dt::sc_lv<W>>, 1> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
     sc_inout_rv<W> &
-    operator = (const sc_inout_rv<W> &)
+    operator = (const sc_inout_rv<W> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
 
-    virtual void end_of_elaboration() {};
+    virtual void
+    end_of_elaboration()
+    {
+        sc_inout<sc_dt::sc_lv<W> >::end_of_elaboration();
+        if (!dynamic_cast<sc_signal_rv<W> *>(this->get_interface())) {
+            std::ostringstream ss;
+            ss << this->name() << " (" << this->kind() << ")";
+            SC_REPORT_ERROR(
+                    "(E117) resolved port not bound to resolved signal",
+                    ss.str().c_str());
+        }
+    }
 
     virtual const char *kind() const { return "sc_inout_rv"; }
 };
diff --git a/src/systemc/ext/channel/sc_out_rv.hh b/src/systemc/ext/channel/sc_out_rv.hh
index af5b055..ace1f05 100644
--- a/src/systemc/ext/channel/sc_out_rv.hh
+++ b/src/systemc/ext/channel/sc_out_rv.hh
@@ -56,33 +56,33 @@
     virtual ~sc_out_rv() {};
 
     sc_out_rv<W> &
-    operator = (const sc_dt::sc_lv<W> &)
+    operator = (const sc_dt::sc_lv<W> &l)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(l);
         return *this;
     }
     sc_out_rv<W> &
-    operator = (const sc_signal_in_if<sc_dt::sc_lv<W>> &)
+    operator = (const sc_signal_in_if<sc_dt::sc_lv<W>> &i)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(i.read());
         return *this;
     }
     sc_out_rv<W> &
-    operator = (const sc_port<sc_signal_in_if<sc_dt::sc_lv<W>>, 1> &)
+    operator = (const sc_port<sc_signal_in_if<sc_dt::sc_lv<W>>, 1> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
     sc_out_rv<W> &
-    operator = (const sc_port<sc_signal_inout_if<sc_dt::sc_lv<W>>, 1> &)
+    operator = (const sc_port<sc_signal_inout_if<sc_dt::sc_lv<W>>, 1> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
     sc_out_rv<W> &
-    operator = (const sc_out_rv<W> &)
+    operator = (const sc_out_rv<W> &p)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        (*this)->write(p->read());
         return *this;
     }
 
diff --git a/src/systemc/ext/channel/sc_signal_rv.hh b/src/systemc/ext/channel/sc_signal_rv.hh
index 723c2b4..d333da0 100644
--- a/src/systemc/ext/channel/sc_signal_rv.hh
+++ b/src/systemc/ext/channel/sc_signal_rv.hh
@@ -31,6 +31,9 @@
 #define __SYSTEMC_EXT_CHANNEL_SC_SIGNAL_RV_HH__
 
 #include "../core/sc_module.hh" // for sc_gen_unique_name
+#include "../core/scheduler.hh"
+#include "../dt/bit/sc_logic.hh"
+#include "../dt/bit/sc_lv.hh"
 #include "sc_signal.hh"
 #include "warn_unimpl.hh"
 
@@ -66,20 +69,29 @@
     }
 
     virtual void
-    write(const sc_dt::sc_lv<W> &)
+    write(const sc_dt::sc_lv<W> &l)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        ::sc_gem5::Process *p = ::sc_gem5::scheduler.current();
+
+        auto it = inputs.find(p);
+        if (it == inputs.end()) {
+            inputs.emplace(p, l);
+            this->request_update();
+        } else if (it->second != l) {
+            it->second = l;
+            this->request_update();
+        }
     }
     sc_signal_rv<W> &
-    operator = (const sc_dt::sc_lv<W> &)
+    operator = (const sc_dt::sc_lv<W> &l)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        write(l);
         return *this;
     }
     sc_signal_rv<W> &
-    operator = (const sc_signal_rv<W> &)
+    operator = (const sc_signal_rv<W> &r)
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        write(r.read());
         return *this;
     }
 
@@ -89,7 +101,28 @@
     virtual void
     update()
     {
-        sc_channel_warn_unimpl(__PRETTY_FUNCTION__);
+        using sc_dt::Log_0;
+        using sc_dt::Log_1;
+        using sc_dt::Log_Z;
+        using sc_dt::Log_X;
+        static sc_dt::sc_logic_value_t merge_table[4][4] = {
+            { Log_0, Log_X, Log_0, Log_X },
+            { Log_X, Log_1, Log_1, Log_X },
+            { Log_0, Log_1, Log_Z, Log_X },
+            { Log_X, Log_X, Log_X, Log_X }
+        };
+
+        // Resolve the inputs, and give the result to the underlying
+        // signal class.
+        for (int i = 0; i < W; i++) {
+            sc_dt::sc_logic_value_t bit = Log_Z;
+            for (auto &input: inputs)
+                bit = merge_table[bit][input.second.get_bit(i)];
+            this->m_new_val.set_bit(i, bit);
+        }
+
+        // Ask the signal to update it's value.
+        sc_signal<sc_dt::sc_lv<W>, SC_MANY_WRITERS>::update();
     }
 
   private:
@@ -97,6 +130,8 @@
     sc_signal_rv(const sc_signal_rv<W> &) :
             sc_signal<sc_dt::sc_lv<W>, SC_MANY_WRITERS>()
     {}
+
+    std::map<::sc_gem5::Process *, sc_dt::sc_lv<W> > inputs;
 };
 
 } // namespace sc_core
diff --git a/src/systemc/tests/systemc/communication/sc_signal_rv/test02/expected_returncode b/src/systemc/tests/systemc/communication/sc_signal_rv/test02/expected_returncode
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/src/systemc/tests/systemc/communication/sc_signal_rv/test02/expected_returncode
@@ -0,0 +1 @@
+1
diff --git a/src/systemc/tests/systemc/communication/sc_signal_rv/test03/expected_returncode b/src/systemc/tests/systemc/communication/sc_signal_rv/test03/expected_returncode
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/src/systemc/tests/systemc/communication/sc_signal_rv/test03/expected_returncode
@@ -0,0 +1 @@
+1