Initial commit of MPACT-Sim codelab exercises
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5503eaf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+bazel-bin
+bazel-mpact-sim-codelabs
+bazel-out
+bazel-testlogs
+external
+.vscode
+.bazelrc
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..87eb757
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,31 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Top level BUILD file for mpact_sim
+
+load("@rules_license//rules:license.bzl", "license")
+
+package(
+    default_applicable_licenses = [":license"],
+    default_visibility = ["//visibility:public"],
+)
+
+license(
+    name = "license",
+    package_name = "mpact-riscv",
+)
+
+licenses(["notice"])
+
+exports_files(["LICENSE"])
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..fb086f1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,96 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of
+experience, education, socio-economic status, nationality, personal appearance,
+race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+*   Using welcoming and inclusive language
+*   Being respectful of differing viewpoints and experiences
+*   Gracefully accepting constructive criticism
+*   Focusing on what is best for the community
+*   Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+*   The use of sexualized language or imagery and unwelcome sexual attention or
+    advances
+*   Trolling, insulting/derogatory comments, and personal or political attacks
+*   Public or private harassment
+*   Publishing others' private information, such as a physical or electronic
+    address, without explicit permission
+*   Other conduct which could reasonably be considered inappropriate in a
+    professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, or to ban temporarily or permanently any
+contributor for other behaviors that they deem inappropriate, threatening,
+offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+This Code of Conduct also applies outside the project spaces when the Project
+Steward has a reasonable belief that an individual's behavior may have a
+negative impact on the project or its community.
+
+## Conflict Resolution
+
+We do not believe that all conflict is bad; healthy debate and disagreement
+often yield positive results. However, it is never okay to be disrespectful or
+to engage in behavior that violates the project’s code of conduct.
+
+If you see someone violating the code of conduct, you are encouraged to address
+the behavior directly with those involved. Many issues can be resolved quickly
+and easily, and this gives people more control over the outcome of their
+dispute. If you are unable to resolve the matter for any reason, or if the
+behavior is threatening or harassing, report it. We are dedicated to providing
+an environment where participants feel welcome and safe.
+
+Reports should be directed to *[Tor Jeremiassen](torerik@google.com)*, the
+Project Steward(s) for *[MPACT-Sim](https://mpact.googlesource.com)*. It is the
+Project Steward’s duty to receive and address reported violations of the code
+of conduct. They will then work with a committee consisting of representatives
+from the Open Source Programs Office and the Google Open Source Strategy team.
+If for any reason you are uncomfortable reaching out to the Project Steward,
+please email opensource@google.com.
+
+We will investigate every complaint, but you may not receive a direct response.
+We will use our discretion in determining when and how to follow up on reported
+incidents, which may range from not taking action to permanent expulsion from
+the project and project-sponsored spaces. We will notify the accused of the
+report and provide them an opportunity to discuss it before any action is taken.
+The identity of the reporter will be omitted from the details of the report
+supplied to the accused. In potentially harmful situations, such as ongoing
+harassment or threats to anyone's safety, we may take action without notice.
+
+## Attribution
+
+This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
+available at
+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+Note: A version of this file is also available in the
+[New Project repo](https://github.com/google/new-project/blob/master/docs/code-of-conduct.md).
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..47634bc
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project.
+
+## Before you begin
+
+### Sign our Contributor License Agreement
+
+Contributions to this project must be accompanied by a
+[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
+You (or your employer) retain the copyright to your contribution; this simply
+gives us permission to use and redistribute your contributions as part of the
+project.
+
+If you or your current employer have already signed the Google CLA (even if it
+was for a different project), you probably don't need to do it again.
+
+Visit <https://cla.developers.google.com/> to see your current agreements or to
+sign a new one.
+
+### Review our Community Guidelines
+
+This project follows [Google's Open Source Community
+Guidelines](https://opensource.google/conduct/).
+
+## Contribution process
+
+### Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c7014aa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+# MPACT-Sim Codelabs
+
+This repoistory contains the codelab exercises for the MPACT-Sim codelabs.
+These codelabs provide a tutorials on how to get started using MPACT-Sim to
+create instruction set simulators.
+
+The codelabs guide you to write most of the required code to build an
+instruction set simulator for the RiscV 32I instruction set (integer
+instructions only). 
+
+There are four directories that contain code and build targets for the coding
+exercises :
+
+*   `riscv_isa_decoder` <br />
+    This directory contains the skeleton and solution for writing the
+    description file for the encoding independent instruction decoder.
+
+*   `riscv_bin_decoder` <br />
+    This directory contains the skeleton and solution for writing the
+    description file for the binary decoder.
+
+*   `riscv_semantic_functions` <br />
+    This directory contains the skeleton and solution for writing the
+    semantic functions that implement the instructions in the codelab.
+
+*   `riscv_full_decoder` <br />
+    This directory contains the skeleton and solution for writing the full
+    instruction decoder that integrates the decoders that were generated in
+    prior exercises.
+
+Additionally, there is a directory `other`, that contains support code that
+is not part of the exercises, but allows a finished simulator to be built
+and executed. A sample "Hellow World" executable is also provided.
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..8f1236c
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,4 @@
+To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz).
+We use g.co/vulnz for our intake, and do coordination and disclosure here on
+GitHub (including using GitHub Security Advisory). The Google Security Team will
+respond within 5 working days of your report on g.co/vulnz.
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..932e481
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,157 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+workspace(name = "com_google_mpact-sim-codelabs")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+
+# Additional bazel rules.
+http_archive(
+    name = "bazel_skylib",
+    sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7",
+    urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz"],
+)
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+bazel_skylib_workspace()
+
+# Google Absail.
+http_archive(
+    name = "com_google_absl",
+    sha256 = "3ea49a7d97421b88a8c48a0de16c16048e17725c7ec0f1d3ea2683a2a75adc21",
+    urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.0.tar.gz"],
+    strip_prefix="abseil-cpp-20230125.0",
+)
+
+# Google protobuf.
+http_archive(
+    name = "com_google_protobuf",
+    sha256 = "4eab9b524aa5913c6fffb20b2a8abf5ef7f95a80bc0701f3a6dbb4c607f73460",
+    urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protobuf-cpp-3.21.12.tar.gz"],
+    strip_prefix="protobuf-3.21.12",
+)
+
+# Google re2
+http_archive(
+    name = "com_google_re2",
+    sha256 = "7a9a4824958586980926a300b4717202485c4b4115ac031822e29aa4ef207e48",
+    urls = ["https://github.com/google/re2/archive/refs/tags/2023-03-01.tar.gz"],
+    strip_prefix = "re2-2023-03-01"
+)
+
+# ELFIO header based library.
+http_archive(
+    build_file = "BUILD.elfio",
+    name = "com_github_serge1_elfio",
+    strip_prefix = "elfio-3.9",
+    sha256 = "767b269063fc35aba6d361139f830aa91c45dc6b77942f082666876c1aa0be0f",
+    urls = ["https://github.com/serge1/ELFIO/releases/download/Release_3.9/elfio-3.9.tar.gz"],
+)
+
+# Bazel rules for proto.
+http_archive(
+    name = "rules_proto_grpc",
+    sha256 = "fb7fc7a3c19a92b2f15ed7c4ffb2983e956625c1436f57a3430b897ba9864059",
+    strip_prefix = "rules_proto_grpc-4.3.0",
+    urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/4.3.0.tar.gz"],
+)
+
+# Google test.
+http_archive(
+    name = "com_google_googletest",
+    strip_prefix = "googletest-1.13.0",
+    sha256 = "ad7fdba11ea011c1d925b3289cf4af2c66a352e18d4c7264392fead75e919363",
+    urls = ["https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz"],
+)
+
+ALL_CONTENT = """\
+filegroup(
+  name = "all_srcs",
+  srcs = glob(["**"]),
+  visibility = ["//visibility:public"],
+)
+"""
+
+# Antlr4 c++ runtime.
+http_archive(
+    name = "org_antlr4_cpp_runtime",
+    build_file = "BUILD.antlr4",
+    sha256 = "8018c335316e61bb768e5bd4a743a9303070af4e1a8577fa902cd053c17249da",
+    urls = ["https://www.antlr.org/download/antlr4-cpp-runtime-4.11.1-source.zip"],
+)
+
+# Antlr4 tool (java).
+http_file(
+    name = "org_antlr_tool",
+    sha256 = "62975e192b4af2622b72b5f0131553ee3cbce97f76dc2a41632dcc55e25473e1",
+    url = "https://www.antlr.org/download/antlr-4.11.1-complete.jar",
+)
+
+# Additional rules for building non-bazel projects. Antlr4 builds using cmake.
+http_archive(
+    name = "rules_foreign_cc",
+    sha256 = "2a4d07cd64b0719b39a7c12218a3e507672b82a97b98c6a89d38565894cf7c51",
+    strip_prefix = "rules_foreign_cc-0.9.0",
+    url = "https://github.com/bazelbuild/rules_foreign_cc/archive/refs/tags/0.9.0.tar.gz",
+)
+
+# MPACT-Sim repo
+git_repository(
+    name = "mpact-sim",
+    branch = "main",
+    remote = "https://mpact.googlesource.com/mpact-sim",
+)
+
+# MPACT-RiscV repo
+git_repository(
+    name = "mpact-riscv",
+    branch = "main",
+    remote = "https://mpact.googlesource.com/mpact-riscv",
+)
+
+# Binding to tool targets in mpact-sim. This is required for the macros
+# in mpact_sim_isa.bzl to work properly when generating code for the
+# decoders. These create the //external:decoder_gen and
+# //external:bin_format_gen aliases used in mpact_sim_isa.bzl.
+bind(
+    name = "decoder_gen",
+    actual = "@mpact-sim//mpact/sim/decoder:decoder_gen",
+)
+
+bind(
+    name = "bin_format_gen",
+    actual = "@mpact-sim//mpact/sim/decoder:bin_format_gen",
+)
+
+# Additional rules for licenses
+http_archive(
+    name = "rules_license",
+    sha256 = "6157e1e68378532d0241ecd15d3c45f6e5cfd98fc10846045509fb2a7cc9e381",
+    url = "https://github.com/bazelbuild/rules_license/releases/download/0.0.4/rules_license-0.0.4.tar.gz"
+)
+
+load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies")
+rules_foreign_cc_dependencies()
+
+load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains", "rules_proto_grpc_repos")
+rules_proto_grpc_toolchains()
+rules_proto_grpc_repos()
+
+load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
+rules_proto_dependencies()
+rules_proto_toolchains()
+
+load("@rules_license//:deps.bzl", "rules_license_dependencies")
+rules_license_dependencies()
diff --git a/other/BUILD b/other/BUILD
new file mode 100644
index 0000000..4ed5735
--- /dev/null
+++ b/other/BUILD
@@ -0,0 +1,71 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# RiscV sim library for use with codelabs
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "rv32i_top",
+    srcs = [
+        "rv32i_top.cc",
+    ],
+    hdrs = [
+        "rv32i_top.h",
+    ],
+    deps = [
+        "//riscv_full_decoder/solution:riscv32i_decoder",
+        "//riscv_isa_decoder/solution:riscv32i_isa",
+        "@com_google_absl//absl/functional:bind_front",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings",
+        "@mpact-riscv//riscv:riscv32_htif_semihost",
+        "@mpact-riscv//riscv:riscv_breakpoint",
+        "@mpact-riscv//riscv:riscv_state",
+        "@mpact-sim//mpact/sim/generic:component",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/generic:core_debug_interface",
+        "@mpact-sim//mpact/sim/generic:decode_cache",
+        "@mpact-sim//mpact/sim/util/memory",
+    ],
+)
+
+cc_binary(
+    name = "rv32i_sim",
+    srcs = [
+        "rv32i_sim.cc",
+    ],
+    copts = ["-O3"],
+    args = ["other/hello_rv32i.elf"],
+    data = [
+        "hello_rv32i.elf",
+    ],
+    deps = [
+        ":rv32i_top",
+        "@com_google_absl//absl/flags:flag",
+        "@com_google_absl//absl/flags:parse",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/strings",
+        "@mpact-riscv//riscv:debug_command_shell",
+        "@mpact-riscv//riscv:riscv32_htif_semihost",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/proto:component_data_cc_proto",
+        "@mpact-sim//mpact/sim/util/memory",
+        "@mpact-sim//mpact/sim/util/program_loader:elf_loader",
+    ],
+)
diff --git a/other/hello_rv32i.elf b/other/hello_rv32i.elf
new file mode 100755
index 0000000..c3dbfda
--- /dev/null
+++ b/other/hello_rv32i.elf
Binary files differ
diff --git a/other/rv32i_sim.cc b/other/rv32i_sim.cc
new file mode 100644
index 0000000..8584985
--- /dev/null
+++ b/other/rv32i_sim.cc
@@ -0,0 +1,172 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <signal.h>
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/log/log.h"
+#include "absl/log/check.h"
+#include "absl/strings/str_cat.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/proto/component_data.pb.h"
+#include "mpact/sim/util/memory/memory_watcher.h"
+#include "mpact/sim/util/program_loader/elf_program_loader.h"
+#include "other/rv32i_top.h"
+#include "riscv/debug_command_shell.h"
+#include "riscv/riscv32_htif_semihost.h"
+#include "src/google/protobuf/text_format.h"
+
+using ::mpact::sim::proto::ComponentData;
+using ::mpact::sim::riscv::RiscV32HtifSemiHost;
+using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange;
+
+// Flags for specifying interactive mode.
+ABSL_FLAG(bool, i, false, "Interactive mode");
+ABSL_FLAG(bool, interactive, false, "Interactive mode");
+// Flag for destination directory of proto file.
+ABSL_FLAG(std::string, output_dir, "", "Output directory");
+
+// Static pointer to the top instance. Used by the control-C handler.
+static mpact::sim::codelab::RV32ITop *top = nullptr;
+
+// Control-c handler to interrupt any running simulation.
+static void sim_sigint_handler(int arg) {
+  if (top != nullptr) {
+    (void)top->Halt();
+    return;
+  } else {
+    exit(-1);
+  }
+}
+
+// Helper function to get the magic semihosting addresses from the loader.
+static bool GetMagicAddresses(mpact::sim::util::ElfProgramLoader *loader,
+                              RiscV32HtifSemiHost::SemiHostAddresses *magic) {
+  auto result = loader->GetSymbol("tohost_ready");
+  if (!result.ok()) return false;
+  magic->tohost_ready = result.value().first;
+
+  result = loader->GetSymbol("tohost");
+  if (!result.ok()) return false;
+  magic->tohost = result.value().first;
+
+  result = loader->GetSymbol("fromhost_ready");
+  if (!result.ok()) return false;
+  magic->fromhost_ready = result.value().first;
+
+  result = loader->GetSymbol("fromhost");
+  if (!result.ok()) return false;
+  magic->fromhost = result.value().first;
+
+  return true;
+}
+
+int main(int argc, char **argv) {
+  auto arg_vec = absl::ParseCommandLine(argc, argv);
+
+  if (arg_vec.size() > 2) {
+    std::cerr << "Only a single input file allowed" << std::endl;
+    return -1;
+  }
+  std::string full_file_name = arg_vec[1];
+  std::string file_name =
+      full_file_name.substr(full_file_name.find_last_of('/') + 1);
+  std::string file_basename = file_name.substr(0, file_name.find_first_of('.'));
+
+  mpact::sim::codelab::RV32ITop rv32i_top("RV32I");
+
+  // Set up control-c handling.
+  top = &rv32i_top;
+  struct sigaction sa;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaddset(&sa.sa_mask, SIGINT);
+  sa.sa_handler = &sim_sigint_handler;
+  sigaction(SIGINT, &sa, nullptr);
+
+  // Load the elf segments into memory.
+  mpact::sim::util::ElfProgramLoader elf_loader(rv32i_top.memory());
+  auto load_result = elf_loader.LoadProgram(full_file_name);
+  if (!load_result.ok()) {
+    std::cerr << "Error while loading '" << full_file_name
+              << "': " << load_result.status().message();
+  }
+
+  // Initialize the PC to the entry point.
+  uint32_t entry_point = load_result.value();
+  auto pc_write = rv32i_top.WriteRegister("pc", entry_point);
+  if (!pc_write.ok()) {
+    std::cerr << "Error writing to pc: " << pc_write.message();
+  }
+
+  // Set up semihosting.
+  RiscV32HtifSemiHost::SemiHostAddresses magic_addresses;
+  if (GetMagicAddresses(&elf_loader, &magic_addresses)) {
+    auto status = rv32i_top.SetUpSemiHosting(magic_addresses);
+    if (!status.ok()) {
+      std::cerr << "Failed to set up semihosting\n";
+      exit(-1);
+    }
+  }
+
+  // Determine if this is being run interactively or as a batch job.
+  bool interactive = absl::GetFlag(FLAGS_i) || absl::GetFlag(FLAGS_interactive);
+  if (interactive) {
+    mpact::sim::riscv::DebugCommandShell cmd_shell({{&rv32i_top, &elf_loader}});
+    cmd_shell.Run(std::cin, std::cout);
+  } else {
+    std::cerr << "Starting simulation\n";
+
+    auto run_status = rv32i_top.Run();
+    if (!run_status.ok()) {
+      std::cerr << run_status.message() << std::endl;
+    }
+
+    auto wait_status = rv32i_top.Wait();
+    if (!wait_status.ok()) {
+      std::cerr << wait_status.message() << std::endl;
+    }
+
+    std::cerr << "Simulation done\n";
+  }
+
+  // Export counters.
+  auto component_proto = std::make_unique<ComponentData>();
+  CHECK_OK(rv32i_top.Export(component_proto.get())) << "Failed to export proto";
+  std::string proto_file_name;
+  if (FLAGS_output_dir.CurrentValue().empty()) {
+    proto_file_name = "./" + file_basename + ".proto";
+  } else {
+    proto_file_name =
+        FLAGS_output_dir.CurrentValue() + "/" + file_basename + ".proto";
+  }
+  std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
+  std::string serialized;
+  if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
+                                *component_proto.get(), &serialized)) {
+    LOG(ERROR) << "Failed to write proto to file";
+  } else {
+    proto_file << serialized;
+    proto_file.close();
+  }
+
+}
diff --git a/other/rv32i_top.cc b/other/rv32i_top.cc
new file mode 100644
index 0000000..6574ea5
--- /dev/null
+++ b/other/rv32i_top.cc
@@ -0,0 +1,472 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "other/rv32i_top.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <thread>
+
+#include "absl/functional/bind_front.h"
+#include "absl/log/check.h"
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "riscv/riscv32_htif_semihost.h"
+#include "riscv/riscv_breakpoint.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using generic::DataBuffer;
+using riscv::RiscVXlen;
+
+constexpr char kRiscV32Name[] = "RiscV";
+constexpr char kRegisterAliases[32][6] = {
+    "zero", "ra", "sp", "gp", "tp",  "t0",  "t1", "t2", "s0", "s1", "a0",
+    "a1",   "a2", "a3", "a4", "a5",  "a6",  "a7", "s2", "s3", "s4", "s5",
+    "s6",   "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"};
+
+RV32ITop::RV32ITop(std::string name)
+    : Component(name),
+      counter_num_instructions_("num_instructions", 0) {
+  // Using a single flat memory for this core.
+  memory_ = new util::FlatDemandMemory(0);
+  // Creat the simulation state.
+  state_ = new RiscVState(kRiscV32Name, RiscVXlen::RV32, memory_);
+  pc_ = state_->GetRegister<RV32Register>(RiscVState::kPcName).first;
+  // Set up the decoder and decode cache.
+  rv32_decoder_ = new RiscV32Decoder(state_, memory_);
+  rv32_decode_cache_ =
+      generic::DecodeCache::Create({16 * 1024, 2}, rv32_decoder_);
+  // Register instruction opcode counters.
+  for (int i = 0; i < static_cast<int>(OpcodeEnum::kPastMaxValue); i++) {
+    counter_opcode_[i].Initialize(absl::StrCat("num_", kOpcodeNames[i]), 0);
+    CHECK_OK(AddCounter(&counter_opcode_[i]));
+  }
+  // Register instruction counter.
+  CHECK_OK(AddCounter(&counter_num_instructions_))
+      << "Failed to register counter";
+  rv_bp_manager_ = new RiscVBreakpointManager(
+      memory_,
+      absl::bind_front(&generic::DecodeCache::Invalidate, rv32_decode_cache_));
+  // Set the software breakpoint callback.
+  state_->AddEbreakHandler([this](const Instruction *inst) -> bool {
+    // If there is a breakpoint, handle it and return true to signal that
+    // the ebreak has been handled. Otherwise return false.
+    if ((inst != nullptr) && (HasBreakpoint(inst->address()))) {
+      RequestHalt(HaltReason::kSoftwareBreakpoint, inst);
+      return true;
+    }
+    return false;
+  });
+  // Make sure the architectural and abi register aliases are added.
+  for (int i = 0; i < 32; i++) {
+    std::string reg_name = absl::StrCat(RiscVState::kXregPrefix, i);
+    (void)state_->AddRegister<RV32Register>(reg_name);
+    (void)state_->AddRegisterAlias<RV32Register>(reg_name, kRegisterAliases[i]);
+  }
+}
+
+RV32ITop::~RV32ITop() {
+  // If the simulator is still running, request a halt (set halted_ to true),
+  // and wait until the simulator finishes before continuing the destructor.
+  if (run_status_ == RunStatus::kRunning) {
+    run_halted_->WaitForNotification();
+    delete run_halted_;
+    run_halted_ = nullptr;
+  }
+
+  delete rv32_semihost_;
+  delete rv_bp_manager_;
+  delete rv32_decode_cache_;
+  delete rv32_decoder_;
+  delete state_;
+  delete watcher_;
+  delete memory_;
+}
+
+absl::Status RV32ITop::Halt() {
+  // If it is already halted, just return.
+  if (run_status_ == RunStatus::kHalted) {
+    return absl::OkStatus();
+  }
+  // If it is not running, then there's an error.
+  if (run_status_ != RunStatus::kRunning) {
+    return absl::FailedPreconditionError("RV32ITop::Halt: Core is not running");
+  }
+  halt_reason_ = HaltReason::kUserRequest;
+  halted_ = true;
+  return absl::OkStatus();
+}
+
+absl::StatusOr<int> RV32ITop::Step(int num) {
+  if (num <= 0) {
+    return absl::InvalidArgumentError("Step count must be > 0");
+  }
+  // If the simulator is running, return with an error.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("RV32ITop::Step: Core must be halted");
+  }
+  run_status_ = RunStatus::kSingleStep;
+  int count = 0;
+  halted_ = false;
+
+  // First check to see if the previous halt was due to a breakpoint. If so,
+  // need to step over the breakpoint.
+  if (halt_reason_ == HaltReason::kSoftwareBreakpoint) {
+    auto bp_pc = previous_pc_;
+    // Disable the breakpoint. Status will show error if there is no breakpoint.
+    auto status = rv_bp_manager_->DisableBreakpoint(bp_pc);
+    // Execute the real instruction.
+    auto prev_inst = rv32_decode_cache_->GetDecodedInstruction(bp_pc);
+    prev_inst->Execute(nullptr);
+    counter_opcode_[prev_inst->opcode()].Increment(1);
+    counter_num_instructions_.Increment(1);
+    count++;
+    // Re-enable the breakpoint.
+    if (status.ok()) {
+      status = rv_bp_manager_->EnableBreakpoint(bp_pc);
+      if (!status.ok()) return status;
+    }
+    // No longer stopped at breakpoint, so update halt reason.
+    halt_reason_ = HaltReason::kNone;
+  }
+
+  // Step the simulator forward until the number of steps have been achieved, or
+  // there is a halt request.
+
+  DataBuffer *pc_db = pc_->data_buffer();
+  uint32_t next_pc = pc_db->Get<uint32_t>(0);
+  uint32_t pc;
+  while (count < num) {
+    pc = next_pc;
+    auto *inst = rv32_decode_cache_->GetDecodedInstruction(pc);
+    inst->Execute(nullptr);
+    count++;
+    next_pc += inst->size();
+    DataBuffer *tmp_db = pc_->data_buffer();
+    if (pc_db != tmp_db) {
+      // PC has been updated by an instruction.
+      pc_db = tmp_db;
+      next_pc = pc_db->Get<uint32_t>(0);
+    }
+    counter_opcode_[inst->opcode()].Increment(1);
+    counter_num_instructions_.Increment(1);
+    if (halted_) break;
+  }
+  previous_pc_ = pc;
+  // Update the pc register, now that it can be read.
+  pc_db->Set<uint32_t>(0, next_pc);
+  // If there is no halt request, there is no specific halt reason.
+  if (!halted_) {
+    halt_reason_ = HaltReason::kNone;
+  }
+  run_status_ = RunStatus::kHalted;
+  return count;
+}
+
+absl::Status RV32ITop::Run() {
+  // Verify that the core isn't running already.
+  if (run_status_ == RunStatus::kRunning) {
+    return absl::FailedPreconditionError(
+        "RV32ITop::Run: core is already running");
+  }
+
+  // First check to see if the previous halt was due to a breakpoint. If so,
+  // need to step over the breakpoint.
+  if (halt_reason_ == HaltReason::kSoftwareBreakpoint) {
+    auto bp_pc = previous_pc_;
+    // Disable the breakpoint.
+    auto status = rv_bp_manager_->DisableBreakpoint(bp_pc);
+    // Execute the real instruction.
+    auto prev_inst = rv32_decode_cache_->GetDecodedInstruction(bp_pc);
+    prev_inst->Execute(nullptr);
+    counter_opcode_[prev_inst->opcode()].Increment(1);
+    counter_num_instructions_.Increment(1);
+    // Re-enable the breakpoint.
+    if (status.ok()) {
+      status = rv_bp_manager_->EnableBreakpoint(bp_pc);
+      if (!status.ok()) return status;
+    }
+    // No longer stopped at breakpoint, so update halt reason.
+    halt_reason_ = HaltReason::kNone;
+  }
+
+  run_status_ = RunStatus::kRunning;
+  halted_ = false;
+
+  // The simulator is now run in a separate thread so as to allow a user
+  // interface to continue operating. Allocate a new run_halted_ Notification
+  // object, as they are single use only.
+  run_halted_ = new absl::Notification();
+
+  // The thread is detached so it executes without having to be joined.
+  std::thread([this]() {
+    DataBuffer *pc_db = pc_->data_buffer();
+    uint32_t next_pc = pc_db->Get<uint32_t>(0);
+    uint32_t pc;
+    while (!halted_) {
+      pc = next_pc;
+      auto *inst = rv32_decode_cache_->GetDecodedInstruction(pc);
+      inst->Execute(nullptr);
+      next_pc += inst->size();
+      DataBuffer *tmp_db = pc_->data_buffer();
+      if (pc_db != tmp_db) {
+        // PC has been updated by an instruction.
+        pc_db = tmp_db;
+        next_pc = pc_db->Get<uint32_t>(0);
+      }
+      counter_opcode_[inst->opcode()].Increment(1);
+      counter_num_instructions_.Increment(1);
+    }
+    previous_pc_ = pc;
+    // Update the pc register, now that it can be read.
+    pc_db->Set<uint32_t>(0, next_pc);
+    run_status_ = RunStatus::kHalted;
+    // Notify that the run has completed.
+    run_halted_->Notify();
+  }).detach();
+  return absl::OkStatus();
+}
+
+absl::Status RV32ITop::Wait() {
+  // If the simulator isn't running, then just return.
+  if (run_status_ != RunStatus::kRunning) {
+    delete run_halted_;
+    run_halted_ = nullptr;
+    return absl::OkStatus();
+  }
+
+  // Wait for the simulator to finish - i.e., notification on run_halted_
+  run_halted_->WaitForNotification();
+  // Now delete the notification object - it is single use only.
+  delete run_halted_;
+  run_halted_ = nullptr;
+  return absl::OkStatus();
+}
+
+absl::StatusOr<RV32ITop::RunStatus> RV32ITop::GetRunStatus() {
+  return run_status_;
+}
+
+absl::StatusOr<RV32ITop::HaltReason> RV32ITop::GetLastHaltReason() {
+  return halt_reason_;
+}
+
+absl::StatusOr<uint64_t> RV32ITop::ReadRegister(const std::string &name) {
+  // The registers aren't protected by a mutex, so let's not read them while
+  // the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("ReadRegister: Core must be halted");
+  }
+  auto iter = state_->registers()->find(name);
+  // Was the register found?
+  if (iter == state_->registers()->end())
+    return absl::NotFoundError(absl::StrCat("Register '", name, "' not found"));
+
+  // If requesting PC and we're stopped at a software breakpoint, the next
+  // instruction to be executed is at the address of the software breakpoint, so
+  // return that address.
+  if ((name == "pc") && (halt_reason_ == HaltReason::kSoftwareBreakpoint)) {
+    return previous_pc_;
+  }
+
+  auto *db = (iter->second)->data_buffer();
+  uint64_t value;
+  switch (db->size<uint8_t>()) {
+    case 1:
+      value = static_cast<uint64_t>(db->Get<uint8_t>(0));
+      return value;
+    case 2:
+      value = static_cast<uint64_t>(db->Get<uint16_t>(0));
+      return value;
+    case 4:
+      value = static_cast<uint64_t>(db->Get<uint32_t>(0));
+      return value;
+    case 8:
+      value = static_cast<uint64_t>(db->Get<uint64_t>(0));
+      return value;
+    default:
+      return absl::InternalError("Register size is not 1, 2, 4, or 8 bytes");
+  }
+}
+
+absl::Status RV32ITop::WriteRegister(const std::string &name, uint64_t value) {
+  // The registers aren't protected by a mutex, so let's not read them while
+  // the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("WriteRegister: Core must be halted");
+  }
+  auto iter = state_->registers()->find(name);
+  // Was the register found?
+  if (iter == state_->registers()->end())
+    return absl::NotFoundError(absl::StrCat("Register '", name, "' not found"));
+
+  // If stopped at a software breakpoing and the pc is changed, change the
+  // halt reason, since the next instruction won't be were we stopped.
+  if ((name == "pc") && (halt_reason_ == HaltReason::kSoftwareBreakpoint)) {
+    halt_reason_ = HaltReason::kNone;
+  }
+
+  auto *db = (iter->second)->data_buffer();
+  switch (db->size<uint8_t>()) {
+    case 1:
+      db->Set<uint8_t>(0, static_cast<uint8_t>(value));
+      break;
+    case 2:
+      db->Set<uint16_t>(0, static_cast<uint16_t>(value));
+      break;
+    case 4:
+      db->Set<uint32_t>(0, static_cast<uint32_t>(value));
+      break;
+    case 8:
+      db->Set<uint64_t>(0, static_cast<uint64_t>(value));
+      break;
+    default:
+      return absl::InternalError("Register size is not 1, 2, 4, or 8 bytes");
+  }
+  return absl::OkStatus();
+}
+
+absl::StatusOr<size_t> RV32ITop::ReadMemory(uint64_t address, void *buffer,
+                                            size_t length) {
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("ReadMemory: Core must be halted");
+  }
+  auto *db = db_factory_.Allocate(length);
+  // Load bypassing any watch points/semihosting.
+  state_->memory()->Load(address, db, nullptr, nullptr);
+  std::memcpy(buffer, db->raw_ptr(), length);
+  db->DecRef();
+  return length;
+}
+
+absl::StatusOr<size_t> RV32ITop::WriteMemory(uint64_t address,
+                                             const void *buffer,
+                                             size_t length) {
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("WriteMemory: Core must be halted");
+  }
+  auto *db = db_factory_.Allocate(length);
+  std::memcpy(db->raw_ptr(), buffer, length);
+  // Store bypassing any watch points/semihosting.
+  state_->memory()->Store(address, db);
+  db->DecRef();
+  return length;
+}
+
+bool RV32ITop::HasBreakpoint(uint64_t address) {
+  return rv_bp_manager_->HasBreakpoint(address);
+}
+
+absl::Status RV32ITop::SetSwBreakpoint(uint64_t address) {
+  // Don't try if the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError(
+        "SetSwBreakpoint: Core must be halted");
+  }
+  // If there is no breakpoint manager, return an error.
+  if (rv_bp_manager_ == nullptr) {
+    return absl::InternalError("Breakpoints are not enabled");
+  }
+  // Try setting the breakpoint.
+  return rv_bp_manager_->SetBreakpoint(address);
+}
+
+absl::Status RV32ITop::ClearSwBreakpoint(uint64_t address) {
+  // Don't try if the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError(
+        "ClearSwBreakpoing: Core must be halted");
+  }
+  if (rv_bp_manager_ == nullptr) {
+    return absl::InternalError("Breakpoints are not enabled");
+  }
+  return rv_bp_manager_->ClearBreakpoint(address);
+}
+
+absl::Status RV32ITop::ClearAllSwBreakpoints() {
+  // Don't try if the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError(
+        "ClearAllSwBreakpoints: Core must be halted");
+  }
+  if (rv_bp_manager_ == nullptr) {
+    return absl::InternalError("Breakpoints are not enabled");
+  }
+  rv_bp_manager_->ClearAllBreakpoints();
+  return absl::OkStatus();
+}
+
+absl::StatusOr<Instruction *> RV32ITop::GetInstruction(uint64_t address) {
+  auto inst = rv32_decode_cache_->GetDecodedInstruction(address);
+  return inst;
+}
+
+absl::StatusOr<std::string> RV32ITop::GetDisassembly(uint64_t address) {
+  // Don't try if the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError("GetDissasembly: Core must be halted");
+  }
+
+  Instruction *inst;
+  // If requesting the disassembly for an instruction at a breakpoint, return
+  // that of the original instruction instead.
+  if (rv_bp_manager_->IsBreakpoint(address)) {
+    auto bp_pc = address;
+    // Disable the breakpoint.
+    auto status = rv_bp_manager_->DisableBreakpoint(bp_pc);
+    if (!status.ok()) return status;
+    // Get the real instruction.
+    inst = rv32_decode_cache_->GetDecodedInstruction(bp_pc);
+    // Re-enable the breakpoint.
+    status = rv_bp_manager_->EnableBreakpoint(bp_pc);
+    if (!status.ok()) return status;
+  } else {
+    // If not at the breakpoint, or requesting a different instruction,
+    inst = rv32_decode_cache_->GetDecodedInstruction(address);
+  }
+  return inst != nullptr ? inst->AsString() : "Invalid instruction";
+}
+
+absl::Status RV32ITop::SetUpSemiHosting(const SemiHostAddresses &magic) {
+  // Don't try if the simulator is running.
+  if (run_status_ != RunStatus::kHalted) {
+    return absl::FailedPreconditionError(
+        "SetupSemihosting: Core must be halted");
+  }
+  watcher_ = new util::MemoryWatcher(memory_);
+  rv32_semihost_ = new RiscV32HtifSemiHost(
+      watcher_, memory_, magic,
+      [this]() { RequestHalt(HaltReason::kSemihostHaltRequest, nullptr); },
+      [this](std::string) {
+        RequestHalt(HaltReason::kSemihostHaltRequest, nullptr);
+      });
+  state_->set_memory(watcher_);
+  return absl::OkStatus();
+}
+
+void RV32ITop::RequestHalt(HaltReason halt_reason, const Instruction *inst) {
+  // First set the halt_reason_, then the half flag.
+  halt_reason_ = halt_reason;
+  halted_ = true;
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/other/rv32i_top.h b/other/rv32i_top.h
new file mode 100644
index 0000000..8db0dbc
--- /dev/null
+++ b/other/rv32i_top.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_OTHER_RV32I_TOP_H_
+#define MPACT_SIM_CODELABS_OTHER_RV32I_TOP_H_
+
+#include <string>
+
+#include "absl/status/status.h"
+#include "absl/synchronization/notification.h"
+#include "mpact/sim/generic/component.h"
+#include "mpact/sim/generic/core_debug_interface.h"
+#include "mpact/sim/generic/decode_cache.h"
+#include "mpact/sim/util/memory/flat_demand_memory.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+#include "mpact/sim/util/memory/memory_watcher.h"
+#include "riscv/riscv32_htif_semihost.h"
+#include "riscv/riscv_breakpoint.h"
+#include "riscv/riscv_register.h"
+#include "riscv/riscv_state.h"
+#include "riscv_full_decoder/solution/riscv32_decoder.h"
+#include "riscv_isa_decoder/solution/riscv32i_enums.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using riscv::RiscV32HtifSemiHost;
+using riscv::RiscVBreakpointManager;
+using riscv::RiscVState;
+using riscv::RV32Register;
+
+// Top level class for the RiscV32G simulator. This is the main interface for
+// interacting and controlling execution of programs running on the simulator.
+// This class brings together the decoder, the architecture state, and control.
+class RV32ITop : public generic::Component, public generic::CoreDebugInterface {
+ public:
+  using RunStatus = generic::CoreDebugInterface::RunStatus;
+  using HaltReason = generic::CoreDebugInterface::HaltReason;
+  using SemiHostAddresses = RiscV32HtifSemiHost::SemiHostAddresses;
+
+  explicit RV32ITop(std::string name);
+  ~RV32ITop() override;
+
+  // Methods inherited from CoreDebugInterface.
+  absl::Status Halt() override;
+  absl::StatusOr<int> Step(int num) override;
+  absl::Status Run() override;
+  absl::Status Wait() override;
+
+  absl::StatusOr<RunStatus> GetRunStatus() override;
+  absl::StatusOr<HaltReason> GetLastHaltReason() override;
+
+  absl::StatusOr<uint64_t> ReadRegister(const std::string &name) override;
+  absl::Status WriteRegister(const std::string &name, uint64_t value) override;
+
+  // Read and Write memory methods bypass any semihosting.
+  absl::StatusOr<size_t> ReadMemory(uint64_t address, void *buf,
+                                    size_t length) override;
+  absl::StatusOr<size_t> WriteMemory(uint64_t address, const void *buf,
+                                     size_t length) override;
+
+  bool HasBreakpoint(uint64_t address) override;
+  absl::Status SetSwBreakpoint(uint64_t address) override;
+  absl::Status ClearSwBreakpoint(uint64_t address) override;
+  absl::Status ClearAllSwBreakpoints() override;
+
+  absl::StatusOr<Instruction *>GetInstruction(uint64_t address) override;
+
+  absl::StatusOr<std::string> GetDisassembly(uint64_t address) override;
+
+  // Set up semihosting with the given magic addresses.
+  absl::Status SetUpSemiHosting(const SemiHostAddresses &magic);
+
+  // Accessors.
+  RiscVState *state() const { return state_; }
+  util::MemoryInterface *memory() const { return memory_; }
+
+ private:
+  // Called when a halt is requested.
+  void RequestHalt(HaltReason halt_reason, const Instruction *inst);
+
+  uint32_t previous_pc_;
+  // The DB factory is used to manage data buffers for memory read/writes.
+  generic::DataBufferFactory db_factory_;
+  // Current status and last halt reasons.
+  RunStatus run_status_ = RunStatus::kHalted;
+  HaltReason halt_reason_ = HaltReason::kNone;
+  // Halting flag. This is set to true when execution must halt.
+  bool halted_ = false;
+  absl::Notification *run_halted_ = nullptr;
+  // The local RiscV32 state.
+  RiscVState *state_;
+  // Semihosting class.
+  RiscV32HtifSemiHost *rv32_semihost_ = nullptr;
+  // Breakpoint manager.
+  RiscVBreakpointManager *rv_bp_manager_ = nullptr;
+  // The pc register instance.
+  RV32Register *pc_;
+  // RiscV32 decoder instance.
+  RiscV32Decoder *rv32_decoder_ = nullptr;
+  // Decode cache, memory and memory watcher.
+  generic::DecodeCache *rv32_decode_cache_ = nullptr;
+  util::FlatDemandMemory *memory_ = nullptr;
+  util::MemoryWatcher *watcher_ = nullptr;
+  // Counter for the number of instructions simulated.
+  generic::SimpleCounter<uint64_t>
+      counter_opcode_[static_cast<int>(OpcodeEnum::kPastMaxValue)];
+  generic::SimpleCounter<uint64_t> counter_num_instructions_;
+};
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_OTHER_RV32I_TOP_H_
diff --git a/riscv_bin_decoder/BUILD b/riscv_bin_decoder/BUILD
new file mode 100644
index 0000000..7be1267
--- /dev/null
+++ b/riscv_bin_decoder/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build file for the bin_decoder part of the RiscV instruction decoder codelab.
+
+load("@mpact-sim//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_bin_fmt_decoder")
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+mpact_bin_fmt_decoder(
+    name = "riscv32i_bin_fmt",
+    src = "riscv32i.bin_fmt",
+    decoder_name = "RiscV32I",
+    includes = [],
+    deps = [
+        "//riscv_isa_decoder/solution:riscv32i_isa",
+    ],
+)
diff --git a/riscv_bin_decoder/riscv32i.bin_fmt b/riscv_bin_decoder/riscv32i.bin_fmt
new file mode 100644
index 0000000..5ebbfd0
--- /dev/null
+++ b/riscv_bin_decoder/riscv32i.bin_fmt
@@ -0,0 +1,82 @@
+// This declares the decoder.
+decoder RiscV32I {
+  // The namespace in which code will be generated.
+  namespace mpact::sim::codelab;
+  // The name (including any namespace qualifiers) of the opcode enum type.
+  opcode_enum = "OpcodeEnum";
+  // Include files specific to this decoder.
+  includes {
+    #include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+  }
+  // Instruction groups for which to generate decode functions.
+  RiscVInst32;
+};
+
+// The generic RiscV 32 bit instruction format.
+format Inst32Format[32] {
+  fields:
+    unsigned bits[25];
+    unsigned opcode[7];
+};
+
+// Exercise 2 format.
+// End of Exercise 2 format.
+
+// RiscV 32 bit instruction format used by a number of instructions
+// needing a 12 bit immediate, including CSR instructions.
+format IType[32] : Inst32Format {
+  fields:
+    signed imm12[12];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+// Exercise 3 format.
+// End of Exercise 3 format.
+
+// Exercise 4 formats.
+// End of Exercise 4 formats.
+
+// Exercise 5 format.
+// End of Exercise 5 format.
+
+// Exercise 6 format.
+// End of Exercise 6 format.
+
+// RiscV instruction format used by fence instructions.
+format Fence[32] : Inst32Format {
+  fields:
+    unsigned fm[4];
+    unsigned pred[4];
+    unsigned succ[4];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+// This defines the RiscVInst32 instruction group which defines the encoding
+// of the RiscV instructions we care about.
+instruction group RiscVInst32[32] : Inst32Format {
+  // Exercise 2 instructions.
+  // End Exercise 2 instructions.
+
+  // Exercise 3 instructions.
+  // End of Exercise 3 instructions.
+
+  // Exercise 4 instructions.
+  // End of Exercise 4 instructions.
+
+  // Exercise 5 instructions.
+  // End of Exercise 5 instructions.
+
+  // Exercise 6 instructions.
+  // End of Exercise 6 instructions.
+
+  fence   : Fence  : func3 == 0b000, opcode == 0b000'1111;
+  csrs    : IType  : func3 == 0b010, rs1 != 0, opcode == 0b111'0011;
+  csrw_nr : IType  : func3 == 0b001, rd == 0,  opcode == 0b111'0011;
+  csrs_nw : IType  : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
+};
diff --git a/riscv_bin_decoder/solution/BUILD b/riscv_bin_decoder/solution/BUILD
new file mode 100644
index 0000000..f7b21a4
--- /dev/null
+++ b/riscv_bin_decoder/solution/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build file for the RiscV binary decoder codelab solution.
+
+load("@mpact-sim//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_bin_fmt_decoder")
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+mpact_bin_fmt_decoder(
+    name = "riscv32i_bin_fmt",
+    src = "riscv32i.bin_fmt",
+    decoder_name = "RiscV32I",
+    includes = [],
+    deps = [
+        "//riscv_isa_decoder/solution:riscv32i_isa",
+    ],
+)
diff --git a/riscv_bin_decoder/solution/riscv32i.bin_fmt b/riscv_bin_decoder/solution/riscv32i.bin_fmt
new file mode 100644
index 0000000..cc51378
--- /dev/null
+++ b/riscv_bin_decoder/solution/riscv32i.bin_fmt
@@ -0,0 +1,168 @@
+// This declares the decoder.
+decoder RiscV32I {
+  // The namespace in which code will be generated.
+  namespace mpact::sim::codelab;
+  // The name (including any namespace qualifiers) of the opcode enum type.
+  opcode_enum = "OpcodeEnum";
+  // Include files specific to this decoder.
+  includes {
+    #include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+  }
+  // Instruction groups for which to generate decode functions.
+  RiscVInst32;
+};
+
+// The generic RiscV 32 bit instruction format.
+format Inst32Format[32] {
+  fields:
+    unsigned bits[25];
+    unsigned opcode[7];
+};
+
+// Exercise 2 format.
+format RType[32] : Inst32Format {
+  fields:
+    unsigned func7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned uimm5[5] = rs2;   // Exercise 3.
+};
+// End of Exercise 2 format.
+
+// RiscV 32 bit instruction format used by a number of instructions
+// needing a 12 bit immediate, including CSR instructions.
+format IType[32] : Inst32Format {
+  fields:
+    signed imm12[12];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+// Exercise 3 format.
+format UType[32] : Inst32Format {
+  fields:
+    unsigned uimm20[20];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned uimm32[32] = uimm20, 0b0000'0000'0000;
+};
+// End of Exercise 3 format.
+
+// Exercise 4 formats.
+format BType[32] : Inst32Format {
+  fields:
+    unsigned imm7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned imm5[5];
+    unsigned opcode[7];
+  overlays:
+    signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;
+};
+
+format JType[32] : Inst32Format {
+  fields:
+    unsigned imm20[20];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;
+};
+// End of Exercise 4 formats.
+
+// Exercise 5 format.
+format SType[32] : Inst32Format {
+  fields:
+    unsigned imm7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned imm5[5];
+    unsigned opcode[7];
+  overlays:
+    signed s_imm[12] = imm7, imm5;
+};
+// End of Exercise 5 format.
+
+// There is no Exercise 6 format.
+
+// RiscV instruction format used by fence instructions.
+format Fence[32] : Inst32Format {
+  fields:
+    unsigned fm[4];
+    unsigned pred[4];
+    unsigned succ[4];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+// This defines the RiscVInst32 instruction group which defines the encoding
+// of the RiscV instructions we care about.
+instruction group RiscVInst32[32] : Inst32Format {
+  // Exercise 2 instructions.
+  add     : RType  : func7 == 0b000'0000, func3 == 0b000, opcode == 0b011'0011;
+  and     : RType  : func7 == 0b000'0000, func3 == 0b111, opcode == 0b011'0011;
+  or      : RType  : func7 == 0b000'0000, func3 == 0b110, opcode == 0b011'0011;
+  sll     : RType  : func7 == 0b000'0000, func3 == 0b001, opcode == 0b011'0011;
+  sltu    : RType  : func7 == 0b000'0000, func3 == 0b011, opcode == 0b011'0011;
+  sub     : RType  : func7 == 0b010'0000, func3 == 0b000, opcode == 0b011'0011;
+  xor     : RType  : func7 == 0b000'0000, func3 == 0b100, opcode == 0b011'0011;
+  // End Exercise 2 instructions.
+
+  // Exercise 3 instructions.
+  addi    : IType : func3 == 0b000, opcode == 0b001'0011;
+  andi    : IType : func3 == 0b111, opcode == 0b001'0011;
+  ori     : IType : func3 == 0b110, opcode == 0b001'0011;
+  xori    : IType : func3 == 0b100, opcode == 0b001'0011;
+
+  slli    : RType : func7 == 0b000'0000, func3 == 0b001, opcode == 0b001'0011;
+  srai    : RType : func7 == 0b010'0000, func3 == 0b101, opcode == 0b001'0011;
+  srli    : RType : func7 == 0b000'0000, func3 == 0b101, opcode == 0b001'0011;
+
+  auipc   : UType : opcode == 0b001'0111;
+  lui     : UType : opcode == 0b011'0111;
+  // End Exercise 3 instructions.
+
+  // Exercise 4 instructions.
+  beq     : BType : opcode == 0b110'0011, func3 == 0b000;
+  bge     : BType : opcode == 0b110'0011, func3 == 0b101;
+  bgeu    : BType : opcode == 0b110'0011, func3 == 0b111;
+  blt     : BType : opcode == 0b110'0011, func3 == 0b100;
+  bltu    : BType : opcode == 0b110'0011, func3 == 0b110;
+  bne     : BType : opcode == 0b110'0011, func3 == 0b001;
+
+  jal     : JType : opcode == 0b110'1111;
+
+  jalr    : IType : opcode == 0b110'0111, func3 == 0b000;
+  // End of Exercise 4 instructions.
+
+  // Exercise 5 instructions.
+  sb      : SType : opcode == 0b010'0011, func3 == 0b000;
+  sh      : SType : opcode == 0b010'0011, func3 == 0b001;
+  sw      : SType : opcode == 0b010'0011, func3 == 0b010;
+  // End of Exercise 5 instructions.
+
+  // Exercise 6 instructions.
+  lb      : IType : opcode == 0b000'0011, func3 == 0b000;
+  lbu     : IType : opcode == 0b000'0011, func3 == 0b100;
+  lh      : IType : opcode == 0b000'0011, func3 == 0b001;
+  lhu     : IType : opcode == 0b000'0011, func3 == 0b101;
+  lw      : IType : opcode == 0b000'0011, func3 == 0b010;
+  // End of Exercise 6 instructions.
+
+  fence   : Fence  : func3 == 0b000, opcode == 0b000'1111;
+  ebreak  : Inst32Format : bits == 0b0000'0000'0001'00000'000'00000, opcode == 0b111'0011;
+  csrs    : IType  : func3 == 0b010, rs1 != 0, opcode == 0b111'0011;
+  csrw_nr : IType  : func3 == 0b001, rd == 0,  opcode == 0b111'0011;
+  csrs_nw : IType  : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
+};
diff --git a/riscv_full_decoder/BUILD b/riscv_full_decoder/BUILD
new file mode 100644
index 0000000..eb61ee6
--- /dev/null
+++ b/riscv_full_decoder/BUILD
@@ -0,0 +1,48 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# riscv_full_decoder codelab project
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "riscv32i_decoder",
+    srcs = [
+        "riscv32_decoder.cc",
+        "riscv32i_encoding.cc",
+    ],
+    hdrs = [
+        "riscv32_decoder.h",
+        "riscv32i_encoding.h",
+    ],
+    deps = [
+        "//riscv_bin_decoder/solution:riscv32i_bin_fmt",
+        "//riscv_isa_decoder/solution:riscv32i_isa",
+        "//riscv_semantic_functions/solution:riscv32i",
+        "@mpact-riscv//riscv:riscv_state",
+        "@mpact-sim//mpact/sim/generic:arch_state",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/generic:instruction",
+        "@mpact-sim//mpact/sim/generic:program_error",
+        "@mpact-sim//mpact/sim/util/memory",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:any_invocable",
+        "@com_google_absl//absl/functional:bind_front",
+        "@com_google_absl//absl/memory",
+        "@com_google_absl//absl/strings",
+    ],
+)
diff --git a/riscv_full_decoder/riscv32_decoder.cc b/riscv_full_decoder/riscv32_decoder.cc
new file mode 100644
index 0000000..27e3532
--- /dev/null
+++ b/riscv_full_decoder/riscv32_decoder.cc
@@ -0,0 +1,38 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_full_decoder/riscv32_decoder.h"
+
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::util::MemoryInterface;
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+// Exercise 1 - Step 3.
+
+// Constructor.
+// RiscV32Decoder::RiscV32Decoder(RiscVState *state, MemoryInterface *memory) {}
+
+// Destructor.
+// RiscV32Decoder::~RiscV32Decoder() {}
+
+// DecodeInstruction method.
+// Instruction *RiscV32Decoder::DecodeInstruction(uint64_t address) {}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_full_decoder/riscv32_decoder.h b/riscv_full_decoder/riscv32_decoder.h
new file mode 100644
index 0000000..410378c
--- /dev/null
+++ b/riscv_full_decoder/riscv32_decoder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_FULL_DECODER_RISCV32_DECODER_H_
+#define MPACT_SIM_CODELABS_FULL_DECODER_RISCV32_DECODER_H_
+
+#include <memory>
+
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/decoder_interface.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/program_error.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+#include "riscv/riscv_state.h"
+#include "riscv_full_decoder/riscv32i_encoding.h"
+#include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using riscv::RiscVState;
+
+// Exercise 1 - step 1. Fill in the factory class definition.
+class RiscV32IsaFactory;
+
+// Exercise 1 - step 2. Fill in the decoder class definition.
+class RiscV32Decoder;
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_FULL_DECODER_RISCV32_DECODER_H_
diff --git a/riscv_full_decoder/riscv32i_encoding.cc b/riscv_full_decoder/riscv32i_encoding.cc
new file mode 100644
index 0000000..1ae6a37
--- /dev/null
+++ b/riscv_full_decoder/riscv32i_encoding.cc
@@ -0,0 +1,42 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_full_decoder/riscv32i_encoding.h"
+
+#include <string>
+
+#include "riscv_bin_decoder/solution/riscv32i_bin_decoder.h"
+#include "mpact/sim/generic/devnull_operand.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv/riscv_register.h"
+#include "riscv/riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using riscv::RiscVState;
+using riscv::RV32Register;
+
+using generic::DevNullOperand;
+using generic::ImmediateOperand;
+using generic::IntLiteralOperand;
+
+// Exercise 2.
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_full_decoder/riscv32i_encoding.h b/riscv_full_decoder/riscv32i_encoding.h
new file mode 100644
index 0000000..8d00498
--- /dev/null
+++ b/riscv_full_decoder/riscv32i_encoding.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_FULL_DECODER_RISCV32I_ENCODING_H_
+#define MPACT_SIM_CODELABS_FULL_DECODER_RISCV32I_ENCODING_H_
+
+#include <functional>
+#include <string>
+
+#include "absl/functional/any_invocable.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv/riscv_state.h"
+#include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+#include "riscv_isa_decoder/solution/riscv32i_enums.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::PredicateOperandInterface;
+using ::mpact::sim::generic::SourceOperandInterface;
+
+// Exercise 2.
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_FULL_DECODER_RISCV32I_ENCODING_H_
diff --git a/riscv_full_decoder/solution/BUILD b/riscv_full_decoder/solution/BUILD
new file mode 100644
index 0000000..cca8c97
--- /dev/null
+++ b/riscv_full_decoder/solution/BUILD
@@ -0,0 +1,48 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# riscv_full_decoder codelab solution project
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "riscv32i_decoder",
+    srcs = [
+        "riscv32_decoder.cc",
+        "riscv32i_encoding.cc",
+    ],
+    hdrs = [
+        "riscv32_decoder.h",
+        "riscv32i_encoding.h",
+    ],
+    deps = [
+        "//riscv_bin_decoder/solution:riscv32i_bin_fmt",
+        "//riscv_isa_decoder/solution:riscv32i_isa",
+        "//riscv_semantic_functions/solution:riscv32i",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:any_invocable",
+        "@com_google_absl//absl/functional:bind_front",
+        "@com_google_absl//absl/memory",
+        "@com_google_absl//absl/strings",
+        "@mpact-riscv//riscv:riscv_state",
+        "@mpact-sim//mpact/sim/generic:arch_state",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/generic:instruction",
+        "@mpact-sim//mpact/sim/generic:program_error",
+        "@mpact-sim//mpact/sim/util/memory",
+    ],
+)
diff --git a/riscv_full_decoder/solution/riscv32_decoder.cc b/riscv_full_decoder/solution/riscv32_decoder.cc
new file mode 100644
index 0000000..eef571e
--- /dev/null
+++ b/riscv_full_decoder/solution/riscv32_decoder.cc
@@ -0,0 +1,57 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_full_decoder/solution/riscv32_decoder.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::util::MemoryInterface;
+
+RiscV32Decoder::RiscV32Decoder(RiscVState *state, MemoryInterface *memory)
+    : state_(state), memory_(memory) {
+  // Allocate the isa factory class, the top level isa decoder instance, and
+  // the encoding parser.
+  riscv_isa_factory_ = new RiscV32IsaFactory();
+  riscv_isa_ = new RiscV32IInstructionSet(state, riscv_isa_factory_);
+  riscv_encoding_ = new RiscV32IEncoding(state);
+  // Need a data buffer to load instructions from memory. Allocate a single
+  // buffer that can be reused for each instruction word.
+  inst_db_ = state_->db_factory()->Allocate<uint32_t>(1);
+}
+
+RiscV32Decoder::~RiscV32Decoder() {
+  inst_db_->DecRef();
+  delete riscv_isa_;
+  delete riscv_isa_factory_;
+  delete riscv_encoding_;
+}
+
+generic::Instruction *RiscV32Decoder::DecodeInstruction(uint64_t address) {
+  // Read the instruction word from memory and parse it in the encoding parser.
+  memory_->Load(address, inst_db_, nullptr, nullptr);
+  uint32_t iword = inst_db_->Get<uint32_t>(0);
+  riscv_encoding_->ParseInstruction(iword);
+
+  // Call the isa decoder to obtain a new instruction object for the instruction
+  // word that was parsed above.
+  auto *instruction = riscv_isa_->Decode(address, riscv_encoding_);
+  return instruction;
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_full_decoder/solution/riscv32_decoder.h b/riscv_full_decoder/solution/riscv32_decoder.h
new file mode 100644
index 0000000..ccae230
--- /dev/null
+++ b/riscv_full_decoder/solution/riscv32_decoder.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32_DECODER_H_
+#define MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32_DECODER_H_
+
+#include <memory>
+
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/decoder_interface.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+#include "riscv/riscv_state.h"
+#include "riscv_full_decoder/solution/riscv32i_encoding.h"
+#include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+// This is the factory class needed by the generated decoder. It is responsible
+// for creating the decoder for each slot instance. Since the riscv architecture
+// only has a single slot, it's a pretty simple class.
+class RiscV32IsaFactory : public RiscV32IInstructionSetFactory {
+ public:
+  std::unique_ptr<Riscv32Slot> CreateRiscv32Slot(ArchState *state) override {
+    return std::make_unique<Riscv32Slot>(state);
+  }
+};
+
+// This class implements the generic DecoderInterface and provides a bridge
+// to the (isa specific) generated decoder classes.
+class RiscV32Decoder : public generic::DecoderInterface {
+ public:
+  using SlotEnum = SlotEnum;
+  using OpcodeEnum = OpcodeEnum;
+
+  RiscV32Decoder(riscv::RiscVState *state, util::MemoryInterface *memory);
+  RiscV32Decoder() = delete;
+  ~RiscV32Decoder() override;
+
+  // This will always return a valid instruction that can be executed. In the
+  // case of a decode error, the semantic function in the instruction object
+  // instance will raise an internal simulator error when executed.
+  generic::Instruction *DecodeInstruction(uint64_t address) override;
+
+ private:
+  riscv::RiscVState *state_;
+  util::MemoryInterface *memory_;
+  RiscV32IsaFactory *riscv_isa_factory_;
+  RiscV32IEncoding *riscv_encoding_;
+  RiscV32IInstructionSet *riscv_isa_;
+  generic::DataBuffer *inst_db_;
+};
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32_DECODER_H_
diff --git a/riscv_full_decoder/solution/riscv32i_encoding.cc b/riscv_full_decoder/solution/riscv32i_encoding.cc
new file mode 100644
index 0000000..b0a3ca0
--- /dev/null
+++ b/riscv_full_decoder/solution/riscv32i_encoding.cc
@@ -0,0 +1,171 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_full_decoder/solution/riscv32i_encoding.h"
+
+#include <string>
+
+#include "mpact/sim/generic/devnull_operand.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv/riscv_register.h"
+#include "riscv/riscv_state.h"
+#include "riscv_bin_decoder/solution/riscv32i_bin_decoder.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using riscv::RiscVState;
+using riscv::RV32Register;
+
+using generic::DevNullOperand;
+using generic::ImmediateOperand;
+using generic::IntLiteralOperand;
+
+// Generic helper functions to create register operands.
+template <typename RegType>
+inline DestinationOperandInterface *GetRegisterDestinationOp(
+    RiscVState *state, const std::string &name, int latency) {
+  auto [reg_ptr, unused] = state->GetRegister<RegType>(name);
+  return reg_ptr->CreateDestinationOperand(latency);
+}
+
+template <typename RegType>
+inline DestinationOperandInterface *GetRegisterDestinationOp(
+    RiscVState *state, const std::string &name, int latency,
+    const std::string &op_name) {
+  auto [reg_ptr, unused] = state->GetRegister<RegType>(name);
+  return reg_ptr->CreateDestinationOperand(latency, op_name);
+}
+
+template <typename RegType>
+inline SourceOperandInterface *GetRegisterSourceOp(RiscVState *state,
+                                                   const std::string &name) {
+  auto [reg_ptr, unused] = state->GetRegister<RegType>(name);
+  auto *op = reg_ptr->CreateSourceOperand();
+  return op;
+}
+
+template <typename RegType>
+inline SourceOperandInterface *GetRegisterSourceOp(RiscVState *state,
+                                                   const std::string &name,
+                                                   const std::string &op_name) {
+  auto [reg_ptr, unused] = state->GetRegister<RegType>(name);
+  auto *op = reg_ptr->CreateSourceOperand(op_name);
+  return op;
+}
+
+RiscV32IEncoding::RiscV32IEncoding(RiscVState *state) : state_(state) {
+  InitializeSourceOperandGetters();
+  InitializeDestinationOperandGetters();
+}
+
+// Parse the instruction word to determine the opcode.
+void RiscV32IEncoding::ParseInstruction(uint32_t inst_word) {
+  inst_word_ = inst_word;
+  opcode_ = mpact::sim::codelab::DecodeRiscVInst32(inst_word_);
+}
+
+DestinationOperandInterface *RiscV32IEncoding::GetDestination(
+    SlotEnum, int, OpcodeEnum, DestOpEnum dest_op, int, int latency) {
+  return dest_op_getters_[static_cast<int>(dest_op)](latency);
+}
+
+SourceOperandInterface *RiscV32IEncoding::GetSource(SlotEnum, int, OpcodeEnum,
+                                                    SourceOpEnum source_op,
+                                                    int) {
+  return source_op_getters_[static_cast<int>(source_op)]();
+}
+
+void RiscV32IEncoding::InitializeDestinationOperandGetters() {
+  // Destination operand getters.
+  dest_op_getters_[static_cast<int>(DestOpEnum::kNone)] = [](int latency) {
+    return nullptr;
+  };
+  dest_op_getters_[static_cast<int>(DestOpEnum::kCsr)] = [this](int latency) {
+    return GetRegisterDestinationOp<RV32Register>(state_, "CSR", latency);
+  };
+  dest_op_getters_[static_cast<int>(DestOpEnum::kNextPc)] =
+      [this](int latency) {
+        return GetRegisterDestinationOp<RV32Register>(
+            state_, RiscVState::kPcName, latency);
+      };
+  dest_op_getters_[static_cast<int>(DestOpEnum::kRd)] =
+      [this](int latency) -> DestinationOperandInterface * {
+    int num = inst32_format::ExtractRd(inst_word_);
+    if (num == 0) return new DevNullOperand<uint32_t>(state_, {1});
+    return GetRegisterDestinationOp<RV32Register>(
+        state_, absl::StrCat(RiscVState::kXregPrefix, num), latency,
+        xreg_alias_[num]);
+  };
+}
+
+void RiscV32IEncoding::InitializeSourceOperandGetters() {
+  // Source operand getters.
+
+  source_op_getters_[static_cast<int>(SourceOpEnum::kNone)] = []() {
+    return nullptr;
+  };
+
+  // Register operands.
+  source_op_getters_[static_cast<int>(SourceOpEnum::kCsr)] = [this]() {
+    return GetRegisterSourceOp<RV32Register>(state_, "CSR");
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kRs1)] =
+      [this]() -> SourceOperandInterface * {
+    int num = inst32_format::ExtractRs1(inst_word_);
+    if (num == 0) return new IntLiteralOperand<0>({1}, xreg_alias_[0]);
+    return GetRegisterSourceOp<RV32Register>(
+        state_, absl::StrCat(RiscVState::kXregPrefix, num), xreg_alias_[num]);
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kRs2)] =
+      [this]() -> SourceOperandInterface * {
+    int num = inst32_format::ExtractRs2(inst_word_);
+    if (num == 0) return new IntLiteralOperand<0>({1}, xreg_alias_[0]);
+    return GetRegisterSourceOp<RV32Register>(
+        state_, absl::StrCat(RiscVState::kXregPrefix, num), xreg_alias_[num]);
+  };
+
+  // Immediates.
+  source_op_getters_[static_cast<int>(SourceOpEnum::kBimm12)] = [this]() {
+    return new ImmediateOperand<int32_t>(
+        inst32_format::ExtractBImm(inst_word_));
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kImm12)] = [this]() {
+    return new ImmediateOperand<int32_t>(
+        inst32_format::ExtractImm12(inst_word_));
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kUimm5)] = [this]() {
+    return new ImmediateOperand<uint32_t>(
+        inst32_format::ExtractUimm5(inst_word_));
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kJimm20)] = [this]() {
+    return new ImmediateOperand<int32_t>(
+        inst32_format::ExtractJImm(inst_word_));
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kSimm12)] = [this]() {
+    return new ImmediateOperand<int32_t>(
+        inst32_format::ExtractSImm(inst_word_));
+  };
+  source_op_getters_[static_cast<int>(SourceOpEnum::kUimm20)] = [this]() {
+    return new ImmediateOperand<int32_t>(
+        inst32_format::ExtractUimm32(inst_word_));
+  };
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_full_decoder/solution/riscv32i_encoding.h b/riscv_full_decoder/solution/riscv32i_encoding.h
new file mode 100644
index 0000000..6aaa07c
--- /dev/null
+++ b/riscv_full_decoder/solution/riscv32i_encoding.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32I_ENCODING_H_
+#define MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32I_ENCODING_H_
+
+#include <functional>
+#include <string>
+
+#include "absl/functional/any_invocable.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv/riscv_state.h"
+#include "riscv_isa_decoder/solution/riscv32i_decoder.h"
+#include "riscv_isa_decoder/solution/riscv32i_enums.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::PredicateOperandInterface;
+using ::mpact::sim::generic::SourceOperandInterface;
+
+// This class provides the interface between the generated instruction decoder
+// framework (which is agnostic of the actual bit representation of
+// instructions) and the instruction representation. This class provides methods
+// to return the opcode, source operands, and destination operands for
+// instructions according to the operand fields in the encoding.
+class RiscV32IEncoding : public RiscV32IEncodingBase {
+ public:
+  explicit RiscV32IEncoding(riscv::RiscVState *state);
+  ~RiscV32IEncoding() override = default;
+
+  // Parses an instruction and determines the opcode.
+  void ParseInstruction(uint32_t inst_word);
+
+  // RiscV32 has a single slot type and single entry, so the following methods
+  // ignore the SlotEnum parameter.
+
+  // Returns the opcode in the current instruction representation.
+  OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; }
+
+  // There is no predicate, so return nullptr.
+  PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum,
+                                          PredOpEnum) override {
+    return nullptr;
+  }
+  // The following method returns a source operand that corresponds to the
+  // particular operand field.
+  SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op,
+                                    int) override;
+
+  // The following method returns a destination operand that corresponds to the
+  // particular operand field.
+  DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum,
+                                              DestOpEnum op, int,
+                                              int latency) override;
+  // This method returns latency for any destination operand for which the
+  // latency specifier in the .isa file is '*'. Since there are none, just
+  // return 0.
+  int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override {
+    return 0;
+  }
+
+  ResourceOperandInterface *GetSimpleResourceOperand(SlotEnum, int, OpcodeEnum,
+                                                     SimpleResourceVector &,
+                                                     int) override {
+    return nullptr;
+  }
+
+  ResourceOperandInterface *GetComplexResourceOperand(SlotEnum, int, OpcodeEnum,
+                                                      ComplexResourceEnum, int,
+                                                      int) override {
+    return nullptr;
+  }
+
+ private:
+  // These two methods initialize the source and destination operand getter
+  // arrays.
+  void InitializeSourceOperandGetters();
+  void InitializeDestinationOperandGetters();
+
+  riscv::RiscVState *state_;
+  uint32_t inst_word_;
+  OpcodeEnum opcode_;
+
+  absl::AnyInvocable<SourceOperandInterface *()>
+      source_op_getters_[static_cast<int>(SourceOpEnum::kPastMaxValue)];
+  absl::AnyInvocable<DestinationOperandInterface *(int)>
+      dest_op_getters_[static_cast<int>(DestOpEnum::kPastMaxValue)];
+
+  const std::string xreg_alias_[32] = {
+      "zero", "ra", "sp", "gp", "tp",  "t0",  "t1", "t2", "s0", "s1", "a0",
+      "a1",   "a2", "a3", "a4", "a5",  "a6",  "a7", "s2", "s3", "s4", "s5",
+      "s6",   "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"};
+};
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_RISCV_FULL_DECODER_SOLUTION_RISCV32I_ENCODING_H_
diff --git a/riscv_isa_decoder/BUILD b/riscv_isa_decoder/BUILD
new file mode 100644
index 0000000..8963230
--- /dev/null
+++ b/riscv_isa_decoder/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build file for the isa_decoder part of the RiscV instruction decoder codelab.
+
+load("@mpact-sim//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_isa_decoder")
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+mpact_isa_decoder(
+    name = "riscv32i_isa",
+    src = "riscv32i.isa",
+    includes = [],
+    isa_name = "RiscV32I",
+    deps = [
+        "//riscv_semantic_functions:riscv32i",
+    ],
+)
diff --git a/riscv_isa_decoder/riscv32i.isa b/riscv_isa_decoder/riscv32i.isa
new file mode 100644
index 0000000..56f809f
--- /dev/null
+++ b/riscv_isa_decoder/riscv32i.isa
@@ -0,0 +1,81 @@
+// This file is used for the go/mpact-sim-codelabs/instruction-decoder
+// codelab. It will contain instruction decode information for a subset
+// of the RiscV32I architecture.
+
+isa RiscV32I {
+  namespace mpact::sim::codelab;
+  slots { riscv32; }
+}
+
+// The RiscV 'I' instructions.
+slot riscv32i {
+  // Include file that contains the declarations of the semantic functions for
+  // the 'I' instructions.
+  includes {
+    #include "riscv_semantic_functions/rv32i_instructions.h"
+  }
+  // These are all 32 bit instructions, so set default size to 4.
+  default size = 4;
+  // Model these with 0 latency to avoid buffering the result. Since RiscV
+  // instructions have sequential semantics this is fine.
+  default latency = 0;
+  // The opcodes.
+  opcodes {
+
+    // Add the instruction definition for the instructions in the codelab
+    // at the place holders below.
+    //
+    // Note Exercise 1 does not involve adding instructions.
+
+    // Exercise 2, Add Register-Register ALU Instructions.
+
+    // Exercise 3, Add ALU Instructions with Immediates.
+
+    // Exercise 4, Add Branch and Jump-And-Link Instructions.
+
+    // Exercise 5, Add Store Instructions.
+
+    // Exercise 6, Add Load Instructions.
+
+    // End of Excercises.
+    fence{: imm12 : },
+      semfunc: "&RV32IFence",
+      disasm: "fence";
+    ebreak{},
+      semfunc: "&RV32IEbreak",
+      disasm: "ebreak";
+  }
+}
+
+// RiscV32 CSR manipulation instructions.
+slot zicsr {
+  // Include file that contains the declarations of the semantic functions for
+  // the 'zicsr' instructions.
+  includes {
+    #include "riscv_semantic_functions/zicsr_instructions.h"
+  }
+  // 32 bit instructions.
+  default size = 4;
+  // No buffering of the result.
+  default latency = 0;
+  // Instructions.
+  opcodes {
+    csrs{: rs1, csr : rd, csr},
+      semfunc: "&RV32ZiCsrs",
+      disasm: "csrs", "%csr, %rs1";
+    csrw_nr{: rs1 : csr},
+      semfunc: "&RV32ZiCsrwNr",  // rd == 0 (x0).
+      disasm: "csrw", "%csr, %rs1";
+    csrs_nw{: csr : rd, csr},
+      semfunc: "&RV32ZiCsrsNw",  // rs1 == 0 (x0).
+      disasm: "csrs", "%csr, %rd";
+  }
+}
+
+// The final instruction set combines riscv32i and zicsr.
+slot riscv32 : riscv32i, zicsr {
+  // Default opcode for any instruction not matched by the decoder.
+  default opcode =
+    disasm: "Illegal instruction at 0x%(@:08x)",
+    semfunc: "&RV32IllegalInstruction";
+}
diff --git a/riscv_isa_decoder/solution/BUILD b/riscv_isa_decoder/solution/BUILD
new file mode 100644
index 0000000..c82dfd9
--- /dev/null
+++ b/riscv_isa_decoder/solution/BUILD
@@ -0,0 +1,32 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build file for the isa_decoder part of the RiscV instruction decoder codelab solution.
+
+load("@mpact-sim//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_isa_decoder")
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+mpact_isa_decoder(
+    name = "riscv32i_isa",
+    src = "riscv32i.isa",
+    includes = [],
+    isa_name = "RiscV32I",
+    deps = [
+        "//riscv_semantic_functions/solution:riscv32i",
+    ],
+)
diff --git a/riscv_isa_decoder/solution/riscv32i.isa b/riscv_isa_decoder/solution/riscv32i.isa
new file mode 100644
index 0000000..5ce4c9e
--- /dev/null
+++ b/riscv_isa_decoder/solution/riscv32i.isa
@@ -0,0 +1,178 @@
+// This file contains the ISA description for the RiscV32G architecture.
+
+isa RiscV32I {
+  namespace mpact::sim::codelab;
+  slots { riscv32; }
+}
+
+// The RiscV 'I' instructions.
+slot riscv32i {
+  // Include file that contains the declarations of the semantic functions for
+  // the 'I' instructions.
+  includes {
+    #include "riscv_semantic_functions/solution/rv32i_instructions.h"
+  }
+  // These are all 32 bit instructions, so set default size to 4.
+  default size = 4;
+  // Model these with 0 latency to avoid buffering the result. Since RiscV
+  // instructions have sequential semantics this is fine.
+  default latency = 0;
+  // The opcodes.
+  opcodes {
+    // Exercise 2, Add Register-Register ALU Instructions.
+    add{ : rs1, rs2 : rd},
+      semfunc: "&RV32IAdd",
+      disasm: "add", "%rd, %rs1, %rs2";
+    and{ : rs1, rs2 : rd},
+      semfunc: "&RV32IAnd",
+      disasm: "and", "%rd, %rs1, %rs2";
+    or{ : rs1, rs2 : rd},
+      semfunc: "&RV32IOr",
+      disasm: "or", "%rd, %rs1, %rs2";
+    sll{ : rs1, rs2 : rd},
+      semfunc: "&RV32ISll",
+      disasm: "sll", "%rd, %rs1, %rs2";
+    sltu{ : rs1, rs2 : rd},
+      semfunc: "&RV32ISltu",
+      disasm: "sltu", "%rd, %rs1, %rs2";
+    sub{ : rs1, rs2 : rd},
+      semfunc: "&RV32ISub",
+      disasm: "sub", "%rd, %rs1, %rs2";
+    xor{ : rs1, rs2 : rd},
+      semfunc: "&RV32IXor",
+      disasm: "xor", "%rd, %rs1, %rs2";
+
+    // Exercise 3, Add ALU Instructions with Immediates.
+    // I-Type.
+    addi{ : rs1, imm12 : rd},
+      semfunc: "&RV32IAdd",
+      disasm: "addi", "%rd, %rs1, %imm12";
+    andi{ : rs1, imm12 : rd},
+      semfunc: "&RV32IAnd",
+      disasm: "andi", "%rd, %rs1, %imm12";
+    ori{ : rs1, imm12 : rd},
+      semfunc: "&RV32IOr",
+      disasm: "ori", "%rd, %rs1, %imm12";
+    xori{ : rs1, imm12 : rd},
+      semfunc: "&RV32IXor",
+      disasm: "xori", "%rd, %rs1, %imm12";
+
+    // Specialized I-Type.
+    slli{ : rs1, uimm5 : rd},
+      semfunc: "&RV32ISll",
+      disasm: "slli", "%rd, %rs1, %uimm5";
+    srai{ : rs1, uimm5 : rd},
+      semfunc: "&RV32ISra",
+      disasm: "srai", "%rd, %rs1, %uimm5";
+    srli{ : rs1, uimm5 : rd},
+      semfunc: "&RV32ISrl",
+      disasm: "srli", "%rd, %rs1, %uimm5";
+
+    // U-Type.
+    auipc{ : uimm20 : rd},
+      semfunc: "&RV32IAuipc",
+      disasm: "auipc", "%rd, %uimm20";
+    lui{ : uimm20 : rd},
+      semfunc: "&RV32ILui",
+      disasm: "lui", "%rd, %uimm20";
+
+    // Exercise 4, Add Branch and Jump-And-Link Instructions.
+    // Branches
+    beq{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBeq",
+      disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)";
+    bge{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBge",
+      disasm: "bge", "%rs1, %rs2, %(@+bimm12:08x)";
+    bgeu{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBgeu",
+      disasm: "bgeu", "%rs1, %rs2, %(@+bimm12:08x)";
+    blt{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBlt",
+      disasm: "blt", "%rs1, %rs2, %(@+bimm12:08x)";
+    bltu{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBltu",
+      disasm: "bltu", "%rs1, %rs2, %(@+bimm12:08x)";
+    bne{ : rs1, rs2, bimm12 : next_pc},
+      semfunc: "&RV32IBne",
+      disasm: "bne", "%rs1, %rs2, %(@+bimm12:08x)";
+
+    // Jumps
+    jal{ : jimm20 : next_pc, rd},
+      semfunc: "&RV32IJal",
+      disasm: "jal", "%rd, %(@+jimm20:08x)";
+    jalr{ : rs1, imm12 : next_pc, rd},
+      semfunc: "&RV32IJalr",
+      disasm: "jalr", "%rd, %rs1, %imm12";
+
+    // Exercise 5, Add Store Instructions.
+    sb{ : rs1, simm12, rs2},
+      semfunc: "&RV32ISb",
+      disasm: "sb", "%rs2, %simm12(%rs1)";
+    sh{ : rs1, simm12, rs2},
+      semfunc: "&RV32ISh",
+      disasm: "sh", "%rs2, %simm12(%rs1)";
+    sw{ : rs1, simm12, rs2},
+      semfunc: "&RV32ISw",
+      disasm: "sw", "%rs2, %simm12(%rs1)";
+
+    // Exercise 6, Add Load Instructions.
+    lb{( : rs1, imm12 : ), ( : : rd)},
+      semfunc: "&RV32ILb", "&RV32ILbChild",
+      disasm: "lb", "%rd, %imm12(%rs1)";
+    lbu{( : rs1, imm12 : ), ( : : rd)},
+      semfunc: "&RV32ILbu", "&RV32ILbuChild",
+      disasm: "lbu", "%rd, %imm12(%rs1)";
+    lh{( : rs1, imm12 : ), ( : : rd)},
+      semfunc: "&RV32ILh", "&RV32ILhChild",
+      disasm: "lh", "%rd, %imm12(%rs1)";
+    lhu{( : rs1, imm12 : ), ( : : rd)},
+      semfunc: "&RV32ILhu", "&RV32ILhuChild",
+      disasm: "lhu", "%rd, %imm12(%rs1)";
+    lw{( : rs1, imm12 : ), ( : : rd)},
+      semfunc: "&RV32ILw", "&RV32ILwChild",
+      disasm: "lw", "%rd, %imm12(%rs1)";
+
+    // End of Excercises.
+
+    fence{: imm12 : },
+      semfunc: "&RV32IFence",
+      disasm: "fence";
+    ebreak{},
+      semfunc: "&RV32IEbreak",
+      disasm: "ebreak";
+  }
+}
+
+// RiscV32 CSR manipulation instructions.
+slot zicsr {
+  // Include file that contains the declarations of the semantic functions for
+  // the 'zicsr' instructions.
+  includes {
+    #include "riscv_semantic_functions/solution/zicsr_instructions.h"
+  }
+  // 32 bit instructions.
+  default size = 4;
+  // No buffering of the result.
+  default latency = 0;
+  // Instructions.
+  opcodes {
+    csrs{: rs1, csr : rd, csr},
+      semfunc: "&RV32ZiCsrs",
+      disasm: "csrs", "%csr, %rs1";
+    csrw_nr{: rs1 : csr},
+      semfunc: "&RV32ZiCsrwNr",  // rd == 0 (x0).
+      disasm: "csrw", "%csr, %rs1";
+    csrs_nw{: csr : rd, csr},
+      semfunc: "&RV32ZiCsrsNw",  // rs1 == 0 (x0).
+      disasm: "csrs", "%csr, %rd";
+  }
+}
+
+// The final instruction set combines riscv32i and zicsr.
+slot riscv32 : riscv32i, zicsr {
+  // Default opcode for any instruction not matched by the decoder.
+  default opcode =
+    disasm: "Illegal instruction at 0x%(@:08x)",
+    semfunc: "&RV32IllegalInstruction";
+}
diff --git a/riscv_semantic_functions/BUILD b/riscv_semantic_functions/BUILD
new file mode 100644
index 0000000..97c914a
--- /dev/null
+++ b/riscv_semantic_functions/BUILD
@@ -0,0 +1,40 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Semantic functions for the MPACT-Sim codelab
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "riscv32i",
+    srcs = [
+        "rv32i_instructions.cc",
+        "zicsr_instructions.cc",
+    ],
+    hdrs = [
+        "rv32i_instructions.h",
+        "zicsr_instructions.h",
+    ],
+    copts = ["-O3"],
+    deps = [
+        "@mpact-riscv//riscv:riscv_state",
+        "@mpact-sim//mpact/sim/generic:arch_state",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/generic:instruction",
+        "@mpact-sim//mpact/sim/util/memory",
+    ],
+)
diff --git a/riscv_semantic_functions/rv32i_instructions.cc b/riscv_semantic_functions/rv32i_instructions.cc
new file mode 100644
index 0000000..18a746f
--- /dev/null
+++ b/riscv_semantic_functions/rv32i_instructions.cc
@@ -0,0 +1,147 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_semantic_functions/rv32i_instructions.h"
+
+#include <functional>
+#include <iostream>
+
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/instruction_helpers.h"
+#include "riscv/riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using generic::DataBuffer;
+using riscv::RiscVState;
+
+void RV32IllegalInstruction(Instruction *inst) {
+  inst->state()
+      ->program_error_controller()
+      ->GetProgramError(generic::ProgramErrorController::kInternalErrorName)
+      ->Raise(
+          absl::StrCat("Illegal instruction at ", absl::Hex(inst->address())));
+  std::cerr << absl::StrCat("Illegal instruction at 0x",
+                            absl::Hex(inst->address()), "\n");
+}
+
+// The following instruction semantic functions implement basic alu operations.
+// They are used for both register-register and register-immediate versions of
+// the corresponding instructions.
+
+// Semantic functions for Exercise 2.
+void RV32IAdd(Instruction *instruction) {}
+
+void RV32IAnd(Instruction *instruction) {}
+
+void RV32IOr(Instruction *instruction) {}
+
+void RV32ISll(Instruction *instruction) {}
+
+void RV32ISltu(Instruction *instruction) {}
+
+void RV32ISra(Instruction *instruction) {}
+
+void RV32ISrl(Instruction *instruction) {}
+
+void RV32ISub(Instruction *instruction) {}
+
+void RV32IXor(Instruction *instruction) {}
+// End semantic functions for exercise 2.
+
+// Semantic functions for Exercise 3.
+// Load upper immediate. It is assumed that the decoder already shifted the
+// immediate.
+void RV32ILui(Instruction *instruction) {}
+
+// Add upper immediate to PC (for PC relative addressing). It is assumed that
+// the decoder already shifted the immediate.
+void RV32IAuipc(Instruction *instruction) {}
+// End semantic functions for Exercise 3.
+
+// Semantic functions for Exercise 4.
+// Branch instructions.
+void RV32IBeq(Instruction *instruction) {}
+
+void RV32IBge(Instruction *instruction) {}
+
+void RV32IBgeu(Instruction *instruction) {}
+
+void RV32IBlt(Instruction *instruction) {}
+
+void RV32IBltu(Instruction *instruction) {}
+
+void RV32IBne(Instruction *instruction) {}
+
+// Jal instruction.
+void RV32IJal(Instruction *instruction) {}
+
+// Jalr instruction.
+void RV32IJalr(Instruction *instruction) {}
+// End of semantic functions for Exercise 4.
+
+// Semantic functions for Exercise 5.
+// Store instructions.
+void RV32ISw(Instruction *instruction) {}
+
+void RV32ISh(Instruction *instruction) {}
+
+void RV32ISb(Instruction *instruction) {}
+// End of semantic functions for Exercise 5.
+
+// Semantic functions for Exercise 6.
+// Load instructions.
+void RV32ILw(Instruction *instruction) {}
+
+void RV32ILwChild(Instruction *instruction) {}
+
+void RV32ILh(Instruction *instruction) {}
+
+void RV32ILhChild(Instruction *instruction) {}
+
+void RV32ILhu(Instruction *instruction) {}
+
+void RV32ILhuChild(Instruction *instruction) {}
+
+void RV32ILb(Instruction *instruction) {}
+
+void RV32ILbChild(Instruction *instruction) {}
+
+void RV32ILbu(Instruction *instruction) {}
+
+void RV32ILbuChild(Instruction *instruction) {}
+// End of semantic functions for Exercise 6.
+
+// Fence.
+void RV32IFence(Instruction *instruction) {
+  uint32_t bits = instruction->Source(0)->AsUint32(0);
+  int fm = (bits >> 8) & 0xf;
+  int predecessor = (bits >> 4) & 0xf;
+  int successor = bits & 0xf;
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  state->Fence(instruction, fm, predecessor, successor);
+}
+
+// Ebreak - software breakpoint instruction.
+void RV32IEbreak(Instruction *instruction) {
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  state->EBreak(instruction);
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_semantic_functions/rv32i_instructions.h b/riscv_semantic_functions/rv32i_instructions.h
new file mode 100644
index 0000000..002e085
--- /dev/null
+++ b/riscv_semantic_functions/rv32i_instructions.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_RV32I_INSTRUCTIONS_H_
+#define MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_RV32I_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file contains the declarations of the instruction semantic functions
+// for the RiscV 32i instructions.
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::Instruction;
+
+//
+// All semantic functions must have the signature void(Instruction *).
+//
+
+// For the following, source operand 0 refers to the register specified in rs1,
+// and source operand 1 refers to either the register specified in rs2, or the
+// immediate. Destination operand 0 refers to the register specified in rd.
+
+// Semantic functions for Exercise 2.
+// End semantic functions for Exercise 2.
+
+// For the following, source operand 0 refers to the 20-bit immediate value,
+// already shifted left by 12 to form a 32-bit immediate.
+
+// Semantic functions for Exercise 3.
+// End semantic functions for Exercise 3.
+
+// For the following branch instructions. Source operand 0 refers to the
+// register specified by rs1, source operand 2 refers to the register specified
+// by rs2, and source operand 3 refers to the immediate offset. Destination
+// operand 0 refers to the pc destination operand.
+
+// Semantic functions for Exercise 4 - branches.
+// End semantic functions for Exercise 4 - branches.
+
+// Source operand 0 contains the immediate value. Destination operand 0 refers
+// to the pc destination operand, wheras destination operand 1 refers to the
+// link register specified in rd.
+
+// Semantic function for Exercise 4 - jal.
+// End semantic function for Exercise 4 - jal.
+
+// Source operand 0 refers to the base registers specified by rs1, source
+// operand 1 contains the immediate value. Destination operand 0 refers to the
+// pc destination operand, wheras destination operand 1 refers to the
+// link register specified in rd.
+
+// Semantic function for Exercise 4 - jalr.
+// End semantic functions for Exercise 4 - jalr.
+
+// For each store instruction semantic function, source operand 0 is the base
+// register, source operand 1 is the offset, while source operand 2 is the value
+// to be stored referred to by rs2.
+
+// Semantic functions for Exercise 5.
+// End semantic functions for Exercise 5.
+
+// Each of the load instructions are modeled by a pair of semantic instruction
+// functions. The "main" function computes the effective address and initiates
+// the load, the "child" function processes the load result and writes it back
+// to the destination register.
+// For the "main" semantic function, source operand 0 is the base register,
+// source operand 1 the offset. Destination operand 0 is the register specified
+// by rd. The "child" semantic function will get a copy of the destination
+// operand.
+
+// Semantic functions for Exercise 6.
+// End semantic functions for Exercise 6.
+
+// Exercises End.
+
+// The Fence instruction takes a single source operand (index 0) which consists
+// of an immediate value containing the right justified concatenation of the FM,
+// predecessor, and successor bit fields of the instruction.
+void RV32IFence(Instruction *instruction);
+
+// Software breakpoint instruction.
+void RV32IEbreak(Instruction *instruction);
+
+void RV32IllegalInstruction(Instruction *inst);
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_RV32I_INSTRUCTIONS_H_
diff --git a/riscv_semantic_functions/solution/BUILD b/riscv_semantic_functions/solution/BUILD
new file mode 100644
index 0000000..97c914a
--- /dev/null
+++ b/riscv_semantic_functions/solution/BUILD
@@ -0,0 +1,40 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Semantic functions for the MPACT-Sim codelab
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "riscv32i",
+    srcs = [
+        "rv32i_instructions.cc",
+        "zicsr_instructions.cc",
+    ],
+    hdrs = [
+        "rv32i_instructions.h",
+        "zicsr_instructions.h",
+    ],
+    copts = ["-O3"],
+    deps = [
+        "@mpact-riscv//riscv:riscv_state",
+        "@mpact-sim//mpact/sim/generic:arch_state",
+        "@mpact-sim//mpact/sim/generic:core",
+        "@mpact-sim//mpact/sim/generic:instruction",
+        "@mpact-sim//mpact/sim/util/memory",
+    ],
+)
diff --git a/riscv_semantic_functions/solution/rv32i_instructions.cc b/riscv_semantic_functions/solution/rv32i_instructions.cc
new file mode 100644
index 0000000..3be4551
--- /dev/null
+++ b/riscv_semantic_functions/solution/rv32i_instructions.cc
@@ -0,0 +1,283 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_semantic_functions/solution/rv32i_instructions.h"
+
+#include <cstdint>
+#include <functional>
+#include <iostream>
+
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/instruction_helpers.h"
+#include "riscv/riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using generic::DataBuffer;
+using riscv::RiscVState;
+
+void RV32IllegalInstruction(Instruction *inst) {
+  inst->state()
+      ->program_error_controller()
+      ->GetProgramError(generic::ProgramErrorController::kInternalErrorName)
+      ->Raise(
+          absl::StrCat("Illegal instruction at ", absl::Hex(inst->address())));
+  std::cerr << absl::StrCat("Illegal instruction at 0x",
+                            absl::Hex(inst->address()), "\n");
+}
+
+// The following instruction semantic functions implement basic alu operations.
+// They are used for both register-register and register-immediate versions of
+// the corresponding instructions.
+
+// Semantic functions for Exercise 2.
+void RV32IAdd(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a + b; });
+}
+
+void RV32IAnd(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a & b; });
+}
+
+void RV32IOr(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a | b; });
+}
+
+void RV32ISll(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(
+      instruction, [](uint32_t a, uint32_t b) { return a << (b & 0x1f); });
+}
+
+void RV32ISltu(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(
+      instruction, [](uint32_t a, uint32_t b) { return (a < b) ? 1 : 0; });
+}
+
+void RV32ISra(Instruction *instruction) {
+  generic::BinaryOp<uint32_t, int32_t, uint32_t>(
+      instruction, [](int32_t a, uint32_t b) { return a >> (b & 0x1f); });
+}
+
+void RV32ISrl(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(
+      instruction, [](uint32_t a, uint32_t b) { return a >> (b & 0x1f); });
+}
+
+void RV32ISub(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a - b; });
+}
+
+void RV32IXor(Instruction *instruction) {
+  generic::BinaryOp<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a ^ b; });
+}
+// End semantic functions for exercise 2.
+
+// Semantic functions for Exercise 3.
+// Load upper immediate. It is assumed that the decoder already shifted the
+// immediate.
+void RV32ILui(Instruction *instruction) {
+  generic::UnaryOp<uint32_t>(instruction, [](uint32_t a) { return a; });
+}
+
+// Add upper immediate to PC (for PC relative addressing). It is assumed that
+// the decoder already shifted the immediate.
+void RV32IAuipc(Instruction *instruction) {
+  generic::UnaryOp<uint32_t>(instruction, [instruction](uint32_t a) {
+    return a + instruction->address();
+  });
+}
+// End semantic functions for Exercise 3.
+
+// Semantic functions for Exercise 4.
+// Branch instructions.
+
+template <typename OperandType>
+static inline void BranchConditional(
+    Instruction *instruction,
+    std::function<bool(OperandType, OperandType)> cond) {
+  OperandType a = generic::GetInstructionSource<OperandType>(instruction, 0);
+  OperandType b = generic::GetInstructionSource<OperandType>(instruction, 1);
+  if (cond(a, b)) {
+    uint32_t offset = generic::GetInstructionSource<uint32_t>(instruction, 2);
+    uint32_t target = offset + instruction->address();
+    DataBuffer *db = instruction->Destination(0)->AllocateDataBuffer();
+    db->Set<uint32_t>(0, target);
+    db->Submit();
+  }
+}
+
+void RV32IBeq(Instruction *instruction) {
+  BranchConditional<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a == b; });
+}
+
+void RV32IBge(Instruction *instruction) {
+  BranchConditional<int32_t>(instruction,
+                             [](int32_t a, int32_t b) { return a >= b; });
+}
+
+void RV32IBgeu(Instruction *instruction) {
+  BranchConditional<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a >= b; });
+}
+
+void RV32IBlt(Instruction *instruction) {
+  BranchConditional<int32_t>(instruction,
+                             [](int32_t a, int32_t b) { return a < b; });
+}
+
+void RV32IBltu(Instruction *instruction) {
+  BranchConditional<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a < b; });
+}
+
+void RV32IBne(Instruction *instruction) {
+  BranchConditional<uint32_t>(instruction,
+                              [](uint32_t a, uint32_t b) { return a != b; });
+}
+
+// Jal instruction.
+void RV32IJal(Instruction *instruction) {
+  uint32_t offset = instruction->Source(0)->AsUint32(0);
+  uint32_t target = offset + instruction->address();
+  uint32_t return_address = instruction->address() + instruction->size();
+  auto *db = instruction->Destination(0)->AllocateDataBuffer();
+  db->Set<uint32_t>(0, target);
+  db->Submit();
+  db = instruction->Destination(1)->AllocateDataBuffer();
+  db->Set<uint32_t>(0, return_address);
+  db->Submit();
+}
+
+// Jalr instruction.
+void RV32IJalr(Instruction *instruction) {
+  uint32_t reg_base = instruction->Source(0)->AsUint32(0);
+  uint32_t offset = instruction->Source(1)->AsUint32(0);
+  uint32_t target = offset + reg_base;
+  uint32_t return_address = instruction->address() + instruction->size();
+  auto *db = instruction->Destination(0)->AllocateDataBuffer();
+  db->Set<uint32_t>(0, target);
+  db->Submit();
+  db = instruction->Destination(1)->AllocateDataBuffer();
+  db->Set<uint32_t>(0, return_address);
+  db->Submit();
+}
+// End of semantic functions for Exercise 4.
+
+// Semantic functions for Exercise 5.
+// Store instructions.
+
+template <typename ValueType>
+inline void StoreValue(Instruction *instruction) {
+  auto base = generic::GetInstructionSource<uint32_t>(instruction, 0);
+  auto offset = generic::GetInstructionSource<uint32_t>(instruction, 1);
+  uint32_t address = base + offset;
+  auto value = generic::GetInstructionSource<ValueType>(instruction, 2);
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  auto *db = state->db_factory()->Allocate(sizeof(ValueType));
+  db->Set<ValueType>(0, value);
+  state->StoreMemory(instruction, address, db);
+  db->DecRef();
+}
+
+void RV32ISw(Instruction *instruction) { StoreValue<uint32_t>(instruction); }
+
+void RV32ISh(Instruction *instruction) { StoreValue<uint16_t>(instruction); }
+
+void RV32ISb(Instruction *instruction) { StoreValue<uint8_t>(instruction); }
+
+// End of semantic functions for Exercise 5.
+
+// Semantic functions for Exercise 6.
+// Load instructions.
+template <typename ValueType>
+inline void LoadValue(Instruction *instruction) {
+  auto base = generic::GetInstructionSource<uint32_t>(instruction, 0);
+  auto offset = generic::GetInstructionSource<uint32_t>(instruction, 1);
+  uint32_t address = base + offset;
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  auto *db = state->db_factory()->Allocate(sizeof(ValueType));
+  db->set_latency(0);
+  auto *context = new riscv::LoadContext(db);
+  state->LoadMemory(instruction, address, db, instruction->child(), context);
+  context->DecRef();
+}
+
+template <typename ValueType>
+inline void LoadValueChild(Instruction *instruction) {
+  auto *context = static_cast<riscv::LoadContext *>(instruction->context());
+  uint32_t value = static_cast<uint32_t>(context->value_db->Get<ValueType>(0));
+  auto *db = instruction->Destination(0)->AllocateDataBuffer();
+  db->Set<uint32_t>(0, value);
+  db->Submit();
+}
+
+void RV32ILw(Instruction *instruction) { LoadValue<uint32_t>(instruction); }
+
+void RV32ILwChild(Instruction *instruction) {
+  LoadValueChild<uint32_t>(instruction);
+}
+
+void RV32ILh(Instruction *instruction) { LoadValue<int16_t>(instruction); }
+
+void RV32ILhChild(Instruction *instruction) {
+  LoadValueChild<int16_t>(instruction);
+}
+
+void RV32ILhu(Instruction *instruction) { LoadValue<uint16_t>(instruction); }
+
+void RV32ILhuChild(Instruction *instruction) {
+  LoadValueChild<uint16_t>(instruction);
+}
+
+void RV32ILb(Instruction *instruction) { LoadValue<int8_t>(instruction); }
+
+void RV32ILbChild(Instruction *instruction) {
+  LoadValueChild<int8_t>(instruction);
+}
+
+void RV32ILbu(Instruction *instruction) { LoadValue<uint8_t>(instruction); }
+
+void RV32ILbuChild(Instruction *instruction) {
+  LoadValueChild<uint8_t>(instruction);
+}
+// End of semantic functions for Exercise 6.
+
+// Fence.
+void RV32IFence(Instruction *instruction) {
+  uint32_t bits = instruction->Source(0)->AsUint32(0);
+  int fm = (bits >> 8) & 0xf;
+  int predecessor = (bits >> 4) & 0xf;
+  int successor = bits & 0xf;
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  state->Fence(instruction, fm, predecessor, successor);
+}
+
+// Ebreak - software breakpoint instruction.
+void RV32IEbreak(Instruction *instruction) {
+  auto *state = static_cast<RiscVState *>(instruction->state());
+  state->EBreak(instruction);
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_semantic_functions/solution/rv32i_instructions.h b/riscv_semantic_functions/solution/rv32i_instructions.h
new file mode 100644
index 0000000..c236cf9
--- /dev/null
+++ b/riscv_semantic_functions/solution/rv32i_instructions.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_RV32I_INSTRUCTIONS_H_
+#define MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_RV32I_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file contains the declarations of the instruction semantic functions
+// for the RiscV 32i instructions.
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::Instruction;
+
+//
+// All semantic functions must have the signature void(Instruction *).
+//
+
+// For the following, source operand 0 refers to the register specified in rs1,
+// and source operand 1 refers to either the register specified in rs2, or the
+// immediate. Destination operand 0 refers to the register specified in rd.
+
+// Semantic functions for Exercise 2.
+void RV32IAdd(Instruction *instruction);
+void RV32IAnd(Instruction *instruction);
+void RV32IOr(Instruction *instruction);
+void RV32ISll(Instruction *instruction);
+void RV32ISltu(Instruction *instruction);
+void RV32ISub(Instruction *instruction);
+void RV32ISra(Instruction *instruction);
+void RV32ISrl(Instruction *instruction);
+void RV32IXor(Instruction *instruction);
+// End semantic functions for Exercise 2.
+
+// For the following, source operand 0 refers to the 20-bit immediate value,
+// already shifted left by 12 to form a 32-bit immediate.
+
+// Semantic functions for Exercise 3.
+void RV32ILui(Instruction *instruction);
+void RV32IAuipc(Instruction *instruction);
+// End semantic functions for Exercise 3.
+
+// For the following branch instructions. Source operand 0 refers to the
+// register specified by rs1, source operand 2 refers to the register specified
+// by rs2, and source operand 3 refers to the immediate offset. Destination
+// operand 0 refers to the pc destination operand.
+
+// Semantic functions for Exercise 4 - branches.
+void RV32IBeq(Instruction *instruction);
+void RV32IBge(Instruction *instruction);
+void RV32IBgeu(Instruction *instruction);
+void RV32IBlt(Instruction *instruction);
+void RV32IBltu(Instruction *instruction);
+void RV32IBne(Instruction *instruction);
+// End semantic functions for Exercise 4 - branches.
+
+// Source operand 0 contains the immediate value. Destination operand 0 refers
+// to the pc destination operand, wheras destination operand 1 refers to the
+// link register specified in rd.
+
+// Semantic function for Exercise 4 - jal.
+void RV32IJal(Instruction *instruction);
+// End semantic function for Exercise 4 - jal.
+
+// Source operand 0 refers to the base registers specified by rs1, source
+// operand 1 contains the immediate value. Destination operand 0 refers to the
+// pc destination operand, wheras destination operand 1 refers to the
+// link register specified in rd.
+
+// Semantic function for Exercise 4 - jalr.
+void RV32IJalr(Instruction *instruction);
+// End semantic functions for Exercise 4 - jalr.
+
+// For each store instruction semantic function, source operand 0 is the base
+// register, source operand 1 is the offset, while source operand 2 is the value
+// to be stored referred to by rs2.
+
+// Semantic functions for Exercise 5.
+void RV32ISb(Instruction *instruction);
+void RV32ISh(Instruction *instruction);
+void RV32ISw(Instruction *instruction);
+// End semantic functions for Exercise 5.
+
+// Each of the load instructions are modeled by a pair of semantic instruction
+// functions. The "main" function computes the effective address and initiates
+// the load, the "child" function processes the load result and writes it back
+// to the destination register.
+// For the "main" semantic function, source operand 0 is the base register,
+// source operand 1 the offset. Destination operand 0 is the register specified
+// by rd. The "child" semantic function will get a copy of the destination
+// operand.
+
+// Semantic functions for Exercise 6.
+void RV32ILb(Instruction *instruction);
+void RV32ILbChild(Instruction *instruction);
+void RV32ILbu(Instruction *instruction);
+void RV32ILbuChild(Instruction *instruction);
+void RV32ILh(Instruction *instruction);
+void RV32ILhChild(Instruction *instruction);
+void RV32ILhu(Instruction *instruction);
+void RV32ILhuChild(Instruction *instruction);
+void RV32ILw(Instruction *instruction);
+void RV32ILwChild(Instruction *instruction);
+// End semantic functions for Exercise 6.
+
+// Exercises End.
+
+// The Fence instruction takes a single source operand (index 0) which consists
+// of an immediate value containing the right justified concatenation of the FM,
+// predecessor, and successor bit fields of the instruction.
+void RV32IFence(Instruction *instruction);
+
+// Software breakpoint instruction.
+void RV32IEbreak(Instruction *instruction);
+
+void RV32IllegalInstruction(Instruction *inst);
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_RV32I_INSTRUCTIONS_H_
diff --git a/riscv_semantic_functions/solution/zicsr_instructions.cc b/riscv_semantic_functions/solution/zicsr_instructions.cc
new file mode 100644
index 0000000..02a60a8
--- /dev/null
+++ b/riscv_semantic_functions/solution/zicsr_instructions.cc
@@ -0,0 +1,35 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_semantic_functions/solution/zicsr_instructions.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+void RV32ZiCsrs(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+void RV32ZiCsrwNr(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+void RV32ZiCsrsNw(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_semantic_functions/solution/zicsr_instructions.h b/riscv_semantic_functions/solution/zicsr_instructions.h
new file mode 100644
index 0000000..3f140a5
--- /dev/null
+++ b/riscv_semantic_functions/solution/zicsr_instructions.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_ZICSR_INSTRUCTIONS_H_
+#define MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_ZICSR_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::Instruction;
+
+void RV32ZiCsrs(Instruction *instruction);
+void RV32ZiCsrwNr(Instruction *instruction);
+void RV32ZiCsrsNw(Instruction *instruction);
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_SOLUTION_ZICSR_INSTRUCTIONS_H_
diff --git a/riscv_semantic_functions/zicsr_instructions.cc b/riscv_semantic_functions/zicsr_instructions.cc
new file mode 100644
index 0000000..e13b9ff
--- /dev/null
+++ b/riscv_semantic_functions/zicsr_instructions.cc
@@ -0,0 +1,35 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv_semantic_functions/zicsr_instructions.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+void RV32ZiCsrs(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+void RV32ZiCsrwNr(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+void RV32ZiCsrsNw(Instruction *instruction) {
+  // TODO(): Fill in semantics.
+}
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv_semantic_functions/zicsr_instructions.h b/riscv_semantic_functions/zicsr_instructions.h
new file mode 100644
index 0000000..788b572
--- /dev/null
+++ b/riscv_semantic_functions/zicsr_instructions.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_ZICSR_INSTRUCTIONS_H_
+#define MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_ZICSR_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace codelab {
+
+using ::mpact::sim::generic::Instruction;
+
+void RV32ZiCsrs(Instruction *instruction);
+void RV32ZiCsrwNr(Instruction *instruction);
+void RV32ZiCsrsNw(Instruction *instruction);
+
+}  // namespace codelab
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // MPACT_SIM_CODELABS_SEMANTIC_FUNCTIONS_ZICSR_INSTRUCTIONS_H_