| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/testing/otbn_testutils.h" | |
| #include "sw/device/lib/base/mmio.h" | |
| #include "sw/device/lib/dif/dif_base.h" | |
| #include "sw/device/lib/dif/dif_otbn.h" | |
| #include "sw/device/lib/runtime/log.h" | |
| #include "sw/device/lib/testing/test_framework/check.h" | |
| enum { | |
| /** | |
| * Data width of big number subset, in bytes. | |
| */ | |
| kOtbnWlenBytes = 256 / 8, | |
| }; | |
| void otbn_testutils_wait_for_done(const dif_otbn_t *otbn, | |
| dif_otbn_err_bits_t expected_err_bits) { | |
| bool busy = true; | |
| dif_otbn_status_t status; | |
| while (busy) { | |
| CHECK_DIF_OK(dif_otbn_get_status(otbn, &status)); | |
| busy = status != kDifOtbnStatusIdle && status != kDifOtbnStatusLocked; | |
| } | |
| // Error out if OTBN is locked. | |
| CHECK(status == kDifOtbnStatusIdle, "OTBN is locked."); | |
| // Get instruction count so that, if error bits are unexpected, we can print | |
| // it to help with debugging. | |
| uint32_t instruction_count; | |
| CHECK_DIF_OK(dif_otbn_get_insn_cnt(otbn, &instruction_count)); | |
| dif_otbn_err_bits_t err_bits; | |
| CHECK_DIF_OK(dif_otbn_get_err_bits(otbn, &err_bits)); | |
| CHECK(err_bits == expected_err_bits, | |
| "OTBN error bits: got: 0x%08x, expected: 0x%08x.\nInstruction count: " | |
| "0x%08x", | |
| err_bits, expected_err_bits, instruction_count); | |
| } | |
| /** | |
| * Checks if the OTBN application's IMEM and DMEM address parameters are valid. | |
| * | |
| * IMEM and DMEM ranges must not be "backwards" in memory, with the end address | |
| * coming before the start address, and the IMEM range must additionally be | |
| * non-empty. Finally, separate sections in DMEM must not overlap each other | |
| * when converted to DMEM address space. | |
| * | |
| * @param app the OTBN application to check | |
| */ | |
| static void check_app_address_ranges(const otbn_app_t *app) { | |
| // IMEM must have a strictly positive range (cannot be backwards or empty) | |
| CHECK(app->imem_end > app->imem_start); | |
| // Initialised DMEM section must not be backwards | |
| CHECK(app->dmem_data_end >= app->dmem_data_start); | |
| } | |
| void otbn_testutils_load_app(const dif_otbn_t *otbn, const otbn_app_t app) { | |
| check_app_address_ranges(&app); | |
| const size_t imem_size = app.imem_end - app.imem_start; | |
| const size_t data_size = app.dmem_data_end - app.dmem_data_start; | |
| // Memory images and offsets must be multiples of 32b words. | |
| CHECK(imem_size % sizeof(uint32_t) == 0); | |
| CHECK(data_size % sizeof(uint32_t) == 0); | |
| CHECK_DIF_OK(dif_otbn_imem_write(otbn, 0, app.imem_start, imem_size)); | |
| // Write initialized data | |
| if (data_size > 0) { | |
| CHECK_DIF_OK(dif_otbn_dmem_write(otbn, 0, app.dmem_data_start, data_size)); | |
| } | |
| } | |
| void otbn_testutils_execute(const dif_otbn_t *otbn) { | |
| CHECK_DIF_OK(dif_otbn_write_cmd(otbn, kDifOtbnCmdExecute)); | |
| } | |
| void otbn_testutils_write_data(const dif_otbn_t *otbn, size_t len_bytes, | |
| const void *src, otbn_addr_t dest) { | |
| CHECK_DIF_OK(dif_otbn_dmem_write(otbn, dest, src, len_bytes)); | |
| } | |
| void otbn_testutils_read_data(const dif_otbn_t *otbn, size_t len_bytes, | |
| otbn_addr_t src, void *dest) { | |
| CHECK_DIF_OK(dif_otbn_dmem_read(otbn, src, dest, len_bytes)); | |
| } | |
| void otbn_dump_dmem(const dif_otbn_t *otbn, uint32_t max_addr) { | |
| CHECK(max_addr % kOtbnWlenBytes == 0); | |
| if (max_addr == 0) { | |
| max_addr = dif_otbn_get_dmem_size_bytes(otbn); | |
| } | |
| for (int i = 0; i < max_addr; i += kOtbnWlenBytes) { | |
| uint32_t data[kOtbnWlenBytes / sizeof(uint32_t)]; | |
| CHECK_DIF_OK(dif_otbn_dmem_read(otbn, i, data, kOtbnWlenBytes)); | |
| LOG_INFO("DMEM @%04d: 0x%08x%08x%08x%08x%08x%08x%08x%08x", | |
| i / kOtbnWlenBytes, data[7], data[6], data[5], data[4], data[3], | |
| data[2], data[1], data[0]); | |
| } | |
| } |