| // 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; |
| } |