Add ml top model test into dv regression Bug: 337312097 Change-Id: I9ef8d4d097eea70521b884b8f6966dbfcafa6ecb
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"); +}