diff --git a/b_asic/architecture.py b/b_asic/architecture.py
index a73d3b054521f98fbb02a650b85995490793978a..f5040cbea7125ad6697732cf6e37772a49087182 100644
--- a/b_asic/architecture.py
+++ b/b_asic/architecture.py
@@ -40,7 +40,7 @@ class Resource:
         self._output_count = -1
 
     def __repr__(self):
-        return self._entity_name
+        return self.entity_name
 
     def __iter__(self):
         return iter(self._collection)
@@ -80,7 +80,7 @@ class Resource:
 
     def _digraph(self) -> Digraph:
         dg = Digraph(node_attr={'shape': 'record'})
-        dg.node(self._entity_name, self._struct_def())
+        dg.node(self.entity_name, self._struct_def())
         return dg
 
     @property
@@ -100,12 +100,18 @@ class Resource:
         if inputs:
             instrs = [f"<{instr}> {instr}" for instr in inputs]
             ret += f"{{{'|'.join(instrs)}}}|"
-        ret += f"{self._entity_name}"
+        ret += f"{self.entity_name}"
         if outputs:
             outstrs = [f"<{outstr}> {outstr}" for outstr in outputs]
             ret += f"|{{{'|'.join(outstrs)}}}"
         return ret
 
+    @property
+    def entity_name(self) -> str:
+        if self._entity_name is None:
+            return "Undefined entity name"
+        return self._entity_name
+
 
 class ProcessingElement(Resource):
     """
@@ -149,14 +155,6 @@ class ProcessingElement(Resource):
     def processes(self) -> Set[OperatorProcess]:
         return {cast(OperatorProcess, p) for p in self._collection}
 
-    def input_count(self) -> int:
-        """Return number of input ports."""
-        raise NotImplementedError()
-
-    def output_count(self) -> int:
-        """Return number of output ports."""
-        raise NotImplementedError()
-
 
 class Memory(Resource):
     """
@@ -170,6 +168,10 @@ class Memory(Resource):
         The type of memory.
     entity_name : str, optional
         Name of memory entity.
+    read_ports : int, optional
+        Number of read ports for memory.
+    write_ports : int, optional
+        Number of write ports for memory.
     """
 
     def __init__(
@@ -177,6 +179,8 @@ class Memory(Resource):
         process_collection: ProcessCollection,
         memory_type: str = "RAM",
         entity_name: Optional[str] = None,
+        read_ports: Optional[int] = None,
+        write_ports: Optional[int] = None,
     ):
         super().__init__(process_collection=process_collection, entity_name=entity_name)
         if not all(
@@ -191,6 +195,20 @@ class Memory(Resource):
             raise ValueError(
                 f"memory_type must be 'RAM' or 'register', not {memory_type!r}"
             )
+        read_ports_bound = self._collection.read_ports_bound()
+        if read_ports is None:
+            self._output_count = read_ports_bound
+        else:
+            if read_ports < read_ports_bound:
+                raise ValueError(f"At least {read_ports_bound} read ports required")
+            self._output_count = read_ports
+        write_ports_bound = self._collection.write_ports_bound()
+        if write_ports is None:
+            self._input_count = write_ports_bound
+        else:
+            if write_ports < write_ports_bound:
+                raise ValueError(f"At least {write_ports_bound} write ports required")
+            self._input_count = write_ports
         self._memory_type = memory_type
 
     def __iter__(self) -> Iterator[MemoryVariable]:
@@ -386,26 +404,20 @@ class Architecture:
     def _digraph(self) -> Digraph:
         dg = Digraph(node_attr={'shape': 'record'})
         for i, mem in enumerate(self._memories):
-            if mem._entity_name is not None:
-                dg.node(mem._entity_name, mem._struct_def())
-            else:
-                dg.node(f"MEM-{i}", mem._struct_def())
+            dg.node(mem.entity_name, mem._struct_def())
         for i, pe in enumerate(self._processing_elements):
-            if pe._entity_name is not None:
-                dg.node(pe._entity_name, pe._struct_def())
-            else:
-                dg.node(f"PE-{i}", pe._struct_def())
+            dg.node(pe.entity_name, pe._struct_def())
         for pe in self._processing_elements:
             inputs, outputs = self.get_interconnects_for_pe(pe)
             for i, inp in enumerate(inputs):
                 for source, cnt in inp.items():
                     dg.edge(
-                        source._entity_name, f"{pe._entity_name}:in{i}", label=f"{cnt}"
+                        source.entity_name, f"{pe.entity_name}:in{i}", label=f"{cnt}"
                     )
             for o, outp in enumerate(outputs):
                 for dest, cnt in outp.items():
                     dg.edge(
-                        f"{pe._entity_name}:out{o}", dest._entity_name, label=f"{cnt}"
+                        f"{pe.entity_name}:out{o}", dest.entity_name, label=f"{cnt}"
                     )
         return dg
 
diff --git a/b_asic/resources.py b/b_asic/resources.py
index 87bc312f34f64ddf1e0c81df531ec0bc9d4e9eaa..f38030b784887ecf2f3386863c52bff4543db414 100644
--- a/b_asic/resources.py
+++ b/b_asic/resources.py
@@ -1,5 +1,6 @@
 import io
 import re
+from collections import Counter
 from functools import reduce
 from typing import Dict, Iterable, List, Optional, Set, Tuple, TypeVar, Union
 
@@ -1266,3 +1267,17 @@ class ProcessCollection:
             self._schedule_time,
             self._cyclic,
         )
+
+    def read_ports_bound(self) -> int:
+        reads = []
+        for process in self._collection:
+            reads.extend(
+                set(read_time % self.schedule_time for read_time in process.read_times)
+            )
+        count = Counter(reads)
+        return max(count.values())
+
+    def write_ports_bound(self) -> int:
+        writes = [process.start_time for process in self._collection]
+        count = Counter(writes)
+        return max(count.values())
diff --git a/test/test_architecture.py b/test/test_architecture.py
index e83ecf4e307405a636d57dfcb1ba0cb87b17562c..b100f23f5c7a7208ebcc4ec9bf1ab326cb451b2a 100644
--- a/test/test_architecture.py
+++ b/test/test_architecture.py
@@ -103,9 +103,13 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
         Memory(pc) for pc in mvs.split_on_ports(read_ports=1, write_ports=1)
     ]
     assert len(memories) == 1
+
     for i, memory in enumerate(memories):
         memory.set_entity_name(f"MEM{i}")
-        s = 'digraph {\n\tnode [shape=record]\n\tMEM0 [label=MEM0]\n}'
+        s = (
+            'digraph {\n\tnode [shape=record]\n\tMEM0 [label="{<in0> in0}|MEM0|{<out0>'
+            ' out0}"]\n}'
+        )
         assert memory._digraph().source in (s, s + '\n')
 
     # Create architecture from