| // 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/alert_handler_testutils.h" | |
| #include "sw/device/lib/base/math.h" | |
| #include "sw/device/lib/dif/dif_alert_handler.h" | |
| #include "sw/device/lib/dif/dif_base.h" | |
| #include "sw/device/lib/dif/dif_rstmgr.h" | |
| #include "sw/device/lib/testing/test_framework/check.h" | |
| #include "alert_handler_regs.h" // Generated | |
| /** | |
| * This is used to traverse the dump treating it as an array of bits, and | |
| * extract a number of bits placing them in a uint32_t. The word and bit index | |
| * are updated by num_bits before returning. | |
| */ | |
| static uint32_t get_next_n_bits( | |
| int num_bits, const dif_rstmgr_alert_info_dump_segment_t *dump, | |
| int *word_index, int *bit_index) { | |
| CHECK(num_bits <= 32); | |
| CHECK(*bit_index < 32); | |
| uint32_t word = dump[*word_index] >> *bit_index; | |
| if (*bit_index + num_bits >= 32) { | |
| (*word_index) += 1; | |
| *bit_index = *bit_index + num_bits - 32; | |
| } else { | |
| *bit_index += num_bits; | |
| } | |
| word &= (1 << num_bits) - 1; | |
| return word; | |
| } | |
| alert_info_t alert_info_dump_to_struct( | |
| const dif_rstmgr_alert_info_dump_segment_t *dump, int dump_size) { | |
| int word_index = 0; | |
| int bit_index = 0; | |
| alert_info_t info; | |
| for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) { | |
| info.class_esc_state[i] = get_next_n_bits(3, dump, &word_index, &bit_index); | |
| } | |
| for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) { | |
| info.class_esc_cnt[i] = get_next_n_bits(32, dump, &word_index, &bit_index); | |
| } | |
| for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) { | |
| info.class_accum_cnt[i] = | |
| get_next_n_bits(16, dump, &word_index, &bit_index); | |
| } | |
| info.loc_alert_cause = get_next_n_bits(7, dump, &word_index, &bit_index); | |
| CHECK(word_index < dump_size); | |
| for (int i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) { | |
| info.alert_cause[i] = get_next_n_bits(1, dump, &word_index, &bit_index); | |
| } | |
| CHECK(word_index < dump_size); | |
| return info; | |
| } | |
| void alert_info_to_string(const alert_info_t *info) { | |
| LOG_INFO("alert_info:"); | |
| LOG_INFO("esc_state [0]=%x, [1]=%x, [2]=%x, [3]=%x", info->class_esc_state[0], | |
| info->class_esc_state[1], info->class_esc_state[2], | |
| info->class_esc_state[3]); | |
| LOG_INFO("esc_cnt [0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x", | |
| info->class_esc_cnt[0], info->class_esc_cnt[1], | |
| info->class_esc_cnt[2], info->class_esc_cnt[3]); | |
| LOG_INFO("accum_cnt [0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x", | |
| info->class_accum_cnt[0], info->class_accum_cnt[1], | |
| info->class_accum_cnt[2], info->class_accum_cnt[3]); | |
| LOG_INFO("loc_alert_cause=0x%x", info->loc_alert_cause); | |
| int set_count = 0; | |
| LOG_INFO("alert_cause bits set:"); | |
| // Typically very few bits are set, so it is more clear to only show the | |
| // on bits. | |
| for (int i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) { | |
| if (info->alert_cause[i]) { | |
| LOG_INFO("alert_cause[%d] = 1", i); | |
| ++set_count; | |
| } | |
| } | |
| if (set_count == 0) { | |
| LOG_INFO("No bits set"); | |
| } | |
| } | |
| void alert_handler_testutils_configure_all( | |
| const dif_alert_handler_t *alert_handler, dif_alert_handler_config_t config, | |
| dif_toggle_t locked) { | |
| CHECK(alert_handler != NULL); | |
| CHECK(dif_is_valid_toggle(locked)); | |
| // Check lengths of alert, local alert, and class arrays. | |
| CHECK((config.alerts_len > 0 && config.alerts != NULL && | |
| config.alert_classes != NULL) || | |
| (config.alerts_len == 0 && config.alerts == NULL && | |
| config.alert_classes == NULL)); | |
| CHECK((config.local_alerts_len > 0 && config.local_alerts != NULL && | |
| config.local_alert_classes != NULL) || | |
| (config.local_alerts_len == 0 && config.local_alerts == NULL && | |
| config.local_alert_classes == NULL)); | |
| CHECK((config.classes_len > 0 && config.classes != NULL && | |
| config.class_configs != NULL) || | |
| (config.classes_len == 0 && config.classes == NULL && | |
| config.class_configs == NULL)); | |
| // Check that the provided ping timeout actually fits in the timeout | |
| // register, which is smaller than a native word length. | |
| CHECK(config.ping_timeout <= | |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK); | |
| // Configure and enable the requested alerts. | |
| for (int i = 0; i < config.alerts_len; ++i) { | |
| CHECK_DIF_OK(dif_alert_handler_configure_alert( | |
| alert_handler, config.alerts[i], config.alert_classes[i], | |
| kDifToggleEnabled, locked)); | |
| } | |
| // Configure and enable the requested local alerts. | |
| for (int i = 0; i < config.local_alerts_len; ++i) { | |
| CHECK_DIF_OK(dif_alert_handler_configure_local_alert( | |
| alert_handler, config.local_alerts[i], config.local_alert_classes[i], | |
| kDifToggleEnabled, locked)); | |
| } | |
| // Configure and enable the requested classes. | |
| for (int i = 0; i < config.classes_len; ++i) { | |
| CHECK_DIF_OK(dif_alert_handler_configure_class( | |
| alert_handler, config.classes[i], config.class_configs[i], | |
| kDifToggleEnabled, locked)); | |
| } | |
| // Configure the ping timer. | |
| CHECK_DIF_OK(dif_alert_handler_configure_ping_timer( | |
| alert_handler, config.ping_timeout, kDifToggleEnabled, locked)); | |
| } | |
| uint32_t alert_handler_testutils_get_cycles_from_us(uint64_t microseconds) { | |
| uint64_t cycles = udiv64_slow(microseconds * kClockFreqPeripheralHz, 1000000, | |
| /*rem_out=*/NULL); | |
| CHECK(cycles < UINT32_MAX, | |
| "The value 0x%08x%08x can't fit into the 32 bits timer counter.", | |
| (cycles >> 32), (uint32_t)cycles); | |
| return (uint32_t)cycles; | |
| } | |
| uint32_t alert_handler_testutils_cycle_rescaling_factor() { | |
| return kDeviceType == kDeviceSimDV ? 1 : 10; | |
| } | |
| bool alert_handler_testutils_is_alert_active( | |
| const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) { | |
| bool is_cause; | |
| CHECK_DIF_OK( | |
| dif_alert_handler_alert_is_cause(alert_handler, alert, &is_cause)); | |
| return is_cause; | |
| } |