blob: 66a8ab2ce9fb1cc7599eb72ae218c35df482cf11 [file] [log] [blame]
// 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);
}