soundstream: load model from spi on bancha

Loads the file "kelvin.bin" from the tarball that it expects to find
in SPI flash. Note this only happens on bancha; on sencha the flash is
owned by the SEC.

Change-Id: I5c7aeb5280d053254b4c12743b08e07dbf1565ab
diff --git a/sw/device/cheriot/soundstream/ml_top.cc b/sw/device/cheriot/soundstream/ml_top.cc
index bd30f55..270e9e7 100644
--- a/sw/device/cheriot/soundstream/ml_top.cc
+++ b/sw/device/cheriot/soundstream/ml_top.cc
@@ -38,6 +38,9 @@
 #include "compat.h"
 #include "hw/top_matcha/sw/autogen/top_matcha.h"
 #include "ml_top_regs.h"
+#if DEVICE_EXISTS_spi_host
+#include "spi.h"
+#endif
 
 typedef uint8_t ml_top_dmem_t[TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES];
 typedef uint32_t
@@ -52,6 +55,9 @@
 static CountingSemaphoreState startup = { 0, 1 };
 
 void ml_top_init(void) {
+#if DEVICE_EXISTS_spi_host
+  spi_init();
+#endif
   CHECK_DIF_OK(dif_ml_top_init(mmio_region_from_addr((uintptr_t)MMIO_CAPABILITY(
                                    ml_top_mmio_t, ml_top_core)),
                                &ml_top));
@@ -138,13 +144,15 @@
 }
 
 void ml_top_set_input(void* const data, size_t data_len_bytes) {
-  uint8_t* ml_top_dmem_base = (uint8_t*) MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
+  uint8_t* ml_top_dmem_base = (uint8_t*)
+      MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
   void* input_ptr = ml_top_dmem_base + (TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES - 4096);
   memcpy(input_ptr, data, data_len_bytes);
 }
 
 void ml_top_get_output_header(struct output_header* header) {
-  const uint8_t* ml_top_dmem_base = (uint8_t*) MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
+  const uint8_t* ml_top_dmem_base = (uint8_t*)
+      MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
   const struct output_header* output_header_ptr = (const struct output_header*)
       (ml_top_dmem_base + (TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES - 0x40));
   header->return_code = output_header_ptr->return_code;
@@ -160,6 +168,20 @@
 }
 
 void ml_top_get_output_data(struct output_header* const header, void* buffer) {
-  const uint8_t* ml_top_dmem_base = (const uint8_t*) MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
+  const uint8_t* ml_top_dmem_base = (const uint8_t*)
+      MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
   memcpy(buffer, ml_top_dmem_base + header->output_ptr, header->length);
 }
+
+void ml_top_load_file_from_tar(const char* filename) {
+  CHECK_INIT();
+#if DEVICE_EXISTS_spi_host
+  uint8_t* ml_top_dmem_base = (uint8_t*)
+      MMIO_CAPABILITY(ml_top_dmem_t, ml_top_dmem);
+  spi_load_file_from_tar(
+      filename, ml_top_dmem_base,
+      TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR + TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES);
+#else
+  Debug::log("No SPI support, expecting {} to be side-loaded", filename);
+#endif
+}
diff --git a/sw/device/cheriot/soundstream/ml_top.h b/sw/device/cheriot/soundstream/ml_top.h
index be93516..5ef207f 100644
--- a/sw/device/cheriot/soundstream/ml_top.h
+++ b/sw/device/cheriot/soundstream/ml_top.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef EXAMPLES_SOUNDSTREAM_ML_TOP_H_
-#define EXAMPLES_SOUNDSTREAM_ML_TOP_H_
+#ifndef SW_DEVICE_CHERIOT_SOUNDSTREAM_ML_TOP_H_
+#define SW_DEVICE_CHERIOT_SOUNDSTREAM_ML_TOP_H_
 
 #include <compartment.h>
 
@@ -46,5 +46,7 @@
     ml_top_get_output_header(struct output_header* header);
 void __cheri_compartment("ml_top")
     ml_top_get_output_data(struct output_header* const header, void* buffer);
+void __cheri_compartment("ml_top")
+    ml_top_load_file_from_tar(const char* filename);
 
-#endif  // EXAMPLES_SOUNDSTREAM_ML_TOP_H_
+#endif  // SW_DEVICE_CHERIOT_SOUNDSTREAM_ML_TOP_H_
diff --git a/sw/device/cheriot/soundstream/soundstream.cc b/sw/device/cheriot/soundstream/soundstream.cc
index a1c4fc6..4397831 100644
--- a/sw/device/cheriot/soundstream/soundstream.cc
+++ b/sw/device/cheriot/soundstream/soundstream.cc
@@ -51,6 +51,9 @@
   i2s_irq_acknowledge_all();
   i2s_irq_set_enabled(kI2sIrqRxWatermark, /*enabled=*/true);
 
+  Debug::log("Loading Kelvin binary from SPI");
+  ml_top_load_file_from_tar("kelvin.bin");
+
   // NB: the stack is 4KiB so this uses 1/4 of it
   int16_t samples_left[kFilterSamples] = {0};
   int16_t samples_right[kFilterSamples] = {0};
diff --git a/sw/device/cheriot/soundstream/spi.cc b/sw/device/cheriot/soundstream/spi.cc
new file mode 100644
index 0000000..8ca31c1
--- /dev/null
+++ b/sw/device/cheriot/soundstream/spi.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Barebones SPI Flash support for reading data from SPI at startup.
+ */
+#include "spi.h"
+
+#include <fail-simulator-on-error.h>
+#include <thread.h>
+
+#include <debug.hh>
+
+#include "hw/top_matcha/sw/autogen/top_matcha.h"
+#include "sw/device/lib/spi_flash.h"
+
+/// Expose debugging features unconditionally for this compartment.
+using Debug = ConditionalDebug<false, "SPI">;
+
+#include "compat.h"
+
+typedef uint8_t spi_host_t[TOP_MATCHA_SPI_HOST0_SIZE_BYTES];
+typedef uint8_t flash_ctrl_t[TOP_MATCHA_FLASH_CTRL_CORE_SIZE_BYTES];
+typedef uint8_t otp_t[TOP_MATCHA_OTP_CTRL_CORE_SIZE_BYTES];
+
+void spi_init(void) {
+  Debug::log("spi_init: called from Thread {}", thread_id_get());
+  CHECK_DIF_OK(
+      spi_flash_init((uintptr_t)MMIO_CAPABILITY(spi_host_t, spi_host),
+                     (uintptr_t)MMIO_CAPABILITY(flash_ctrl_t, flash_ctrl),
+                     (uintptr_t)MMIO_CAPABILITY(otp_t, otp)));
+}
+
+void spi_load_file_from_tar(const char* filename, void* addr,
+                            size_t max_mem_addr) {
+  Debug::log("Load {} at {}, max {} ", filename, addr, max_mem_addr);
+  CHECK_DIF_OK(load_file_from_tar(filename, addr, max_mem_addr));
+}
diff --git a/sw/device/cheriot/soundstream/spi.h b/sw/device/cheriot/soundstream/spi.h
new file mode 100644
index 0000000..09278c1
--- /dev/null
+++ b/sw/device/cheriot/soundstream/spi.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SW_DEVICE_CHERIOT_SOUNDSTREAM_SPI_H_
+#define SW_DEVICE_CHERIOT_SOUNDSTREAM_SPI_H_
+
+#include <compartment.h>
+
+void __cheri_compartment("spi") spi_init(void);
+void __cheri_compartment("spi")
+    spi_load_file_from_tar(const char* filename, void* addr,
+                           size_t max_mem_addr);
+
+#endif  // SW_DEVICE_CHERIOT_SOUNDSTREAM_SPI_H_
diff --git a/sw/device/cheriot/soundstream/xmake.lua b/sw/device/cheriot/soundstream/xmake.lua
index a99ec77..07719f7 100644
--- a/sw/device/cheriot/soundstream/xmake.lua
+++ b/sw/device/cheriot/soundstream/xmake.lua
@@ -32,15 +32,27 @@
     set_default("sencha")
 
 local matcha_dir = path.join(shodan_dir, "hw/matcha")
-local dif_dir = path.join(matcha_dir, "sw/device/lib/dif")
+local matcha_lib_dir = path.join(matcha_dir, "sw/device/lib")
+local dif_dir = path.join(matcha_lib_dir, "dif")
 local dif_autogen_dir = path.join(dif_dir, "autogen")
 local opentitan_dir = path.join(shodan_dir, "hw/opentitan-upstream")
+local opentitan_lib_dir = path.join(opentitan_dir, "sw/device/lib")
+local opentitan_base_dir = path.join(opentitan_lib_dir, "base")
+local opentitan_dif_dir = path.join(opentitan_lib_dir, "dif")
+local opentitan_dif_autogen_dir = path.join(opentitan_dif_dir, "autogen")
+local opentitan_runtime_dir = path.join(opentitan_lib_dir, "runtime")
+local opentitan_silicon_dir = path.join(opentitan_dir, "sw/device/silicon_creator/lib")
+local opentitan_silicon_drivers_dir = path.join(opentitan_silicon_dir, "drivers")
+local opentitan_silicon_base_dir = path.join(opentitan_silicon_dir, "base")
 local matcha_gen_dir = path.join(cheriot_out_dir, "opentitan-gen/include/opentitan")
 
 -- Support libraries
 includes(path.join(sdkdir, "lib"))
 includes(path.join(sdkdir, "lib/freestanding"))
 
+local function is_bancha()
+    return is_config("board", "bancha")
+end
 local function is_sencha()
     return is_config("board", "sencha")
 end
@@ -69,6 +81,29 @@
     add_includedirs(matcha_dir, matcha_gen_dir, opentitan_dir)
     add_defines("CHERIOT_NO_AMBIENT_MALLOC")
 
+if is_bancha() then
+compartment("spi")
+    add_files("spi.cc")
+    add_files(path.join(matcha_lib_dir, "spi_flash.c"),
+              path.join(matcha_lib_dir, "eflash.c"),
+                path.join(opentitan_dif_dir, "dif_flash_ctrl.c"),
+                  path.join(opentitan_dif_autogen_dir, "dif_flash_ctrl_autogen.c"),
+                path.join(opentitan_lib_dir, "testing/flash_ctrl_testutils.c"),
+                path.join(opentitan_silicon_base_dir, "sec_mmio.c"),
+                path.join(opentitan_silicon_drivers_dir, "flash_ctrl.c"),
+                path.join(opentitan_silicon_drivers_dir, "otp.c"),
+              path.join(matcha_lib_dir, "arch/device_sc_fpga_nexus.c"),
+              path.join(opentitan_dif_dir, "dif_spi_host.c"),
+                path.join(opentitan_dif_autogen_dir, "dif_spi_host_autogen.c"),
+              path.join(opentitan_base_dir, "status.c"),
+              path.join(opentitan_lib_dir, "testing/test_framework/status.c"),
+              path.join(opentitan_runtime_dir, "log.c"),
+              path.join(opentitan_runtime_dir, "print.c"))
+    add_includedirs(matcha_dir, matcha_gen_dir)
+    add_includedirs(opentitan_dir)
+    add_defines("CHERIOT_NO_AMBIENT_MALLOC")
+end
+
 -- Soundstream application.
 compartment("soundstream")
     add_files("soundstream.cc", "encode.cc")
@@ -82,6 +117,9 @@
     if is_sencha() then
         add_deps("mailbox")
     end
+    if is_bancha() then
+        add_deps("spi", "string")
+    end
     add_deps("soundstream")
     add_deps("freestanding", "debug")
     on_load(function(target)