python: Make Port roles a more generic concept.
authorGabe Black <gabeblack@google.com>
Tue, 16 Apr 2019 02:39:14 +0000 (19:39 -0700)
committerGabe Black <gabeblack@google.com>
Wed, 24 Apr 2019 08:08:32 +0000 (08:08 +0000)
A recent change got rid of the strict Master => Slave port relationship
which used to be checked in python and instead left the checking up to
C++. One major downside to this approach is that it was no longer
obvious in the configuration what was supposed to be connected to what,
and it still left the arbitrary and misleading MasterPort and SlavePort
types in the Ethernet devices which could now connect with each other
symmetrically but couldn't actually connect to an arbitrary
MasterPort/SlavePort.

This change exposes the base Port and VectorPort types, and makes them
accept a "role" parameter in __init__ which used to be set directly by
their subclasses. This role can be any string, and will be used later
to check for compatiblity and to give a hint as to what can be
connected to what in the SimObject definitions.

To make the checks work with arbitrary compatible pairs, the base Port
type now has a class method called compat() which accepts a pair of
roles which will become mutually compatible, ie any port with the first
role will be allowed to connect to any port with the second role, and
vice versa. To be self compatible, the same role should be passed in
for both parameters.

To maintain compatibility, the MasterPort and SlavePort types are
retained, but now they're nothing special and could have been set up
in any arbitrary SimObject .py file. The same is true for
MasterVectorPort and SlaveVectorPort.

Also, since we can no longer assume that all edges in the dot graph of
the config should start with a port with the MASTER role and end with
a port with the SLAVE role, Ports now track an is_source property which
says whether the arrow head should be surpressed at that end of the
edge representing the connection.

Change-Id: Ifcc6faab05e437ad87cd21f0ba613b09cf21c321
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/18168
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Gabe Black <gabeblack@google.com>

src/python/m5/params.py

index 52360221b41c36113a3c87a318115c087a37246a..9b2ee39721e1b002c4e84610e26cf3485231bd1d 100644 (file)
@@ -1834,11 +1834,12 @@ AllMemory = AddrRange(0, MaxAddr)
 # Port reference: encapsulates a reference to a particular port on a
 # particular SimObject.
 class PortRef(object):
-    def __init__(self, simobj, name, role):
+    def __init__(self, simobj, name, role, is_source):
         assert(isSimObject(simobj) or isSimObjectClass(simobj))
         self.simobj = simobj
         self.name = name
         self.role = role
+        self.is_source = is_source
         self.peer = None   # not associated with another port yet
         self.ccConnected = False # C++ port connection done?
         self.index = -1  # always -1 for non-vector ports
@@ -1857,7 +1858,8 @@ class PortRef(object):
 
     # for config.json
     def get_config_as_dict(self):
-        return {'role' : self.role, 'peer' : str(self.peer)}
+        return {'role' : self.role, 'peer' : str(self.peer),
+                'is_source' : str(self.is_source)}
 
     def __getattr__(self, attr):
         if attr == 'peerObj':
@@ -1878,15 +1880,21 @@ class PortRef(object):
             fatal("Port %s is already connected to %s, cannot connect %s\n",
                   self, self.peer, other);
         self.peer = other
+
         if proxy.isproxy(other):
             other.set_param_desc(PortParamDesc())
-        elif isinstance(other, PortRef):
-            if other.peer is not self:
-                other.connect(self)
-        else:
+            return
+        elif not isinstance(other, PortRef):
             raise TypeError("assigning non-port reference '%s' to port '%s'" \
                   % (other, self))
 
+        if not Port.is_compat(self, other):
+            fatal("Ports %s and %s with roles '%s' and '%s' "
+                    "are not compatible", self, other, self.role, other.role)
+
+        if other.peer is not self:
+            other.connect(self)
+
     # Allow a master/slave port pair to be spliced between
     # a port and its connected peer. Useful operation for connecting
     # instrumentation structures into a system when it is necessary
@@ -1959,8 +1967,8 @@ class PortRef(object):
 # A reference to an individual element of a VectorPort... much like a
 # PortRef, but has an index.
 class VectorPortElementRef(PortRef):
-    def __init__(self, simobj, name, role, index):
-        PortRef.__init__(self, simobj, name, role)
+    def __init__(self, simobj, name, role, is_source, index):
+        PortRef.__init__(self, simobj, name, role, is_source)
         self.index = index
 
     def __str__(self):
@@ -1969,11 +1977,12 @@ class VectorPortElementRef(PortRef):
 # A reference to a complete vector-valued port (not just a single element).
 # Can be indexed to retrieve individual VectorPortElementRef instances.
 class VectorPortRef(object):
-    def __init__(self, simobj, name, role):
+    def __init__(self, simobj, name, role, is_source):
         assert(isSimObject(simobj) or isSimObjectClass(simobj))
         self.simobj = simobj
         self.name = name
         self.role = role
+        self.is_source = is_source
         self.elements = []
 
     def __str__(self):
@@ -1991,14 +2000,16 @@ class VectorPortRef(object):
     # for config.json
     def get_config_as_dict(self):
         return {'role' : self.role,
-                'peer' : [el.ini_str() for el in self.elements]}
+                'peer' : [el.ini_str() for el in self.elements],
+                'is_source' : str(self.is_source)}
 
     def __getitem__(self, key):
         if not isinstance(key, int):
             raise TypeError("VectorPort index must be integer")
         if key >= len(self.elements):
             # need to extend list
-            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
+            ext = [VectorPortElementRef(
+                    self.simobj, self.name, self.role, self.is_source, i)
                    for i in range(len(self.elements), key+1)]
             self.elements.extend(ext)
         return self.elements[key]
@@ -2042,10 +2053,31 @@ class VectorPortRef(object):
 # logical port in the SimObject class, not a particular port on a
 # SimObject instance.  The latter are represented by PortRef objects.
 class Port(object):
+    # Port("role", "description")
+
+    _compat_dict = { }
+
+    @classmethod
+    def compat(cls, role, peer):
+        cls._compat_dict.setdefault(role, set()).add(peer)
+        cls._compat_dict.setdefault(peer, set()).add(role)
+
+    @classmethod
+    def is_compat(cls, one, two):
+        for port in one, two:
+            if not port.role in Port._compat_dict:
+                fatal("Unrecognized role '%s' for port %s\n", port.role, port)
+        return one.role in Port._compat_dict[two.role]
+
+    def __init__(self, role, desc, is_source=False):
+        self.desc = desc
+        self.role = role
+        self.is_source = is_source
+
     # Generate a PortRef for this port on the given SimObject with the
     # given name
     def makeRef(self, simobj):
-        return PortRef(simobj, self.name, self.role)
+        return PortRef(simobj, self.name, self.role, self.is_source)
 
     # Connect an instance of this port (on the given SimObject with
     # the given name) with the port described by the supplied PortRef
@@ -2066,52 +2098,38 @@ class Port(object):
     def cxx_decl(self, code):
         code('unsigned int port_${{self.name}}_connection_count;')
 
+Port.compat('MASTER', 'SLAVE')
+
 class MasterPort(Port):
     # MasterPort("description")
-    def __init__(self, *args):
-        if len(args) == 1:
-            self.desc = args[0]
-            self.role = 'MASTER'
-        else:
-            raise TypeError('wrong number of arguments')
+    def __init__(self, desc):
+        super(MasterPort, self).__init__('MASTER', desc, is_source=True)
 
 class SlavePort(Port):
     # SlavePort("description")
-    def __init__(self, *args):
-        if len(args) == 1:
-            self.desc = args[0]
-            self.role = 'SLAVE'
-        else:
-            raise TypeError('wrong number of arguments')
+    def __init__(self, desc):
+        super(SlavePort, self).__init__('SLAVE', desc)
 
 # VectorPort description object.  Like Port, but represents a vector
 # of connections (e.g., as on a XBar).
 class VectorPort(Port):
-    def __init__(self, *args):
+    # VectorPort("role", "description")
+    def __init__(self, role, desc, is_source=False):
+        super(VectorPort, self).__init__(role, desc, is_source)
         self.isVec = True
 
     def makeRef(self, simobj):
-        return VectorPortRef(simobj, self.name, self.role)
+        return VectorPortRef(simobj, self.name, self.role, self.is_source)
 
 class VectorMasterPort(VectorPort):
     # VectorMasterPort("description")
-    def __init__(self, *args):
-        if len(args) == 1:
-            self.desc = args[0]
-            self.role = 'MASTER'
-            VectorPort.__init__(self, *args)
-        else:
-            raise TypeError('wrong number of arguments')
+    def __init__(self, desc):
+        super(VectorMasterPort, self).__init__('MASTER', desc, is_source=True)
 
 class VectorSlavePort(VectorPort):
     # VectorSlavePort("description")
-    def __init__(self, *args):
-        if len(args) == 1:
-            self.desc = args[0]
-            self.role = 'SLAVE'
-            VectorPort.__init__(self, *args)
-        else:
-            raise TypeError('wrong number of arguments')
+    def __init__(self, desc):
+        super(VectorSlavePort, self).__init__('SLAVE', desc)
 
 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
 # proxy objects (via set_param_desc()) so that proxy error messages
@@ -2145,5 +2163,5 @@ __all__ = ['Param', 'VectorParam',
            'MaxAddr', 'MaxTick', 'AllMemory',
            'Time',
            'NextEthernetAddr', 'NULL',
-           'MasterPort', 'SlavePort',
-           'VectorMasterPort', 'VectorSlavePort']
+           'Port', 'MasterPort', 'SlavePort',
+           'VectorPort', 'VectorMasterPort', 'VectorSlavePort']