diff --git a/b_asic/architecture.py b/b_asic/architecture.py
index 2638cb6220e118919c3827fbc506d760a82b58bc..559cb48564468e0bbc4d5ebd3aa4d0bf14867b0f 100644
--- a/b_asic/architecture.py
+++ b/b_asic/architecture.py
@@ -34,7 +34,7 @@ from b_asic.codegen.vhdl.common import is_valid_vhdl_identifier
 from b_asic.operation import Operation
 from b_asic.port import InputPort, OutputPort
 from b_asic.process import MemoryProcess, MemoryVariable, OperatorProcess, Process
-from b_asic.resources import ProcessCollection
+from b_asic.resources import ProcessCollection, _sanitize_port_option
 
 
 def _interconnect_dict() -> int:
@@ -435,6 +435,8 @@ class Memory(Resource):
         Number of read ports for memory.
     write_ports : int, optional
         Number of write ports for memory.
+    total_ports : int, optional
+        Total number of read and write ports for memory.
     assign : bool, default False
         Perform assignment when creating the Memory (using the default properties).
     """
@@ -448,6 +450,7 @@ class Memory(Resource):
         entity_name: Optional[str] = None,
         read_ports: Optional[int] = None,
         write_ports: Optional[int] = None,
+        total_ports: Optional[int] = None,
         assign: bool = False,
     ):
         super().__init__(process_collection=process_collection, entity_name=entity_name)
@@ -462,6 +465,10 @@ class Memory(Resource):
             raise ValueError(
                 f"memory_type must be 'RAM' or 'register', not {memory_type!r}"
             )
+        if read_ports is not None or write_ports is not None or total_ports is not None:
+            read_ports, write_ports, total_ports = _sanitize_port_option(
+                read_ports, write_ports, total_ports
+            )
         read_ports_bound = self._collection.read_ports_bound()
         if read_ports is None:
             self._output_count = read_ports_bound
@@ -476,6 +483,11 @@ class Memory(Resource):
             if write_ports < write_ports_bound:
                 raise ValueError(f"At least {write_ports_bound} write ports required")
             self._input_count = write_ports
+
+        total_ports_bound = self._collection.total_ports_bound()
+        if total_ports is not None and total_ports < total_ports_bound:
+            raise ValueError(f"At least {total_ports_bound} total ports required")
+
         self._memory_type = memory_type
         if assign:
             self.assign()
diff --git a/test/test_architecture.py b/test/test_architecture.py
index ef02ad0c1320072ba7bb1e0080faa8fcbabca7c8..690b8c2720a7b998ff517d3bc31e2ec77b94d63d 100644
--- a/test/test_architecture.py
+++ b/test/test_architecture.py
@@ -1,3 +1,4 @@
+import re
 from itertools import chain
 from typing import List
 
@@ -43,26 +44,6 @@ def test_add_remove_process_from_resource(schedule_direct_form_iir_lp_filter: Sc
         memory.add_process(PlainMemoryVariable(0, 0, {0: 2}, "PlainMV"))
 
 
-def test_extract_processing_elements(schedule_direct_form_iir_lp_filter: Schedule):
-    # Extract operations from schedule
-    operations = schedule_direct_form_iir_lp_filter.get_operations()
-
-    # Split into new process collections on overlapping execution time
-    adders = operations.get_by_type_name(Addition.type_name()).split_on_execution_time()
-    const_mults = operations.get_by_type_name(
-        ConstantMultiplication.type_name()
-    ).split_on_execution_time()
-
-    # List of ProcessingElements
-    processing_elements: List[ProcessingElement] = []
-    for adder_collection in adders:
-        processing_elements.append(ProcessingElement(adder_collection))
-    for const_mult_collection in const_mults:
-        processing_elements.append(ProcessingElement(const_mult_collection))
-
-    assert len(processing_elements) == len(adders) + len(const_mults)
-
-
 def test_memory_exceptions(schedule_direct_form_iir_lp_filter: Schedule):
     mvs = schedule_direct_form_iir_lp_filter.get_memory_variables()
     operations = schedule_direct_form_iir_lp_filter.get_operations()
@@ -82,6 +63,11 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
     mvs = schedule_direct_form_iir_lp_filter.get_memory_variables()
     operations = schedule_direct_form_iir_lp_filter.get_operations()
 
+    with pytest.raises(
+        TypeError, match="Different Operation types in ProcessCollection"
+    ):
+        ProcessingElement(operations)
+
     # Split operations further into chunks
     adders = operations.get_by_type_name(Addition.type_name()).split_on_execution_time()
     assert len(adders) == 1
@@ -96,7 +82,10 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
 
     # Create necessary processing elements
     adder = ProcessingElement(adders[0], entity_name="adder")
-    multiplier = ProcessingElement(const_mults[0], entity_name="multiplier")
+    multiplier = ProcessingElement(const_mults[0])
+    assert multiplier.entity_name == "Undefined entity name"
+    multiplier.set_entity_name("multiplier")
+    assert multiplier.entity_name == "multiplier"
     input_pe = ProcessingElement(inputs[0], entity_name="input")
     output_pe = ProcessingElement(outputs[0], entity_name="output")
     processing_elements: List[ProcessingElement] = [
@@ -140,6 +129,7 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
     with pytest.raises(ValueError, match='32 is not a valid VHDL identifier'):
         adder.set_entity_name("32")
     assert adder.entity_name == "adder"
+    assert repr(adder) == "adder"
 
     # Create architecture from
     architecture = Architecture(
@@ -227,3 +217,47 @@ def test_move_process(schedule_direct_form_iir_lp_filter: Schedule):
         architecture.move_process('cmul4.0', memories[0], processing_elements[0])
     with pytest.raises(KeyError, match="invalid_name not in"):
         architecture.move_process('invalid_name', memories[0], processing_elements[1])
+
+
+def test_resource_errors(precedence_sfg_delays):
+    precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
+    precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
+    precedence_sfg_delays.set_execution_time_of_type(Addition.type_name(), 1)
+    precedence_sfg_delays.set_execution_time_of_type(
+        ConstantMultiplication.type_name(), 1
+    )
+
+    schedule = Schedule(precedence_sfg_delays)
+    operations = schedule.get_operations()
+    additions = operations.get_by_type_name(Addition.type_name())
+    with pytest.raises(
+        ValueError, match='Cannot map ProcessCollection to single ProcessingElement'
+    ):
+        ProcessingElement(additions)
+
+    mv = schedule.get_memory_variables()
+    with pytest.raises(
+        ValueError,
+        match=(
+            "If total_ports is unset, both read_ports and write_ports must be provided."
+        ),
+    ):
+        Memory(mv, read_ports=1)
+    with pytest.raises(
+        ValueError, match=re.escape("Total ports (2) less then read ports (6)")
+    ):
+        Memory(mv, read_ports=6, total_ports=2)
+    with pytest.raises(
+        ValueError, match=re.escape("Total ports (6) less then write ports (7)")
+    ):
+        Memory(mv, read_ports=6, write_ports=7, total_ports=6)
+    with pytest.raises(ValueError, match="At least 6 read ports required"):
+        Memory(mv, read_ports=1, write_ports=1)
+    with pytest.raises(ValueError, match="At least 5 write ports required"):
+        Memory(mv, read_ports=6, write_ports=1)
+    with pytest.raises(ValueError, match="At least 9 total ports required"):
+        Memory(mv, read_ports=6, write_ports=5, total_ports=6)
+    with pytest.raises(
+        ValueError, match="memory_type must be 'RAM' or 'register', not 'foo'"
+    ):
+        Memory(mv, read_ports=6, write_ports=5, total_ports=6, memory_type="foo")