website: Add gem5 standard library documentation

Change-Id: I572d8052ed0102cd80a9cb089e09b601279c2900
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5-website/+/55123
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: Bobby Bruce <bbruce@ucdavis.edu>
diff --git a/_data/documentation.yml b/_data/documentation.yml
index 86c636d..c4ad7b2 100755
--- a/_data/documentation.yml
+++ b/_data/documentation.yml
@@ -55,6 +55,18 @@
         - page: v21.2.0.0
           url: http://doxygen.gem5.org/release/v21-2-0-0/index.html
 
+    - title: gem5 standard library
+      id: gem5-standard-library
+      subitems:
+        - page: Standard Library Overview
+          url: /documentation/gem5-stdlib/overview
+        - page: Hello World Tutorial
+          url: /documentation/gem5-stdlib/hello-world-tutorial
+        - page: X86 Full-System Tutorial
+          url: /documentation/gem5-stdlib/x86-full-system-tutorial
+        - page: Developing Your Own Components Tutorial
+          url: /documentation/gem5-stdlib/develop-own-components-tutorial
+
     - title: gem5 Resources
       id: gem5_resources
       url: /documentation/general_docs/gem5_resources
diff --git a/_pages/documentation/gem5-stdlib/0-overview.md b/_pages/documentation/gem5-stdlib/0-overview.md
new file mode 100644
index 0000000..89b2ea3
--- /dev/null
+++ b/_pages/documentation/gem5-stdlib/0-overview.md
@@ -0,0 +1,120 @@
+---
+layout: documentation
+title: Standard Library Overview
+parent: gem5-standard-library
+doc: gem5 documentation
+permalink: /documentation/gem5-stdlib/overview
+author: Bobby R. Bruce
+---
+
+An overview of the gem5 standard library
+========================================
+
+Similar to standard libraries in programming languages, the gem5 standard library is designed to provide users of gem5 with commonly used components, features, and functionality with the goal of improving their productivity.
+The gem5 stdlib was introduced in [v21.1](https://gem5.googlesource.com/public/gem5/+/refs/tags/v21.1.0.0) in an alpha-release state (then referred to as "gem5 components"), and has been fully released as of [v21.2](https://gem5.googlesource.com/public/gem5/+/refs/tags/v21.2.0.0).
+
+For users new to the gem5 standard library, the following tutorials may be of help in understanding how the gem5 stdlib may be used to improve the creation of gem5 simulations.
+They include a tutorial on building SE and FS simulations, as well as a guide on how to extend the library and contribute.
+The [`configs/examples/gem5_library`](https://gem5.googlesource.com/public/gem5/+/refs/heads/stable/configs/example/gem5_library/) directory in the gem5 repository also contains example scripts which use the library.
+
+The following subsections give a broad overview of the gem5 stdlib packages and what there intended purposes are.
+
+**Note: The documentation/tutorial/etc. related to the standard library are for the v21.2 release.
+Please ensure you have the correct version of gem5 before proceeding.**
+
+
+The gem5 stdlib components package and its design philosophy
+------------------------------------------------------------
+
+The gem5 stdlib components package is the central part of the gem5 stdlib.
+With it users can built complex systems from simple components which connect together using standardized APIs.
+
+The metaphor that guided the components package development was that of building a computer using off-the-shelf components.
+When building a computer, someone may select components, plug them into a board, and assume the interface between the board and the component have been designed in a way in which they will "just work".
+For example, someone can remove a processor from a board and add a different one, compatible with the same socket, without needing to change everything else in their setup.
+While there are always limitations to this design philosophy, the components package has a highly modular design with components of the same type being interchangeable with one another as much as is possible.
+
+At the core of the components package is the idea of a _board_.
+This plays a similar role to the motherboard in a real-world system.
+While it may contain embedded caches, controllers, and other complex components, its main purpose is to expose standardized interfaces for other hardware to be added and handle communication between them.
+For example, a memory stick and a processor may be added to a board with the board responsible for communication without the designer of the memory or the processor having to consider this assuming they conform to known APIs.
+
+Typically, a gem5 components package _board_ requires declaration of these three components:
+
+1. The _processor_ : The system processor. A processor component contains at least one _core_ which may be Atomic, O3, Timing, or KVM.
+2. The _memory_ system: The memory system, for example, a DDR3_1600.
+3. The _cache hierarchies_: This component defines any and all components between the processor and main memory, most notably the cache setup. In the simplest of setups this will connect memory directly to the processor.
+
+A typical usage of the components may therefore look like:
+
+```python
+
+cache_hierarchy = MESITwoLevelCacheHierarchy(
+    l1d_size="16kB",
+    l1d_assoc=8,
+    l1i_size="16kB",
+    l1i_assoc=8,
+    l2_size="256kB",
+    l2_assoc=16,
+    num_l2_banks=1,
+)
+
+memory = SingleChannelDDR3_1600(size="3GB")
+
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, num_cores=1)
+
+board = X86Board(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+```
+
+The following tutorials go into greater detail on how to use the components package to create gem5 simulations.
+
+The gem5 resources package
+--------------------------
+
+The gem5 stdlib's resource package is used to obtain and incorporate resources.
+A resource, in the context of gem5, is something used in a simulation, or by a simulation, but not directly used to construct a system to be simulated.
+Typically these are applications, kernels, disk images, benchmarks, or tests.
+
+As these resources can be hard to find, or hard to create, we provide pre-built resources as part of [gem5-resources](/documentation/general_docs/gem5_resources).
+For example, via gem5-resources, a user may download an Ubuntu 18.04 disk image with known compatibility with gem5.
+They need not setup this themselves.
+
+A core feature of the gem5 stdlib resource package is that it allows users to _automatically obtain_ prebuilt gem5 resources for their simulation.
+A user may specify in their Python config file that a specific gem5 resource is required and, when run, the package will check if there is a local copy on the host system, and if not, download it.
+
+The tutorials will demonstrate how to use the resource package in greater detail, but for now, a typical pattern is as follows:
+
+```python
+from gem5.resources.resource import Resource
+
+resource = Resource("riscv-disk-img")
+
+print(f"The resources is available at {resource.get_local_path()}")
+```
+
+This will obtain the `riscv-disk-img` resource and store it locally for use in a gem5 simulation.
+
+The resources package references the [`resources.json` file](https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/stable/resources.json) in the [gem5-resources repository](https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/stable) to get info on what resources are available and where they may be downloaded from.
+While this is a machine-readable JSON file, users may use it to lookup the resources available.
+**We hope in the near future to have a website which renders this in a more human-readable manner**.
+
+The Simulate package
+--------------------
+
+**WARNING: The Simulate package is still in a BETA state. APIs in this package may change in future releases of gem5**.
+
+The simulate package is used to run gem5 simulations.
+While there is some boilerplate code this module handles on the users behalf, its primary purpose is to provde default behavior and APIs for what we refer to as _Exit Events_.
+Exit events are when a simulation exits for a particular reason.
+
+A typical example of an exit event would be a `Workbegin` exit event.
+This is used to specify that a Region-of-Interest (ROI) has been reached.
+Usually this exit would be used to allow a user to begin logging statistics or to switch to a more detailed CPU model.
+Prior to the stdlib, the user would need to specify precisely what the expected behavior was at exit events such as this.
+The simulation would exit and the configuration script would contain Python code specifying what to do next.
+Now, with the simulate package, there is a default behavior for this kind of event (the stats are reset), and an easy interface to override this behavior with something the user requires.
diff --git a/_pages/documentation/gem5-stdlib/1-tutorial-hello-world.md b/_pages/documentation/gem5-stdlib/1-tutorial-hello-world.md
new file mode 100644
index 0000000..4fac2d3
--- /dev/null
+++ b/_pages/documentation/gem5-stdlib/1-tutorial-hello-world.md
@@ -0,0 +1,221 @@
+---
+layout: documentation
+title: Hello World Tutorial
+parent: gem5-standard-library
+doc: gem5 documentation
+permalink: /documentation/gem5-stdlib/hello-world-tutorial
+author: Bobby R. Bruce
+---
+
+Building a "Hello World" example with the gem5 standard library
+===============================================================
+
+In this tutorial we will cover how to create a very basic simulation using gem5 components.
+This simulation will setup a system consisting of a single-core processor, running in Atomic mode, connected directly to main memory with no caches, I/O, or other components.
+The system will run an X86 binary in Syscall Execution (SE) mode.
+The binary will be obtained from gem5-resources and which will print a "Hello World!" string to stdout upon execution.
+
+To start we must compile gem5 to simulate the X86 ISA:
+
+```
+# In the root of the gem5 directory
+scons build/X86/gem5.opt -j <number of threads>
+```
+
+Then a new Python file should be created (we will refer to this as `hello-world.py` going forward).
+The first lines in this file should be the needed imports:
+
+```python
+from gem5.components.boards.simple_board import SimpleBoard
+from gem5.components.cachehierarchies.classic.no_cache import NoCache
+from gem5.components.memory.single_channel import SingleChannelDDR3_1600
+from gem5.components.processors.simple_processor import SimpleProcessor
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
+```
+
+All these libraries are included inside the compiled gem5 binary.
+Therefore, you will not need to obtain them from elsewhere.
+`from gem5.` indicates we are importing from the `gem5` standard library, and the lines starting with `from gem5.components` are importing components from the gem5 components package.
+The `from gem5.resources` line means we are importing from the resources package, and `from gem5.simulate`, the simulate package.
+All these packages, `components`, `resources`, and `simulate` are part of the gem5 standard library.
+
+Next we begin specifying the system.
+The gem5 library requires the user to specify four main components: the _board_, the _cache hierarchy_, the _memory system_, and the _processor_.
+
+Let's start with the _cache hierarchy_:
+
+```python
+cache_hierarchy = NoCache()
+```
+
+Here we are using `NoCache()`.
+This means, for our system, we are stating there is no cache hierarchy (i.e., no caches).
+In the gem5 library the cache hierarchy is a broad term for anything that exists between the processor cores and main memory.
+Here we are stating the processor is connected directly to main memory.
+
+Next we declare the _memory system_:
+
+```python
+memory = SingleChannelDDR3_1600("1GiB")
+```
+
+There exists many memory components to choose from within `gem5.components.memory`.
+Here we are using a single-channel DDR3 1600, and setting its size to 1GiB.
+It should be noted that setting the size here is technically optional.
+If not set, the `SingleChannelDDR3_1600` will default to 8GiB.
+
+Then we consider the _processor_:
+
+```python
+processor = SimpleProcessor(cpu_type=CPUTypes.ATOMIC, num_cores=1)
+```
+
+A processor in `gem5.components` is an object which contains a number of gem5 CPU cores, of a particular or varying type (`ATOMIC`, `TIMING`, `KVM`, or `O3`).
+The `SimpleProcessor` used in this example is a processor where all the CPU Cores are of an identical type.
+It requires two arguments: the `cpu_type`, which we set to `ATOMIC`, and `num_cores`, the number of cores, which we set to one.
+
+Finally we specify which _board_ we are using:
+
+```python
+board = SimpleBoard(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+```
+
+While the constructor of each board may vary, they will typically require the user to specify the _processor_, _memory system_, and _cache hierarchy_, as well as the clock frequency to use.
+In this example we use the `SimpleBoard`.
+The `SimpleBoard` is a very basic system with no I/O which only supports SE mode and can only work with classic cache hierarchy setups.
+
+At this point in the script we have specified everything we require to simulate our system.
+Of course, in order to run a meaningful simulation, we must specify a workload for this system to run.
+To do so we add the following lines:
+
+```python
+binary = Resource("x86-hello64-static")
+board.set_se_binary_workload(binary)
+```
+
+The `Resource` class takes a string which specifies which resource, from [gem5-resources](/documentation/general_docs/gem5_resources), is to be obtained for the simulation.
+All the gem5 resources are specified in the [gem5 resources `resources.json` file](https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/stable/resources.json).
+If the resource is not present on the host system it'll be automatically downloaded.
+In this example we are going to use the `x86-hello-64-static` resource;
+an x86, 64-bit, statically compiled binary which will print "Hello World!" to stdout.
+After specifying the resource we set the workload via the board's `set_se_binary_workload` function.
+As the name suggests `set_se_binary_workload` is a function used to set a binary to be executed in Syscall Execution mode.
+
+This is all that is required to setup your simulation.
+From this you simply need to construct and run the `Simulator`:
+
+```python
+simulator = Simulator(board=board, full_system=False)
+simulator.run()
+```
+
+Please ensure you have set the `full_system` parameter to `False`.
+By default it is `True`, but in this tutorial we are running in SE mode, not FS.
+It should also be noted that **the `Simulator` module is still in a beta state, so its APIs may change upon the next release**.
+
+As a recap, your script should look like the following:
+
+```python
+from gem5.components.boards.simple_board import SimpleBoard
+from gem5.components.cachehierarchies.classic.no_cache import NoCache
+from gem5.components.memory.single_channel import SingleChannelDDR3_1600
+from gem5.components.processors.simple_processor import SimpleProcessor
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
+
+
+# Obtain the components.
+cache_hierarchy = NoCache()
+memory = SingleChannelDDR3_1600("1GiB")
+processor = SimpleProcessor(cpu_type=CPUTypes.ATOMIC, num_cores=1)
+
+#Add them to the board.
+board = SimpleBoard(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+
+# Set the workload.
+binary = Resource("x86-hello64-static")
+board.set_se_binary_workload(binary)
+
+# Setup the Simulator and run the simulation.
+simulator = Simulator(board=board, full_system=False)
+simulator.run()
+```
+
+It can then be executed with:
+
+```sh
+./build/X86/gem5.opt hello-world.py
+```
+
+If setup correctly, the output will look something like:
+
+```
+gem5 Simulator System.  http://gem5.org
+gem5 is copyrighted software; use the --copyright option for details.
+
+gem5 version 21.2.0.0
+gem5 compiled Dec 29 2021 16:06:43
+gem5 started Dec 29 2021 16:16:36
+gem5 executing on liberty.cs.ucdavis.edu, pid 477522
+command line: ./build/X86/gem5.opt hello-world-tutorial.py
+
+warn: The simulate package is still in a beta state. The gem5 project does not guarantee the APIs within this package will remain consistent across upcoming releases.
+Global frequency set at 1000000000000 ticks per second
+build/X86/mem/mem_interface.cc:791: warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (1024 Mbytes)
+0: board.remote_gdb: listening for remote gdb on port 7000
+build/X86/sim/simulate.cc:194: info: Entering event queue @ 0.  Starting simulation...
+build/X86/sim/syscall_emul.hh:1014: warn: readlink() called on '/proc/self/exe' may yield unexpected results in various settings.
+      Returning '/scr/bbruce/.cache/gem5/x86-hello64-static'
+build/X86/sim/mem_state.cc:443: info: Increasing stack size by one page.
+Hello world!
+```
+
+It should be obvious from this point that a _board's_ parameters may be altered to test other designs.
+For example, if we want to test a `TIMING` CPU setup we'd change our _processor_ to:
+
+```python
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, num_cores=1)
+```
+
+This is all that is required.
+The gem5 standard library will reconfigure the design as is necessary.
+
+As another example, consider swapping out a component for another.
+In this design we decided on `NoCache` but we could use another classic cache hierarchy, such as `PrivateL1CacheHierarchy`.
+To do so we'd change our `cache_hierarchy` parameter:
+
+```
+# We import the cache hierarchy we want.
+from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import PrivateL1CacheHierarchy
+
+...
+
+# Then set it.
+cache_hierarchy = PrivateL1CacheHierarchy(l1d_size="32kB", l1i_size="32kB")
+```
+
+Note here that `PrivateL1CacheHierarchy` requires the user to specify the L1 data and instruction cache sizes to be constructed.
+No other part of the design need change.
+The gem5 standard library will incorporate the cache hierarchy as required.
+
+To recap on what was learned in this tutorial:
+
+* A system can be built with the gem5 components package using _processor_, _cache hierarchy_, _memory system_, and _board_ components.
+* Generally speaking, components of the same type are interchangeable as much as is possible. E.g., different _cache hierarchy_ components may be swapped in and out of a design without reconfiguration needed in other components.
+* _board_s contain functions to set workloads.
+* The resources package may be used to obtain prebuilt resources from gem5-resources.
+These are typically workloads that may be run via set workload functions.
+* The simulate package can be used to run a board within a gem5 simulation.
diff --git a/_pages/documentation/gem5-stdlib/2-tutorial-x86-fs.md b/_pages/documentation/gem5-stdlib/2-tutorial-x86-fs.md
new file mode 100644
index 0000000..0d73292
--- /dev/null
+++ b/_pages/documentation/gem5-stdlib/2-tutorial-x86-fs.md
@@ -0,0 +1,302 @@
+---
+layout: documentation
+title: X86 Full-System Tutorial
+parent: gem5-standard-library
+doc: gem5 documentation
+permalink: /documentation/gem5-stdlib/x86-full-system-tutorial
+author: Bobby R. Bruce
+---
+
+Building an x86 full-system simulation with the gem5 standard library
+=====================================================================
+
+One of the key ideas behind the gem5 standard library is to allow users to simulate, big, complex systems, with minimal effort.
+This is done by making sensible assumptions about the nature of the system to simulate and connecting components in a manner which "makes sense".
+While this takes away some flexibility, it massively simplifies simulating typical hardware setups in gem5.
+
+In this tutorial we will build an X86 simulation, capable of running a full-system simulation, booting an Ubuntu operating system, and running a script.
+This system will utilize gem5's ability to switch cores, allowing booting of the operating system in KVM mode and switching to a detailed CPU model to run the script, and utilize a MESI Two Level Ruby cache hierarchy in a dual-core setup.
+Without using the gem5 library this would take several hundred lines of Python, forcing the user to specify details such as every IO component and exactly how the cache hierarchy is setup.
+Here, we will demonstrate how simple this task can be with using the gem5 standard library.
+
+As we focus on X86, we must must build the gem5 X86 binary:
+
+```sh
+scons build/X86/gem5.opt -j <number of threads>
+```
+
+To start, create a new Python file.
+We will refer to this as `x86-ubuntu-run.py`.
+
+To begin we add our import statements:
+
+```python
+from gem5.utils.requires import requires
+from gem5.components.boards.x86_board import X86Board
+from gem5.components.memory.single_channel import SingleChannelDDR3_1600
+from gem5.components.cachehierarchies.ruby.mesi_two_level_cache_hierarchy import MESITwoLevelCacheHierarchy
+from gem5.components.processors.simple_switchable_processor import SimpleSwitchableProcessor
+from gem5.coherence_protocol import CoherenceProtocol
+from gem5.isas import ISA
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
+from gem5.simulate.exit_event import ExitEvent
+```
+
+As in other Python scripts, these are simply classes/functions needed in our script.
+They are all included as part of the gem5 binary and therefore do not need to obtained elsewhere.
+
+A good start is to use the `requires` function to specify what kind of gem5 binary/setup is required to run the script:
+
+```
+requires(
+    isa_required=ISA.X86,
+    coherence_protocol_required=CoherenceProtocol.MESI_TWO_LEVEL,
+    kvm_required=True,
+)
+```
+
+Here we state that we need gem5 compiled to run the X86 ISA and support the MESI Two Level protocol.
+We also require the host system to have KVM.
+**NOTE: Please ensure your host system supports KVM. If your system does not please remove the `kvm_required` check here**.
+
+This `requires` call is not required but provides a good safety net to those running the script.
+Errors that occur due to incompatible gem5 binaries may not make much sense otherwise.
+
+Next we start specifying the components in our system.
+We start with the _cache hierarchy_:
+
+```python
+cache_hierarchy = MESITwoLevelCacheHierarchy(
+    l1d_size="16kB",
+    l1d_assoc=8,
+    l1i_size="16kB",
+    l1i_assoc=8,
+    l2_size="256kB",
+    l2_assoc=16,
+    num_l2_banks=1,
+)
+```
+
+Here we setup a MESI Two Level (ruby) cache hierarchy.
+Via the constructor we set the L1 data cache and L1 instruction cache to 16kB, and the L2 cache to 256kB.
+
+Next we setup the `memory system`:
+
+```python
+memory = SingleChannelDDR3_1600(size="2GiB")
+```
+
+This is quite simple and should be intuitive: A single channel DDR3 1600 setup of size 2GiB.
+**Note:** by default the `SingleChannelDDR3_1600` component has a size of 8GiB.
+However, due to [a known limitation with the X86Board](https://gem5.atlassian.net/browse/GEM5-1142), we cannot use a memory system greater than 3GiB.
+We therefore must set the size.
+
+Next we setup the _processor_:
+
+```python
+processor = SimpleSwitchableProcessor(
+    starting_core_type=CPUTypes.KVM,
+    switch_core_type=CPUTypes.TIMING,
+    num_cores=2,
+)
+```
+
+Here we are utilizing the gem5 standard library's special `SimpleSwitchableProcessor`.
+This processor can be used for simulations in which a user wants to switch out one type of core for another during a simulation.
+The `starting_core_type` parameter specifies which CPU type to start a simulation with.
+In this case a KVM core.
+**(Note: If your host system does not support KVM, this simulation will not run. You must change this to another CPU type, such as `CPUTypes.ATOMIC`)**
+The `switch_core_type` parameter specifies which CPU type to switch to in a simulation.
+In this case we'll be switching from KVM cores to a TIMING cores.
+The final parameter, `num_cores`, specifies the number of cores within the processor.
+
+With this processor a user can call `processor.switch()` to switch to and from the starting cores and the switch cores, which we will demonstrate later on in this tutorial.
+
+Next we add these components to the _board_:
+
+```python
+board = X86Board(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+```
+
+Here we use the `X86Board`.
+This is a board used to simulate a typical X86 system in full-system mode.
+As a minimum, the board needs the `clk_freq`, `processor`, `memory`, and `cache_hierarchy` parameters specified.
+This finalizes our system design.
+
+Now we set the workload to run on the system:
+
+```python
+command = "m5 exit;" \
+        + "echo 'This is running on Timing CPU cores.';" \
+        + "sleep 1;" \
+        + "m5 exit;"
+
+board.set_kernel_disk_workload(
+    kernel=Resource("x86-linux-kernel-5.4.49",),
+    disk_image=Resource("x86-ubuntu-18.04-img"),
+    readfile_contents=command,
+)
+```
+
+The `X86Board`'s `set_kernel_disk_workload` function requires a `kernel` and `disk_image` to be set.
+Both these are obtainable from the gem5 resources repository.
+Therefore, via the `Resource` class, we specify `x86-linux-kernel-5.4.49` for the Kernel (a Linux kernel, version 5.4.49, compiled to X86) and `x86-ubuntu-18.04-img` for the disk image (a disk image containing Ubuntu 18.04, for X86).
+The `Resource` class will automatically retrieve these resources if they are not already present on the host system.
+**Note: If a user wishes to use their own resource (that is, a resource not prebuilt as part of gem5-resources), they may use the `CustomResource` and `CustomDiskImageResource` classes, included in the [resource module](https://gem5.googlesource.com/public/gem5/+/refs/heads/stable/src/python/gem5/resources/resource.py))**.
+
+The `x86-ubuntu-18.04-img` has been designed to boot the OS, automatically login, and run `m5 readfile`.
+The `m5 readfile` will read a file and execute it.
+The contents of this file are specified via the `readfile_contents` parameter.
+Therefore the value of` readfile_contents` will be executed on system startup.
+**(Note: `readfile_contents` is an optional argument. If it is not specified in `set_kernel_disk_workload` the simulation will exit after boot)**.
+
+In this tutorial the script first runs `m5 exit`.
+This temporarily exits the simulation allowing us to switch the CPUs from `KVM` to `TIMING`.
+Then, when the simulation is resumed, the echo and sleep statements are executed (on the `TIMING` CPUs) and `m5 exit` is called again, thus exiting and completing the simulation.
+Users may inspect `m5out/system.pc.com_1.device` to see the echo output.
+
+Finally, we specify how the simulation is to be run with the following:
+
+```python
+simulator = Simulator(
+    board=board,
+    on_exit_event={
+        ExitEvent.EXIT : (func() for func in [processor.switch]),
+    },
+)
+simulator.run()
+```
+
+The important thing to note here is the `on_exit_event` argument.
+Here we can override default behavior.
+The `m5 exit` command triggers an `EXIT` exit event in the `Simulator` module.
+By default this exits the simulation run completely.
+In our case we want the first `m5 exit` call to switch processors from KVM to TIMING cores.
+
+The `on_exit_event` parameter is a Python dictionary of exit events and [Python generators](https://wiki.python.org/moin/Generators).
+In this tutorial we are setting `ExitEvent.Exit` to the generator `(func() for func in [processor.switch])`.
+This means the `processor.switch` function is called on the first yield of the generator (that is, on the first instance of `m5 exit`).
+After this the generator is exhausted and the `Simulator` module will return to the default `Exit` exit event behavior.
+
+
+This completes the setup of our script, to execute the script we run:
+
+```
+./build/X86/gem5.opt x86-ubuntu-run.py
+```
+
+You can see the output of the simulator in `m5out/system.pc.com_1.device`.
+
+Below is the configuration script in full.
+It mirrors closely the example script at `configs/example/gem5_library/x86-ubuntu-run.py` in the gem5 repository.
+
+```python
+from gem5.utils.requires import requires
+from gem5.components.boards.x86_board import X86Board
+from gem5.components.memory.single_channel import SingleChannelDDR3_1600
+from gem5.components.cachehierarchies.ruby.mesi_two_level_cache_hierarchy import (MESITwoLevelCacheHierarchy,)
+from gem5.components.processors.simple_switchable_processor import SimpleSwitchableProcessor
+from gem5.coherence_protocol import CoherenceProtocol
+from gem5.isas import ISA
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
+from gem5.simulate.exit_event import ExitEvent
+
+# This runs a check to ensure the gem5 binary is compiled to X86 and supports
+# the MESI Two Level coherence protocol.
+requires(
+    isa_required=ISA.X86,
+    coherence_protocol_required=CoherenceProtocol.MESI_TWO_LEVEL,
+    kvm_required=True,
+)
+
+# Here we setup a MESI Two Level Cache Hierarchy.
+cache_hierarchy = MESITwoLevelCacheHierarchy(
+    l1d_size="16kB",
+    l1d_assoc=8,
+    l1i_size="16kB",
+    l1i_assoc=8,
+    l2_size="256kB",
+    l2_assoc=16,
+    num_l2_banks=1,
+)
+
+# Setup the system memory.
+# Note, by default DDR3_1600 defaults to a size of 8GiB. However, a current
+# limitation with the X86 board is it can only accept memory systems up to 3GB.
+# As such, we must fix the size.
+memory = SingleChannelDDR3_1600("2GiB")
+
+# Here we setup the processor. This is a special switchable processor in which
+# a starting core type and a switch core type must be specified. Once a
+# configuration is instantiated a user may call `processor.switch()` to switch
+# from the starting core types to the switch core types. In this simulation
+# we start with KVM cores to simulate the OS boot, then switch to the Timing
+# cores for the command we wish to run after boot.
+processor = SimpleSwitchableProcessor(
+    starting_core_type=CPUTypes.KVM,
+    switch_core_type=CPUTypes.TIMING,
+    num_cores=2,
+)
+
+# Here we setup the board. The X86Board allows for Full-System X86 simulations.
+board = X86Board(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+
+# This is the command to run after the system has booted. The first `m5 exit`
+# will stop the simulation so we can switch the CPU cores from KVM to timing
+# and continue the simulation to run the echo command, sleep for a second,
+# then, again, call `m5 exit` to terminate the simulation. After simulation
+# has ended you may inspect `m5out/system.pc.com_1.device` to see the echo
+# output.
+command = "m5 exit;" \
+        + "echo 'This is running on Timing CPU cores.';" \
+        + "sleep 1;" \
+        + "m5 exit;"
+
+# Here we set the Full System workload.
+# The `set_workload` function for the X86Board takes a kernel, a disk image,
+# and, optionally, a the contents of the "readfile". In the case of the
+# "x86-ubuntu-18.04-img", a file to be executed as a script after booting the
+# system.
+board.set_kernel_disk_workload(
+    kernel=Resource("x86-linux-kernel-5.4.49",),
+    disk_image=Resource("x86-ubuntu-18.04-img"),
+    readfile_contents=command,
+)
+
+simulator = Simulator(
+    board=board,
+    on_exit_event={
+        # Here we want override the default behavior for the first m5 exit
+        # exit event. Instead of exiting the simulator, we just want to
+        # switch the processor. The 2nd 'm5 exit' after will revert to using
+        # default behavior where the simulator run will exit.
+        ExitEvent.EXIT : (func() for func in [processor.switch]),
+    },
+)
+simulator.run()
+```
+
+To recap what we learned in this tutorial:
+
+* The `requires` function can be used to specify the gem5 and host requirements for a script.
+* The `SimpleSwitchableProcessor` can be used to create a setup in which cores can be switched out for others.
+* The `X86Board` can be used to setup full-system simulations.
+Its `set_kernel_disk_workload` is used specify the kernel and disk image to use.
+* The `set_kernel_disk_work` accepts a `readfile_contents` argument.
+This is used to set the contents of the file to be read via gem5's `m5 readfile` function.
+With the `x86-ubuntu-18.04-img` this is processed as a script to be executed after the system boot is complete.
+* The `Simulator` module allows for the overriding of exit events using Python generators.
diff --git a/_pages/documentation/gem5-stdlib/3-tutorial-developing-own-components.md b/_pages/documentation/gem5-stdlib/3-tutorial-developing-own-components.md
new file mode 100644
index 0000000..772b555
--- /dev/null
+++ b/_pages/documentation/gem5-stdlib/3-tutorial-developing-own-components.md
@@ -0,0 +1,256 @@
+---
+layout: documentation
+title: Developing Your Own Components Tutorial
+parent: gem5-standard-library
+doc: gem5 documentation
+permalink: /documentation/gem5-stdlib/develop-own-components-tutorial
+author: Bobby R. Bruce
+---
+
+Developing your own gem5 standard library components
+===========================================
+
+![](/assets/img/stdlib/gem5-components-design.png)
+
+The above diagram shows the basic design of the gem5 library components.
+There are four important abstract classes: `AbstractBoard`, `AbstractProcessor`, `AbstractMemorySystem`, and `AbstractCacheHierarchy`.
+Every gem5 component inherits from one of these to be a gem5 component usable in a design.
+The `AbstractBoard` must be constructed by specifying an `AbstractProcessor`, `AbstractMemorySystem`, and an `AbstractCacheHierarchy`.
+With this design any board may use any combination of components which inherit from `AbstractProcessor`, `AbstractMemorySystem`, and `AbstractCacheHierarchy`.
+For example, using the image as a guide, we can add a `SimpleProcessor`, `SingleChannelDDR3_1600` and a `PrivateL1PrivateL2CacheHierarchy` to an `X86Board`.
+If we desire, we can swap out the `PrivateL1PrivateL2CacheHierarchy` for another class which inherits from `AbstractCacheHierarchy`.
+
+In this tutorial we will imagine a user wishes to create a new cache hierarchy.
+As you can see from the diagram, there are two subclasses which inherit from `AbstractCacheHierarchy`: `AbstractRubyCacheHierarchy` and `AbstractClassicCacheHierarchy`.
+While you _can_ inherit directly from `AbstractCacheHierarchy`, we recommend inheriting from the subclasses (depending on whether you wish to develop a ruby or classic cache hierarchy setup).
+We will inherit from the `AbstractClassicCacheHierarchy` class to create a classic cache setup.
+
+To begin, we should create a new Python class which inherits from the `AbstractClassicCacheHierarchy`.
+In this example we will call this `UniqueCacheHierarchy`, contained within a file `unique_cache_hierarchy.py`:
+
+
+```python
+from gem5.components.cachehierarchies.classic.abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy
+from gem5.components.boards.abstract_board import AbstractBoard
+
+from m5.objects import Port
+
+class UniqueCacheHierarchy(AbstractClassicCacheHierarchy):
+
+
+    def __init__() -> None:
+        AbstractClassicCacheHierarchy.__init__(self=self)
+
+    def get_mem_side_port(self) -> Port:
+        pass
+
+    def get_cpu_side_port(self) -> Port:
+        pass
+
+    def incorporate_cache(self, board: AbstractBoard) -> None:
+        pass
+```
+
+As with every abstract base class, there are virtual functions which must be implemented.
+Once implemented the `UniqueCacheHierarchy` can be used in simulations.
+The `get_mem_side_port` and `get_cpu_side_port` are declared in the [AbstractClassicCacheHierarchy](https://gem5.googlesource.com/public/gem5/+/refs/heads/stable/src/python/gem5/components/cachehierarchies/classic/abstract_classic_cache_hierarchy.py), while `incorporate_cache` is declared in the [AbstractCacheHierarchy](https://gem5.googlesource.com/public/gem5/+/refs/heads/stable/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py)
+
+The `get_mem_side_port` and `get_cpu_side_port` functions return a `Port` each.
+As their name suggests, these are ports used by the board to access the cache hierarchy from the memory side and the cpu side.
+These must be specified for all classic cache hierarchy setups.
+
+The `incorporate_cache` function is the function which is called to incorporate the cache into the board.
+The contents of this function will vary between cache hierarchy setups but will typically inspect the board it is connected to, and use the board's API to connect the cache hierarchy.
+
+In this example we assume the user is looking to implement a private L1 cache hierarchy, consisting of a data cache and instruction cache for each CPU core.
+This has actually already been implemented in the gem5 stdlib as the [PrivateL1CacheHierarchy](https://gem5.googlesource.com/public/gem5/+/refs/heads/stable/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py), but for this example we shall duplicate the effort.
+
+First we start by implementing the `get_mem_side_port` and `get_cpu_side_port` functions:
+
+```python
+from gem5.components.cachehierarchies.classic.abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy
+from gem5.components.boards.abstract_board import AbstractBoard
+
+from m5.objects import Port, SystemXBar, BadAddr
+
+class UniqueCacheHierarchy(AbstractClassicCacheHierarchy):
+
+    def __init__(self) -> None:
+        AbstractClassicCacheHierarchy.__init__(self=self)
+        self.membus =  SystemXBar(width=64)
+        self.membus.badaddr_responder = BadAddr()
+        self.membus.default = self.membus.badaddr_responder.pio
+
+    def get_mem_side_port(self) -> Port:
+        return self.membus.mem_side_ports
+
+    def get_cpu_side_port(self) -> Port:
+        return self.membus.cpu_side_ports
+
+    def incorporate_cache(self, board: AbstractBoard) -> None:
+        pass
+```
+
+Here we have used a simple memory bus.
+
+Next, we implement the `incorporate_cache` function:
+
+```python
+from gem5.components.cachehierarchies.classic.abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy
+from gem5.components.cachehierarchies.classic.caches.l1dcache import L1DCache
+from gem5.components.cachehierarchies.classic.caches.l1icache import L1ICache
+from gem5.components.cachehierarchies.classic.caches.mmu_cache import MMUCache
+from gem5.components.boards.abstract_board import AbstractBoard
+
+from m5.objects import Port, SystemXBar, BadAddr, Cache
+
+class UniqueCacheHierarchy(AbstractClassicCacheHierarchy):
+
+    def __init__(self) -> None:
+        AbstractClassicCacheHierarchy.__init__(self=self)
+        self.membus =  SystemXBar(width=64)
+        self.membus.badaddr_responder = BadAddr()
+        self.membus.default = self.membus.badaddr_responder.pio
+
+    def get_mem_side_port(self) -> Port:
+        return self.membus.mem_side_ports
+
+    def get_cpu_side_port(self) -> Port:
+        return self.membus.cpu_side_ports
+
+    def incorporate_cache(self, board: AbstractBoard) -> None:
+        # Set up the system port for functional access from the simulator.
+        board.connect_system_port(self.membus.cpu_side_ports)
+
+        for cntr in board.get_memory().get_memory_controllers():
+            cntr.port = self.membus.mem_side_ports
+
+        self.l1icaches = [
+            L1ICache(size="32KiB")
+            for i in range(board.get_processor().get_num_cores())
+        ]
+
+        self.l1dcaches = [
+            L1DCache(size="32KiB")
+            for i in range(board.get_processor().get_num_cores())
+        ]
+        # ITLB Page walk caches
+        self.iptw_caches = [
+            MMUCache(size="8KiB") for _ in range(board.get_processor().get_num_cores())
+        ]
+        # DTLB Page walk caches
+        self.dptw_caches = [
+            MMUCache(size="8KiB") for _ in range(board.get_processor().get_num_cores())
+        ]
+
+        if board.has_coherent_io():
+            self._setup_io_cache(board)
+
+        for i, cpu in enumerate(board.get_processor().get_cores()):
+
+            cpu.connect_icache(self.l1icaches[i].cpu_side)
+            cpu.connect_dcache(self.l1dcaches[i].cpu_side)
+
+            self.l1icaches[i].mem_side = self.membus.cpu_side_ports
+            self.l1dcaches[i].mem_side = self.membus.cpu_side_ports
+
+            self.iptw_caches[i].mem_side = self.membus.cpu_side_ports
+            self.dptw_caches[i].mem_side = self.membus.cpu_side_ports
+
+            cpu.connect_walker_ports(
+                self.iptw_caches[i].cpu_side, self.dptw_caches[i].cpu_side
+            )
+
+            int_req_port = self.membus.mem_side_ports
+            int_resp_port = self.membus.cpu_side_ports
+            cpu.connect_interrupt(int_req_port, int_resp_port)
+
+    def _setup_io_cache(self, board: AbstractBoard) -> None:
+        """Create a cache for coherent I/O connections"""
+        self.iocache = Cache(
+            assoc=8,
+            tag_latency=50,
+            data_latency=50,
+            response_latency=50,
+            mshrs=20,
+            size="1kB",
+            tgts_per_mshr=12,
+            addr_ranges=board.mem_ranges,
+        )
+        self.iocache.mem_side = self.membus.cpu_side_ports
+        self.iocache.cpu_side = board.get_mem_side_coherent_io_port()
+```
+
+This completes the code we'd need to create our own cache hierarchy.
+
+To use this code, a user can import it as they would any other Python module.
+
+Compiling your component into the gem5 standard library
+-------------------------------------------------------
+
+The gem5 standard library code resides in `src/python/gem5`.
+The basic directory structure is as follows:
+
+```
+gem5/
+    components/                 # All the components to build the system to simulate.
+        boards/                 # The boards, typically broken down by ISA target.
+            experimental/       # Experimental boards.
+        cachehierarchies/       # The Cache Hierarchy components.
+            chi/                # CHI protocol cache hierarchies.
+            classic/            # Classic cache hierarchies.
+            ruby/               # Ruby cache hierarchies.
+        memory/                 # Memory systems.
+        processors/             # Processors.
+    prebuilt/                   # Prebuilt systems, ready to use.
+        demo/                   # Prebuilt System for demonstrations. (not be representative of real-world targets).
+    resources/                  # Utilities used for referencing and obtaining gem5-resources.
+    simulate/                   # A package for the automated running of gem5 simulations.
+    utils/                      # General utilities.
+```
+
+We recommend putting the `unique_cache_hierarchy.py` in `src/python/gem5/components/cachehierarchies/classic/`.
+
+From then you need to add the following line to `src/python/SConscript`:
+
+```
+PySource('gem5.components.cachehierarchies.classic',
+    'gem5/components/cachehierarchies/classic/unique_cache_hierarchy.py')
+```
+
+Then, when you recompile the gem5 binary, the `UniqueCacheHierarchy` class will be included.
+To use it in your own scripts you need only include it:
+
+```python
+from gem5.components.cachehierarchies.classic.unique_cache_hierarchy import UniqueCacheHierarchy
+
+...
+
+cache_hierarchy = UniqueCacheHierarchy()
+
+...
+
+```
+
+Contributing your component to the gem5 stdlib
+----------------------------------------------
+
+If you believe your addition to the gem5 stdlib would be beneficial to the gem5 community, you may submit it as a patch.
+Please follow our [Contributing Guidelines](/contributing) if you have not contributed to gem5 before or need a reminder on our procedures.
+
+In addition to our normal contribution guidelines, we strongly advise you do the following to your stdlib contribution:
+
+* **Add Documentation**: Classes and methods should be documented using [reStructured text](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html).
+Please look over other source code in the stdlib to see how this is typically done.
+* **Use Python Typing**: Utilize the [Python typing module](https://docs.python.org/3/library/typing.html) to specify parameter and method return types.
+* **Use relative imports**: Within the gem5 stdlib, relative imports should be used to reference other modules/package in the stdlib (i.e., that contained in `src/python/gem5`).
+* **Format using black**: Please format your Python code with [Python black](https://pypi.org/project/black/), with 79 max line widths: `black --line-length=79 <file/directory>`.
+**Note**: Python black does not always enforce line lengths.
+For example, it will not reduce string lengths.
+You may have to manually reduce the length of some lines.
+
+
+Code will be reviewed via our [Gerrit code review system](https://gem5-review.googlesource.com/) like all other contributions.
+We would, however, emphasize that we will not accept patches to the library for simply being functional and tested;
+we require some persuasion that the contribution improves the library and benefits the community.
+For example, niche components may not be incorporated if they are seen to be low utility while increasing the library's maintenance overhead.
diff --git a/assets/img/stdlib/gem5-components-design.png b/assets/img/stdlib/gem5-components-design.png
new file mode 100644
index 0000000..610ad67
--- /dev/null
+++ b/assets/img/stdlib/gem5-components-design.png
Binary files differ