slicc: support for multiple message types on the same buffer
authorDavid Hashe <david.hashe@amd.com>
Mon, 20 Jul 2015 14:15:18 +0000 (09:15 -0500)
committerDavid Hashe <david.hashe@amd.com>
Mon, 20 Jul 2015 14:15:18 +0000 (09:15 -0500)
This patch allows SLICC protocols to use more than one message type with a
message buffer. For example, you can declare two in ports as such:

  in_port(ResponseQueue_in, ResponseMsg, responseFromDir, rank=3) { ... }
  in_port(tgtResponseQueue_in, TgtResponseMsg, responseFromDir, rank=2) { ... }

src/mem/protocol/RubySlicc_Exports.sm
src/mem/ruby/slicc_interface/AbstractController.hh
src/mem/slicc/ast/InPortDeclAST.py
src/mem/slicc/ast/PeekStatementAST.py
src/mem/slicc/symbols/StateMachine.py
src/mem/slicc/symbols/Var.py

index b2a8bcb000796537546efd197ab0d7dc03f3a409..20ef697c548f7be7446f533d752cd521f88eddf8 100644 (file)
@@ -116,6 +116,7 @@ enumeration(TransitionResult, desc="...") {
   Valid,         desc="Valid transition";
   ResourceStall, desc="Stalled due to insufficient resources";
   ProtocolStall, desc="Protocol specified stall";
+  Reject,        desc="Rejected because of a type mismatch";
 }
 
 // RubyRequestType
index f8970fb590d1edbaf284713d187cc7ce2e34fb5c..e01a2a8245d23982e40ec153646ffaa4d275951e 100644 (file)
@@ -29,6 +29,7 @@
 #ifndef __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
 #define __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
 
+#include <exception>
 #include <iostream>
 #include <string>
 
 
 class Network;
 
+// used to communicate that an in_port peeked the wrong message type
+class RejectException: public std::exception
+{
+    virtual const char* what() const throw()
+    { return "Port rejected message based on type"; }
+};
+
 class AbstractController : public MemObject, public Consumer
 {
   public:
index 75f917f9ab16ed5b212f455a3cd3163cd59a434b..c5539fe52cec1f8e0068ebbf07f86f44e5bd59f9 100644 (file)
@@ -59,7 +59,7 @@ class InPortDeclAST(DeclAST):
 
         type = self.queue_type.type
         in_port = Var(self.symtab, self.ident, self.location, type, str(code),
-                      self.pairs)
+                      self.pairs, machine, self.var_expr)
         symtab.newSymbol(in_port)
 
         symtab.pushFrame()
index d267df26e1f7399d596c49853a4ce72210390c2a..cecb9aa9fc091373d6dc9863eb74b48b85150497 100644 (file)
@@ -62,7 +62,12 @@ class PeekStatementAST(StatementAST):
     // Declare message
     const $mtid* in_msg_ptr M5_VAR_USED;
     in_msg_ptr = dynamic_cast<const $mtid *>(($qcode).${{self.method}}());
-    assert(in_msg_ptr != NULL); // Check the cast result
+    if (in_msg_ptr == NULL) {
+        // If the cast fails, this is the wrong inport (wrong message type).
+        // Throw an exception, and the caller will decide to either try a
+        // different inport or punt.
+        throw RejectException();
+    }
 ''')
 
         if self.pairs.has_key("block_on"):
index fc1e0792a98914cafb248f2fa9dcf475e3a6e0af..174d66e0fbd1f1e3ed47399676855fd9bb6f190b 100644 (file)
@@ -179,6 +179,21 @@ class StateMachine(Symbol):
                 action.warning(error_msg)
         self.table = table
 
+    # determine the port->msg buffer mappings
+    def getBufferMaps(self, ident):
+        msg_bufs = []
+        port_to_buf_map = {}
+        in_msg_bufs = {}
+        for port in self.in_ports:
+            buf_name = "m_%s_ptr" % port.buffer_expr.name
+            msg_bufs.append(buf_name)
+            port_to_buf_map[port] = msg_bufs.index(buf_name)
+            if buf_name not in in_msg_bufs:
+                in_msg_bufs[buf_name] = [port]
+            else:
+                in_msg_bufs[buf_name].append(port)
+        return port_to_buf_map, in_msg_bufs, msg_bufs
+
     def writeCodeFiles(self, path, includes):
         self.printControllerPython(path)
         self.printControllerHH(path)
@@ -423,6 +438,7 @@ void unset_tbe(${{self.TBEType.c_ident}}*& m_tbe_ptr);
  */
 
 #include <sys/types.h>
+#include <typeinfo>
 #include <unistd.h>
 
 #include <cassert>
@@ -935,7 +951,13 @@ void
 $c_ident::${{action.ident}}(${{self.TBEType.c_ident}}*& m_tbe_ptr, ${{self.EntryType.c_ident}}*& m_cache_entry_ptr, const Address& addr)
 {
     DPRINTF(RubyGenerated, "executing ${{action.ident}}\\n");
-    ${{action["c_code"]}}
+    try {
+       ${{action["c_code"]}}
+    } catch (const RejectException & e) {
+       fatal("Error in action ${{ident}}:${{action.ident}}: "
+             "executed a peek statement with the wrong message "
+             "type specified. ");
+    }
 }
 
 ''')
@@ -1028,6 +1050,7 @@ $c_ident::functionalWriteBuffers(PacketPtr& pkt)
 // ${ident}: ${{self.short}}
 
 #include <sys/types.h>
+#include <typeinfo>
 #include <unistd.h>
 
 #include <cassert>
@@ -1051,6 +1074,8 @@ $c_ident::functionalWriteBuffers(PacketPtr& pkt)
         for include_path in includes:
             code('#include "${{include_path}}"')
 
+        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
+
         code('''
 
 using namespace std;
@@ -1060,6 +1085,8 @@ ${ident}_Controller::wakeup()
 {
     int counter = 0;
     while (true) {
+        unsigned char rejected[${{len(msg_bufs)}}];
+        memset(rejected, 0, sizeof(unsigned char)*${{len(msg_bufs)}});
         // Some cases will put us into an infinite loop without this limit
         assert(counter <= m_transitions_per_cycle);
         if (counter == m_transitions_per_cycle) {
@@ -1084,15 +1111,43 @@ ${ident}_Controller::wakeup()
                 code('m_cur_in_port = ${{port.pairs["rank"]}};')
             else:
                 code('m_cur_in_port = 0;')
+            if port in port_to_buf_map:
+                code('try {')
+                code.indent()
             code('${{port["c_code_in_port"]}}')
-            code.dedent()
 
+            if port in port_to_buf_map:
+                code.dedent()
+                code('''
+            } catch (const RejectException & e) {
+                rejected[${{port_to_buf_map[port]}}]++;
+            }
+''')
+            code.dedent()
             code('')
 
         code.dedent()
         code.dedent()
         code('''
-        break;  // If we got this far, we have nothing left todo
+        // If we got this far, we have nothing left todo or something went
+        // wrong''')
+        for buf_name, ports in in_msg_bufs.items():
+            if len(ports) > 1:
+                # only produce checks when a buffer is shared by multiple ports
+                code('''
+        if (${{buf_name}}->isReady() && rejected[${{port_to_buf_map[ports[0]]}}] == ${{len(ports)}})
+        {
+            // no port claimed the message on the top of this buffer
+            panic("Runtime Error at Ruby Time: %d. "
+                  "All ports rejected a message. "
+                  "You are probably sending a message type to this controller "
+                  "over a virtual network that do not define an in_port for "
+                  "the incoming message type.\\n",
+                  Cycles(1));
+        }
+''')
+        code('''
+        break;
     }
 }
 ''')
@@ -1170,6 +1225,8 @@ TransitionResult result =
         else:
             code('doTransitionWorker(event, state, next_state, addr);')
 
+        port_to_buf_map, in_msg_bufs, msg_bufs = self.getBufferMaps(ident)
+
         code('''
 
 if (result == TransitionResult_Valid) {
index 2a4ef23db3f28bf1ad122bafd2aec3cc79badabe..85b9e67cd24bd0901ba310f73d8da00eace0d7d7 100644 (file)
@@ -29,9 +29,10 @@ from slicc.symbols.Symbol import Symbol
 
 class Var(Symbol):
     def __init__(self, symtab, ident, location, type, code, pairs,
-                 machine=None):
+                 machine=None, buffer_expr=""):
         super(Var, self).__init__(symtab, ident, location, pairs)
 
+        self.buffer_expr = buffer_expr
         self.machine = machine
         self.type = type
         self.code = code