| // 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/flash_ctrl_testutils.h" |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/dif/dif_flash_ctrl.h" |
| #include "sw/device/lib/runtime/hart.h" |
| #include "sw/device/lib/testing/check.h" |
| |
| bool flash_ctrl_testutils_wait_transaction_end( |
| dif_flash_ctrl_state_t *flash_state) { |
| dif_flash_ctrl_output_t output; |
| while (true) { |
| dif_result_t dif_result = dif_flash_ctrl_end(flash_state, &output); |
| CHECK(dif_result != kDifBadArg); |
| CHECK(dif_result != kDifError); |
| if (dif_result == kDifOk) { |
| break; |
| } |
| } |
| if (output.operation_error) { |
| LOG_INFO("Transaction end error_codes = %0x", output.error_code.codes); |
| } |
| CHECK_DIF_OK( |
| dif_flash_ctrl_clear_error_codes(flash_state, output.error_code.codes)); |
| return output.operation_error; |
| } |
| |
| uint32_t flash_ctrl_testutils_data_region_setup( |
| dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index, |
| uint32_t data_region, uint32_t region_size) { |
| dif_flash_ctrl_region_properties_t region_properties = { |
| .ecc_en = true, |
| .high_endurance_en = false, |
| .erase_en = true, |
| .prog_en = true, |
| .rd_en = true, |
| .scramble_en = false}; |
| |
| dif_flash_ctrl_data_region_properties_t data_region_properties = { |
| .base = base_page_index, |
| .properties = region_properties, |
| .size = region_size}; |
| |
| CHECK_DIF_OK(dif_flash_ctrl_set_data_region_properties( |
| flash_state, data_region, data_region_properties)); |
| CHECK_DIF_OK(dif_flash_ctrl_set_data_region_enablement( |
| flash_state, data_region, kDifToggleEnabled)); |
| |
| dif_flash_ctrl_device_info_t device_info = dif_flash_ctrl_get_device_info(); |
| return (base_page_index * device_info.bytes_per_page); |
| } |
| |
| uint32_t flash_ctrl_testutils_info_region_setup( |
| dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank, |
| uint32_t partition_id) { |
| dif_flash_ctrl_region_properties_t region_properties = { |
| .ecc_en = true, |
| .high_endurance_en = false, |
| .erase_en = true, |
| .prog_en = true, |
| .rd_en = true, |
| .scramble_en = false}; |
| |
| dif_flash_ctrl_info_region_t info_region = { |
| .bank = bank, .page = page_id, .partition_id = partition_id}; |
| |
| CHECK_DIF_OK(dif_flash_ctrl_set_info_region_properties( |
| flash_state, info_region, region_properties)); |
| CHECK_DIF_OK(dif_flash_ctrl_set_info_region_enablement( |
| flash_state, info_region, kDifToggleEnabled)); |
| |
| dif_flash_ctrl_device_info_t device_info = dif_flash_ctrl_get_device_info(); |
| return (page_id * device_info.bytes_per_page); |
| } |
| |
| bool flash_ctrl_testutils_erase_page( |
| dif_flash_ctrl_state_t *flash_state, uint32_t byte_address, |
| uint32_t partition_id, dif_flash_ctrl_partition_type_t partition_type) { |
| dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address, |
| .op = kDifFlashCtrlOpPageErase, |
| .partition_type = partition_type, |
| .partition_id = partition_id, |
| .word_count = 0x0}; |
| CHECK_DIF_OK(dif_flash_ctrl_start(flash_state, transaction)); |
| return flash_ctrl_testutils_wait_transaction_end(flash_state); |
| } |
| |
| bool flash_ctrl_testutils_write_page( |
| dif_flash_ctrl_state_t *flash_state, uint32_t byte_address, |
| uint32_t partition_id, const uint32_t *data, |
| dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count) { |
| dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address, |
| .op = kDifFlashCtrlOpProgram, |
| .partition_type = partition_type, |
| .partition_id = partition_id, |
| .word_count = 0x0}; |
| uint32_t words_written = 0; |
| bool retval = false; |
| // The flash will accept a maximum of MAX_BEATS_PER_BURST per write |
| // before the transaction must be ended and a new one started. |
| while (words_written < word_count) { |
| uint32_t words_to_write = |
| ((word_count - words_written) < MAX_BEATS_PER_BURST) |
| ? (word_count - words_written) |
| : MAX_BEATS_PER_BURST; |
| transaction.word_count = words_to_write; |
| transaction.byte_address = |
| byte_address + (sizeof(uint32_t) * words_written); |
| CHECK_DIF_OK(dif_flash_ctrl_start(flash_state, transaction)); |
| CHECK_DIF_OK(dif_flash_ctrl_prog_fifo_push(flash_state, words_to_write, |
| data + words_written)); |
| retval |= flash_ctrl_testutils_wait_transaction_end(flash_state); |
| words_written += words_to_write; |
| } |
| |
| return retval; |
| } |
| |
| bool flash_ctrl_testutils_erase_and_write_page( |
| dif_flash_ctrl_state_t *flash_state, uint32_t byte_address, |
| uint32_t partition_id, const uint32_t *data, |
| dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count) { |
| bool retval = flash_ctrl_testutils_erase_page(flash_state, byte_address, |
| partition_id, partition_type); |
| retval |= |
| flash_ctrl_testutils_write_page(flash_state, byte_address, partition_id, |
| data, partition_type, word_count); |
| return retval; |
| } |
| |
| bool flash_ctrl_testutils_read_page( |
| dif_flash_ctrl_state_t *flash_state, uint32_t byte_address, |
| uint32_t partition_id, uint32_t *data_out, |
| dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count, |
| uint32_t delay) { |
| dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address, |
| .op = kDifFlashCtrlOpRead, |
| .partition_type = partition_type, |
| .partition_id = partition_id, |
| .word_count = word_count}; |
| |
| // Read Page. |
| CHECK_DIF_OK(dif_flash_ctrl_start(flash_state, transaction)); |
| // Optional delay to allow for read fifo fill testing. |
| busy_spin_micros(delay); |
| CHECK_DIF_OK(dif_flash_ctrl_read_fifo_pop(flash_state, word_count, data_out)); |
| return flash_ctrl_testutils_wait_transaction_end(flash_state); |
| } |