Add initial commit for TockOs OpenTitan Matcha chip and board support.
Change-Id: Ia08b39dbd01a6234b9894eb5c03ff1deaeac3035
diff --git a/Makefile.common b/Makefile.common
new file mode 100644
index 0000000..822ba83
--- /dev/null
+++ b/Makefile.common
@@ -0,0 +1,294 @@
+# Force the Shell to be bash as some systems have strange default shells
+SHELL := bash
+
+# Remove built-in rules and variables
+# n.b. no-op for make --version < 4.0
+MAKEFLAGS += -r
+MAKEFLAGS += -R
+
+# The absolute path of the directory containing this `Makefile.common` file.
+MAKEFILE_COMMON_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))../tock/boards/
+
+# The absolute path of Tock's root directory.
+# This is currently the parent directory of MAKEFILE_COMMON_PATH.
+TOCK_ROOT_DIRECTORY := $(dir $(abspath $(MAKEFILE_COMMON_PATH)))
+
+# Common defaults that specific boards can override, but likely do not need to.
+TOOLCHAIN ?= llvm
+CARGO ?= cargo
+RUSTUP ?= rustup
+
+# Default location of target directory (relative to board makefile)
+# passed to cargo --target_dir
+TARGET_DIRECTORY ?= $(TOCK_ROOT_DIRECTORY)target/
+
+# RUSTC_FLAGS allows boards to define board-specific options.
+# This will hopefully move into Cargo.toml (or Cargo.toml.local) eventually.
+# lld uses the page size to align program sections. It defaults to 4096 and this
+# puts a gap between before the .relocate section. `zmax-page-size=512` tells
+# lld the actual page size so it doesn't have to be conservative.
+RUSTC_FLAGS ?= \
+ -C link-arg=-Tlayout.ld \
+ -C linker=rust-lld \
+ -C linker-flavor=ld.lld \
+ -C relocation-model=dynamic-no-pic \
+ -C link-arg=-zmax-page-size=512 \
+ -C link-arg=-icf=all \
+
+# RISC-V-specific flags.
+ifneq ($(findstring riscv32i, $(TARGET)),)
+ # NOTE: This flag causes kernel panics on some ARM cores. Since the
+ # size benefit is almost exclusively for RISC-V, we only apply it for
+ # those targets.
+ RUSTC_FLAGS += -C force-frame-pointers=no
+endif
+
+# RUSTC_FLAGS_TOCK by default extends RUSTC_FLAGS with options
+# that are global to all Tock boards.
+#
+# We use `remap-path-prefix` to remove user-specific filepath strings for error
+# reporting from appearing in the generated binary.
+RUSTC_FLAGS_TOCK ?= \
+ $(RUSTC_FLAGS) \
+ --remap-path-prefix=$(TOCK_ROOT_DIRECTORY)= \
+
+# Disallow warnings for continuous integration builds. Disallowing them here
+# ensures that warnings during testing won't prevent compilation from succeeding.
+ifeq ($(CI),true)
+ RUSTC_FLAGS_TOCK += -D warnings
+endif
+
+# The following flags should only be passed to the board's binary crate, but
+# not to any of its dependencies (the kernel, capsules, chips, etc.). The
+# dependencies wouldn't use it, but because the link path is different for each
+# board, Cargo wouldn't be able to cache builds of the dependencies.
+#
+# Indeed, as far as Cargo is concerned, building the kernel with
+# `-C link-arg=-L/tock/boards/imix` is different than building the kernel with
+# `-C link-arg=-L/tock/boards/hail`, so Cargo would have to rebuild the kernel
+# for each board instead of caching it per board (even if in reality the same
+# kernel is built because the link-arg isn't used by the kernel).
+#
+# Ultimately, this should move to the Cargo.toml, for example when
+# https://github.com/rust-lang/cargo/pull/7811 is merged into Cargo.
+#
+# The difference between `RUSTC_FLAGS_TOCK` and `RUSTC_FLAGS_FOR_BIN` is that
+# the former is forwarded to all the dependencies (being passed to cargo via
+# the `RUSTFLAGS` environment variable), whereas the latter is only applied to
+# the final binary crate (being passed as parameter to `cargo rustc`).
+RUSTC_FLAGS_FOR_BIN ?= \
+ -C link-arg=-L$(abspath .) \
+
+# http://stackoverflow.com/questions/10858261/abort-makefile-if-variable-not-set
+# Check that given variables are set and all have non-empty values, print an
+# error otherwise.
+check_defined = $(strip $(foreach 1,$1,$(if $(value $1),,$(error Undefined variable "$1"))))
+
+# Check that we know the basics of what we are compiling for.
+# `PLATFORM`: The name of the board that the kernel is being compiled for.
+# `TARGET` : The Rust target architecture the kernel is being compiled for.
+$(call check_defined, PLATFORM)
+$(call check_defined, TARGET)
+
+# Location of target-specific build
+TARGET_PATH := $(TARGET_DIRECTORY)$(TARGET)
+
+# If environment variable V is non-empty, be verbose.
+ifneq ($(V),)
+ Q =
+ VERBOSE = --verbose
+else
+ Q = @
+ VERBOSE =
+endif
+
+# Ask git what version of the Tock kernel we are compiling, so we can include
+# this within the binary. If Tock is not within a git repo then we fallback to
+# a set string which should be updated with every release.
+export TOCK_KERNEL_VERSION := $(shell cd $(TOCK_ROOT_DIRECTORY) && git describe --tags --always 2> /dev/null || echo "1.4+")
+
+# Validate that rustup is new enough.
+MINIMUM_RUSTUP_VERSION := 1.11.0
+RUSTUP_VERSION := $(strip $(word 2, $(shell $(RUSTUP) --version)))
+ifeq ($(shell $(TOCK_ROOT_DIRECTORY)tools/semver.sh $(RUSTUP_VERSION) \< $(MINIMUM_RUSTUP_VERSION)), true)
+ $(warning Required tool `$(RUSTUP)` is out-of-date.)
+ $(warning Running `$(RUSTUP) update` in 3 seconds (ctrl-c to cancel))
+ $(shell sleep 3s)
+ DUMMY := $(shell $(RUSTUP) update)
+endif
+
+# Verify that various required Rust components are installed. All of these steps
+# only have to be done once per Rust version, but will take some time when
+# compiling for the first time.
+LLVM_TOOLS_INSTALLED := $(shell $(RUSTUP) component list | grep 'llvm-tools-preview.*(installed)' > /dev/null; echo $$?)
+ifeq ($(LLVM_TOOLS_INSTALLED),1)
+ $(shell $(RUSTUP) component add llvm-tools-preview)
+endif
+ifneq ($(shell $(RUSTUP) component list | grep rust-src),rust-src (installed))
+ $(shell $(RUSTUP) component add rust-src)
+endif
+ifneq ($(shell $(RUSTUP) target list | grep "$(TARGET) (installed)"),$(TARGET) (installed))
+ $(shell $(RUSTUP) target add $(TARGET))
+endif
+
+# If the user is using the standard toolchain we need to get the full path.
+# rustup should take care of this for us by putting in a proxy in .cargo/bin,
+# but until that is setup we workaround it.
+ifeq ($(TOOLCHAIN),llvm)
+ TOOLCHAIN = "$(shell dirname $(shell find `rustc --print sysroot` -name llvm-size))/llvm"
+endif
+
+# Set variables of the key tools we need to compile a Tock kernel.
+SIZE ?= $(TOOLCHAIN)-size
+OBJCOPY ?= $(TOOLCHAIN)-objcopy
+OBJDUMP ?= $(TOOLCHAIN)-objdump
+
+# Set additional flags to produce binary from .elf.
+# * --strip-sections prevents enormous binaries when SRAM is below flash.
+# * --remove-section .apps prevents the .apps section from being included in the
+# kernel binary file. This section is a placeholder for optionally including
+# application binaries, and only needs to exist in the .elf. By removing it,
+# we prevent the kernel binary from overwriting applications.
+OBJCOPY_FLAGS ?= --strip-sections -S --remove-section .apps
+# This make variable allows board-specific Makefiles to pass down options to
+# the Cargo build command. For example, in boards/<custom_board>/Makefile:
+# `CARGO_FLAGS += --features=foo` would pass feature `foo` to the top level
+# Cargo.toml.
+CARGO_FLAGS ?=
+# Add default flags to cargo. Boards can add additional options in CARGO_FLAGS
+CARGO_FLAGS_TOCK ?= $(VERBOSE) --target=$(TARGET) --package $(PLATFORM) --target-dir=$(TARGET_DIRECTORY) $(CARGO_FLAGS)
+# Set the default flags we need for objdump to get a .lst file.
+OBJDUMP_FLAGS ?= --disassemble-all --source --section-headers --demangle
+# Set default flags for size
+SIZE_FLAGS ?=
+
+# Need an extra flag for OBJDUMP if we are on a thumb platform.
+ifneq (,$(findstring thumb,$(TARGET)))
+ OBJDUMP_FLAGS += --arch-name=thumb
+endif
+
+# Check whether the system already has a sha256sum application
+# present, if not use the custom shipped one
+ifeq (, $(shell sha256sum --version 2>/dev/null))
+ # No system sha256sum available
+ SHA256SUM := $(CARGO) run --manifest-path $(TOCK_ROOT_DIRECTORY)tools/sha256sum/Cargo.toml -- 2>/dev/null
+else
+ # Use system sha256sum
+ SHA256SUM := sha256sum
+endif
+
+# Dump configuration for verbose builds
+ifneq ($(V),)
+ $(info )
+ $(info *******************************************************)
+ $(info TOCK KERNEL BUILD SYSTEM -- VERBOSE BUILD CONFIGURATION)
+ $(info *******************************************************)
+ $(info MAKEFILE_COMMON_PATH = $(MAKEFILE_COMMON_PATH))
+ $(info TOCK_ROOT_DIRECTORY = $(TOCK_ROOT_DIRECTORY))
+ $(info TARGET_DIRECTORY = $(TARGET_DIRECTORY))
+ $(info )
+ $(info PLATFORM = $(PLATFORM))
+ $(info TARGET = $(TARGET))
+ $(info TOCK_KERNEL_VERSION = $(TOCK_KERNEL_VERSION))
+ $(info RUSTC_FLAGS = $(RUSTC_FLAGS))
+ $(info RUSTC_FLAGS_TOCK = $(RUSTC_FLAGS_TOCK))
+ $(info MAKEFLAGS = $(MAKEFLAGS))
+ $(info OBJDUMP_FLAGS = $(OBJDUMP_FLAGS))
+ $(info OBJCOPY_FLAGS = $(OBJCOPY_FLAGS))
+ $(info CARGO_FLAGS = $(CARGO_FLAGS))
+ $(info CARGO_FLAGS_TOCK = $(CARGO_FLAGS_TOCK))
+ $(info SIZE_FLAGS = $(SIZE_FLAGS))
+ $(info )
+ $(info TOOLCHAIN = $(TOOLCHAIN))
+ $(info SIZE = $(SIZE))
+ $(info OBJCOPY = $(OBJCOPY))
+ $(info OBJDUMP = $(OBJDUMP))
+ $(info CARGO = $(CARGO))
+ $(info RUSTUP = $(RUSTUP))
+ $(info SHA256SUM = $(SHA256SUM))
+ $(info )
+ $(info cargo --version = $(shell $(CARGO) --version))
+ $(info rustc --version = $(shell rustc --version))
+ $(info rustup --version = $(shell $(RUSTUP) --version))
+ $(info *******************************************************)
+ $(info )
+endif
+
+.PRECIOUS: %.elf
+# Support rules
+
+# User-facing targets
+.PHONY: all
+all: release
+
+# `make check` runs the Rust compiler but does not actually output the final
+# binary. This makes checking for Rust errors much faster.
+.PHONY: check
+check:
+ $(Q)$(CARGO) check $(VERBOSE) $(CARGO_FLAGS_TOCK)
+
+
+.PHONY: clean
+clean::
+ $(Q)$(CARGO) clean $(VERBOSE) --target-dir=$(TARGET_DIRECTORY)
+
+.PHONY: release
+release: $(TARGET_PATH)/release/$(PLATFORM).bin
+
+.PHONY: debug
+debug: $(TARGET_PATH)/debug/$(PLATFORM).bin
+
+.PHONY: debug-lst
+debug-lst: $(TARGET_PATH)/debug/$(PLATFORM).lst
+
+.PHONY: doc
+doc: | target
+ @# This mess is all to work around rustdoc giving no way to return an
+ @# error if there are warnings. This effectively simulates that.
+ $(Q)RUSTDOCFLAGS='-Z unstable-options --document-hidden-items -D warnings' $(CARGO) --color=always doc $(VERBOSE) --release --package $(PLATFORM) --target-dir=$(TARGET_DIRECTORY) 2>&1 | tee /dev/tty | grep -q warning && (echo "Warnings detected during doc build" && if [[ $$CI == "true" ]]; then echo "Erroring due to CI context" && exit 33; fi) || if [ $$? -eq 33 ]; then exit 1; fi
+
+
+.PHONY: lst
+lst: $(TARGET_PATH)/release/$(PLATFORM).lst
+
+# Helper rule for showing the TARGET used by this board. Useful when building
+# the documentation for all boards.
+.PHONY: show-target
+show-target:
+ $(info $(TARGET))
+
+# Support rules
+
+target:
+ @mkdir -p $(TARGET_PATH)
+
+# Cargo outputs an elf file (just without a file extension)
+%.elf: %
+ $(Q)cp $< $@
+
+
+%.bin: %.elf
+ $(Q)$(OBJCOPY) --output-target=binary $(OBJCOPY_FLAGS) $< $@
+ $(Q)$(SHA256SUM) $@
+
+%.lst: %.elf
+ $(Q)$(OBJDUMP) $(OBJDUMP_FLAGS) $< > $@
+
+
+$(TOCK_ROOT_DIRECTORY)tools/sha256sum/target/debug/sha256sum:
+ $(Q)$(CARGO) build $(VERBOSE) --manifest-path $(TOCK_ROOT_DIRECTORY)tools/sha256sum/Cargo.toml
+
+
+# Cargo-drivers
+# We want to always invoke cargo (yay nested build systems), so these need to
+# be phony, which means they can't be pattern rules.
+
+.PHONY: $(TARGET_PATH)/release/$(PLATFORM)
+$(TARGET_PATH)/release/$(PLATFORM):
+ $(Q)RUSTFLAGS="$(RUSTC_FLAGS_TOCK)" $(CARGO) rustc $(CARGO_FLAGS_TOCK) --bin $(PLATFORM) --release -- $(RUSTC_FLAGS_FOR_BIN)
+ $(Q)$(SIZE) $(SIZE_FLAGS) $@
+
+.PHONY: $(TARGET_PATH)/debug/$(PLATFORM)
+$(TARGET_PATH)/debug/$(PLATFORM):
+ $(Q)RUSTFLAGS="$(RUSTC_FLAGS_TOCK)" $(CARGO) rustc $(CARGO_FLAGS_TOCK) --bin $(PLATFORM) -- $(RUSTC_FLAGS_FOR_BIN)
+ $(Q)$(SIZE) $(SIZE_FLAGS) $@
diff --git a/boards/opentitan-matcha/Cargo.lock b/boards/opentitan-matcha/Cargo.lock
new file mode 100644
index 0000000..92c2512
--- /dev/null
+++ b/boards/opentitan-matcha/Cargo.lock
@@ -0,0 +1,88 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "capsules"
+version = "0.1.0"
+dependencies = [
+ "enum_primitive",
+ "kernel",
+]
+
+[[package]]
+name = "components"
+version = "0.1.0"
+dependencies = [
+ "capsules",
+ "kernel",
+]
+
+[[package]]
+name = "enum_primitive"
+version = "0.1.0"
+
+[[package]]
+name = "kernel"
+version = "0.1.0"
+dependencies = [
+ "tock-cells",
+ "tock-registers",
+]
+
+[[package]]
+name = "lowrisc"
+version = "0.1.0"
+dependencies = [
+ "kernel",
+ "rv32i",
+ "tock-rt0",
+]
+
+[[package]]
+name = "matcha"
+version = "0.1.0"
+dependencies = [
+ "kernel",
+ "lowrisc",
+ "rv32i",
+]
+
+[[package]]
+name = "opentitan-matcha"
+version = "0.1.0"
+dependencies = [
+ "capsules",
+ "components",
+ "kernel",
+ "lowrisc",
+ "matcha",
+ "rv32i",
+]
+
+[[package]]
+name = "riscv-csr"
+version = "0.1.0"
+dependencies = [
+ "tock-registers",
+]
+
+[[package]]
+name = "rv32i"
+version = "0.1.0"
+dependencies = [
+ "kernel",
+ "riscv-csr",
+ "tock-registers",
+ "tock-rt0",
+]
+
+[[package]]
+name = "tock-cells"
+version = "0.1.0"
+
+[[package]]
+name = "tock-registers"
+version = "0.5.0"
+
+[[package]]
+name = "tock-rt0"
+version = "0.1.0"
diff --git a/boards/opentitan-matcha/Cargo.toml b/boards/opentitan-matcha/Cargo.toml
new file mode 100644
index 0000000..6d99d20
--- /dev/null
+++ b/boards/opentitan-matcha/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "opentitan-matcha"
+version = "0.1.0"
+authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
+build = "build.rs"
+edition = "2018"
+
+[dependencies]
+components = { path = "../../../tock/boards/components" }
+rv32i = { path = "../../../tock/arch/rv32i" }
+capsules = { path = "../../../tock/capsules" }
+kernel = { path = "../../../tock/kernel" }
+matcha = { path = "../../chips/matcha" }
+lowrisc = { path = "../../../tock/chips/lowrisc" }
+
+[features]
+# OpenTitan Matcha SoC design can be synthesized or compiled for different targets. A
+# target can be a specific FPGA board, an ASIC technology, or a simulation tool.
+# Please see: https://docs.opentitan.org/doc/ug/getting_started/ for further
+# information.
+#
+# OpenTitan Matcha CPU and possibly other components must be configured appropriately
+# for a specific target:
+# - fpga_nexysvideo:
+# OpenTitan Matcha SoC design running on Nexys Video Artix-7 FPGA.
+#
+# - sim_verilator:
+# OpenTitan Matcha SoC design simulated in Verilator.
+fpga_nexysvideo = ["matcha/config_fpga_nexysvideo"]
+sim_verilator = ["matcha/config_sim_verilator"]
diff --git a/boards/opentitan-matcha/Makefile b/boards/opentitan-matcha/Makefile
new file mode 100644
index 0000000..f891877
--- /dev/null
+++ b/boards/opentitan-matcha/Makefile
@@ -0,0 +1,34 @@
+# Makefile for building the tock kernel for the OpenTitan Matcha platform
+
+DEFAULT_BOARD_CONFIGURATION=fpga_nexysvideo
+TARGET=riscv32imc-unknown-none-elf
+PLATFORM=opentitan-matcha
+FLASHID=--dev-id="0403:6010"
+RISC_PREFIX = riscv64-elf
+
+
+include ../../Makefile.common
+
+# Pass OpenTitan board configuration option in `BOARD_CONFIGURATION` through
+# Cargo `--features`. Please see `Cargo.toml` for available options.
+ifneq ($(BOARD_CONFIGURATION),)
+ CARGO_FLAGS += --features=$(BOARD_CONFIGURATION)
+else
+ CARGO_FLAGS += --features=$(DEFAULT_BOARD_CONFIGURATION)
+endif
+
+qemu: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf
+ $(call check_defined, OPENTITAN_BOOT_ROM)
+ qemu-system-riscv32 -M opentitan -kernel $^ -bios $(OPENTITAN_BOOT_ROM) -nographic -serial mon:stdio
+
+qemu-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf
+ $(call check_defined, OPENTITAN_BOOT_ROM)
+ qemu-system-riscv32 -M opentitan -kernel $^ -bios $(OPENTITAN_BOOT_ROM) -device loader,file=$(APP),addr=0x20030000 -nographic -serial mon:stdio
+
+flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
+ $(OPENTITAN_TREE)/build-out/sw/host/spiflash/spiflash $(FLASHID) --input=$(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
+
+flash-app: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf
+ $(RISC_PREFIX)-objcopy --update-section .apps=$(APP) $^ $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf
+ $(RISC_PREFIX)-objcopy --output-target=binary $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.elf $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.bin
+ $(OPENTITAN_TREE)/build-out/sw/host/spiflash/spiflash $(FLASHID) --input=$(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM)-app.bin
diff --git a/boards/opentitan-matcha/README.md b/boards/opentitan-matcha/README.md
new file mode 100644
index 0000000..551991f
--- /dev/null
+++ b/boards/opentitan-matcha/README.md
@@ -0,0 +1,4 @@
+OpenTitan Matcha RISC-V Board
+=================
+
+This is provides board support for the Matcha config of OpenTitan.
\ No newline at end of file
diff --git a/boards/opentitan-matcha/build.rs b/boards/opentitan-matcha/build.rs
new file mode 100644
index 0000000..ab031c3
--- /dev/null
+++ b/boards/opentitan-matcha/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+ println!("cargo:rerun-if-changed=layout.ld");
+ println!("cargo:rerun-if-changed=../../../tock/boards/kernel_layout.ld");
+}
diff --git a/boards/opentitan-matcha/layout.ld b/boards/opentitan-matcha/layout.ld
new file mode 100644
index 0000000..61f2e9a
--- /dev/null
+++ b/boards/opentitan-matcha/layout.ld
@@ -0,0 +1,22 @@
+MEMORY
+{
+ rom (rx) : ORIGIN = 0x20000000, LENGTH = 0x30000
+ prog (rx) : ORIGIN = 0x20030000, LENGTH = 0x100000-0x30000
+ ram (!rx) : ORIGIN = 0x10000000, LENGTH = 0x10000
+}
+
+MPU_MIN_ALIGN = 1K;
+SECTIONS {
+ /*
+ * The flash header needs to match what the boot ROM for OpenTitan is
+ * expecting. At the moment, it contains only the entry point, but it
+ * will eventually contain the signature -- and (hopefully?!) some
+ * versioning information to make it slightly easier to debug when the
+ * boot ROM and Tock are out of sync with respect to the definition...
+ */
+ .flash_header : {
+ LONG(_stext)
+ } > rom
+}
+
+INCLUDE ../../../tock/boards/kernel_layout.ld
diff --git a/boards/opentitan-matcha/src/io.rs b/boards/opentitan-matcha/src/io.rs
new file mode 100644
index 0000000..2439aec
--- /dev/null
+++ b/boards/opentitan-matcha/src/io.rs
@@ -0,0 +1,50 @@
+use core::fmt::Write;
+use core::panic::PanicInfo;
+use core::str;
+use kernel::debug;
+use kernel::debug::IoWrite;
+use kernel::hil::gpio;
+use kernel::hil::led;
+
+use crate::CHIP;
+use crate::PROCESSES;
+
+struct Writer {}
+
+static mut WRITER: Writer = Writer {};
+
+impl Write for Writer {
+ fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
+ self.write(s.as_bytes());
+ Ok(())
+ }
+}
+
+impl IoWrite for Writer {
+ fn write(&mut self, buf: &[u8]) {
+ unsafe {
+ matcha::uart::UART0.transmit_sync(buf);
+ }
+ }
+}
+
+/// Panic handler.
+#[cfg(not(test))]
+#[no_mangle]
+#[panic_handler]
+pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
+ // turn off the non panic leds, just in case
+ let first_led = &mut led::LedLow::new(&mut matcha::gpio::PORT[7]);
+ gpio::Pin::make_output(&matcha::gpio::PORT[7]);
+
+ let writer = &mut WRITER;
+
+ debug::panic(
+ &mut [first_led],
+ writer,
+ pi,
+ &rv32i::support::nop,
+ &PROCESSES,
+ &CHIP,
+ )
+}
diff --git a/boards/opentitan-matcha/src/main.rs b/boards/opentitan-matcha/src/main.rs
new file mode 100644
index 0000000..9dcc2ab
--- /dev/null
+++ b/boards/opentitan-matcha/src/main.rs
@@ -0,0 +1,309 @@
+//! Board file for LowRISC OpenTitan Matcha RISC-V development platform.
+//!
+//! - <https://opentitan.org/>
+
+#![no_std]
+// Disable this attribute when documenting, as a workaround for
+// https://github.com/rust-lang/rust/issues/62184.
+#![cfg_attr(not(doc), no_main)]
+#![feature(const_in_array_repeat_expressions)]
+
+use capsules::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
+use capsules::virtual_hmac::VirtualMuxHmac;
+use kernel::capabilities;
+use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
+use kernel::component::Component;
+use kernel::hil;
+use kernel::hil::i2c::I2CMaster;
+use kernel::hil::time::Alarm;
+use kernel::Chip;
+use kernel::Platform;
+use kernel::{create_capability, debug, static_init};
+use rv32i::csr;
+
+pub mod io;
+
+//
+// Actual memory for holding the active process structures. Need an empty list
+// at least.
+static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; 4] =
+ [None, None, None, None];
+
+static mut CHIP: Option<
+ &'static matcha::chip::Matcha<VirtualMuxAlarm<'static, matcha::timer::RvTimer>>,
+> = None;
+
+// How should the kernel respond when a process faults.
+const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
+
+/// Dummy buffer that causes the linker to reserve enough space for the stack.
+#[no_mangle]
+#[link_section = ".stack_buffer"]
+pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
+
+/// A structure representing this platform that holds references to all
+/// capsules for this platform. We've included an alarm and console.
+struct OpenTitan {
+ led: &'static capsules::led::LED<'static, matcha::gpio::GpioPin<'static>>,
+ gpio: &'static capsules::gpio::GPIO<'static, matcha::gpio::GpioPin<'static>>,
+ console: &'static capsules::console::Console<'static>,
+ alarm: &'static capsules::alarm::AlarmDriver<
+ 'static,
+ VirtualMuxAlarm<'static, matcha::timer::RvTimer<'static>>,
+ >,
+ hmac: &'static capsules::hmac::HmacDriver<
+ 'static,
+ VirtualMuxHmac<'static, lowrisc::hmac::Hmac<'static>, [u8; 32]>,
+ [u8; 32],
+ >,
+ lldb: &'static capsules::low_level_debug::LowLevelDebug<
+ 'static,
+ capsules::virtual_uart::UartDevice<'static>,
+ >,
+ i2c_master: &'static capsules::i2c_master::I2CMasterDriver<lowrisc::i2c::I2c<'static>>,
+}
+
+/// Mapping of integer syscalls to objects that implement syscalls.
+impl Platform for OpenTitan {
+ fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
+ where
+ F: FnOnce(Option<&dyn kernel::Driver>) -> R,
+ {
+ match driver_num {
+ capsules::led::DRIVER_NUM => f(Some(self.led)),
+ capsules::hmac::DRIVER_NUM => f(Some(self.hmac)),
+ capsules::gpio::DRIVER_NUM => f(Some(self.gpio)),
+ capsules::console::DRIVER_NUM => f(Some(self.console)),
+ capsules::alarm::DRIVER_NUM => f(Some(self.alarm)),
+ capsules::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
+ capsules::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
+ _ => f(None),
+ }
+ }
+}
+
+/// Reset Handler.
+///
+/// This function is called from the arch crate after some very basic RISC-V
+/// setup.
+#[no_mangle]
+pub unsafe fn reset_handler() {
+ // Basic setup of the platform.
+ rv32i::init_memory();
+ // Ibex-specific handler
+ matcha::chip::configure_trap_handler();
+
+ // initialize capabilities
+ let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
+ let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
+
+ let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
+
+ let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES));
+
+ let dynamic_deferred_call_clients =
+ static_init!([DynamicDeferredCallClientState; 1], Default::default());
+ let dynamic_deferred_caller = static_init!(
+ DynamicDeferredCall,
+ DynamicDeferredCall::new(dynamic_deferred_call_clients)
+ );
+ DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
+
+ // Configure kernel debug gpios as early as possible
+ kernel::debug::assign_gpios(
+ Some(&matcha::gpio::PORT[7]), // First LED
+ None,
+ None,
+ );
+
+ // Create a shared UART channel for the console and for kernel debug.
+ let uart_mux = components::console::UartMuxComponent::new(
+ &matcha::uart::UART0,
+ matcha::uart::UART0_BAUDRATE,
+ dynamic_deferred_caller,
+ )
+ .finalize(());
+
+ // LEDs
+ // Start with half on and half off
+ let led = components::led::LedsComponent::new(components::led_component_helper!(
+ matcha::gpio::GpioPin,
+ (
+ &matcha::gpio::PORT[8],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[9],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[10],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[11],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[12],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[13],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[14],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ ),
+ (
+ &matcha::gpio::PORT[15],
+ kernel::hil::gpio::ActivationMode::ActiveHigh
+ )
+ ))
+ .finalize(components::led_component_buf!(matcha::gpio::GpioPin));
+
+ let gpio = components::gpio::GpioComponent::new(
+ board_kernel,
+ components::gpio_component_helper!(
+ matcha::gpio::GpioPin,
+ 0 => &matcha::gpio::PORT[0],
+ 1 => &matcha::gpio::PORT[1],
+ 2 => &matcha::gpio::PORT[2],
+ 3 => &matcha::gpio::PORT[3],
+ 4 => &matcha::gpio::PORT[4],
+ 5 => &matcha::gpio::PORT[5],
+ 6 => &matcha::gpio::PORT[6],
+ 7 => &matcha::gpio::PORT[15]
+ ),
+ )
+ .finalize(components::gpio_component_buf!(matcha::gpio::GpioPin));
+
+ let alarm = &matcha::timer::TIMER;
+ alarm.setup();
+
+ // Create a shared virtualization mux layer on top of a single hardware
+ // alarm.
+ let mux_alarm = static_init!(
+ MuxAlarm<'static, matcha::timer::RvTimer>,
+ MuxAlarm::new(alarm)
+ );
+ hil::time::Alarm::set_alarm_client(&matcha::timer::TIMER, mux_alarm);
+
+ // Alarm
+ let virtual_alarm_user = static_init!(
+ VirtualMuxAlarm<'static, matcha::timer::RvTimer>,
+ VirtualMuxAlarm::new(mux_alarm)
+ );
+ let scheduler_timer_virtual_alarm = static_init!(
+ VirtualMuxAlarm<'static, matcha::timer::RvTimer>,
+ VirtualMuxAlarm::new(mux_alarm)
+ );
+ let alarm = static_init!(
+ capsules::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, matcha::timer::RvTimer>>,
+ capsules::alarm::AlarmDriver::new(
+ virtual_alarm_user,
+ board_kernel.create_grant(&memory_allocation_cap)
+ )
+ );
+ hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
+
+ let chip = static_init!(
+ matcha::chip::Matcha<VirtualMuxAlarm<'static, matcha::timer::RvTimer>>,
+ matcha::chip::Matcha::new(scheduler_timer_virtual_alarm)
+ );
+ scheduler_timer_virtual_alarm.set_alarm_client(chip.scheduler_timer());
+ CHIP = Some(chip);
+
+ // Need to enable all interrupts for Tock Kernel
+ chip.enable_plic_interrupts();
+ // enable interrupts globally
+ csr::CSR
+ .mie
+ .modify(csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET + csr::mie::mie::mext::SET);
+ csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
+
+ // Setup the console.
+ let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
+ // Create the debugger object that handles calls to `debug!()`.
+ components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
+
+ let lldb = components::lldb::LowLevelDebugComponent::new(board_kernel, uart_mux).finalize(());
+
+ let hmac_data_buffer = static_init!([u8; 64], [0; 64]);
+ let hmac_dest_buffer = static_init!([u8; 32], [0; 32]);
+
+ let mux_hmac = components::hmac::HmacMuxComponent::new(&matcha::hmac::HMAC).finalize(
+ components::hmac_mux_component_helper!(lowrisc::hmac::Hmac, [u8; 32]),
+ );
+
+ let hmac = components::hmac::HmacComponent::new(
+ board_kernel,
+ &mux_hmac,
+ hmac_data_buffer,
+ hmac_dest_buffer,
+ )
+ .finalize(components::hmac_component_helper!(
+ lowrisc::hmac::Hmac,
+ [u8; 32]
+ ));
+
+ let i2c_master = static_init!(
+ capsules::i2c_master::I2CMasterDriver<lowrisc::i2c::I2c<'static>>,
+ capsules::i2c_master::I2CMasterDriver::new(
+ &matcha::i2c::I2C,
+ &mut capsules::i2c_master::BUF,
+ board_kernel.create_grant(&memory_allocation_cap)
+ )
+ );
+
+ matcha::i2c::I2C.set_master_client(i2c_master);
+
+ /// These symbols are defined in the linker script.
+ extern "C" {
+ /// Beginning of the ROM region containing app images.
+ static _sapps: u8;
+ /// End of the ROM region containing app images.
+ static _eapps: u8;
+ /// Beginning of the RAM region for app memory.
+ static mut _sappmem: u8;
+ /// End of the RAM region for app memory.
+ static _eappmem: u8;
+ }
+
+ let opentitan = OpenTitan {
+ gpio: gpio,
+ led: led,
+ console: console,
+ alarm: alarm,
+ hmac,
+ lldb: lldb,
+ i2c_master,
+ };
+
+ kernel::procs::load_processes(
+ board_kernel,
+ chip,
+ core::slice::from_raw_parts(
+ &_sapps as *const u8,
+ &_eapps as *const u8 as usize - &_sapps as *const u8 as usize,
+ ),
+ core::slice::from_raw_parts_mut(
+ &mut _sappmem as *mut u8,
+ &_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
+ ),
+ &mut PROCESSES,
+ FAULT_RESPONSE,
+ &process_mgmt_cap,
+ )
+ .unwrap_or_else(|err| {
+ debug!("Error loading processes!");
+ debug!("{:?}", err);
+ });
+ debug!("OpenTitan initialisation complete. Entering main loop");
+ debug!("Woo Tock!");
+
+
+ let scheduler = components::sched::priority::PriorityComponent::new(board_kernel).finalize(());
+ board_kernel.kernel_loop(&opentitan, chip, None, scheduler, &main_loop_cap);
+}
diff --git a/chips/matcha/Cargo.toml b/chips/matcha/Cargo.toml
new file mode 100644
index 0000000..1c39868
--- /dev/null
+++ b/chips/matcha/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "matcha"
+version = "0.1.0"
+authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
+edition = "2018"
+
+[features]
+# Compiling this crate requires enabling one of these features, otherwise
+# the default will be chosen.
+config_fpga_nexysvideo = ["config_disable_default"]
+config_sim_verilator = ["config_disable_default"]
+config_disable_default = []
+
+[dependencies]
+lowrisc = { path = "../../../tock/chips/lowrisc" }
+rv32i = { path = "../../../tock/arch/rv32i" }
+kernel = { path = "../../../tock/kernel" }
+
diff --git a/chips/matcha/README.md b/chips/matcha/README.md
new file mode 100644
index 0000000..b5b6a4c
--- /dev/null
+++ b/chips/matcha/README.md
@@ -0,0 +1,4 @@
+OpenTitan Matcha SoC
+=======================
+Matcha is the dual-hart OpenTitan system. At the center of the Matcha are two
+Ibex RISC-V compliant processors.
\ No newline at end of file
diff --git a/chips/matcha/src/aes.rs b/chips/matcha/src/aes.rs
new file mode 100644
index 0000000..7ab4bf9
--- /dev/null
+++ b/chips/matcha/src/aes.rs
@@ -0,0 +1,350 @@
+//! Support for the AES hardware block on OpenTitan
+//!
+//! https://docs.opentitan.org/hw/ip/aes/doc/
+
+use kernel::common::cells::{OptionalCell, TakeCell};
+use kernel::common::registers::{
+ register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly,
+};
+use kernel::common::StaticRef;
+use kernel::debug;
+use kernel::hil;
+use kernel::hil::symmetric_encryption;
+use kernel::hil::symmetric_encryption::{AES128_BLOCK_SIZE, AES128_KEY_SIZE};
+use kernel::ReturnCode;
+
+const MAX_LENGTH: usize = 128;
+
+register_structs! {
+ pub AesRegisters {
+ (0x00 => key0: WriteOnly<u32>),
+ (0x04 => key1: WriteOnly<u32>),
+ (0x08 => key2: WriteOnly<u32>),
+ (0x0c => key3: WriteOnly<u32>),
+ (0x10 => key4: WriteOnly<u32>),
+ (0x14 => key5: WriteOnly<u32>),
+ (0x18 => key6: WriteOnly<u32>),
+ (0x1c => key7: WriteOnly<u32>),
+ (0x20 => data_in0: WriteOnly<u32>),
+ (0x24 => data_in1: WriteOnly<u32>),
+ (0x28 => data_in2: WriteOnly<u32>),
+ (0x2c => data_in3: WriteOnly<u32>),
+ (0x30 => data_out0: ReadOnly<u32>),
+ (0x34 => data_out1: ReadOnly<u32>),
+ (0x38 => data_out2: ReadOnly<u32>),
+ (0x3c => data_out3: ReadOnly<u32>),
+ (0x40 => ctrl: ReadWrite<u32, CTRL::Register>),
+ (0x44 => trigger: WriteOnly<u32, TRIGGER::Register>),
+ (0x48 => status: ReadOnly<u32, STATUS::Register>),
+ (0x4c => @END),
+ }
+}
+
+register_bitfields![u32,
+ CTRL [
+ OPERATION OFFSET(0) NUMBITS(1) [
+ Encrypting = 0,
+ Decrypting = 1
+ ],
+ KEY_LEN OFFSET(1) NUMBITS(3) [
+ Key128 = 1,
+ Key192 = 2,
+ Key256 = 4
+ ],
+ MANUAL_OPERATION OFFSET(4) NUMBITS(1) []
+ ],
+ TRIGGER [
+ START OFFSET(0) NUMBITS(1) [],
+ KEY_CLEAR OFFSET(1) NUMBITS(1) [],
+ DATA_IN_CLEAR OFFSET(2) NUMBITS(1) [],
+ DATA_OUT_CLEAR OFFSET(3) NUMBITS(1) []
+ ],
+ STATUS [
+ IDLE 0,
+ STALL 1,
+ OUTPUT_VALID 2,
+ INPUT_READY 3
+ ]
+];
+
+// https://docs.opentitan.org/hw/top_earlgrey/doc/
+const AES_BASE: StaticRef<AesRegisters> =
+ unsafe { StaticRef::new(0x41100000 as *const AesRegisters) };
+
+pub struct Aes<'a> {
+ registers: StaticRef<AesRegisters>,
+
+ client: OptionalCell<&'a dyn hil::symmetric_encryption::Client<'a>>,
+ source: TakeCell<'a, [u8]>,
+ dest: TakeCell<'a, [u8]>,
+}
+
+impl<'a> Aes<'a> {
+ const fn new() -> Aes<'a> {
+ Aes {
+ registers: AES_BASE,
+ client: OptionalCell::empty(),
+ source: TakeCell::empty(),
+ dest: TakeCell::empty(),
+ }
+ }
+
+ fn clear(&self) {
+ let regs = self.registers;
+ regs.trigger.write(
+ TRIGGER::KEY_CLEAR::SET + TRIGGER::DATA_IN_CLEAR::SET + TRIGGER::DATA_OUT_CLEAR::SET,
+ );
+ }
+
+ fn configure(&self, encrypting: bool) {
+ let regs = self.registers;
+ let e = if encrypting {
+ CTRL::OPERATION::Encrypting
+ } else {
+ CTRL::OPERATION::Decrypting
+ };
+ // Set this in manual mode for the moment since automatic block mode
+ // does not appear to be working
+
+ regs.ctrl
+ .write(e + CTRL::KEY_LEN::Key128 + CTRL::MANUAL_OPERATION::SET);
+ }
+
+ fn idle(&self) -> bool {
+ let regs = self.registers;
+ regs.status.is_set(STATUS::IDLE)
+ }
+
+ fn input_ready(&self) -> bool {
+ let regs = self.registers;
+ regs.status.is_set(STATUS::INPUT_READY)
+ }
+
+ fn output_valid(&self) -> bool {
+ let regs = self.registers;
+ regs.status.is_set(STATUS::OUTPUT_VALID)
+ }
+
+ fn trigger(&self) {
+ let regs = self.registers;
+ regs.trigger.write(TRIGGER::START::SET);
+ }
+
+ fn read_block(&self, blocknum: usize) {
+ let regs = self.registers;
+ let blocknum = blocknum * AES128_BLOCK_SIZE;
+
+ loop {
+ if self.output_valid() {
+ break;
+ }
+ }
+
+ self.dest.map_or_else(
+ || {
+ debug!("Called read_block() with no data");
+ },
+ |dest| {
+ for i in 0..4 {
+ // we work off an array of u8 so we need to assemble those
+ // back into a u32
+ let mut v = 0;
+ match i {
+ 0 => v = regs.data_out0.get(),
+ 1 => v = regs.data_out1.get(),
+ 2 => v = regs.data_out2.get(),
+ 3 => v = regs.data_out3.get(),
+ _ => {}
+ }
+ dest[blocknum + (i * 4) + 0] = (v >> 0) as u8;
+ dest[blocknum + (i * 4) + 1] = (v >> 8) as u8;
+ dest[blocknum + (i * 4) + 2] = (v >> 16) as u8;
+ dest[blocknum + (i * 4) + 3] = (v >> 24) as u8;
+ }
+ },
+ );
+ }
+
+ fn write_block(&self, blocknum: usize) {
+ let regs = self.registers;
+ let blocknum = blocknum * AES128_BLOCK_SIZE;
+
+ loop {
+ if self.input_ready() {
+ break;
+ }
+ }
+
+ self.source.map_or_else(
+ || {
+ // This is the case that dest = source
+ self.dest.map_or_else(
+ || {
+ debug!("Called write_block() with no data");
+ },
+ |dest| {
+ for i in 0..4 {
+ // we work off an array of u8 so we need to
+ // assemble those back into a u32
+ let mut v = dest[blocknum + (i * 4) + 0] as usize;
+ v |= (dest[blocknum + (i * 4) + 1] as usize) << 8;
+ v |= (dest[blocknum + (i * 4) + 2] as usize) << 16;
+ v |= (dest[blocknum + (i * 4) + 3] as usize) << 24;
+ match i {
+ 0 => regs.data_in0.set(v as u32),
+ 1 => regs.data_in1.set(v as u32),
+ 2 => regs.data_in2.set(v as u32),
+ 3 => regs.data_in3.set(v as u32),
+ _ => {}
+ }
+ }
+ },
+ )
+ },
+ |source| {
+ for i in 0..4 {
+ // we work off an array of u8 so we need to assemble
+ // those back into a u32
+ let mut v = source[blocknum + (i * 4) + 0] as usize;
+ v |= (source[blocknum + (i * 4) + 1] as usize) << 8;
+ v |= (source[blocknum + (i * 4) + 2] as usize) << 16;
+ v |= (source[blocknum + (i * 4) + 3] as usize) << 24;
+ match i {
+ 0 => regs.data_in0.set(v as u32),
+ 1 => regs.data_in1.set(v as u32),
+ 2 => regs.data_in2.set(v as u32),
+ 3 => regs.data_in3.set(v as u32),
+ _ => {}
+ }
+ }
+ },
+ );
+ }
+
+ fn set_key(&self, key: &[u8]) -> ReturnCode {
+ let regs = self.registers;
+
+ loop {
+ if self.idle() {
+ break;
+ }
+ }
+
+ if key.len() != AES128_KEY_SIZE {
+ return ReturnCode::EINVAL;
+ }
+
+ for i in 0..4 {
+ let mut k = key[i * 4 + 0] as usize;
+ k |= (key[i * 4 + 1] as usize) << 8;
+ k |= (key[i * 4 + 2] as usize) << 16;
+ k |= (key[i * 4 + 3] as usize) << 24;
+ match i {
+ 0 => regs.key0.set(k as u32),
+ 1 => regs.key1.set(k as u32),
+ 2 => regs.key2.set(k as u32),
+ 3 => regs.key3.set(k as u32),
+ _ => {}
+ }
+ }
+
+ // We must write the rest of the registers as well
+ regs.key4.set(0);
+ regs.key5.set(0);
+ regs.key6.set(0);
+ regs.key7.set(0);
+ ReturnCode::SUCCESS
+ }
+
+ fn do_crypt(&self, start_index: usize, stop_index: usize, wr_start_index: usize) {
+ // convert our indicies into the array into block numbers
+ // start and end are pointer for reading
+ // write is the pointer for writing
+ // Note that depending on whether or not we have separate source
+ // and dest buffers the write and read pointers may index into
+ // different arrays.
+ let start_block = start_index / AES128_BLOCK_SIZE;
+ let end_block = stop_index / AES128_BLOCK_SIZE;
+ let mut write_block = wr_start_index / AES128_BLOCK_SIZE;
+ for i in start_block..end_block {
+ self.write_block(write_block);
+ self.trigger();
+ self.read_block(i);
+ write_block = write_block + 1;
+ }
+ }
+}
+
+impl<'a> hil::symmetric_encryption::AES128<'a> for Aes<'a> {
+ fn enable(&self) {
+ self.configure(true);
+ }
+
+ fn disable(&self) {
+ self.clear();
+ }
+
+ fn set_client(&'a self, client: &'a dyn symmetric_encryption::Client<'a>) {
+ self.client.set(client);
+ }
+
+ fn set_iv(&self, _iv: &[u8]) -> ReturnCode {
+ // nothing because this is ECB
+ ReturnCode::SUCCESS
+ }
+
+ fn start_message(&self) {}
+
+ fn set_key(&self, key: &[u8]) -> ReturnCode {
+ self.set_key(key)
+ }
+
+ fn crypt(
+ &'a self,
+ source: Option<&'a mut [u8]>,
+ dest: &'a mut [u8],
+ start_index: usize,
+ stop_index: usize,
+ ) -> Option<(ReturnCode, Option<&'a mut [u8]>, &'a mut [u8])> {
+ match stop_index.checked_sub(start_index) {
+ None => return Some((ReturnCode::EINVAL, source, dest)),
+ Some(s) => {
+ if s > MAX_LENGTH {
+ return Some((ReturnCode::EINVAL, source, dest));
+ }
+ if s % AES128_BLOCK_SIZE != 0 {
+ return Some((ReturnCode::EINVAL, source, dest));
+ }
+ }
+ }
+ self.dest.replace(dest);
+ // The crypt API has two cases: separate source and destination
+ // buffers and a single source buffer.
+ // If we don't have a separate source buffer, we overwrite the
+ // destination with the data. This means that read index and write
+ // index match
+ // If we do have a separate source buffer, we start writing from
+ // 0 and the read index is separate.
+ match source {
+ None => {
+ self.do_crypt(start_index, stop_index, start_index);
+ }
+ Some(src) => {
+ self.source.replace(src);
+ self.do_crypt(start_index, stop_index, 0);
+ }
+ }
+ self.client.map(|client| {
+ client.crypt_done(self.source.take(), self.dest.take().unwrap());
+ });
+ None
+ }
+}
+
+pub static mut AES: Aes<'static> = Aes::new();
+
+impl kernel::hil::symmetric_encryption::AES128ECB for Aes<'_> {
+ fn set_mode_aes128ecb(&self, encrypting: bool) {
+ self.configure(encrypting);
+ }
+}
diff --git a/chips/matcha/src/chip.rs b/chips/matcha/src/chip.rs
new file mode 100644
index 0000000..58a95b9
--- /dev/null
+++ b/chips/matcha/src/chip.rs
@@ -0,0 +1,338 @@
+//! High-level setup and interrupt mapping for the chip.
+
+use core::fmt::Write;
+use core::hint::unreachable_unchecked;
+use kernel;
+use kernel::debug;
+use kernel::hil::time::Alarm;
+use kernel::Chip;
+use rv32i::csr::{mcause, mie::mie, mip::mip, mtvec::mtvec, CSR};
+use rv32i::syscall::SysCall;
+use rv32i::PMPConfigMacro;
+
+use crate::chip_config::CONFIG;
+use crate::gpio;
+use crate::hmac;
+use crate::interrupts;
+use crate::plic;
+use crate::pwrmgr;
+use crate::timer;
+use crate::uart;
+use crate::usbdev;
+
+PMPConfigMacro!(4);
+
+pub struct Matcha<A: 'static + Alarm<'static>> {
+ userspace_kernel_boundary: SysCall,
+ pmp: PMP,
+ scheduler_timer: kernel::VirtualSchedulerTimer<A>,
+}
+
+impl<A: 'static + Alarm<'static>> Matcha<A> {
+ pub unsafe fn new(alarm: &'static A) -> Self {
+ Self {
+ userspace_kernel_boundary: SysCall::new(),
+ pmp: PMP::new(),
+ scheduler_timer: kernel::VirtualSchedulerTimer::new(alarm),
+ }
+ }
+
+ pub unsafe fn enable_plic_interrupts(&self) {
+ plic::disable_all();
+ plic::clear_all_pending();
+ plic::enable_all();
+ }
+
+ unsafe fn handle_plic_interrupts(&self) {
+ while let Some(interrupt) = plic::next_pending() {
+ match interrupt {
+ interrupts::UART0_TX_WATERMARK..=interrupts::UART0_RX_PARITY_ERR => {
+ uart::UART0.handle_interrupt()
+ }
+ int_pin @ interrupts::GPIO_PIN0..=interrupts::GPIO_PIN31 => {
+ let pin = &gpio::PORT[(int_pin - interrupts::GPIO_PIN0) as usize];
+ pin.handle_interrupt();
+ }
+ interrupts::HMAC_HMAC_DONE..=interrupts::HMAC_HMAC_ERR => {
+ hmac::HMAC.handle_interrupt()
+ }
+ interrupts::USBDEV_PKT_RECEIVED..=interrupts::USBDEV_CONNECTED => {
+ usbdev::USB.handle_interrupt()
+ }
+ interrupts::PWRMGRWAKEUP => {
+ pwrmgr::PWRMGR.handle_interrupt();
+ self.check_until_true_or_interrupt(
+ || pwrmgr::PWRMGR.check_clock_propagation(),
+ None,
+ );
+ }
+ _ => debug!("Pidx {}", interrupt),
+ }
+ plic::complete(interrupt);
+ }
+ }
+
+ /// Run a function in an interruptable loop.
+ ///
+ /// The function will run until it returns true, an interrupt occurs or if
+ /// `max_tries` is not `None` and that limit is reached.
+ /// If the function returns true this call will also return true. If an
+ /// interrupt occurs or `max_tries` is reached this call will return false.
+ fn check_until_true_or_interrupt<F>(&self, f: F, max_tries: Option<usize>) -> bool
+ where
+ F: Fn() -> bool,
+ {
+ match max_tries {
+ Some(t) => {
+ for _i in 0..t {
+ if self.has_pending_interrupts() {
+ return false;
+ }
+ if f() {
+ return true;
+ }
+ }
+ }
+ None => {
+ while !self.has_pending_interrupts() {
+ if f() {
+ return true;
+ }
+ }
+ }
+ }
+
+ false
+ }
+}
+
+impl<A: 'static + Alarm<'static>> kernel::Chip for Matcha<A> {
+ type MPU = PMP;
+ type UserspaceKernelBoundary = SysCall;
+ type SchedulerTimer = kernel::VirtualSchedulerTimer<A>;
+ type WatchDog = ();
+
+ fn mpu(&self) -> &Self::MPU {
+ &self.pmp
+ }
+
+ fn scheduler_timer(&self) -> &Self::SchedulerTimer {
+ &self.scheduler_timer
+ }
+
+ fn watchdog(&self) -> &Self::WatchDog {
+ &()
+ }
+
+ fn userspace_kernel_boundary(&self) -> &SysCall {
+ &self.userspace_kernel_boundary
+ }
+
+ fn service_pending_interrupts(&self) {
+ loop {
+ let mip = CSR.mip.extract();
+
+ if mip.is_set(mip::mtimer) {
+ unsafe {
+ timer::TIMER.service_interrupt();
+ }
+ }
+ if mip.is_set(mip::mext) {
+ unsafe {
+ self.handle_plic_interrupts();
+ }
+ }
+
+ if !mip.matches_any(mip::mext::SET + mip::mtimer::SET) {
+ break;
+ }
+ }
+
+ // Re-enable all MIE interrupts that we care about. Since we looped
+ // until we handled them all, we can re-enable all of them.
+ CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
+ }
+
+ fn has_pending_interrupts(&self) -> bool {
+ let mip = CSR.mip.extract();
+ mip.matches_any(mip::mext::SET + mip::mtimer::SET)
+ }
+
+ fn sleep(&self) {
+ unsafe {
+ pwrmgr::PWRMGR.enable_low_power();
+ self.check_until_true_or_interrupt(|| pwrmgr::PWRMGR.check_clock_propagation(), None);
+ rv32i::support::wfi();
+ }
+ }
+
+ unsafe fn atomic<F, R>(&self, f: F) -> R
+ where
+ F: FnOnce() -> R,
+ {
+ rv32i::support::atomic(f)
+ }
+
+ unsafe fn print_state(&self, writer: &mut dyn Write) {
+ let _ = writer.write_fmt(format_args!(
+ "\r\n---| Matcha configuration for {} |---",
+ CONFIG.name
+ ));
+ rv32i::print_riscv_state(writer);
+ }
+}
+
+fn handle_exception(exception: mcause::Exception) {
+ match exception {
+ mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
+
+ mcause::Exception::InstructionMisaligned
+ | mcause::Exception::InstructionFault
+ | mcause::Exception::IllegalInstruction
+ | mcause::Exception::Breakpoint
+ | mcause::Exception::LoadMisaligned
+ | mcause::Exception::LoadFault
+ | mcause::Exception::StoreMisaligned
+ | mcause::Exception::StoreFault
+ | mcause::Exception::MachineEnvCall
+ | mcause::Exception::InstructionPageFault
+ | mcause::Exception::LoadPageFault
+ | mcause::Exception::StorePageFault
+ | mcause::Exception::Unknown => {
+ panic!("fatal exception");
+ }
+ }
+}
+
+unsafe fn handle_interrupt(intr: mcause::Interrupt) {
+ match intr {
+ mcause::Interrupt::UserSoft
+ | mcause::Interrupt::UserTimer
+ | mcause::Interrupt::UserExternal => {
+ panic!("unexpected user-mode interrupt");
+ }
+ mcause::Interrupt::SupervisorExternal
+ | mcause::Interrupt::SupervisorTimer
+ | mcause::Interrupt::SupervisorSoft => {
+ panic!("unexpected supervisor-mode interrupt");
+ }
+
+ mcause::Interrupt::MachineSoft => {
+ CSR.mie.modify(mie::msoft::CLEAR);
+ }
+ mcause::Interrupt::MachineTimer => {
+ CSR.mie.modify(mie::mtimer::CLEAR);
+ }
+ mcause::Interrupt::MachineExternal => {
+ CSR.mie.modify(mie::mext::CLEAR);
+ }
+
+ mcause::Interrupt::Unknown => {
+ panic!("interrupt of unknown cause");
+ }
+ }
+}
+
+/// Trap handler for board/chip specific code.
+///
+/// For the Ibex this gets called when an interrupt occurs while the chip is
+/// in kernel mode. All we need to do is check which interrupt occurred and
+/// disable it.
+#[export_name = "_start_trap_rust"]
+pub unsafe extern "C" fn start_trap_rust() {
+ match mcause::Trap::from(CSR.mcause.extract()) {
+ mcause::Trap::Interrupt(interrupt) => {
+ handle_interrupt(interrupt);
+ }
+ mcause::Trap::Exception(exception) => {
+ handle_exception(exception);
+ }
+ }
+}
+
+/// Function that gets called if an interrupt occurs while an app was running.
+/// mcause is passed in, and this function should correctly handle disabling the
+/// interrupt that fired so that it does not trigger again.
+#[export_name = "_disable_interrupt_trap_handler"]
+pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
+ match mcause::Trap::from(mcause_val) {
+ mcause::Trap::Interrupt(interrupt) => {
+ handle_interrupt(interrupt);
+ }
+ _ => {
+ panic!("unexpected non-interrupt\n");
+ }
+ }
+}
+
+pub unsafe fn configure_trap_handler() {
+ // The Ibex CPU does not support non-vectored trap entries.
+ CSR.mtvec
+ .write(mtvec::trap_addr.val(_start_trap_vectored as u32 >> 2) + mtvec::mode::Vectored)
+}
+
+// Mock implementation for crate tests that does not include the section
+// specifier, as the test will not use our linker script, and the host
+// compilation environment may not allow the section name.
+#[cfg(not(any(target_arch = "riscv32", target_os = "none")))]
+pub extern "C" fn _start_trap_vectored() {
+ unsafe {
+ unreachable_unchecked();
+ }
+}
+
+#[cfg(all(target_arch = "riscv32", target_os = "none"))]
+#[link_section = ".riscv.trap_vectored"]
+#[export_name = "_start_trap_vectored"]
+#[naked]
+pub extern "C" fn _start_trap_vectored() -> ! {
+ unsafe {
+ // According to the Ibex user manual:
+ // [NMI] has interrupt ID 31, i.e., it has the highest priority of all
+ // interrupts and the core jumps to the trap-handler base address (in
+ // mtvec) plus 0x7C to handle the NMI.
+ //
+ // Below are 32 (non-compressed) jumps to cover the entire possible
+ // range of vectored traps.
+ #[cfg(all(target_arch = "riscv32", target_os = "none"))]
+ llvm_asm!("
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ j _start_trap
+ "
+ :
+ :
+ :
+ : "volatile");
+ unreachable_unchecked()
+ }
+}
diff --git a/chips/matcha/src/chip_config.rs b/chips/matcha/src/chip_config.rs
new file mode 100644
index 0000000..9a26ff9
--- /dev/null
+++ b/chips/matcha/src/chip_config.rs
@@ -0,0 +1,46 @@
+//! Chip specific configuration.
+//!
+//! This file includes configuration values for different implementations and
+//! uses of the same matcha chip. For example, running the chip on an FPGA
+//! requires different parameters from running it in a verilog simulator.
+//! Additionally, chips on different platforms can be used differently, so this
+//! also permits changing values like the UART baud rate to enable better
+//! debugging on platforms that can support it.
+//!
+//! The configuration used is selected via Cargo features specified when the
+//! board is compiled.
+
+/// Matcha configuration based on the target device.
+pub struct Config<'a> {
+ /// Identifier for the platform. This is useful for debugging to confirm the
+ /// correct configuration of the chip is being used.
+ pub name: &'a str,
+ /// The clock speed of the CPU in Hz.
+ pub cpu_freq: u32,
+ /// The clock speed of the peripherals in Hz.
+ pub peripheral_freq: u32,
+ /// The baud rate for UART. This allows for a version of the chip that can
+ /// support a faster baud rate to use it to help with debugging.
+ pub uart_baudrate: u32,
+}
+
+/// Config for running Matcha on an FPGA. Also the default configuration.
+#[cfg(any(
+ feature = "config_fpga_nexysvideo",
+ not(feature = "config_disable_default")
+))]
+pub const CONFIG: Config = Config {
+ name: "fpga_nexysvideo",
+ cpu_freq: 10_000_000,
+ peripheral_freq: 2_500_000,
+ uart_baudrate: 115200,
+};
+
+/// Config for running Matcha in a verilog simulator.
+#[cfg(feature = "config_sim_verilator")]
+pub const CONFIG: Config = Config {
+ name: "sim_verilator",
+ cpu_freq: 500_000,
+ peripheral_freq: 125_000,
+ uart_baudrate: 9600,
+};
diff --git a/chips/matcha/src/gpio.rs b/chips/matcha/src/gpio.rs
new file mode 100644
index 0000000..03adf98
--- /dev/null
+++ b/chips/matcha/src/gpio.rs
@@ -0,0 +1,69 @@
+//! GPIO instantiation.
+
+use core::ops::{Index, IndexMut};
+
+use kernel::common::StaticRef;
+pub use lowrisc::gpio::GpioPin;
+use lowrisc::gpio::{pins, GpioRegisters};
+use lowrisc::padctrl::PadCtrlRegisters;
+
+const PADCTRL_BASE: StaticRef<PadCtrlRegisters> =
+ unsafe { StaticRef::new(0x4046_0000 as *const PadCtrlRegisters) };
+
+const GPIO0_BASE: StaticRef<GpioRegisters> =
+ unsafe { StaticRef::new(0x4004_0000 as *const GpioRegisters) };
+
+pub struct Port<'a> {
+ pins: [GpioPin<'a>; 32],
+}
+
+impl<'a> Index<usize> for Port<'a> {
+ type Output = GpioPin<'a>;
+
+ fn index(&self, index: usize) -> &GpioPin<'a> {
+ &self.pins[index]
+ }
+}
+
+impl<'a> IndexMut<usize> for Port<'a> {
+ fn index_mut(&mut self, index: usize) -> &mut GpioPin<'a> {
+ &mut self.pins[index]
+ }
+}
+
+pub static mut PORT: Port = Port {
+ pins: [
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin0),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin1),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin2),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin3),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin4),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin5),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin6),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin7),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin8),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin9),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin10),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin11),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin12),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin13),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin14),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin15),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin16),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin17),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin18),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin19),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin20),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin21),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin22),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin23),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin24),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin25),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin26),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin27),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin28),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin29),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin30),
+ GpioPin::new(GPIO0_BASE, PADCTRL_BASE, pins::pin31),
+ ],
+};
diff --git a/chips/matcha/src/hmac.rs b/chips/matcha/src/hmac.rs
new file mode 100644
index 0000000..182ed53
--- /dev/null
+++ b/chips/matcha/src/hmac.rs
@@ -0,0 +1,7 @@
+use kernel::common::StaticRef;
+use lowrisc::hmac::{Hmac, HmacRegisters};
+
+pub static mut HMAC: Hmac = Hmac::new(HMAC0_BASE);
+
+const HMAC0_BASE: StaticRef<HmacRegisters> =
+ unsafe { StaticRef::new(0x4111_0000 as *const HmacRegisters) };
diff --git a/chips/matcha/src/i2c.rs b/chips/matcha/src/i2c.rs
new file mode 100644
index 0000000..4cc7332
--- /dev/null
+++ b/chips/matcha/src/i2c.rs
@@ -0,0 +1,9 @@
+use crate::chip_config::CONFIG;
+use kernel::common::StaticRef;
+use lowrisc::i2c::{I2c, I2cRegisters};
+
+pub static mut I2C: I2c = I2c::new(I2C_BASE, (1 / CONFIG.cpu_freq) * 1000 * 1000);
+
+// This is a placeholder address as the I2C MMIO interface isn't avaliable yet
+const I2C_BASE: StaticRef<I2cRegisters> =
+ unsafe { StaticRef::new(0x4008_0000 as *const I2cRegisters) };
diff --git a/chips/matcha/src/interrupts.rs b/chips/matcha/src/interrupts.rs
new file mode 100644
index 0000000..d503127
--- /dev/null
+++ b/chips/matcha/src/interrupts.rs
@@ -0,0 +1,210 @@
+//! Named interrupts for the Matcha chip.
+
+#![allow(dead_code)]
+
+pub const PWRMGRWAKEUP: u32 = 0x50;
+
+pub const NO_INTERRUPT: u32 = 0;
+
+pub const UART0_TX_WATERMARK: u32 = 1;
+pub const UART0_RX_WATERMARK: u32 = 2;
+pub const UART0_TX_EMPTY: u32 = 3;
+pub const UART0_RX_OVERFLOW: u32 = 4;
+pub const UART0_RX_FRAME_ERR: u32 = 5;
+pub const UART0_RX_BREAK_ERR: u32 = 6;
+pub const UART0_RX_TIMEOUT: u32 = 7;
+pub const UART0_RX_PARITY_ERR: u32 = 8;
+
+pub const UART1_TX_WATERMARK: u32 = 9;
+pub const UART1_RX_WATERMARK: u32 = 10;
+pub const UART1_TX_EMPTY: u32 = 11;
+pub const UART1_RX_OVERFLOW: u32 = 12;
+pub const UART1_RX_FRAME_ERR: u32 = 13;
+pub const UART1_RX_BREAK_ERR: u32 = 14;
+pub const UART1_RX_TIMEOUT: u32 = 15;
+pub const UART1_RX_PARITY_ERR: u32 = 16;
+
+pub const UART2_TX_WATERMARK: u32 = 17;
+pub const UART2_RX_WATERMARK: u32 = 18;
+pub const UART2_TX_EMPTY: u32 = 19;
+pub const UART2_RX_OVERFLOW: u32 = 20;
+pub const UART2_RX_FRAME_ERR: u32 = 21;
+pub const UART2_RX_BREAK_ERR: u32 = 22;
+pub const UART2_RX_TIMEOUT: u32 = 23;
+pub const UART2_RX_PARITY_ERR: u32 = 24;
+
+pub const UART3_TX_WATERMARK: u32 = 25;
+pub const UART3_RX_WATERMARK: u32 = 26;
+pub const UART3_TX_EMPTY: u32 = 27;
+pub const UART3_RX_OVERFLOW: u32 = 28;
+pub const UART3_RX_FRAME_ERR: u32 = 29;
+pub const UART3_RX_BREAK_ERR: u32 = 30;
+pub const UART3_RX_TIMEOUT: u32 = 31;
+pub const UART3_RX_PARITY_ERR: u32 = 32;
+
+pub const GPIO_PIN0: u32 = 33;
+pub const GPIO_PIN1: u32 = 34;
+pub const GPIO_PIN2: u32 = 35;
+pub const GPIO_PIN3: u32 = 36;
+pub const GPIO_PIN4: u32 = 37;
+pub const GPIO_PIN5: u32 = 38;
+pub const GPIO_PIN6: u32 = 39;
+pub const GPIO_PIN7: u32 = 40;
+pub const GPIO_PIN8: u32 = 41;
+pub const GPIO_PIN9: u32 = 42;
+pub const GPIO_PIN10: u32 = 43;
+pub const GPIO_PIN11: u32 = 44;
+pub const GPIO_PIN12: u32 = 45;
+pub const GPIO_PIN13: u32 = 46;
+pub const GPIO_PIN14: u32 = 47;
+pub const GPIO_PIN15: u32 = 48;
+pub const GPIO_PIN16: u32 = 49;
+pub const GPIO_PIN17: u32 = 50;
+pub const GPIO_PIN18: u32 = 51;
+pub const GPIO_PIN19: u32 = 52;
+pub const GPIO_PIN20: u32 = 53;
+pub const GPIO_PIN21: u32 = 54;
+pub const GPIO_PIN22: u32 = 55;
+pub const GPIO_PIN23: u32 = 56;
+pub const GPIO_PIN24: u32 = 57;
+pub const GPIO_PIN25: u32 = 58;
+pub const GPIO_PIN26: u32 = 59;
+pub const GPIO_PIN27: u32 = 60;
+pub const GPIO_PIN28: u32 = 61;
+pub const GPIO_PIN29: u32 = 62;
+pub const GPIO_PIN30: u32 = 63;
+pub const GPIO_PIN31: u32 = 64;
+
+pub const SPI_DEVICE_RXF: u32 = 65;
+pub const SPI_DEVICE_RXLVL: u32 = 66;
+pub const SPI_DEVICE_TXLVL: u32 = 67;
+pub const SPI_DEVICE_RXERR: u32 = 68;
+pub const SPI_DEVICE_RXOVERFLOW: u32 = 69;
+pub const SPI_DEVICE_TXUNDERFLOW: u32 = 70;
+
+
+pub const SPI_HOST0_ERROR: u32 = 71;
+pub const SPI_HOST0_SPIEVENT: u32 = 72;
+pub const SPI_HOST1_ERROR: u32 = 73;
+pub const SPI_HOST1_SPIEVENT: u32 = 74;
+
+pub const I2C0_FMT_WATERMARK: u32 = 75;
+pub const I2C0_RX_WATERMARK: u32 = 76;
+pub const I2C0_FMT_OVERFLOW: u32 = 77;
+pub const I2C0_RX_OVERFLOW: u32 = 78;
+pub const I2C0_NAK: u32 = 79;
+pub const I2C0_SCL_INTERFERENCE: u32 = 80;
+pub const I2C0_SDA_INTERFERENCE: u32 = 81;
+pub const I2C0_STRETCH_TIMEOUT: u32 = 82;
+pub const I2C0_SDA_UNSTABLE: u32 = 83;
+pub const I2C0_TRANS_COMPLETE: u32 = 84;
+pub const I2C0_TX_EMPTY: u32 = 85;
+pub const I2C0_TX_NONEMPTY: u32 = 86;
+pub const I2C0_TX_OVERFLOW: u32 = 87;
+pub const I2C0_ACQ_OVERFLOW: u32 = 88;
+pub const I2C0_ACK_STOP: u32 = 89;
+pub const I2C0_HOST_TIMEOUT: u32 = 90;
+
+pub const I2C1_FMT_WATERMARK: u32 = 91;
+pub const I2C1_RX_WATERMARK: u32 = 92;
+pub const I2C1_FMT_OVERFLOW: u32 = 93;
+pub const I2C1_RX_OVERFLOW: u32 = 94;
+pub const I2C1_NAK: u32 = 95;
+pub const I2C1_SCL_INTERFERENCE: u32 = 96;
+pub const I2C1_SDA_INTERFERENCE: u32 = 97;
+pub const I2C1_STRETCH_TIMEOUT: u32 = 98;
+pub const I2C1_SDA_UNSTABLE: u32 = 99;
+pub const I2C1_TRANS_COMPLETE: u32 = 100;
+pub const I2C1_TX_EMPTY: u32 = 101;
+pub const I2C1_TX_NONEMPTY: u32 = 102;
+pub const I2C1_TX_OVERFLOW: u32 = 103;
+pub const I2C1_ACQ_OVERFLOW: u32 = 104;
+pub const I2C1_ACK_STOP: u32 = 105;
+pub const I2C1_HOST_TIMEOUT: u32 = 106;
+
+pub const I2C2_FMT_WATERMARK: u32 = 107;
+pub const I2C2_RX_WATERMARK: u32 = 108;
+pub const I2C2_FMT_OVERFLOW: u32 = 109;
+pub const I2C2_RX_OVERFLOW: u32 = 110;
+pub const I2C2_NAK: u32 = 111;
+pub const I2C2_SCL_INTERFERENCE: u32 = 112;
+pub const I2C2_SDA_INTERFERENCE: u32 = 113;
+pub const I2C2_STRETCH_TIMEOUT: u32 = 114;
+pub const I2C2_SDA_UNSTABLE: u32 = 115;
+pub const I2C2_TRANS_COMPLETE: u32 = 116;
+pub const I2C2_TX_EMPTY: u32 = 117;
+pub const I2C2_TX_NONEMPTY: u32 = 118;
+pub const I2C2_TX_OVERFLOW: u32 = 119;
+pub const I2C2_ACQ_OVERFLOW: u32 = 120;
+pub const I2C2_ACK_STOP: u32 = 121;
+pub const I2C2_HOST_TIMEOUT: u32 = 122;
+
+pub const PATTGEN_DONE_CH0: u32 = 123;
+pub const PATTGEN_DONE_CH1: u32 = 124;
+
+pub const RV_TIMER_EXPIRED0_0: u32 = 125;
+
+pub const USBDEV_PKT_RECEIVED: u32 = 126;
+pub const USBDEV_PKT_SENT: u32 = 127;
+pub const USBDEV_DISCONNECTED: u32 = 128;
+pub const USBDEV_HOST_LOST: u32 = 129;
+pub const USBDEV_LINK_RESET: u32 = 130;
+pub const USBDEV_LINK_SUSPEND: u32 = 131;
+pub const USBDEV_LINK_RESUME: u32 = 132;
+pub const USBDEV_AV_EMPTY: u32 = 133;
+pub const USBDEV_RX_FULL: u32 = 134;
+pub const USBDEV_AV_OVERFLOW: u32 = 135;
+pub const USBDEV_LINK_IN_ERR: u32 = 136;
+pub const USBDEV_RX_CRC_ERR: u32 = 137;
+pub const USBDEV_RX_PID_ERR: u32 = 138;
+pub const USBDEV_RX_BITSTUFF_ERR: u32 = 139;
+pub const USBDEV_FRAME: u32 = 140;
+pub const USBDEV_CONNECTED: u32 = 141;
+pub const USBDEV_LINK_OUT_ERR: u32 = 142;
+
+pub const OTP_CTRL_OTP_OPERATION_DONE: u32 = 143;
+pub const OTP_CTRL_OTP_ERR: u32 = 144;
+
+pub const ALERT_CLASSA: u32 = 145;
+pub const ALERT_CLASSB: u32 = 146;
+pub const ALERT_CLASSC: u32 = 147;
+pub const ALERT_CLASSD: u32 = 148;
+
+pub const PWRMGR_AON_WAKEUP: u32 = 149;
+
+pub const ADC_CTRL_AON_DEBUG_CABLE: u32 = 150;
+
+pub const AON_TIMER_AON_WAKEUP_TIMER_EXPIRED: u32 = 151;
+pub const AON_TIMER_AON_WATCHDOG_EXPIRED: u32 = 152;
+
+pub const FLASH_PROG_EMPTY: u32 = 153;
+pub const FLASH_PROG_LVL: u32 = 154;
+pub const FLASH_RD_FULL: u32 = 155;
+pub const FLASH_RD_LVL: u32 = 156;
+pub const FLASH_OP_DONE: u32 = 157;
+
+pub const HMAC_HMAC_DONE: u32 = 158;
+pub const HMAC_FIFO_EMPTY: u32 = 159;
+pub const HMAC_HMAC_ERR: u32 = 160;
+
+pub const KMAC_KMAC_DONE: u32 = 161;
+pub const KMAC_FIFO_EMPTY: u32 = 162;
+pub const KMAC_KMAC_ERR: u32 = 163;
+
+pub const KEYMGR_OP_DONE: u32 = 164;
+
+pub const CSRNG_CS_CMD_REQ_DONE: u32 = 165;
+pub const CSRNG_CS_ENTROPY_REQ: u32 = 166;
+pub const CSRNG_CS_HW_INST_EXC: u32 = 167;
+pub const CSRNG_CS_FATAL_ERR: u32 = 168;
+pub const ENTROPY_SRC_ES_ENTROPY_VALID: u32 = 169;
+pub const ENTROPY_SRC_ES_HEALTH_TEST_FAILED: u32 = 170;
+pub const ENTROPY_SRC_ES_FATAL_ERR: u32 = 171;
+
+pub const EDN0_EDN_CMD_REQ_DONE: u32 = 172;
+pub const EDN0_EDN_FATAL_ERR: u32 = 173;
+
+pub const EDN1_EDN_CMD_REQ_DONE: u32 = 174;
+pub const EDN1_EDN_FATAL_ERR: u32 = 175;
+
+pub const OTBN_DONE: u32 = 176;
\ No newline at end of file
diff --git a/chips/matcha/src/lib.rs b/chips/matcha/src/lib.rs
new file mode 100644
index 0000000..234c235
--- /dev/null
+++ b/chips/matcha/src/lib.rs
@@ -0,0 +1,20 @@
+//! Drivers and chip support for Matcha.
+
+#![feature(llvm_asm, const_fn, naked_functions)]
+#![no_std]
+#![crate_name = "matcha"]
+#![crate_type = "rlib"]
+
+mod chip_config;
+mod interrupts;
+
+pub mod aes;
+pub mod chip;
+pub mod gpio;
+pub mod hmac;
+pub mod i2c;
+pub mod plic;
+pub mod pwrmgr;
+pub mod timer;
+pub mod uart;
+pub mod usbdev;
diff --git a/chips/matcha/src/plic.rs b/chips/matcha/src/plic.rs
new file mode 100644
index 0000000..be80568
--- /dev/null
+++ b/chips/matcha/src/plic.rs
@@ -0,0 +1,103 @@
+//! Platform Level Interrupt Control peripheral driver.
+
+use kernel::common::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
+use kernel::common::StaticRef;
+//use kernel::debug;
+
+register_structs! {
+ pub PlicRegisters {
+ /// Interrupt Pending Register
+ (0x000 => pending: [ReadOnly<u32>; 3]),
+ /// Interrupt Source Register
+ (0x00C => source: [ReadWrite<u32>; 3]),
+ /// Interrupt Priority Registers
+ (0x018 => priority: [ReadWrite<u32, priority::Register>; 79]),
+ (0x154 => _reserved0: [ReadWrite<u32>; 43]),
+ /// Interrupt Enable Register
+ (0x200 => enable: [ReadWrite<u32>; 3]),
+ /// Priority Threshold Register
+ (0x20C => threshold: ReadWrite<u32, priority::Register>),
+ /// Claim/Complete Register
+ (0x210 => claim: ReadWrite<u32>),
+ /// MSIP Register
+ (0x214 => msip: ReadWrite<u32>),
+ (0x218 => @END),
+ }
+}
+
+register_bitfields![u32,
+ priority [
+ Priority OFFSET(0) NUMBITS(3) []
+ ]
+];
+
+const PLIC_BASE: StaticRef<PlicRegisters> =
+ unsafe { StaticRef::new(0x4101_0000 as *const PlicRegisters) };
+
+/// Clear all pending interrupts.
+pub unsafe fn clear_all_pending() {
+ let _plic: &PlicRegisters = &*PLIC_BASE;
+}
+
+/// Enable all interrupts.
+pub unsafe fn enable_all() {
+ let plic: &PlicRegisters = &*PLIC_BASE;
+
+ // USB hardware on current OT master branch seems to have
+ // interrupt bugs: running Alarms causes persistent USB
+ // CONNECTED interrupts that can't be masked from USBDEV and
+ // cause the system to hang. So enable all interrupts except
+ // for the USB ones. Some open PRs on OT fix this, we'll re-enable
+ // USB interrurupts.
+ //
+ // https://github.com/lowRISC/opentitan/issues/3388
+ plic.enable[0].set(0xFFFF_FFFF);
+ plic.enable[1].set(0xFFFF_FFFF);
+ plic.enable[2].set(0xFFFF_0000); // USB are 64-79
+
+ // Set the max priority for each interrupt. This is not really used
+ // at this point.
+ for priority in plic.priority.iter() {
+ priority.write(priority::Priority.val(3));
+ }
+
+ // Accept all interrupts.
+ plic.threshold.write(priority::Priority.val(1));
+}
+
+/// Disable all interrupts.
+pub unsafe fn disable_all() {
+ let plic: &PlicRegisters = &*PLIC_BASE;
+ for enable in plic.enable.iter() {
+ enable.set(0);
+ }
+}
+
+/// Get the index (0-256) of the lowest number pending interrupt, or `None` if
+/// none is pending. RISC-V PLIC has a "claim" register which makes it easy
+/// to grab the highest priority pending interrupt.
+pub unsafe fn next_pending() -> Option<u32> {
+ let plic: &PlicRegisters = &*PLIC_BASE;
+
+ let claim = plic.claim.get();
+ if claim == 0 {
+ None
+ } else {
+ Some(claim)
+ }
+}
+
+/// Signal that an interrupt is finished being handled. In Tock, this should be
+/// called from the normal main loop (not the interrupt handler).
+pub unsafe fn complete(index: u32) {
+ let plic: &PlicRegisters = &*PLIC_BASE;
+ plic.claim.set(index);
+}
+
+/// Return `true` if there are any pending interrupts in the PLIC, `false`
+/// otherwise.
+pub unsafe fn has_pending() -> bool {
+ let plic: &PlicRegisters = &*PLIC_BASE;
+
+ plic.pending.iter().fold(0, |i, pending| pending.get() | i) != 0
+}
diff --git a/chips/matcha/src/pwrmgr.rs b/chips/matcha/src/pwrmgr.rs
new file mode 100644
index 0000000..23db221
--- /dev/null
+++ b/chips/matcha/src/pwrmgr.rs
@@ -0,0 +1,7 @@
+use kernel::common::StaticRef;
+use lowrisc::pwrmgr::{PwrMgr, PwrMgrRegisters};
+
+pub static mut PWRMGR: PwrMgr = PwrMgr::new(PWRMGR_BASE);
+
+const PWRMGR_BASE: StaticRef<PwrMgrRegisters> =
+ unsafe { StaticRef::new(0x4040_0000 as *const PwrMgrRegisters) };
diff --git a/chips/matcha/src/timer.rs b/chips/matcha/src/timer.rs
new file mode 100644
index 0000000..3c2e09e
--- /dev/null
+++ b/chips/matcha/src/timer.rs
@@ -0,0 +1,198 @@
+//! Timer driver.
+
+use crate::chip_config::CONFIG;
+use kernel::common::cells::OptionalCell;
+use kernel::common::registers::{register_bitfields, register_structs, ReadWrite, WriteOnly};
+use kernel::common::StaticRef;
+use kernel::hil::time;
+use kernel::hil::time::{Ticks, Ticks64, Time};
+use kernel::ReturnCode;
+
+const PRESCALE: u16 = ((CONFIG.cpu_freq / 10_000) - 1) as u16; // 10Khz
+
+/// 10KHz `Frequency`
+#[derive(Debug)]
+pub struct Freq10KHz;
+impl time::Frequency for Freq10KHz {
+ fn frequency() -> u32 {
+ 10_000
+ }
+}
+
+register_structs! {
+ pub TimerRegisters {
+ (0x000 => ctrl: ReadWrite<u32, ctrl::Register>),
+
+ (0x004 => _reserved),
+
+ (0x100 => config: ReadWrite<u32, config::Register>),
+
+ (0x104 => value_low: ReadWrite<u32>),
+ (0x108 => value_high: ReadWrite<u32>),
+
+ (0x10c => compare_low: ReadWrite<u32>),
+ (0x110 => compare_high: ReadWrite<u32>),
+
+ (0x114 => intr_enable: ReadWrite<u32, intr::Register>),
+ (0x118 => intr_state: ReadWrite<u32, intr::Register>),
+ (0x11c => intr_test: WriteOnly<u32, intr::Register>),
+ (0x120 => @END),
+ }
+}
+
+register_bitfields![u32,
+ ctrl [
+ enable OFFSET(0) NUMBITS(1) []
+ ],
+ config [
+ prescale OFFSET(0) NUMBITS(12) [],
+ step OFFSET(16) NUMBITS(8) []
+ ],
+ intr [
+ timer0 OFFSET(0) NUMBITS(1) []
+ ]
+];
+
+pub struct RvTimer<'a> {
+ registers: StaticRef<TimerRegisters>,
+ alarm_client: OptionalCell<&'a dyn time::AlarmClient>,
+ overflow_client: OptionalCell<&'a dyn time::OverflowClient>,
+}
+
+impl<'a> RvTimer<'a> {
+ const fn new(base: StaticRef<TimerRegisters>) -> RvTimer<'a> {
+ RvTimer {
+ registers: base,
+ alarm_client: OptionalCell::empty(),
+ overflow_client: OptionalCell::empty(),
+ }
+ }
+
+ pub fn setup(&self) {
+ let regs = self.registers;
+ // Set proper prescaler and the like
+ regs.config
+ .write(config::prescale.val(PRESCALE as u32) + config::step.val(1u32));
+ regs.compare_high.set(0);
+ regs.value_low.set(0xFFFF_0000);
+ regs.intr_enable.write(intr::timer0::CLEAR);
+ regs.ctrl.write(ctrl::enable::SET);
+ }
+
+ pub fn service_interrupt(&self) {
+ let regs = self.registers;
+ regs.intr_enable.write(intr::timer0::CLEAR);
+ regs.intr_state.write(intr::timer0::SET);
+ self.alarm_client.map(|client| {
+ client.alarm();
+ });
+ }
+}
+
+impl time::Time for RvTimer<'_> {
+ type Frequency = Freq10KHz;
+ type Ticks = Ticks64;
+
+ fn now(&self) -> Ticks64 {
+ // RISC-V has a 64-bit counter but you can only read 32 bits
+ // at once, which creates a race condition if the lower register
+ // wraps between the reads. So the recommended approach is to read
+ // low, read high, read low, and if the second low is lower, re-read
+ // high. -pal 8/6/20
+ let first_low: u32 = self.registers.value_low.get();
+ let mut high: u32 = self.registers.value_high.get();
+ let second_low: u32 = self.registers.value_low.get();
+ if second_low < first_low {
+ // Wraparound
+ high = self.registers.value_high.get();
+ }
+ Ticks64::from(((high as u64) << 32) | second_low as u64)
+ }
+}
+
+impl<'a> time::Counter<'a> for RvTimer<'a> {
+ fn set_overflow_client(&'a self, client: &'a dyn time::OverflowClient) {
+ self.overflow_client.set(client);
+ }
+
+ fn start(&self) -> ReturnCode {
+ ReturnCode::SUCCESS
+ }
+
+ fn stop(&self) -> ReturnCode {
+ // RISCV counter can't be stopped...
+ ReturnCode::EBUSY
+ }
+
+ fn reset(&self) -> ReturnCode {
+ // RISCV counter can't be reset
+ ReturnCode::FAIL
+ }
+
+ fn is_running(&self) -> bool {
+ true
+ }
+}
+
+impl<'a> time::Alarm<'a> for RvTimer<'a> {
+ fn set_alarm_client(&self, client: &'a dyn time::AlarmClient) {
+ self.alarm_client.set(client);
+ }
+
+ fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
+ // This does not handle the 64-bit wraparound case.
+ // Because mtimer fires if the counter is >= the compare,
+ // handling wraparound requires setting compare to the
+ // maximum value, issuing a callback on the overflow client
+ // if there is one, spinning until it wraps around to 0, then
+ // setting the compare to the correct value.
+ let regs = self.registers;
+ let now = self.now();
+ let mut expire = reference.wrapping_add(dt);
+
+ if !now.within_range(reference, expire) {
+ expire = now;
+ }
+
+ let val = expire.into_u64();
+ let high = (val >> 32) as u32;
+ let low = (val & 0xffffffff) as u32;
+
+ // Recommended approach for setting the two compare registers
+ // (RISC-V Privileged Architectures 3.1.15) -pal 8/6/20
+ regs.compare_low.set(0xffffffff);
+ regs.compare_high.set(high);
+ regs.compare_low.set(low);
+ //debug!("TIMER: set to {}", expire.into_u64());
+ self.registers.intr_enable.write(intr::timer0::SET);
+ }
+
+ fn get_alarm(&self) -> Self::Ticks {
+ let mut val: u64 = (self.registers.compare_high.get() as u64) << 32;
+ val |= self.registers.compare_low.get() as u64;
+ Ticks64::from(val)
+ }
+
+ fn disarm(&self) -> ReturnCode {
+ // You clear the RISCV mtime interrupt by writing to the compare
+ // registers. Since the only way to do so is to set a new alarm,
+ // and this is also the only way to re-enable the interrupt, disabling
+ // the interrupt is sufficient. Calling set_alarm will clear the
+ // pending interrupt before re-enabling. -pal 8/6/20
+ self.registers.intr_enable.write(intr::timer0::CLEAR);
+ ReturnCode::SUCCESS
+ }
+
+ fn is_armed(&self) -> bool {
+ self.registers.intr_enable.is_set(intr::timer0)
+ }
+
+ fn minimum_dt(&self) -> Self::Ticks {
+ Self::Ticks::from(1 as u64)
+ }
+}
+
+const TIMER_BASE: StaticRef<TimerRegisters> =
+ unsafe { StaticRef::new(0x4010_0000 as *const TimerRegisters) };
+
+pub static mut TIMER: RvTimer = RvTimer::new(TIMER_BASE);
diff --git a/chips/matcha/src/uart.rs b/chips/matcha/src/uart.rs
new file mode 100644
index 0000000..63d9b72
--- /dev/null
+++ b/chips/matcha/src/uart.rs
@@ -0,0 +1,10 @@
+use crate::chip_config::CONFIG;
+use kernel::common::StaticRef;
+use lowrisc::uart::{Uart, UartRegisters};
+
+pub const UART0_BAUDRATE: u32 = CONFIG.uart_baudrate;
+
+pub static mut UART0: Uart = Uart::new(UART0_BASE, CONFIG.peripheral_freq);
+
+const UART0_BASE: StaticRef<UartRegisters> =
+ unsafe { StaticRef::new(0x4000_0000 as *const UartRegisters) };
diff --git a/chips/matcha/src/usbdev.rs b/chips/matcha/src/usbdev.rs
new file mode 100644
index 0000000..aa70c29
--- /dev/null
+++ b/chips/matcha/src/usbdev.rs
@@ -0,0 +1,7 @@
+use kernel::common::StaticRef;
+use lowrisc::usbdev::{Usb, UsbRegisters};
+
+pub static mut USB: Usb = Usb::new(USB0_BASE);
+
+const USB0_BASE: StaticRef<UsbRegisters> =
+ unsafe { StaticRef::new(0x4011_0000 as *const UsbRegisters) };
diff --git a/rust-toolchain b/rust-toolchain
new file mode 100644
index 0000000..b18a3f3
--- /dev/null
+++ b/rust-toolchain
@@ -0,0 +1 @@
+nightly-2020-06-03