Merge "Update bazel airgap prep script with removed chisel dependency"
diff --git a/hw/top_matcha/dv/chip_sim_cfg.hjson b/hw/top_matcha/dv/chip_sim_cfg.hjson
index 5f2770c..740c38d 100644
--- a/hw/top_matcha/dv/chip_sim_cfg.hjson
+++ b/hw/top_matcha/dv/chip_sim_cfg.hjson
@@ -586,6 +586,14 @@
       run_opts:["+sw_test_timeout_ns=40_000_000"]
     }
     {
+      name: chip_sw_smc_kelvin_model_test
+      uvm_test_seq: chip_sw_base_vseq
+      sw_images: ["//sw/device/tests/smc:smc_kelvin_model_test:6:matcha",
+                  "//sw/device/examples/testdata:kelvin_model_ml:7:matcha"]
+      en_run_modes: ["sw_test_mode_test_simple_sec"]
+      run_opts:["+sw_test_timeout_ns=160_000_000"]
+    }
+    {
       name: chip_sw_smc_isp_wrapper_test
       uvm_test_seq: chip_sw_base_vseq
       sw_images: ["//sw/device/tests/smc:smc_isp_wrapper_test:6:matcha"]
@@ -2218,6 +2226,7 @@
         "chip_sw_smc_isp_wrapper_tpg_128_64_test",
         "chip_sw_smc_kelvin_checksum_test",
         "chip_sw_smc_kelvin_hello_test",
+        "chip_sw_smc_kelvin_model_test",
         "chip_sw_smc_lsu_interrupt_boundary_test",
         "chip_sw_smc_lsu_page_boundary_test",
         "chip_sw_smc_ml_sram_smoketest",
@@ -2367,6 +2376,7 @@
         "chip_sw_smc_isp_wrapper_tpg_128_64_test",
         "chip_sw_smc_kelvin_checksum_test",
         "chip_sw_smc_kelvin_hello_test",
+        "chip_sw_smc_kelvin_model_test",
         "chip_sw_smc_lsu_interrupt_boundary_test",
         "chip_sw_smc_lsu_page_boundary_test",
         "chip_sw_smc_ml_sram_smoketest",
diff --git a/sw/device/examples/testdata/BUILD b/sw/device/examples/testdata/BUILD
index 8bd7acf..45d2ca1 100644
--- a/sw/device/examples/testdata/BUILD
+++ b/sw/device/examples/testdata/BUILD
@@ -1,12 +1,49 @@
 # Copyright 2023 Google LLC
 
+load("@lowrisc_opentitan//rules:opentitan.bzl", "bin_to_vmem")
 load("//rules:matcha.bzl", "bin_to_c_file")
 
 package(default_visibility = ["//visibility:public"])
 
+name = "kelvin_model_ml"
+
+bin_name = "{}_bin".format(name)
+
 # Binary is built from //sw/device/tests/kelvin/hps-c-port
 bin_to_c_file(
-    name = "kelvin_model_ml_bin",
+    name = bin_name,
     srcs = ["//sw/device/tests/kelvin/hps-c-port:kelvin_hps_model.bin"],
     var_name = "kelvin_bin",
 )
+
+vmem_32_name = "{}.32.vmem".format(name)
+
+vmem_256_name = "{}.256.vmem".format(name)
+
+vmem_name = "{}_vmem".format(name)
+
+bin_to_vmem(
+    name = vmem_32_name,
+    bin = "//sw/device/tests/kelvin/hps-c-port:kelvin_hps_model.bin",
+    word_size = 32,
+)
+
+genrule(
+    name = vmem_name,
+    srcs = [vmem_32_name],
+    outs = [vmem_256_name],
+    cmd = """
+        $(location //util:gen_vmem_256) \
+            --input=$< \
+            --output=$@
+        """,
+    tools = ["@matcha//util:gen_vmem_256"],
+)
+
+filegroup(
+    name = name,
+    srcs = [
+        ":{}".format(bin_name),
+        ":{}".format(vmem_name),
+    ],
+)
diff --git a/sw/device/tests/kelvin/hps-c-port/main_fpga.cc b/sw/device/tests/kelvin/hps-c-port/main_fpga.cc
index a42fe33..5a91d3b 100644
--- a/sw/device/tests/kelvin/hps-c-port/main_fpga.cc
+++ b/sw/device/tests/kelvin/hps-c-port/main_fpga.cc
@@ -10,7 +10,7 @@
   uint32_t padding[1024 - 2];
 };
 
-static int8_t input_[kImageLen] __aligned__ __noinit__;
+static int8_t input_[kImageLen] __aligned__ = {0};
 
 // .model_header is at the DMEM - 4KB
 command_t command __attribute__((section(".model_header")));
diff --git a/sw/device/tests/kelvin/hps-c-port/model/src/model.cc b/sw/device/tests/kelvin/hps-c-port/model/src/model.cc
index 84cb798..8ba69ae 100644
--- a/sw/device/tests/kelvin/hps-c-port/model/src/model.cc
+++ b/sw/device/tests/kelvin/hps-c-port/model/src/model.cc
@@ -21,38 +21,21 @@
 int8_t buffer_016[2] __aligned__ __noinit__;
 
 void model(const int8_t *input, int8_t *output) {
-  printf("Layer(000)\n");
   layer_000_conv_2d(input, buffer_000);
-  printf("Layer(001)\n");
   layer_001_conv_2d(buffer_000, buffer_001);
-  printf("Layer(002)\n");
   layer_002_conv_2d(buffer_001, buffer_002);
-  printf("Layer(003)\n");
   layer_003_max_pool_2d(buffer_002, buffer_003);
-  printf("Layer(004)\n");
   layer_004_conv_2d(buffer_003, buffer_004);
-  printf("Layer(005)\n");
   layer_005_conv_2d(buffer_004, buffer_005);
-  printf("Layer(006)\n");
   layer_006_max_pool_2d(buffer_005, buffer_006);
-  printf("Layer(007)\n");
   layer_007_conv_2d(buffer_006, buffer_007);
-  printf("Layer(008)\n");
   layer_008_max_pool_2d(buffer_007, buffer_008);
-  printf("Layer(009)\n");
   layer_009_conv_2d(buffer_008, buffer_009);
-  printf("Layer(010)\n");
   layer_010_max_pool_2d(buffer_009, buffer_010);
-  printf("Layer(011)\n");
   layer_011_conv_2d(buffer_010, buffer_011);
-  printf("Layer(012)\n");
   layer_012_reshape(buffer_011, buffer_012);
-  printf("Layer(013)\n");
   layer_013_fullyconnected(buffer_012, buffer_013);
-  printf("Layer(014)\n");
   layer_014_fullyconnected(buffer_013, buffer_014);
-  printf("Layer(015)\n");
   layer_015_fullyconnected(buffer_014, buffer_015);
-  printf("Layer(016)\n");
   layer_016_logistic(buffer_015, output);
 }
diff --git a/sw/device/tests/smc/BUILD b/sw/device/tests/smc/BUILD
index ec158be..02e0949 100644
--- a/sw/device/tests/smc/BUILD
+++ b/sw/device/tests/smc/BUILD
@@ -307,6 +307,30 @@
 )
 
 smc_flash_binary(
+    name = "smc_kelvin_model_test",
+    srcs = [
+        "smc_kelvin_model_test.c",
+        "//sw/device/examples/testdata:kelvin_model_ml_bin.h",
+        "//sw/device/tests/testdata:test_image.h",
+    ],
+    copts = [
+        "-nostdlib",
+        "-ffreestanding",
+    ],
+    per_device_deps = {
+        "sim_verilator": [VERILATOR_CORE_TARGETS.get("smc")],
+        "sim_dv": ["//sw/device/lib/arch:smc_sim_dv"],
+    },
+    deps = [
+        "//hw/top_matcha/ip/ml_top/data:ml_top_regs",
+        "//hw/top_matcha/sw/autogen:top_matcha",
+        "//sw/device/lib/dif:ml_top",
+        "//sw/device/lib/dif:rv_plic_smc",
+        "//sw/device/tests:test_lib_smc",
+    ],
+)
+
+smc_flash_binary(
     name = "smc_isp_wrapper_irq_test",
     srcs = [
         "smc_isp_wrapper_irq_test.c",
diff --git a/sw/device/tests/smc/smc_kelvin_model_test.c b/sw/device/tests/smc/smc_kelvin_model_test.c
new file mode 100644
index 0000000..91d4cfb
--- /dev/null
+++ b/sw/device/tests/smc/smc_kelvin_model_test.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2023 Google LLC
+ * Copyright lowRISC contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hw/top_matcha/ip/ml_top/data/ml_top_regs.h"  // Generated.
+#include "hw/top_matcha/sw/autogen/top_matcha.h"
+#include "sw/device/examples/testdata/kelvin_model_ml_bin.h"  // Generated.
+#include "sw/device/lib/arch/device.h"
+#include "sw/device/lib/dif/dif_ml_top.h"
+#include "sw/device/lib/dif/dif_rv_plic.h"
+#include "sw/device/lib/dif/dif_uart.h"
+#include "sw/device/lib/runtime/irq.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/testing/test_framework/check.h"
+#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
+#include "sw/device/lib/testing/test_framework/status.h"
+#include "sw/device/lib/testing/test_framework/test_util.h"
+#include "sw/device/tests/testdata/test_image.h"  // Generated.
+
+#define TOP_MATCHA_RAM_ML_DMEM_IMG_OFFSET_ADDR 0x00300000
+#define TOP_MATCHA_RAM_ML_DMEM_OUT_OFFSET_ADDR 0x00380000
+#define TOP_MATCHA_RAM_ML_DMEM_CMD_OFFSET_ADDR 0x003FF000
+
+OTTF_DEFINE_TEST_CONFIG();
+
+static dif_ml_top_t ml_top;
+static dif_rv_plic_t plic_smc;
+static dif_uart_t smc_uart;
+
+static volatile bool ml_top_finish_done = false;
+
+static void handle_ml_top_isr(const dif_rv_plic_irq_id_t interrupt_id) {
+  switch (interrupt_id) {
+    case kTopMatchaPlicIrqIdMlTopFinish:
+      ml_top_finish_done = true;
+      break;
+    case kTopMatchaPlicIrqIdMlTopFinish | kTopMatchaPlicIrqIdMlTopFault:
+      LOG_ERROR("ML core raised fault interrupt.");
+      test_status_set(kTestStatusFailed);
+    default:
+      LOG_FATAL("ISR is not implemented!");
+      test_status_set(kTestStatusFailed);
+  }
+  CHECK_DIF_OK(dif_ml_top_reset_ctrl_en(&ml_top));
+  CHECK_DIF_OK(dif_ml_top_irq_acknowledge_all(&ml_top));
+}
+
+void ottf_external_isr(void) {
+  // Claim the IRQ by reading the Ibex specific CC register.
+  dif_rv_plic_irq_id_t interrupt_id;
+
+  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic_smc, kTopMatchaPlicTargetIbex0Smc,
+                                     &interrupt_id));
+
+  // Check if the interrupted peripheral is ISP WRAPPER.
+  top_matcha_plic_peripheral_smc_t peripheral_id =
+      top_matcha_plic_interrupt_for_peripheral_smc[interrupt_id];
+  CHECK(peripheral_id == kTopMatchaPlicPeripheralMlTop,
+        "Unexpected peripheral in ISR: %d", peripheral_id);
+  switch (peripheral_id) {
+    case kTopMatchaPlicPeripheralMlTop: {
+      handle_ml_top_isr(interrupt_id);
+      break;
+    }
+    default:
+      LOG_FATAL("Peripheral is not implemented!");
+  }
+
+  // Complete the IRQ by writing the IRQ source to the Ibex specific CC
+  // register.
+  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic_smc, kTopMatchaPlicTargetIbex0Smc,
+                                        interrupt_id));
+}
+
+// Configures all relevant interrupts in PLIC_SMC.
+static void plic_smc_configure_irqs(dif_rv_plic_t *plic) {
+  // Set IRQ priorities to MAX
+  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
+      plic, kTopMatchaPlicIrqIdMlTopFinish, kDifRvPlicMaxPriority));
+  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(plic, kTopMatchaPlicIrqIdMlTopFault,
+                                            kDifRvPlicMaxPriority));
+
+  // Set Ibex IRQ priority threshold level
+  CHECK_DIF_OK(dif_rv_plic_target_set_threshold(
+      plic, kTopMatchaPlicTargetIbex0Smc, kDifRvPlicMinPriority));
+
+  // Enable ML core IRQs
+  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, kTopMatchaPlicIrqIdMlTopFinish,
+                                           kTopMatchaPlicTargetIbex0Smc,
+                                           kDifToggleEnabled));
+  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(plic, kTopMatchaPlicIrqIdMlTopFault,
+                                           kTopMatchaPlicTargetIbex0Smc,
+                                           kDifToggleEnabled));
+}
+
+void _ottf_main(void) {
+  test_status_set(kTestStatusInTest);
+  init_uart(TOP_MATCHA_SMC_UART_BASE_ADDR, &smc_uart);
+  LOG_INFO("[SMC] Start from SMC!");
+
+  // Init IRQs
+  CHECK_DIF_OK(dif_rv_plic_init(
+      mmio_region_from_addr(TOP_MATCHA_RV_PLIC_SMC_BASE_ADDR), &plic_smc));
+  plic_smc_configure_irqs(&plic_smc);
+  irq_global_ctrl(true);
+  irq_external_ctrl(true);
+
+  // Init ML_TOP
+  CHECK_DIF_OK(dif_ml_top_init(
+      mmio_region_from_addr(TOP_MATCHA_ML_TOP_CORE_BASE_ADDR), &ml_top));
+  CHECK_DIF_OK(dif_ml_top_irq_set_enabled(&ml_top, kDifMlTopIrqFinish,
+                                          kDifToggleEnabled));
+  CHECK_DIF_OK(dif_ml_top_irq_set_enabled(&ml_top, kDifMlTopIrqFault,
+                                          kDifToggleEnabled));
+  dif_ml_top_reset_ctrl_en(&ml_top);
+
+  // Create an array with the pointers to the image frame
+  // Cast the type from unsigned char into uint32_t
+  const uint32_t *const hps_image_frame[] = {(const uint32_t *)hps_0};
+  const unsigned int hps_image_frame_byte_len[] = {hps_0_len};
+  const int kNumOfFrames = 1;
+
+  const int expected_output[][2] = {{-118, -128}};
+
+  mmio_region_t ml_dmem =
+      mmio_region_from_addr(TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR);
+  mmio_region_write32(ml_dmem, TOP_MATCHA_RAM_ML_DMEM_CMD_OFFSET_ADDR,
+                      TOP_MATCHA_RAM_ML_DMEM_IMG_OFFSET_ADDR);
+  mmio_region_write32(ml_dmem,
+                      TOP_MATCHA_RAM_ML_DMEM_CMD_OFFSET_ADDR + sizeof(uint32_t),
+                      TOP_MATCHA_RAM_ML_DMEM_OUT_OFFSET_ADDR);
+
+  mmio_region_t ml_hps_base =
+      mmio_region_from_addr(TOP_MATCHA_RAM_ML_DMEM_BASE_ADDR +
+                            TOP_MATCHA_RAM_ML_DMEM_IMG_OFFSET_ADDR);
+  CHECK_DIF_OK(dif_ml_top_reset_ctrl_en(&ml_top));
+  for (int frame_idx = 0; frame_idx < kNumOfFrames; ++frame_idx) {
+    // Load HPS images 0-6 into ML DMEM.
+    LOG_INFO("[SMC] Start frame [%d]", frame_idx);
+    CHECK(hps_image_frame_byte_len[frame_idx] % sizeof(uint32_t) == 0,
+          "The frame is not word align");
+    for (uintptr_t word_idx = 0;
+         word_idx < hps_image_frame_byte_len[frame_idx] / sizeof(uint32_t);
+         ++word_idx) {
+      uintptr_t offset = word_idx * sizeof(uint32_t);
+      mmio_region_write32(ml_hps_base, offset,
+                          hps_image_frame[frame_idx][word_idx]);
+    }
+
+    // Start up Kelvin.
+    ml_top_finish_done = false;
+    CHECK_DIF_OK(dif_ml_top_release_ctrl_en(&ml_top));
+    while (!ml_top_finish_done) {
+      asm volatile("wfi");
+    }
+
+    // Verify model output
+    mmio_region_t ml_model_out_base =
+        mmio_region_from_addr(TOP_MATCHA_RAM_ML_DMEM_BASE_ADDR +
+                              TOP_MATCHA_RAM_ML_DMEM_OUT_OFFSET_ADDR);
+    const int8_t model_out_val0 = mmio_region_read8(ml_model_out_base, 0);
+    const int8_t model_out_val1 = mmio_region_read8(ml_model_out_base, 1);
+    CHECK(model_out_val0 == expected_output[frame_idx][0] &&
+              model_out_val1 == expected_output[frame_idx][1],
+          "Frame %d failed - Expected: {%d, %d} | Actual: {%d, %d}", frame_idx,
+          expected_output[frame_idx][0], expected_output[frame_idx][1],
+          model_out_val0, model_out_val1);
+  }
+
+  test_status_set(kTestStatusPassed);
+  asm volatile("wfi");
+}