Add platform for sparrow board

Bug: 325672220
Change-Id: Ie1a825ab55bdab903f4b13f0c9da29af959f4ea3
diff --git a/platforms/sparrow/builtins.repl b/platforms/sparrow/builtins.repl
new file mode 100644
index 0000000..470f2c7
--- /dev/null
+++ b/platforms/sparrow/builtins.repl
@@ -0,0 +1,36 @@
+builtins
+start hello
+sleep 100
+stop hello
+
+start fibonacci
+sleep 1000
+stop fibonacci
+
+start keyval
+sleep 100
+stop keyval
+
+start logtest
+sleep 100
+stop logtest
+
+start panic
+sleep 100
+stop panic
+
+start timer
+sleep 2500
+stop timer
+
+bundles
+mstats
+
+mstats
+start mltest
+mstats
+sleep 10000
+stop mltest
+
+bundles
+mstats
diff --git a/platforms/sparrow/cantrip.mk b/platforms/sparrow/cantrip.mk
new file mode 100644
index 0000000..622ed67
--- /dev/null
+++ b/platforms/sparrow/cantrip.mk
@@ -0,0 +1,13 @@
+# For debug builds override the hardware memory config (4MiB -> 64MiB);
+# this assumes debug builds run in simulation only or the target hardware
+# has a large memory config.
+CANTRIP_EXTRA_CMAKE_OPTS_DEBUG := -DKernelCustomDTSOverlay="${ROOTDIR}/build/platforms/nexus/overlay-debug.dts"
+
+# seL4 platform identity passed to cargo to bring in platform-specific deps
+CONFIG_PLATFORM           := CONFIG_PLAT_NEXUS
+
+# NB: seL4 platform identity is passed to cargo to bring in platform-specific deps
+# NB: the kcargo shell function searchs for CONFIG_PLATFORM
+CANTRIP_GLOBAL_FEATURES_RELEASE    := ${CONFIG_PLATFORM}
+CANTRIP_GLOBAL_FEATURES_DEBUG      := ${CANTRIP_GLOBAL_FEATURES_RELEASE}
+
diff --git a/platforms/sparrow/cantrip_apps.mk b/platforms/sparrow/cantrip_apps.mk
new file mode 100644
index 0000000..de31bee
--- /dev/null
+++ b/platforms/sparrow/cantrip_apps.mk
@@ -0,0 +1,3 @@
+# CantripOS Test Application configuration
+CANTRIP_APP_ARCH   := riscv32
+RUST_TARGET     := riscv32imac-unknown-none-elf
diff --git a/platforms/sparrow/cantrip_builtins.mk b/platforms/sparrow/cantrip_builtins.mk
new file mode 100644
index 0000000..c8d5c33
--- /dev/null
+++ b/platforms/sparrow/cantrip_builtins.mk
@@ -0,0 +1,50 @@
+# Copyright 2022 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.
+
+CANTRIP_APPS_RELEASE  := $(CANTRIP_OUT_C_APP_RELEASE)/hello/hello.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/fibonacci/fibonacci.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/keyval/keyval.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/logtest/logtest.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/mltest/mltest.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/panic/panic.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/playback/playback.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/sound/sound.app \
+                         $(CANTRIP_OUT_C_APP_RELEASE)/suicide/suicide.app \
+                         $(CANTRIP_OUT_RUST_APP_RELEASE)/timer/timer.app
+# Builtins are loaded from SPI flash which is 16M by default
+CANTRIP_MODEL_RELEASE := $(OUT)/kelvin/sw/bazel_out/hello_world.kelvin \
+                         $(OUT)/kelvin_iree/sparrow_iree/samples/microbenchmarks/conv1x1_test_emitc_static.kelvin
+ifneq ("$(wildcard $(ROOTDIR)/ml/ml-models)", "")
+CANTRIP_MODEL_RELEASE  += $(OUT)/kelvin/sw/bazel_out/soundstream_encoder_non_streaming.kelvin
+else
+# TODO(sleffler): fallback to IREE version
+endif
+
+CANTRIP_APPS_DEBUG    := $(CANTRIP_OUT_C_APP_DEBUG)/hello/hello.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/fibonacci/fibonacci.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/keyval/keyval.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/logtest/logtest.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/mltest/mltest.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/panic/panic.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/playback/playback.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/sound/sound.app \
+                         $(CANTRIP_OUT_C_APP_DEBUG)/suicide/suicide.app \
+                         $(CANTRIP_OUT_RUST_APP_DEBUG)/timer/timer.app
+# NB: debug builds only run on Renode where we have 16M for builtins
+CANTRIP_MODEL_DEBUG   := $(OUT)/kelvin_iree/sparrow_iree/samples/microbenchmarks/conv1x1_test_emitc_static.kelvin
+
+CANTRIP_SCRIPTS       := $(ROOTDIR)/build/platforms/$(PLATFORM)/builtins.repl
+
+$(patsubst %.kelvin,%.elf,$(CANTRIP_MODEL_RELEASE)): kelvin_hello_world kelvin_soundstream
+$(CANTRIP_MODEL_DEBUG): iree_model_builtins
diff --git a/platforms/sparrow/iree.mk b/platforms/sparrow/iree.mk
new file mode 100644
index 0000000..782d93b
--- /dev/null
+++ b/platforms/sparrow/iree.mk
@@ -0,0 +1,181 @@
+# 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.
+
+IREE_SRC:=$(ROOTDIR)/toolchain/iree
+TOOLCHAINRV32_PATH:=$(CACHE)/toolchain_kelvin
+IREE_COMPILER_DIR:=${CACHE}/iree_compiler
+
+IREE_RUNTIME_ROOT:=$(ROOTDIR)/sw/vec_iree
+MODEL_SRC_DIR:=$(ROOTDIR)/ml/ml-models-public
+IREE_RUNTIME_OUT=$(OUT)/kelvin_iree
+IREE_RUNTIME_STATS_OUT=$(OUT)/kelvin_iree_stats
+
+MODEL_INTERNAL_SRC_DIR:=$(ROOTDIR)/ml/ml-models
+IREE_RUNTIME_INTERNAL_OUT=$(OUT)/kelvin_iree_internal
+IREE_RUNTIME_INTERNAL_STATS_OUT=$(OUT)/kelvin_iree_internal_stats
+
+RV32_EXE_LINKER_FLAGS=-Wl,--print-memory-usage
+
+RV32_COMPILER_FLAGS=-g3 \
+    -ggdb
+
+# The following targets are always rebuilt when the iree target is made
+
+iree_check:
+	@if echo "$${PIN_TOOLCHAINS}" |grep -qw 'iree'; then \
+        echo "****************************************************"; \
+        echo "*                                                  *"; \
+        echo "*  PIN_TOOLCHAINS includes iree! Skipping the      *"; \
+        echo "*  download of the latest IREE compiler binaries.  *"; \
+        echo "*  Please DO NOT file bugs for IREE mis-behavior!  *"; \
+        echo "*                                                  *"; \
+        echo "****************************************************"; \
+	else \
+		echo Updating $(IREE_SRC) submodules...; \
+		git -C $(IREE_SRC) submodule sync && \
+	  		git -C $(IREE_SRC) submodule update --init --jobs=8 --depth=10; \
+	fi
+
+$(IREE_COMPILER_DIR)/build.ninja: | iree_check
+	cmake -G Ninja -B $(IREE_COMPILER_DIR) \
+	    -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \
+	    -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+	    -DCMAKE_INSTALL_PREFIX=$(IREE_COMPILER_DIR)/install \
+	    -DIREE_BUILD_TESTS=OFF \
+	    $(IREE_SRC)
+
+## Builds the IREE compiler from source and records the HEAD commit ID
+iree_compiler_src: $(IREE_COMPILER_DIR)/build.ninja | iree_check
+	cmake --build $(IREE_COMPILER_DIR) --target install
+	git -C "$(IREE_SRC)" rev-parse HEAD > $(IREE_COMPILER_DIR)/tag
+
+$(IREE_COMPILER_DIR):
+	mkdir -p $(IREE_COMPILER_DIR)
+
+## Downloads the latest release of the IREE compiler and tflite tools.
+#
+# The release tag and commit are recorded for consistency checks in
+# the `iree_runtime` target. The outputs of this target are placed in
+# out/host/iree_compiler.
+#
+iree_compiler: | $(IREE_COMPILER_DIR)
+	$(ROOTDIR)/scripts/download_iree_compiler.py --iree_compiler_dir "$(IREE_COMPILER_DIR)"
+iree_commit_check:
+	$(ROOTDIR)/scripts/check-iree-commit.sh "$(IREE_SRC)" "$(IREE_COMPILER_DIR)"
+
+IREE_RUNTIME_DEFAULT_CONFIG :=\
+	-DCMAKE_TOOLCHAIN_FILE="$(IREE_RUNTIME_ROOT)/cmake/riscv_iree_gcc.cmake" \
+	-DCMAKE_BUILD_TYPE=MinSizeRel \
+	-DIREE_HOST_BIN_DIR="$(IREE_COMPILER_DIR)/install/bin" \
+	-DRISCV_TOOLCHAIN_ROOT=$(TOOLCHAINRV32_PATH) \
+	-DRISCV_COMPILER_FLAGS="$(RV32_COMPILER_FLAGS)" \
+	-DCMAKE_EXE_LINKER_FLAGS="$(RV32_EXE_LINKER_FLAGS)" \
+	-DBUILD_WITH_KELVIN=ON \
+	-DBUILD_WITH_RVV=OFF
+
+IREE_RUNTIME_CONFIG :=\
+	$(IREE_RUNTIME_DEFAULT_CONFIG) \
+	-DPRINT_IREE_STATS=OFF
+
+IREE_RUNTIME_STATS_CONFIG :=\
+	$(IREE_RUNTIME_DEFAULT_CONFIG) \
+	-DPRINT_IREE_STATS=ON
+
+$(IREE_RUNTIME_OUT)/build.ninja: | iree_compiler iree_check iree_commit_check
+	cmake -G Ninja -B $(IREE_RUNTIME_OUT) \
+	    $(IREE_RUNTIME_CONFIG) \
+	    $(MODEL_SRC_DIR)
+
+## Model artifact used in cantrip-builtins-*
+#
+# IREE executables used in cantrip-builtins-*
+iree_model_builtins: $(IREE_RUNTIME_OUT)/build.ninja | iree_check iree_commit_check
+	PYTHONPATH=$(IREE_COMPILER_DIR) cmake --build $(IREE_RUNTIME_OUT) --target \
+		sparrow_iree/samples/microbenchmarks/conv1x1_test_emitc_static
+	ln -sfn $(IREE_RUNTIME_OUT)/sparrow_iree/samples/microbenchmarks/conv1x1_test_emitc_static \
+		$(IREE_RUNTIME_OUT)/sparrow_iree/samples/microbenchmarks/conv1x1_test_emitc_static.elf
+
+$(IREE_RUNTIME_INTERNAL_OUT)/build.ninja: | iree_check iree_commit_check
+	cmake -G Ninja -B $(IREE_RUNTIME_INTERNAL_OUT) \
+	    $(IREE_RUNTIME_CONFIG) \
+	    $(MODEL_INTERNAL_SRC_DIR)
+
+$(IREE_RUNTIME_STATS_OUT)/build.ninja: | iree_check iree_commit_check
+	cmake -G Ninja -B $(IREE_RUNTIME_STATS_OUT) \
+	    $(IREE_RUNTIME_STATS_CONFIG) \
+	    $(MODEL_SRC_DIR)
+
+$(IREE_RUNTIME_INTERNAL_STATS_OUT)/build.ninja: | iree_check iree_commit_check
+	cmake -G Ninja -B $(IREE_RUNTIME_INTERNAL_STATS_OUT) \
+	    $(IREE_RUNTIME_STATS_CONFIG) \
+	    $(MODEL_INTERNAL_SRC_DIR)
+
+## Installs the IREE runtime applications.
+#
+# Unlike the `iree_compiler` target, this target actually builds the runtime
+# from source in toolchain/iree. The results of the build are placed in
+# out/kelvin_iree.
+#
+# In general, you probably want the `iree` target instead, which combines
+# `iree_compiler` and `iree_runtime`.
+iree_runtime: $(IREE_RUNTIME_OUT)/build.ninja | iree_check iree_commit_check
+	PYTHONPATH=$(IREE_COMPILER_DIR) cmake --build $(IREE_RUNTIME_OUT)
+
+## Installs the IREE compiler and its runtime applications.
+iree: iree_compiler iree_runtime
+
+## Installs the IREE runtime internal applications.
+#
+# Unlike the `iree_runtime` target, this target builds the runtime application
+# for internal models. The results of the build are placed in
+# out/kelvin_iree_internal.
+#
+# In general, you probably want the `iree_runtime` target instead.
+iree_runtime_internal: $(IREE_RUNTIME_INTERNAL_OUT)/build.ninja | \
+		iree_check iree_commit_check
+	PYTHONPATH=$(IREE_COMPILER_DIR) cmake --build $(IREE_RUNTIME_INTERNAL_OUT)
+
+## Installs the IREE compiler and internal runtime applications.
+#
+# In general, you probably want to run `iree` target to build the public
+# applications.
+iree_internal: iree_compiler iree_runtime_internal
+
+## Installs the IREE runtime applications, with IREE stats printed.
+iree_runtime_stats: $(IREE_RUNTIME_STATS_OUT)/build.ninja | iree_check iree_commit_check
+	PYTHONPATH=$(IREE_COMPILER_DIR) cmake --build $(IREE_RUNTIME_STATS_OUT)
+
+## Installs the IREE compiler and its runtime applications with IREE stats, for single core testing.
+iree_stats: iree_compiler iree_runtime_stats
+
+## Installs the IREE runtime internal applications, with IREE stats printed.
+iree_internal_runtime_stats: $(IREE_RUNTIME_INTERNAL_STATS_OUT)/build.ninja | iree_check iree_commit_check
+	PYTHONPATH=$(IREE_COMPILER_DIR) cmake --build $(IREE_RUNTIME_INTERNAL_STATS_OUT)
+
+## Installs the IREE compiler and its runtime applications with IREE stats, for single core testing.
+iree_internal_stats: iree_compiler iree_internal_runtime_stats
+
+## Clean IREE compiler and runtime applications.
+iree_clean:
+	rm -rf $(IREE_COMPILER_DIR) $(IREE_RUNTIME_OUT) $(IREE_RUNTIME_STATS_OUT)
+
+## Clean IREE compiler and runtime application of internal models
+iree_internal_clean:
+	rm -rf $(IREE_COMPILER_DIR) $(IREE_RUNTIME_INTERNAL_OUT) $(IREE_RUNTIME_INTERNAL_STATS_OUT)
+
+.PHONY:: iree iree_check iree_compiler iree_runtime iree_clean
+.PHONY:: iree_commit_check iree_compiler_src
+.PHONY:: iree_runtime_internal iree_internal iree_internal_clean
+.PHONY:: iree_runtime_stats iree_stats iree_internal_runtime_stats iree_internal_stats
+.PHONY:: iree_model_builtins
diff --git a/platforms/sparrow/kelvin.mk b/platforms/sparrow/kelvin.mk
new file mode 100644
index 0000000..f986586
--- /dev/null
+++ b/platforms/sparrow/kelvin.mk
@@ -0,0 +1,130 @@
+# Build Kelvin ISS and artifacts.
+
+KELVIN_SW_SRC_DIR := $(ROOTDIR)/sw/kelvin
+KELVIN_SW_OUT_DIR := $(OUT)/kelvin/sw
+KELVIN_SW_BAZEL_OUT_DIR := $(KELVIN_SW_OUT_DIR)/bazel_out
+KELVIN_SW_TESTLOG_DIR := $(KELVIN_SW_OUT_DIR)/bazel_testlog
+
+KELVIN_HW_SRC_DIR := $(ROOTDIR)/hw/kelvin
+KELVIN_HW_OUT_DIR := $(OUT)/kelvin/hw
+KELVIN_HW_BAZEL_OUT_DIR := $(KELVIN_HW_OUT_DIR)/bazel_out
+KELVIN_HW_TESTLOG_DIR := $(KELVIN_HW_OUT_DIR)/bazel_testlog
+
+KELVIN_SIM_SRC_DIR := $(ROOTDIR)/sim/kelvin
+KELVIN_SIM_OUT_DIR := $(OUT)/kelvin/sim
+
+MODEL_INTERNAL_SRC_DIR:=$(ROOTDIR)/ml/ml-models
+
+$(KELVIN_SW_BAZEL_OUT_DIR):
+	mkdir -p "$(KELVIN_SW_BAZEL_OUT_DIR)"
+
+$(KELVIN_SW_TESTLOG_DIR):
+	mkdir -p "$(KELVIN_SW_TESTLOG_DIR)"
+
+## Build Kelvin SW artifacts
+#
+# Some of the artifacts are built with bazel, and need to be copied to out/
+kelvin_sw: | $(KELVIN_SW_BAZEL_OUT_DIR)
+	cd "$(KELVIN_SW_SRC_DIR)" && \
+		bazel build //...
+	cd "$(KELVIN_SW_SRC_DIR)/bazel-out" && \
+		find . -type f \( \
+			-wholename "*ST-*/*.elf" -o \
+			-wholename "*ST-*/*.bin" \) \
+			-exec cp -f {} "$(KELVIN_SW_BAZEL_OUT_DIR)/" \;
+
+## Build Kelvin hello_world artifacts
+#
+# Used as the simplest Kelvin SW that has all the syntax CantripOS expects
+kelvin_hello_world: | $(KELVIN_SW_BAZEL_OUT_DIR)
+	cd "$(KELVIN_SW_SRC_DIR)" && \
+		bazel build //examples/hello_world:all && \
+		cd bazel-out ; find . -type f \( \
+			-wholename "*ST-*/*.elf" -o \
+			-wholename "*ST-*/*.bin" \) \
+			-exec cp -f {} "$(KELVIN_SW_BAZEL_OUT_DIR)/" \;
+
+## Build Kelvin soundstream artifacts
+kelvin_soundstream: | $(KELVIN_SW_BAZEL_OUT_DIR)
+	cd "$(MODEL_INTERNAL_SRC_DIR)" && \
+		bazel build //quant_models/tflm/soundstream:soundstream_encoder_non_streaming && \
+		cd bazel-out ; find . -type f -wholename "*ST-*/*.elf" \
+			-exec cp -f {} "$(KELVIN_SW_BAZEL_OUT_DIR)/" \;
+
+
+## Test Kelvin SW artifacts
+#
+# Test Kelvin SW artifacts with kelvin ISS and SystemC simulations
+kelvin_sw_test: kelvin_sim kelvin_hw_sim | $(KELVIN_SW_TESTLOG_DIR)
+	cd "$(KELVIN_SW_SRC_DIR)" && \
+		bazel test --test_output=errors --test_tag_filters="-broken"  //... && \
+		cp -rf bazel-testlogs/tests "$(KELVIN_SW_TESTLOG_DIR)"
+
+## Clean Kelvin SW artifacts
+kelvin_sw_clean:
+	rm -rf "$(KELVIN_SW_OUT_DIR)"
+	cd "$(KELVIN_SW_SRC_DIR)" && bazel clean --expunge
+
+
+$(KELVIN_HW_BAZEL_OUT_DIR):
+	mkdir -p "$(KELVIN_HW_BAZEL_OUT_DIR)"
+
+$(KELVIN_HW_TESTLOG_DIR):
+	mkdir -p "$(KELVIN_HW_TESTLOG_DIR)"
+
+## Verilog Source for Kelvin
+#
+# This generates the kelvin.v file that can be used to update hw/matcha
+kelvin_hw_verilog: | $(KELVIN_HW_BAZEL_OUT_DIR)
+	cd "$(KELVIN_HW_SRC_DIR)" && \
+		bazel clean --expunge && \
+			bazel build //hdl/chisel:matcha_kelvin_verilog && \
+		cp -rf bazel-bin/hdl/chisel/kelvin.v "$(KELVIN_HW_BAZEL_OUT_DIR)"
+
+## Verilated Kelvin HW simulator
+kelvin_hw_sim: | $(KELVIN_HW_BAZEL_OUT_DIR)
+	cd "$(KELVIN_HW_SRC_DIR)" && \
+		bazel build //tests/verilator_sim:core_sim && \
+		cp -rf bazel-bin/tests/verilator_sim/core_sim "$(KELVIN_HW_BAZEL_OUT_DIR)"
+
+## Tests for Kelvin HW
+kelvin_hw_test: | $(KELVIN_HW_TESTLOG_DIR)
+	cd "$(KELVIN_HW_SRC_DIR)" && \
+		bazel test --test_output=errors //... \
+		--sandbox_tmpfs_path=/tmp && \
+		cp -rf bazel-testlogs/tests "$(KELVIN_HW_TESTLOG_DIR)"
+
+## Clean Kelvin HW artifacts
+kelvin_hw_clean:
+	rm -rf "$(KELVIN_HW_OUT_DIR)"
+	cd "$(KELVIN_HW_SRC_DIR)" && bazel clean --expunge
+
+
+$(KELVIN_SIM_OUT_DIR):
+	mkdir -p "$(KELVIN_SIM_OUT_DIR)"
+
+## Build Kelvin ISS
+#
+# Build mpact-sim-based Kelvin ISS with bazel, and copy it to out/
+# Use /tmp as the bazel tmpfs to unblock CI
+kelvin_sim: | $(KELVIN_SIM_OUT_DIR)
+	cd "$(KELVIN_SIM_SRC_DIR)" && \
+		bazel build --sandbox_tmpfs_path=/tmp //sim:kelvin_sim \
+		  //sim/renode:renode_kelvin
+	cd "$(KELVIN_SIM_SRC_DIR)/bazel-bin" && \
+		cp -f sim/kelvin_sim "$(KELVIN_SIM_OUT_DIR)" && \
+		cp -f sim/renode/librenode_kelvin.so "$(KELVIN_SIM_OUT_DIR)"
+
+## Clean Kelvin ISS
+#
+# Clean the Kelvin ISS
+kelvin_sim_clean:
+	cd "$(KELVIN_SIM_SRC_DIR)" && \
+		bazel clean --expunge
+	rm -rf $(KELVIN_SIM_OUT_DIR)
+
+PHONY:: kelvin_hw_clean kelvin_hw_sim kelvin_hw_test kelvin_hw_verilog
+PHONY:: kelvin_sw kelvin_sw_clean kelvin_sw_test
+PHONY:: kelvin_hello_world
+PHONY:: kelvin_soundstream
+PHONY:: kelvin_sim kelvin_sim_clean
diff --git a/platforms/sparrow/matcha_hw.mk b/platforms/sparrow/matcha_hw.mk
new file mode 100644
index 0000000..3dd470c
--- /dev/null
+++ b/platforms/sparrow/matcha_hw.mk
@@ -0,0 +1,169 @@
+# Copyright 2022 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.
+
+MATCHA_SRC_DIR                := $(ROOTDIR)/hw/matcha
+MATCHA_OUT_DIR                := $(OUT)/matcha/hw
+MATCHA_VERILATOR_TB           := $(MATCHA_OUT_DIR)/sim-verilator/Vchip_sim_tb
+MATCHA_TESTLOG_DIR            := $(MATCHA_OUT_DIR)/test-log
+MATCHA_FPGA_BINARY_DIR        := $(MATCHA_OUT_DIR)/fpga_tests
+MATCHA_FPGA_MATCHA_BINARY_DIR := $(MATCHA_FPGA_BINARY_DIR)/matcha
+MATCHA_FPGA_KELVIN_BINARY_DIR := $(MATCHA_FPGA_BINARY_DIR)/kelvin
+BOOT_ROM_ELF                  := boot_rom.elf
+
+$(MATCHA_OUT_DIR):
+	mkdir -p $(MATCHA_OUT_DIR)
+
+## Regenerate Matcha HW files frop IPs and top_matcha definition.
+# This target uses Open Titan's autogen tools as well as the HW IPs to generate
+# the system verilog files as well as the DV register definition cores and
+# system verilog files. The source code is from both hw/opentitan-upstream and
+# hw/matcha/, while the output is stored at out/matcha/hw.
+#
+# This is a dev-only target (not for CI), as it modifies the hw/matcha source
+# tree with generated code.
+matcha_hw_generate_all: | $(MATCHA_OUT_DIR)
+	$(MAKE) -C "$(MATCHA_SRC_DIR)/hw" all
+
+## Build Matcha verilator testbench.
+# This target builds the verilator testbench binary from hw/matcha using
+# hw/opentitan-upstream as the library. The output is stored in
+# out/matcha/hw/.
+# This target is compute-intensive. Make sure you have a powerful enough machine
+# to build it.
+matcha_hw_verilator_sim: $(MATCHA_VERILATOR_TB)
+
+# TODO(ykwang): Copy only needed files into matcha output directory.
+# TODO(ykwang): Revise the structure of matcha output directory.
+$(MATCHA_VERILATOR_TB): $(MATCHA_OUT_DIR) verilator
+	cd $(MATCHA_SRC_DIR) && \
+		bazel build //hw:verilator
+	cd $(MATCHA_SRC_DIR) && \
+		cp -rf --no-preserve=mode bazel-bin/hw/build.verilator_real/* "$(MATCHA_OUT_DIR)" && \
+		chmod +x "$(MATCHA_OUT_DIR)/sim-verilator/Vchip_sim_tb"
+
+$(MATCHA_TESTLOG_DIR):
+	mkdir -p $(MATCHA_TESTLOG_DIR)
+
+## Build Matcha sw artifacts
+#
+# Checks the matcha sw code integrity for targets not covered by the verilator
+# tests.
+#
+matcha_sw_all: | $(MATCHA_OUT_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+	  bazel build --define DISABLE_VERILATOR_BUILD=true --build_tag_filters="-kelvin_fpga" \
+			//sw/device/...
+	cd $(MATCHA_SRC_DIR) && \
+	  find "bazel-out/" \( -type f -name "*.elf" -o -name "*.bin" -o -name "*.vmem" \) \
+		-exec cp -f --parents "{}" "$(MATCHA_OUT_DIR)" \;
+
+$(MATCHA_FPGA_MATCHA_BINARY_DIR):
+	mkdir -p $(MATCHA_FPGA_MATCHA_BINARY_DIR)
+
+## Build test binaries that run on FPGA
+matcha_fpga_tests: | $(MATCHA_FPGA_MATCHA_BINARY_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+		bazel query //sw/device/tests/... \
+			| grep 'fpga_nexus' \
+			| egrep '_bin$$' \
+			| grep -v '/smc:' \
+			| grep -v 'kelvin' \
+			| grep -v 'example_test_from_rom_rom_prog_fpga_nexus_bin' \
+			| xargs bazel build
+	cd $(MATCHA_SRC_DIR) && \
+		find 'bazel-out/' \
+			-type f \
+			\( -wholename "*ST-*/*fpga_nexus.elf" -o -wholename "*ST-*/*fpga_nexus.bin" \) \
+			-exec cp -f '{}' "$(MATCHA_FPGA_MATCHA_BINARY_DIR)" \;
+
+$(MATCHA_FPGA_KELVIN_BINARY_DIR):
+	mkdir -p "$(MATCHA_FPGA_KELVIN_BINARY_DIR)"
+
+
+## Build Matcha Kelvin SW FPGA test artifacts
+#
+# Build kelvin artifacts and package it in a tarball and ready for use on the FPGA
+# The output is at out/matcha/hw/fpga_tests/kelvin
+#
+matcha_kelvin_fpga_tarballs: kelvin_sw | $(MATCHA_FPGA_KELVIN_BINARY_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+	  bazel build --define DISABLE_VERILATOR_BUILD=true \
+			@kelvin-binary//...
+# Copy the tarballs and sc binary to out/.
+	cd $(MATCHA_SRC_DIR) && \
+		find "bazel-out/" -type f -wholename "*fastbuild-*/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc_extflash_fpga_nexus.bin" |\
+			xargs -I {} cp -f {} "$(MATCHA_FPGA_KELVIN_BINARY_DIR)"
+	cd $(MATCHA_SRC_DIR) && \
+		find "bazel-bin/external/kelvin-binary" -name "*.tar" |\
+			xargs -I {} cp -f {} "$(MATCHA_FPGA_KELVIN_BINARY_DIR)"
+
+
+## Build opentitantool for matcha FPGA tests
+opentitantool_pkg: | $(MATCHA_OUT_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+	  bazel build //sw:opentitantool_pkg
+	cd $(MATCHA_SRC_DIR) && \
+		cp -f bazel-bin/sw/opentitantool_pkg.tar.gz $(MATCHA_OUT_DIR)
+
+## Build and run matcha verilator test suite
+#
+matcha_hw_verilator_tests: verilator | $(MATCHA_TESTLOG_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+		bazel test --test_output=errors --test_timeout=180,600,1800,3600 \
+			--local_test_jobs=HOST_CPUS*0.25 \
+			--//hw:make_options=-j,16 \
+			//sw/device/tests:verilator_test_suite
+	cd $(MATCHA_SRC_DIR) && cp -rf "bazel-testlogs/sw" "$(MATCHA_TESTLOG_DIR)"
+
+## Build the boot ROM image
+#
+# This builds the HW boot rom executable for simulation.
+# Source is in hw/matcha, while output is placed in
+# out/matcha/hw/
+multihart_boot_rom:  | $(MATCHA_OUT_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+		bazel build --config=riscv32 //sw/device/lib/testing/test_rom:test_rom_no_otp_fpga_nexus.elf
+	cd $(MATCHA_SRC_DIR) && \
+		find "bazel-out/" -wholename "*test_rom/test_rom_no_otp_fpga_nexus.elf" \
+		-exec cp -f '{}' "$(MATCHA_OUT_DIR)/$(BOOT_ROM_ELF)" \;
+
+## Clean Matcha HW artifact
+matcha_hw_clean:
+	rm -rf $(MATCHA_OUT_DIR)
+	cd $(MATCHA_SRC_DIR) && \
+		bazel clean --expunge
+
+## Build the spi_passthough binary
+#
+# This builds the spi_passthrough_fpga_nexus.bin binary that can be used to
+# write flash tarballs to the nexus SPI boot flash. The binary will be copied
+# to out/spi_passthrough_fpga_nexus.bin
+spi_passthrough: | $(OUT)
+	cd $(MATCHA_SRC_DIR) && \
+	  bazel build --define DISABLE_VERILATOR_BUILD=true \
+		sw/device/examples/spi_passthrough
+	find "$(MATCHA_SRC_DIR)/bazel-out/" -type f -wholename "*fastbuild-*/sw/device/examples/spi_passthrough/spi_passthrough_fpga_nexus.bin" |\
+			xargs -I '{}' cp -f '{}' "$(OUT)"
+
+## Fetch latest prebuilt opentitantool
+latest_opentitantool: | $(OUT)
+	curl https://storage.googleapis.com/shodan-public-artifacts/opentitantool_pkg_latest.tar.gz -o $(OUT)/opentitantool_pkg.tar.gz
+	tar -C $(OUT) -xf $(OUT)/opentitantool_pkg.tar.gz
+
+.PHONY:: matcha_hw_verilator_sim matcha_hw_clean matcha_hw_verilator_tests
+.PHONY:: matcha_sw_all opentitantool_pkg
+.PHONY:: matcha_kelvin_fpga_tarballs
+.PHONY:: multihart_boot_rom
+.PHONY:: spi_passthrough
+.PHONY:: latest_opentitantool
\ No newline at end of file
diff --git a/platforms/sparrow/opentitan_hw.mk b/platforms/sparrow/opentitan_hw.mk
new file mode 100644
index 0000000..548f7ec
--- /dev/null
+++ b/platforms/sparrow/opentitan_hw.mk
@@ -0,0 +1,48 @@
+# Copyright 2022 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.
+
+## Run OpenTitan earlgrey smoketests in verilator
+# Build the verilated model, the test software and runs the tests
+earlgrey_hw_verilator_smoketests: | $(VERILATOR_BIN) $(OPENTITAN_BUILD_LOG_DIR)
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel query "kind(test, //sw/device/tests:all)" \
+			| grep "_smoketest" \
+			| xargs bazel test --test_tag_filters=verilator,-broken \
+				--define bitstream=skip \
+				--test_timeout=180,600,1800,3600 \
+				--test_output=errors \
+				--local_test_jobs=HOST_CPUS*0.25 \
+				--//hw:make_options=-j,8 \
+				--build_tests_only=true
+	cd $(OPENTITAN_SRC_DIR) && \
+		cp -rf "bazel-testlogs/sw/device" "$(OPENTITAN_BUILD_LOG_SW_DIR)"
+
+## Run All the OpenTitan earlgrey device tests in verilator
+# Build the verilated model, the test software and runs the tests.
+#
+# Note: This will take hours to finish. Run it with caution.
+earlgrey_hw_verilator_tests_all: | $(VERILATOR_BIN) $(OPENTITAN_BUILD_LOG_DIR)
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel test --test_tag_filters=verilator,-broken \
+			--define bitstream=skip \
+			--test_timeout=180,900,1800,3600 \
+			--test_output=errors \
+			--local_test_jobs=HOST_CPUS*0.25 \
+			--//hw:make_options=-j,8 \
+			--build_tests_only=true \
+			//sw/device/tests:all
+	cd $(OPENTITAN_SRC_DIR) && \
+		cp -rf "bazel-testlogs/sw/device" "$(OPENTITAN_BUILD_LOG_SW_DIR)"
+
+.PHONY:: earlgrey_hw_verilator_smoketests earlgrey_hw_verilator_tests_all
diff --git a/platforms/sparrow/opentitan_sw.mk b/platforms/sparrow/opentitan_sw.mk
new file mode 100644
index 0000000..95e098a
--- /dev/null
+++ b/platforms/sparrow/opentitan_sw.mk
@@ -0,0 +1,117 @@
+# Copyright 2022 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.
+
+OPENTITAN_SRC_DIR             := $(ROOTDIR)/hw/opentitan-upstream
+OPENTITAN_BUILD_DIR           := $(OUT)/opentitan/sw
+OPENTITAN_BUILD_OUT_DIR       := $(OPENTITAN_BUILD_DIR)/build-out
+OPENTITAN_BUILD_SW_DIR        := $(OPENTITAN_BUILD_DIR)/build-out/sw
+OPENTITAN_BUILD_SW_DEVICE_DIR := $(OPENTITAN_BUILD_DIR)/build-out/sw/device
+OPENTITAN_BUILD_SW_DEVICE_TESTS_DIR := $(OPENTITAN_BUILD_DIR)/build-out/sw/device/tests
+OPENTITAN_BUILD_LOG_DIR       := $(OPENTITAN_BUILD_DIR)/build-log
+OPENTITAN_BUILD_LOG_SW_DIR    := $(OPENTITAN_BUILD_DIR)/build-log/sw
+
+$(OPENTITAN_BUILD_OUT_DIR):
+	@echo "Creating output directory $(OPENTITAN_BUILD_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DEVICE_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DEVICE_TESTS_DIR)"
+
+## Builds the hardware testing binaries from OpenTitan in hw/opentitan-upstream
+# The output is stored at out/opentitan/sw/build-out/sw/device
+opentitan_sw_all: | $(OPENTITAN_BUILD_OUT_DIR) \
+                  opentitan_sw_verilator_sim
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel query "kind(test, //sw/device/tests/...)" | \
+			grep "_fpga" | grep -v "power_virus" | \
+			xargs bazel build --define bitstream=skip && \
+		find "bazel-out/" -type f -wholename "*fastbuild-*/sw/device/tests*/*.elf" | \
+			xargs -I {} cp -f {} "$(OPENTITAN_BUILD_SW_DEVICE_TESTS_DIR)/"
+	cd $(OPENTITAN_SRC_DIR) && \
+	  bazel build //hw/ip/otbn:all && \
+		cp -f bazel-bin/hw/ip/otbn/otbn_simple_smoke_test.elf "$(OPENTITAN_BUILD_SW_DEVICE_TESTS_DIR)/"
+
+$(OPENTITAN_BUILD_SW_DEVICE_DIR)/examples/hello_world: | $(OPENTITAN_BUILD_OUT_DIR)
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/examples/hello_world"
+
+## Build the sw helloworld ELF from hw/opentitan-upstream
+# The output is stored at out/opentitan/sw/build-out/sw/device/examples/hello_world
+opentitan_sw_helloworld: | $(OPENTITAN_BUILD_SW_DEVICE_DIR)/examples/hello_world
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel build //sw/device/examples/hello_world:hello_world
+	cd $(OPENTITAN_SRC_DIR) && \
+		find "bazel-out/" -name "hello_world*.elf" \
+		-exec cp -f '{}' "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/examples/hello_world" \;
+
+$(OPENTITAN_BUILD_SW_DEVICE_DIR)/boot_rom: | $(OPENTITAN_BUILD_OUT_DIR)
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/boot_rom"
+
+## Build the boot rom artifacts for Open Titan from hw/opentitan-upstream
+# The artifacts are stored at out/opentitan/sw/build-out/sw/device/boot_rom
+# TODO(ykwang): revise boot_rom to test_rom.
+opentitan_sw_bootrom: | $(OPENTITAN_BUILD_SW_DEVICE_DIR)/boot_rom
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel build //sw/device/lib/testing/test_rom:test_rom
+	cd $(OPENTITAN_SRC_DIR) && \
+		find "bazel-out/" -wholename "*fastbuild-*/*test_rom_fpga_cw310.39.scr.vmem" \
+		-exec cp -f '{}' "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/boot_rom/" \;
+
+$(OPENTITAN_BUILD_SW_DEVICE_DIR)/otp_img: | $(OPENTITAN_BUILD_OUT_DIR)
+	@mkdir -p "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/otp_img"
+
+## Build the opt image for Open Titan from hw/opentitan-upstream
+# The artifacts are stored at out/opentitan/sw/build-out/sw/device/otp_img/otp_img_sim_verilator.vmem
+opentitan_opt_img: | $(OPENTITAN_BUILD_SW_DEVICE_DIR)/otp_img
+	cd $(OPENTITAN_SRC_DIR) && \
+		bazel build //hw/ip/otp_ctrl/data:img_dev
+	cd $(OPENTITAN_SRC_DIR) && \
+		find "bazel-bin/" -name "img_dev*.vmem" \
+		-exec cp -f '{}' "$(OPENTITAN_BUILD_SW_DEVICE_DIR)/otp_img/otp_img_sim_verilator.vmem" \;
+
+## Build the Verilator sim SW for Open Titan from hw/opentitan-upstream
+# The artifacts include boot_rom, hello_world ELF, and otp image. The artifacts
+# are stored at out/opentitan/sw/build-bin
+opentitan_sw_verilator_sim: | $(OPENTITAN_BUILD_OUT_DIR) \
+                              opentitan_sw_helloworld \
+                              opentitan_sw_bootrom \
+                              opentitan_opt_img
+
+$(OPENTITAN_BUILD_LOG_DIR):
+	@echo "Creating log directory $(OPENTITAN_BUILD_LOG_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_LOG_DIR)"
+	@mkdir -p "$(OPENTITAN_BUILD_LOG_SW_DIR)"
+
+## Build and run host based opentitan unittests
+# The artifacts are stored at out/opentitan/sw/build-out/sw/device
+opentitan_sw_test: | $(OPENTITAN_BUILD_OUT_DIR) \
+                   $(OPENTITAN_BUILD_LOG_DIR)
+	cd $(OPENTITAN_SRC_DIR) && \
+		export CC=gcc-11; export CXX=g++-11; \
+		bazel query "kind(test, //sw/device/...)" | \
+			grep "_unittest" | \
+			xargs bazel test --build_tests_only=false \
+				--define DISABLE_VERILATOR_BUILD=true \
+				--test_tag_filters=-broken,-cw310,-verilator,-dv
+	cd $(OPENTITAN_SRC_DIR) && \
+		cp -rf "bazel-bin/sw/device" "$(OPENTITAN_BUILD_SW_DIR)"
+	cd $(OPENTITAN_SRC_DIR) && \
+		cp -rf "bazel-testlogs/sw/device" "$(OPENTITAN_BUILD_LOG_SW_DIR)"
+
+
+## Removes only the OpenTitan build artifacts from out/opentitan/sw
+opentitan_sw_clean:
+	rm -rf $(OPENTITAN_BUILD_DIR)
+
+.PHONY:: opentitan_sw_all opentitan_sw_clean opentitan_sw_verilator_sim opentitan_sw_test
+.PHONY:: opentitan_sw_helloworld opentitan_sw_bootrom opentitan_opt_img
diff --git a/platforms/sparrow/overlay-debug.dts b/platforms/sparrow/overlay-debug.dts
new file mode 100644
index 0000000..2a746c1
--- /dev/null
+++ b/platforms/sparrow/overlay-debug.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021, HENSOLDT Cyber
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later or BSD-3-Clause
+ */
+
+/ {
+  /*
+   * Override memory configuration for debug in simulation. The
+   * real hardware has 4MiB of RAM while the debug simulation
+   * uses 64MiB.
+   */
+  memory@50000000 {
+    #address-cells = <1>;
+    #size-cells = <1>;
+    device_type = "memory";
+    reg = <0x50000000 0x4000000>;
+  };
+};
diff --git a/platforms/sparrow/platform.mk b/platforms/sparrow/platform.mk
new file mode 100644
index 0000000..f9a603c
--- /dev/null
+++ b/platforms/sparrow/platform.mk
@@ -0,0 +1,95 @@
+# Platform-specific requirements handled by "m prereqs"
+CANTRIP_PLATFORM_PYTHON_DEPS=\
+    ${ROOTDIR}/hw/opentitan-upstream/python-requirements.txt
+CANTRIP_PLATFORM_APT_DEPS=\
+    ${ROOTDIR}/hw/opentitan-upstream/apt-requirements.txt
+
+# Put host tool targets first.
+include $(ROOTDIR)/build/platforms/sparrow/renode.mk
+
+include $(ROOTDIR)/build/platforms/sparrow/cantrip.mk
+include $(ROOTDIR)/build/platforms/sparrow/cantrip_builtins.mk
+include $(ROOTDIR)/build/platforms/sparrow/iree.mk
+include $(ROOTDIR)/build/platforms/sparrow/kelvin.mk
+include $(ROOTDIR)/build/platforms/sparrow/opentitan_sw.mk
+include $(ROOTDIR)/build/platforms/sparrow/opentitan_hw.mk
+include $(ROOTDIR)/build/platforms/sparrow/matcha_hw.mk
+include $(ROOTDIR)/build/platforms/sparrow/tock.mk
+
+
+# Put simulation targets at the end
+include $(ROOTDIR)/build/platforms/sparrow/sim.mk
+include $(ROOTDIR)/build/platforms/sparrow/sim_sel4test.mk
+
+# Driver include files auto-generated from opentitan definitions.
+
+ifeq ($(OPENTITAN_SOURCE),)
+$(error "OPENTITAN_SOURCE not set. Did build/platforms/sparrow/setup.sh get sourced?")
+endif
+
+ifeq ($(OPENTITAN_GEN_DIR),)
+$(error "OPENTITAN_GEN_DIR not set. Did build/platforms/sparrow/setup.sh get sourced?")
+endif
+
+$(OPENTITAN_GEN_DIR):
+	mkdir -p $(OPENTITAN_GEN_DIR)
+
+TIMER_IP_DIR=$(OPENTITAN_SOURCE)/hw/ip/rv_timer
+TIMER_NINJA=$(TIMER_IP_DIR)/util/reg_timer.py
+TIMER_TEMPLATE=$(TIMER_IP_DIR)/data/rv_timer.hjson.tpl
+
+TIMER_HEADER=$(OPENTITAN_GEN_DIR)/timer.h
+
+# NB: TIMER_HJSON is set in platforms/sparrow/setup.sh.
+$(TIMER_HJSON): $(TIMER_NINJA) $(TIMER_TEMPLATE) | $(OPENTITAN_GEN_DIR)
+	$(TIMER_NINJA) -s 2 -t 1 $(TIMER_TEMPLATE) > $(TIMER_HJSON)
+
+$(TIMER_HEADER): $(REGTOOL) $(TIMER_HJSON)
+	$(REGTOOL) -D -o $@ $(TIMER_HJSON)
+
+# Matcha hw config #defines generated from RTL
+
+TOP_MATCHA_DIR=${CANTRIP_OUT_DIR}/top_matcha
+TOP_MATCHA_MEMORY_HEADER=$(TOP_MATCHA_DIR)/sw/autogen/top_matcha_memory.h
+TOP_MATCHA_IRQ_HEADER=$(TOP_MATCHA_DIR)/sw/autogen/top_matcha_smc_irq.h
+# NB: could depend on the templates instead
+TOPGEN_MATCHA=${ROOTDIR}/hw/matcha/util/topgen_matcha.py
+
+${TOP_MATCHA_DIR}:
+	mkdir -p $(TOP_MATCHA_DIR)
+
+# NB: no way to generate just the files we need
+$(TOP_MATCHA_IRQ_HEADER): $(TOPGEN_MATCHA) ${TOP_MATCHA_DIR} $(TOP_MATCHA_HJSON)
+	PYTHONPATH=${OPENTITAN_SOURCE}/util/ ${TOPGEN_MATCHA} -t ${TOP_MATCHA_HJSON} -o ${TOP_MATCHA_DIR}/ --top-only
+$(TOP_MATCHA_MEMORY_HEADER): $(TOPGEN_MATCHA) ${TOP_MATCHA_DIR} $(TOP_MATCHA_HJSON)
+	PYTHONPATH=${OPENTITAN_SOURCE}/util/ ${TOPGEN_MATCHA} -t ${TOP_MATCHA_HJSON} -o ${TOP_MATCHA_DIR}/ --top-only
+
+# Targets to install the symlink to opentitan headers for each build
+
+cantrip-build-debug-prepare:: | $(CANTRIP_OUT_DEBUG)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(CANTRIP_OUT_DEBUG)/
+
+cantrip-build-release-prepare:: | $(CANTRIP_OUT_RELEASE)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(CANTRIP_OUT_RELEASE)/
+
+sel4test-build-debug-prepare:: | $(SEL4TEST_OUT_DEBUG)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(SEL4TEST_OUT_DEBUG)/
+
+sel4test-build-release-prepare:: | $(SEL4TEST_OUT_RELEASE)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(SEL4TEST_OUT_RELEASE)/
+
+sel4test-build-wrapper-release-prepare:: | $(SEL4TEST_WRAPPER_OUT_RELEASE)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(SEL4TEST_WRAPPER_OUT_RELEASE)/
+
+sel4test-build-wrapper-debug-prepare:: | $(SEL4TEST_WRAPPER_OUT_DEBUG)
+	ln -sf $(CANTRIP_OUT_DIR)/opentitan-gen $(SEL4TEST_WRAPPER_OUT_DEBUG)/
+
+cantrip-gen-headers:: $(TIMER_HEADER) $(TOP_MATCHA_IRQ_HEADER) $(TOP_MATCHA_MEMORY_HEADER)
+
+cantrip-clean-headers::
+	rm -f $(TIMER_HJSON)
+	rm -f $(TIMER_HEADER)
+	rm -f $(TOP_MATCHA_IRQ_HEADER) $(TOP_MATCHA_MEMORY_HEADER)
+
+# Ensure we build verilator as part of tools for this platform
+tools:: verilator
diff --git a/platforms/sparrow/renode.mk b/platforms/sparrow/renode.mk
new file mode 100644
index 0000000..7100793
--- /dev/null
+++ b/platforms/sparrow/renode.mk
@@ -0,0 +1,55 @@
+# Copyright 2022 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.
+
+RENODE_SRC_DIR := $(ROOTDIR)/sim/renode
+RENODE_OUT_DIR := $(CACHE)/renode
+RENODE_BIN     := $(RENODE_OUT_DIR)/renode
+RENODE_PORT    ?= 1234
+RENODE_CMD     := cd $(ROOTDIR) && $(RENODE_BIN) --disable-xwt --port $(RENODE_PORT)
+RENODE_COMMIT  := $(shell git -C $(RENODE_SRC_DIR) rev-parse  --short=8 HEAD)
+RENODE_SIM_GENERATOR_SCRIPT := $(ROOTDIR)/scripts/generate_renode_configs.sh
+
+$(RENODE_OUT_DIR):
+	mkdir -p $(RENODE_OUT_DIR)
+
+$(RENODE_BIN): | $(RENODE_SRC_DIR) $(RENODE_OUT_DIR)
+	cd $(RENODE_SRC_DIR); unset PLATFORM; \
+		./build.sh --no-gui -d -o $(RENODE_OUT_DIR)
+	echo -e "built_from_src\ncommit_sha: $(RENODE_COMMIT)\n" > $(RENODE_OUT_DIR)/tag
+
+## Builds the Renode system simulator
+#
+# Using sources in sim/renode, this target builds Renode from source and stores
+# its output in out/host/renode.
+#
+# To rebuild this target, run `m renode_clean` and re-run.
+#
+# This is for debug purpose only. You probably want to use `m renode` target.
+renode_src: $(RENODE_BIN)
+
+## Download and install the nightly release Renode system simulator
+#
+# From AntMicro's release. If there is a local build from `m renode_src` with
+# the same commit sha as the release build, it will be treated as up-to-date.
+renode: | $(RENODE_OUT_DIR)
+	$(ROOTDIR)/scripts/download_renode.py --renode_dir $(RENODE_OUT_DIR)
+
+## Removes Renode build artifacts from sim/renode and out/
+renode_clean:
+	@rm -rf $(RENODE_OUT_DIR)
+	@rm -rf $(RENODE_SRC_DIR)/output
+	@cd $(RENODE_SRC_DIR); find . -type d -name bin | xargs rm -rf
+	@cd $(RENODE_SRC_DIR); find . -type d -name obj | xargs rm -rf
+
+.PHONY:: renode renode_src renode_clean
diff --git a/platforms/sparrow/setup.sh b/platforms/sparrow/setup.sh
new file mode 100644
index 0000000..e8fb481
--- /dev/null
+++ b/platforms/sparrow/setup.sh
@@ -0,0 +1,247 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+export C_PREFIX="riscv32-unknown-elf-"
+export IREE_PREFIX="riscv32-unknown-elf-"
+export IREE_ARCH="rv32imf"
+export RUST_PREFIX="riscv32-unknown-linux-gnu"
+
+# NB: $(CANTRIP_SRC_DIR)/apps/system/rust.cmake forces riscv32imac for
+#     the target when building Rust code
+export CANTRIP_TARGET_ARCH="riscv32-unknown-elf"
+
+# Shodan's toolchains are setup specially, just setup the environment.
+export RUSTDIR="${CACHE}/rust_toolchain"
+export CARGO_HOME="${RUSTDIR}"
+export RUSTUP_HOME="${RUSTDIR}"
+export PATH="${RUSTDIR}/bin:${PATH}"
+
+export OPENTITAN_GEN_DIR="${CANTRIP_OUT_DIR}/opentitan-gen/include/opentitan"
+export OPENTITAN_SOURCE="${ROOTDIR}/hw/opentitan-upstream"
+
+# Some build.rs files use the regtool crate which needs to know where to find
+# regtool.py.
+export REGTOOL="${OPENTITAN_SOURCE}/util/regtool.py"
+# The following files are the input to regtool.py
+export I2S_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/ip/i2s/data/i2s.hjson"
+export MBOX_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/ip/tlul_mailbox/data/tlul_mailbox.hjson"
+export ML_TOP_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/ip/ml_top/data/ml_top.hjson"
+export TIMER_HJSON="${OPENTITAN_GEN_DIR}/rv_timer.hjson"
+export UART_HJSON="${OPENTITAN_SOURCE}/hw/ip/uart/data/uart.hjson"
+export VC_TOP_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/ip/vc_top/data/vc_top.hjson"
+# Input for topgen_matcha.py to generate hw configuration
+export TOP_MATCHA_HJSON="${ROOTDIR}/hw/matcha/hw/top_matcha/data/top_matcha.hjson"
+
+# Standard locations of Sparrow configs and binaries
+export OT_TOOL="${ROOTDIR}/out/opentitantool"
+export SPARROW_JSON="${ROOTDIR}/out/nexus.json"
+export SPARROW_SPI_PASSTHRU="${ROOTDIR}/out/spi_passthrough_asic.bin"
+
+function parting_messages() {
+    if [[ ! -d "${RUSTDIR}" ]] ||
+       [[ ! -d "${ROOTDIR}/cache/toolchain" ]] ||
+       [[ ! -d "${ROOTDIR}/cache/toolchain_iree_rv32imf" ]] ||
+       [[ ! -d "${ROOTDIR}/cache/renode" ]]; then
+        echo
+        echo '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
+        echo You have missing tools. Please run \'m prereqs\' followed
+        echo by \'m tools\' to install them.
+        echo '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
+        echo
+        [[ -d "${RUSTDIR}" ]] || echo "${RUSTDIR} is missing!"
+        [[ -d "${ROOTDIR}/cache/toolchain" ]] || echo "${ROOTDIR}/cache/toolchain is missing"
+        [[ -d "${ROOTDIR}/cache/toolchain_iree_rv32imf" ]] || echo "${ROOTDIR}/cache/toolchain_iree_rv32imf is missing!"
+        [[ -d "${ROOTDIR}/cache/renode" ]] || echo "${ROOTDIR}/cache/renode is missing!"
+    fi
+    echo Run \'set-sparrow-id NN\' to set your Sparrow board ID
+}
+
+function sim_kelvin
+{
+    # Run the ELF/Bin program with kelvin_sim
+    local bin_file="$(realpath $1)"
+    local magic_bytes="$(xxd -p -l 4 ${bin_file})"
+    local -a flags=()
+    local is_elf=false
+    if [[ ${magic_bytes} == "7f454c46" ]]; then
+      is_elf=true
+    else
+      flags+=("--entry_point 0 ")
+    fi
+
+    if [[ "$2" == "debug" ]]; then
+        flags+=("-i")
+        if [[ "${is_elf}" == false ]]; then
+            echo "debug mode only works on ELF files"
+            return 1
+        fi
+    fi
+
+    ("${OUT}/kelvin/sim/kelvin_sim" "${bin_file}" ${flags[@]})
+}
+
+function sim_kelvin_renode
+{
+    # Run the Bin program with renode
+    local bin_file="$(realpath $1)"
+    local command="start;"
+
+    (cd "${ROOTDIR}" && renode -e "\$bin=@${bin_file}; i @sim/config/kelvin.resc; \
+        ${command} sysbus.ml_top_controlblock WriteDoubleWord 0xc 0" \
+        --disable-xwt --console)
+
+}
+
+function set-sparrow-id
+{
+    local sparrow_id="${1}"; shift
+
+    if [[ -z "${sparrow_id}" ]]; then
+        (
+            echo "Usage: set-sparrow-id <NN>"
+            echo
+            echo "Sets the target Sparrow board to run on.  Must be a two digit number"
+            echo
+        ) | fmt
+        return 1
+    fi
+    if [[ "${#sparrow_id}" -ne 2 ]]; then
+        echo "Sparrow number must be two digits"
+        return 1
+    fi
+
+    export SPARROW_ID="${sparrow_id}"
+}
+
+function opentitantool
+{
+    # Run built opentitantool with standard flags
+    if [[ -z "${SPARROW_ID}" ]]; then
+        echo "Run set-sparrow-id first"
+        return 1
+    fi
+
+    "${OT_TOOL}" --conf "${SPARROW_JSON}" --interface nexus --usb-serial \
+        "Sparrow-${SPARROW_ID}" "$@"
+}
+
+function sparrow_reset
+{
+    # Reset sparrow board
+    if [[ -z "${SPARROW_ID}" ]]; then
+        echo "Run set-sparrow-id first"
+        return 1
+    fi
+
+    echo "Resetting sparrow-${SPARROW_ID}"
+    opentitantool gpio write RESET true
+    opentitantool gpio write RESET false
+    opentitantool gpio write RESET true
+}
+
+function sparrow_flash
+{
+    # Write tar file to sparrow SPI flash and verify
+
+    local flash_tar="$(realpath $1)"
+    if [[ -z "${flash_tar}" ]]; then
+        (
+            echo "Usage: sparrow_flash path/to/file.tar"
+            echo
+            echo "Writes tar file to Sparrow SPI flash for booting."
+            echo
+        ) | fmt
+        return 1
+    fi
+
+    if [[ -z "${SPARROW_ID}" ]]; then
+        echo "Run set-sparrow-id first"
+        return 1
+    fi
+
+    if [[ ! -f "${SPARROW_SPI_PASSTHRU}" ]]; then
+        echo "INFO: spi_passthrough binary not found at ${SPARROW_SPI_PASSTHRU}"
+        echo "running `m spi_passthrough` to build it"
+        m spi_passthrough
+        if [[ "$?" -ne 0 ]]; then
+            echo "ERROR: Unable to find or build spi_passthrough!"
+            return 1
+        fi
+    fi
+
+    sparrow_reset
+
+    local read_size="$(du -b ${flash_tar} | awk '{print $1}')"
+    local erase_size="$(du -b ${flash_tar} \
+        | awk '{ print $1 + (n - $1 % n) % n }' n=262144)"
+
+    echo "Program SPI using ${SPARROW_SPI_PASSTHRU} on sparrow-${SPARROW_ID}"
+    opentitantool bootstrap "${SPARROW_SPI_PASSTHRU}"
+    opentitantool spi block-erase --start 0 --length "${erase_size}"
+    opentitantool spi program --start 0 "${flash_tar}"
+
+    local readback_file="$(mktemp -d)/$(basename ${flash_tar}).readback"
+    opentitantool  spi read --start 0 --length "${read_size}" "${readback_file}"
+    diff "${flash_tar}" "${readback_file}"
+    if [[ "$?" -ne 0 ]]; then
+        echo "ERROR: Sparrow flash verify failed!"
+        return 1
+    fi
+    rm "${readback_file}"
+
+    sparrow_reset
+}
+
+function sparrow_clear_flash
+{
+    # Clear the first page of the SPI flash to prevent restarting from the
+    # loaded program at the next reset.
+    if [[ -z "${SPARROW_ID}" ]]; then
+        echo "Run set-sparrow-id first"
+        return 1
+    fi
+
+    echo "Clearing sparrow-${SPARROW_ID} SPI flash"
+    sparrow_reset
+    opentitantool bootstrap "${SPARROW_SPI_PASSTHRU}"
+    opentitantool spi block-erase --start 0 --length 262144
+}
+
+function sparrow_boot
+{
+    # Boot the Sparrow board.  If flashed, will boot from flash
+    # This command is a synomym for `opentitantool boot`
+    echo "Booting Sparrow-${SPARROW_ID}"
+    opentitantool boot
+}
+
+function sparrow_bootstrap
+{
+    # Bootstrap a binary on the Sparrow board
+
+    local binary="$(realpath $1)"
+    if [[ -z "${binary}" ]]; then
+        (
+            echo "Usage: sparrow_flash path/to/file.bin"
+            echo
+            echo "Run a binary on the sparrow board."
+            echo
+        ) | fmt
+        return 1
+    fi
+    echo "Bootstrapping ${binary} onto Sparrow-${SPARROW_ID}"
+    opentitantool bootstrap "${binary}"
+}
diff --git a/platforms/sparrow/sim.mk b/platforms/sparrow/sim.mk
new file mode 100644
index 0000000..4708391
--- /dev/null
+++ b/platforms/sparrow/sim.mk
@@ -0,0 +1,164 @@
+# Copyright 2022 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.
+
+# NB: keep tarballs in CANTRIP_OUT_DIR to avoid shodan/nexus collisions
+EXT_FLASH_DEBUG=$(CANTRIP_OUT_DEBUG)/ext_flash.tar
+EXT_FLASH_RELEASE=$(CANTRIP_OUT_RELEASE)/ext_flash.tar
+
+TMP_DEBUG=$(CANTRIP_OUT_DEBUG)/tmp
+TMP_RELEASE=$(CANTRIP_OUT_RELEASE)/tmp
+
+sim_configs:
+	$(RENODE_SIM_GENERATOR_SCRIPT)
+
+clean_sim_configs:
+	@rm -rf $(OUT)/renode_configs
+
+$(TMP_DEBUG):
+	mkdir $(TMP_DEBUG)
+$(TMP_RELEASE):
+	mkdir $(TMP_RELEASE)
+
+# NB: $(CANTRIP_ROOTSERVER_*) is built together with $(CANTRIP_KERNEL_*)
+
+$(EXT_FLASH_DEBUG): $(MATCHA_BUNDLE_DEBUG) $(CANTRIP_KERNEL_DEBUG) $(CANTRIP_ROOTSERVER_DEBUG) | $(TMP_DEBUG)
+	cp -f $(MATCHA_BUNDLE_DEBUG) $(TMP_DEBUG)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_DEBUG)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_DEBUG)/matcha-tock-bundle $(TMP_DEBUG)/matcha-tock-bundle.bin
+	ln -sf $(CANTRIP_KERNEL_DEBUG) $(TMP_DEBUG)/kernel
+	ln -sf $(CANTRIP_ROOTSERVER_DEBUG) $(TMP_DEBUG)/capdl-loader
+	tar -C $(TMP_DEBUG) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+ext_flash_debug: $(EXT_FLASH_DEBUG)
+
+$(EXT_FLASH_RELEASE): $(MATCHA_BUNDLE_RELEASE) $(CANTRIP_KERNEL_RELEASE) $(CANTRIP_ROOTSERVER_RELEASE) | $(TMP_RELEASE)
+	cp -f $(MATCHA_BUNDLE_RELEASE) $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_RELEASE)/matcha-tock-bundle $(TMP_RELEASE)/matcha-tock-bundle.bin
+	ln -sf $(CANTRIP_KERNEL_RELEASE) $(TMP_RELEASE)/kernel
+	ln -sf $(CANTRIP_ROOTSERVER_RELEASE) $(TMP_RELEASE)/capdl-loader
+	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+
+## Packages the builtins in the tarball for writing to SPI flash.
+# TODO(sleffler): reorder builtins to before build artifacts to reduce lookup time
+ext_flash_release: $(EXT_FLASH_RELEASE) $(EXT_BUILTINS_RELEASE) | $(TMP_RELEASE)
+	cd $(CANTRIP_OUT_RELEASE)/builtins && \
+			${CPIO} -i < ${CANTRIP_OUT_RELEASE}/archive.capdl-loader_archive.o.cpio && \
+			tar -rvhf ${EXT_FLASH_RELEASE} $$(${CPIO} -t < ${CANTRIP_OUT_RELEASE}/archive.capdl-loader_archive.o.cpio)
+	tar -C $(CANTRIP_OUT_RELEASE)/builtins -rvhf ${EXT_FLASH_RELEASE} $$(${CPIO} -t < ${EXT_BUILTINS_RELEASE})
+
+# Renode commands to issue before the initial start of a simulation.
+# This pauses all cores and then sets cpu0 (SC) & cpu1 (SMC) running.
+RENODE_PRESTART_CMDS=pause; cpu0 IsHalted false;
+PORT_PRESTART_CMDS:=$(shell $(ROOTDIR)/scripts/generate-renode-port-cmd.sh $(RENODE_PORT))
+
+## Launches an end-to-end build of the Shodan system and starts Renode
+#
+# This top-level target triggers the `matcha_tock_release`, `cantrip`, `renode`,
+# `multihart_boot_rom`, and `iree` targets to build the entire system and then
+# finally starts the Renode simulator.
+#
+# This is the default target for the build system, and is generally what you
+# need for day-to-day work on the software side of Shodan.
+simulate: renode kelvin_sim multihart_boot_rom ext_flash_release kelvin_hello_world cantrip-builtins-release
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/nexus-release.repl; \
+    \$$tar = @$(EXT_FLASH_RELEASE); \
+    \$$cpio = @/dev/null; \
+    \$$kernel = @$(CANTRIP_KERNEL_RELEASE); \
+    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+        $(RENODE_PRESTART_CMDS) start"
+
+## Debug version of the `simulate` target
+#
+# This top-level target does the same job as `simulate`, but instead of
+# unhalting the CPUs and starting the system, this alternate target only unhalts
+# cpu0, and uses the debug build of TockOS from the `matcha_tock_debug` target.
+#
+# NB: requires editing of platform.camkes for alternate cpio_load_address
+simulate-debug: renode kelvin_sim multihart_boot_rom ext_flash_debug iree_model_builtins cantrip-builtins-debug
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/nexus-debug.repl; \
+    \$$tar = @$(EXT_FLASH_DEBUG); \
+    \$$cpio = @/dev/null; \
+    \$$kernel = @$(CANTRIP_KERNEL_DEBUG); \
+    \$$sc_bin =@$(TMP_DEBUG)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+        $(RENODE_PRESTART_CMDS) cpu1 CreateSeL4 0xffffffee; start"
+
+## Debug version of the `simulate` target
+#
+# This top-level target does the same job as `simulate-debug`, but instead of
+# unhalting the CPUs and starting the system, this alternate target starts
+# renode with no CPUs unhalted, allowing for GDB to be used for early system
+# start.
+#
+# NB: requires editing of platform.camkes for alternate cpio_load_address
+debug-simulation: renode kelvin_sim multihart_boot_rom ext_flash_debug iree_model_builtins cantrip-builtins-debug
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/nexus-debug.repl; \
+    \$$tar = @$(EXT_FLASH_DEBUG); \
+    \$$cpio = @/dev/null; \
+    \$$kernel = @$(CANTRIP_KERNEL_DEBUG); \
+    \$$sc_bin =@$(TMP_DEBUG)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; start"
+
+EXT_FLASH_MINISEL_DEBUG=$(CANTRIP_OUT_DEBUG)/ext_flash_minisel.tar
+EXT_FLASH_MINISEL_RELEASE=$(CANTRIP_OUT_RELEASE)/ext_flash_minisel.tar
+
+# Launches Shodan with Minisel as the rootserver for low-level testing purposes.
+# NB: the minisel bundle renames "minisel.elf" to "capdl-loader"
+# because we don't currently have any way to specify the rootserver app other
+# than via filename
+# TODO(sleffler): the rootserver is built as a byproduct of building the kernel
+$(EXT_FLASH_MINISEL_DEBUG): $(MATCHA_BUNDLE_DEBUG) $(CANTRIP_KERNEL_DEBUG) $(CANTRIP_OUT_DEBUG)/minisel/minisel.elf | $(TMP_DEBUG)
+	cp -f $(MATCHA_BUNDLE_DEBUG) $(TMP_DEBUG)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_DEBUG)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_DEBUG)/matcha-tock-bundle $(TMP_DEBUG)/matcha-tock-bundle.bin
+	ln -sf $(CANTRIP_KERNEL_DEBUG) $(TMP_DEBUG)/kernel
+	ln -sf $(CANTRIP_OUT_DEBUG)/minisel/minisel.elf $(TMP_DEBUG)/capdl-loader
+	tar -C $(TMP_DEBUG) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+ext_flash_minisel_debug: $(EXT_FLASH_MINISEL_DEBUG)
+
+# TODO(sleffler): the rootserver is built as a byproduct of building the kernel
+$(EXT_FLASH_MINISEL_RELEASE): $(MATCHA_BUNDLE_RELEASE) $(CANTRIP_KERNEL_RELEASE) $(CANTRIP_OUT_RELEASE)/minisel/minisel.elf | $(TMP_RELEASE)
+	cp -f $(MATCHA_BUNDLE_RELEASE) $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_RELEASE)/matcha-tock-bundle $(TMP_RELEASE)/matcha-tock-bundle.bin
+	ln -sf $(CANTRIP_KERNEL_RELEASE) $(TMP_RELEASE)/kernel
+	ln -sf $(CANTRIP_OUT_RELEASE)/minisel/minisel.elf $(TMP_RELEASE)/capdl-loader
+	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+ext_flash_minisel_release: $(EXT_FLASH_MINISEL_RELEASE)
+
+simulate_minisel: renode multihart_boot_rom ext_flash_minisel_debug
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/nexus-debug.repl; \
+    \$$tar = @$(EXT_FLASH_MINISEL_DEBUG); \
+    \$$kernel = @$(CANTRIP_KERNEL_DEBUG); \
+    \$$cpio = @/dev/null; \
+    \$$sc_bin =@$(TMP_DEBUG)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+        $(RENODE_PRESTART_CMDS) start"
+
+simulate_minisel_release: renode multihart_boot_rom ext_flash_minisel_release
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/nexus-release.repl; \
+    \$$tar = @$(EXT_FLASH_MINISEL_RELEASE); \
+    \$$kernel = @$(CANTRIP_KERNEL_RELEASE); \
+    \$$cpio = @/dev/null; \
+    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+        $(RENODE_PRESTART_CMDS) start"
+
+.PHONY:: sim_configs clean_sim_configs simulate simulate-debug debug-simulation
diff --git a/platforms/sparrow/sim_sel4test.mk b/platforms/sparrow/sim_sel4test.mk
new file mode 100644
index 0000000..ecaf7d2
--- /dev/null
+++ b/platforms/sparrow/sim_sel4test.mk
@@ -0,0 +1,78 @@
+# Copyright 2022 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.
+
+# sel4test simulation support; this is meant to be included from sim.mk
+
+SEL4TEST_TMP=$(SEL4TEST_OUT_DEBUG)/tmp
+${SEL4TEST_TMP}:
+	mkdir $@
+
+SEL4TEST_EXT_FLASH_TAR=$(SEL4TEST_OUT_DEBUG)/ext_flash.tar
+
+# NB: release builds do not have enough memory to run sel4test; they
+#   will fail to build when elfloader looks for a location to load the image
+
+$(SEL4TEST_EXT_FLASH_TAR): $(MATCHA_BUNDLE_DEBUG) \
+		$(SEL4TEST_KERNEL_DEBUG) $(SEL4TEST_ROOTSERVER_DEBUG) | $(SEL4TEST_TMP)
+	cp -f $(MATCHA_BUNDLE_DEBUG) $(SEL4TEST_TMP)/matcha-tock-bundle
+	${C_PREFIX}strip $(SEL4TEST_TMP)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(SEL4TEST_TMP)/matcha-tock-bundle ${SEL4TEST_TMP}/matcha-tock-bundle.bin
+	ln -sf $(SEL4TEST_KERNEL_DEBUG) $(SEL4TEST_TMP)/kernel
+	ln -sf $(SEL4TEST_ROOTSERVER_DEBUG) $(SEL4TEST_TMP)/capdl-loader
+	tar -C $(SEL4TEST_TMP) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+
+## Launches the sel4test target with minimal modifications.
+sel4test: renode multihart_boot_rom $(SEL4TEST_EXT_FLASH_TAR)
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/$(PLATFORM)-debug.repl; \
+    \$$tar = @$(SEL4TEST_EXT_FLASH_TAR); \
+    \$$kernel = @$(SEL4TEST_KERNEL_DEBUG); \
+    \$$cpio = @/dev/null; \
+    \$$sc_bin =@$(SEL4TEST_TMP)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+        $(RENODE_PRESTART_CMDS) start"
+
+# NB: for compatability
+sel4test-debug: sel4test
+
+SEL4TEST_WRAPPER_TMP=$(SEL4TEST_WRAPPER_OUT_DEBUG)/tmp
+${SEL4TEST_WRAPPER_TMP}:
+	mkdir $@
+
+SEL4TEST_WRAPPER_EXT_FLASH_TAR=$(SEL4TEST_WRAPPER_OUT_DEBUG)/ext_flash.tar
+
+$(SEL4TEST_WRAPPER_EXT_FLASH_TAR): $(MATCHA_BUNDLE_DEBUG) \
+		$(SEL4TEST_KERNEL_DEBUG) $(SEL4TEST_WRAPPER_ROOTSERVER_DEBUG) | $(SEL4TEST_WRAPPER_TMP)
+	cp -f $(MATCHA_BUNDLE_DEBUG) $(SEL4TEST_WRAPPER_TMP)/matcha-tock-bundle
+	${C_PREFIX}strip $(SEL4TEST_WRAPPER_TMP)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(SEL4TEST_WRAPPER_TMP)/matcha-tock-bundle ${SEL4TEST_WRAPPER_TMP}/matcha-tock-bundle.bin
+	ln -sf $(SEL4TEST_KERNEL_DEBUG) $(SEL4TEST_WRAPPER_TMP)/kernel
+	ln -sf $(SEL4TEST_WRAPPER_ROOTSERVER_DEBUG) $(SEL4TEST_WRAPPER_TMP)/capdl-loader
+	tar -C $(SEL4TEST_WRAPPER_TMP) -cvhf $@ matcha-tock-bundle.bin kernel capdl-loader
+
+## Launches a version of the sel4test target that uses the sel4-sys Rust
+## crate wrapped with C shims. The result is run under Renode.
+sel4test+wrapper: renode multihart_boot_rom $(SEL4TEST_WRAPPER_EXT_FLASH_TAR)
+	$(RENODE_CMD) -e "\
+    \$$repl_file = @sim/config/platforms/$(PLATFORM)-debug.repl; \
+    \$$tar = @$(SEL4TEST_WRAPPER_EXT_FLASH_TAR); \
+    \$$kernel = @$(SEL4TEST_KERNEL_DEBUG); \
+    \$$cpio = @/dev/null; \
+    \$$sc_bin =@$(SEL4TEST_WRAPPER_TMP)/matcha-tock-bundle.bin; \
+    $(PORT_PRESTART_CMDS) i @sim/config/shodan.resc; \
+				$(RENODE_PRESTART_CMDS) start"
+
+.PHONY:: sel4test
+.PHONY:: sel4test-debug
+.PHONY:: sel4test+wrapper
diff --git a/platforms/sparrow/tock.mk b/platforms/sparrow/tock.mk
new file mode 100644
index 0000000..348251d
--- /dev/null
+++ b/platforms/sparrow/tock.mk
@@ -0,0 +1,73 @@
+# Copyright 2022 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.
+
+# Matcha platform + kernel binaries
+MATCHA_PLATFORM_SRC_DIR := $(ROOTDIR)/sw/matcha/platform/
+MATCHA_PLATFORM_DEBUG   := $(OUT)/matcha/riscv32imc-unknown-none-elf/debug/matcha_platform
+MATCHA_PLATFORM_RELEASE := $(OUT)/matcha/riscv32imc-unknown-none-elf/release/matcha_platform
+
+# Matcha app binaries
+MATCHA_APP_SRC_DIR := $(ROOTDIR)/sw/matcha/app/
+MATCHA_APP_DEBUG   := $(OUT)/matcha/riscv32imc-unknown-none-elf/debug/matcha_app
+MATCHA_APP_RELEASE := $(OUT)/matcha/riscv32imc-unknown-none-elf/release/matcha_app
+
+# Bundled platform+kernel+app binaries
+MATCHA_BUNDLE_DEBUG   := $(OUT)/matcha-bundle-debug.elf
+MATCHA_BUNDLE_RELEASE := $(OUT)/matcha-bundle-release.elf
+
+########################################
+
+$(RUSTDIR)/bin/elf2tab: | rust_presence_check
+	cargo install elf2tab --version 0.6.0
+
+$(MATCHA_APP_DEBUG): | rust_presence_check
+	export CARGO_NET_GIT_FETCH_WITH_CLI=true; \
+		cd $(MATCHA_PLATFORM_SRC_DIR); cargo -Z unstable-options --config 'build.rustflags = ["-C", "link-arg=-Tlayout_debug.ld"]' build
+	cd $(MATCHA_APP_SRC_DIR); PLATFORM=opentitan cargo -Z unstable-options --config 'build.rustflags = ["-C", "link-arg=-Tlayout_matcha_debug.ld",]' build
+
+$(MATCHA_APP_RELEASE): | rust_presence_check
+	export CARGO_NET_GIT_FETCH_WITH_CLI=true; \
+		cd $(MATCHA_PLATFORM_SRC_DIR); cargo -Z unstable-options --config 'build.rustflags = ["-C", "link-arg=-Tlayout.ld"]' build --release
+	cd $(MATCHA_APP_SRC_DIR); PLATFORM=opentitan cargo -Z unstable-options --config 'build.rustflags = ["-C", "link-arg=-Tlayout_matcha.ld",]' build --release
+
+$(MATCHA_BUNDLE_DEBUG): $(MATCHA_APP_DEBUG) $(RUSTDIR)/bin/elf2tab | rust_presence_check
+	elf2tab -n matcha -o $(MATCHA_APP_DEBUG).tab $(MATCHA_APP_DEBUG) --stack 4096 --app-heap 1024 --kernel-heap 1024 --protected-region-size=64
+	cp $(MATCHA_PLATFORM_DEBUG) $(MATCHA_BUNDLE_DEBUG)
+	riscv32-unknown-elf-objcopy --update-section .apps=$(MATCHA_APP_DEBUG).tbf $(MATCHA_BUNDLE_DEBUG)
+
+$(MATCHA_BUNDLE_RELEASE): $(MATCHA_APP_RELEASE) $(RUSTDIR)/bin/elf2tab | rust_presence_check
+	elf2tab -n matcha -o $(MATCHA_APP_RELEASE).tab $(MATCHA_APP_RELEASE) --stack 4096 --app-heap 1024 --kernel-heap 1024 --protected-region-size=64
+	cp $(MATCHA_PLATFORM_RELEASE) $(MATCHA_BUNDLE_RELEASE)
+	riscv32-unknown-elf-objcopy --update-section .apps=$(MATCHA_APP_RELEASE).tbf $(MATCHA_BUNDLE_RELEASE)
+
+########################################
+
+## Builds TockOS for the security core in debug mode
+matcha_tock_debug: $(MATCHA_BUNDLE_DEBUG)
+
+## Builds TockOS for the security core in release mode
+matcha_tock_release: $(MATCHA_BUNDLE_RELEASE)
+
+## Removes the TockOS and libtockrs build artifacts from out/
+matcha_tock_clean:
+	cd $(MATCHA_PLATFORM_SRC_DIR); PLATFORM=opentitan cargo clean --target-dir=$(OUT)/matcha
+	cd $(MATCHA_APP_SRC_DIR); PLATFORM=opentitan cargo clean --target-dir=$(OUT)/matcha
+	rm -rf $(OUT)/matcha
+	rm -f $(MATCHA_BUNDLE_DEBUG) $(MATCHA_BUNDLE_RELEASE)
+
+.PHONY:: matcha_tock_debug matcha_tock_release matcha_tock_clean
+
+# Mark these real files as phony since building them requires calling out to a
+# separate build system (cargo)
+.PHONY:: $(MATCHA_APP_DEBUG) $(MATCHA_APP_RELEASE)