diff --git a/b_asic/architecture.py b/b_asic/architecture.py
index cd318b29e1b92bc8b5fe6f2037bb5fdead2d42fd..510a29b4bcef6e1e10b3dd798e024f5b9dc60945 100644
--- a/b_asic/architecture.py
+++ b/b_asic/architecture.py
@@ -2,6 +2,7 @@
 B-ASIC architecture classes.
 """
 from collections import defaultdict
+from io import TextIOWrapper
 from typing import Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union, cast
 
 from graphviz import Digraph
@@ -77,6 +78,37 @@ class HardwareBlock:
     def _digraph(self) -> Digraph:
         raise NotImplementedError()
 
+    @property
+    def schedule_time(self) -> int:
+        """The schedule time for hardware block"""
+        raise NotImplementedError()
+
+    def write_component_declaration(self, f: TextIOWrapper, indent: int = 1) -> None:
+        """
+        Write component declaration of hardware block.
+
+        Parameters
+        ----------
+        f : TextIOWrapper
+            File object (or other TextIOWrapper object) to write the declaration to.
+        indent : int, default: 1
+            Indentation level to use for this process.
+        """
+        raise NotImplementedError()
+
+    def write_component_instantiation(self, f: TextIOWrapper, indent: int = 1) -> None:
+        """
+        Write component instantiation of hardware block.
+
+        Parameters
+        ----------
+        f : TextIOWrapper
+            File object (or other TextIOWrapper object) to write the instantiation to.
+        indent : int, default: 1
+            Indentation level to use for this process.
+        """
+        raise NotImplementedError()
+
 
 class Resource(HardwareBlock):
     """
@@ -135,6 +167,11 @@ class Resource(HardwareBlock):
             ret += f"|{{{'|'.join(outstrs)}}}"
         return "{" + ret + "}"
 
+    @property
+    def schedule_time(self) -> int:
+        # doc-string inherited
+        return self._collection.schedule_time
+
 
 class ProcessingElement(Resource):
     """
@@ -277,11 +314,25 @@ of :class:`~b_asic.architecture.ProcessingElement`
         self._operation_inport_to_resource: Dict[InputPort, Resource] = {}
         self._operation_outport_to_resource: Dict[OutputPort, Resource] = {}
 
+        self._schedule_time = self._check_and_get_schedule_time()
+
         # Validate input and output ports
         self.validate_ports()
 
         self._build_dicts()
 
+    def _check_and_get_schedule_time(self) -> int:
+        schedule_times = set()
+        for memory in self._memories:
+            schedule_times.add(memory.schedule_time)
+        for pe in self._processing_elements:
+            schedule_times.add(pe.schedule_time)
+        if self._direct_interconnects is not None:
+            schedule_times.add(self._direct_interconnects.schedule_time)
+        if len(schedule_times) != 1:
+            raise ValueError(f"Different schedule times: {schedule_times}")
+        return schedule_times.pop()
+
     def _build_dicts(self):
         for pe in self.processing_elements:
             for operator in pe.processes:
@@ -446,3 +497,8 @@ of :class:`~b_asic.architecture.ProcessingElement`
     @property
     def direct_interconnects(self) -> Optional[ProcessCollection]:
         return self._direct_interconnects
+
+    @property
+    def schedule_time(self) -> int:
+        # doc-string inherited
+        return self._schedule_time
diff --git a/b_asic/codegen/vhdl/architecture.py b/b_asic/codegen/vhdl/architecture.py
index d59386df944d352491f48aa28af527692ad51ee4..eca28cdc4728a019dfa165e7521d8203bb16e9ca 100644
--- a/b_asic/codegen/vhdl/architecture.py
+++ b/b_asic/codegen/vhdl/architecture.py
@@ -26,14 +26,14 @@ def memory_based_storage(
 
     Parameters
     ----------
+    f : TextIOWrapper
+        File object (or other TextIOWrapper object) to write the architecture onto.
     assignment : dict
         A possible cell assignment to use when generating the memory based storage.
         The cell assignment is a dictionary int to ProcessCollection where the integer
         corresponds to the cell to assign all MemoryVariables in corresponding process
         collection.
         If unset, each MemoryVariable will be assigned to a unique cell.
-    f : TextIOWrapper
-        File object (or other TextIOWrapper object) to write the architecture onto.
     word_length : int
         Word length of the memory variable objects.
     read_ports : int
diff --git a/test/test_architecture.py b/test/test_architecture.py
index 9830679c78240ea4f864bed9006cf3e3faa10079..8b52b0700e21097264185241d2d486200e6c4397 100644
--- a/test/test_architecture.py
+++ b/test/test_architecture.py
@@ -110,6 +110,7 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
             'digraph {\n\tnode [shape=record]\n\tMEM0 [label="{{<in0> in0}|MEM0|{<out0>'
             ' out0}}"]\n}'
         )
+        assert memory.schedule_time == 18
         assert memory._digraph().source in (s, s + '\n')
 
     # Create architecture from
@@ -117,9 +118,12 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule):
         processing_elements, memories, direct_interconnects=direct_conn
     )
 
+    assert architecture.schedule_time == 18
+
     # assert architecture._digraph().source == "foo"
     for pe in processing_elements:
         print(pe)
+        assert pe.schedule_time == 18
         for operation in pe._collection:
             operation = cast(OperatorProcess, operation)
             print(f'  {operation}')