Software APIs
flash_ctrl_testutils.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/lib/testing/flash_ctrl_testutils.h"
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "dt/dt_flash_ctrl.h" // Generated
17#include "sw/device/lib/testing/test_framework/check.h"
18
19#include "flash_ctrl_regs.h" // Generated
20
21#define MODULE_ID MAKE_MODULE_ID('f', 'c', 't')
22
23status_t flash_ctrl_testutils_wait_for_init(
24 dif_flash_ctrl_state_t *flash_state) {
26 do {
27 TRY(dif_flash_ctrl_get_status(flash_state, &status));
28 } while (status.controller_init_wip);
29 return OK_STATUS();
30}
31
32status_t flash_ctrl_testutils_wait_transaction_end(
33 dif_flash_ctrl_state_t *flash_state) {
34 dif_flash_ctrl_output_t output;
36 do {
37 dif_result = dif_flash_ctrl_end(flash_state, &output);
38 TRY_CHECK(dif_result != kDifBadArg);
39 TRY_CHECK(dif_result != kDifError);
40 } while (dif_result != kDifOk);
41
42 if (output.operation_error) {
43 dif_flash_ctrl_error_codes_t codes = output.error_code.codes;
44 uint32_t error_reg = 0;
45 error_reg = bitfield_bit32_write(error_reg, FLASH_CTRL_ERR_CODE_MP_ERR_BIT,
47 error_reg = bitfield_bit32_write(error_reg, FLASH_CTRL_ERR_CODE_RD_ERR_BIT,
48 codes.read_error);
49 error_reg =
50 bitfield_bit32_write(error_reg, FLASH_CTRL_ERR_CODE_PROG_WIN_ERR_BIT,
51 codes.prog_window_error);
52 error_reg =
53 bitfield_bit32_write(error_reg, FLASH_CTRL_ERR_CODE_PROG_TYPE_ERR_BIT,
54 codes.prog_type_error);
55 error_reg =
56 bitfield_bit32_write(error_reg, FLASH_CTRL_ERR_CODE_UPDATE_ERR_BIT,
58 }
59 TRY(dif_flash_ctrl_clear_error_codes(flash_state, output.error_code.codes));
60 return output.operation_error == 0 ? OK_STATUS() : INTERNAL();
61}
62
63status_t flash_ctrl_testutils_data_region_setup_properties(
64 dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
65 uint32_t data_region, uint32_t region_size,
66 dif_flash_ctrl_region_properties_t region_properties, uint32_t *offset) {
67 dif_flash_ctrl_data_region_properties_t data_region_properties = {
68 .base = base_page_index,
69 .properties = region_properties,
70 .size = region_size};
71
72 TRY(dif_flash_ctrl_set_data_region_properties(flash_state, data_region,
73 data_region_properties));
74 TRY(dif_flash_ctrl_set_data_region_enablement(flash_state, data_region,
76
77 if (offset != NULL) {
78 dif_flash_ctrl_device_info_t device_info = dif_flash_ctrl_get_device_info();
79 *offset = base_page_index * device_info.bytes_per_page;
80 }
81 return OK_STATUS();
82}
83
84status_t flash_ctrl_testutils_data_region_setup(
85 dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
86 uint32_t data_region, uint32_t region_size, uint32_t *offset) {
87 dif_flash_ctrl_region_properties_t region_properties = {
88 .ecc_en = kMultiBitBool4True,
89 .high_endurance_en = kMultiBitBool4False,
90 .erase_en = kMultiBitBool4True,
91 .prog_en = kMultiBitBool4True,
92 .rd_en = kMultiBitBool4True,
93 .scramble_en = kMultiBitBool4False};
94 return flash_ctrl_testutils_data_region_setup_properties(
95 flash_state, base_page_index, data_region, region_size, region_properties,
96 offset);
97}
98
99status_t flash_ctrl_testutils_data_region_scrambled_setup(
100 dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
101 uint32_t data_region, uint32_t region_size, uint32_t *offset) {
102 dif_flash_ctrl_region_properties_t region_properties = {
103 .ecc_en = kMultiBitBool4True,
104 .high_endurance_en = kMultiBitBool4False,
105 .erase_en = kMultiBitBool4True,
106 .prog_en = kMultiBitBool4True,
107 .rd_en = kMultiBitBool4True,
108 .scramble_en = kMultiBitBool4True};
109 return flash_ctrl_testutils_data_region_setup_properties(
110 flash_state, base_page_index, data_region, region_size, region_properties,
111 offset);
112}
113
114status_t flash_ctrl_testutils_info_region_setup_properties(
115 dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
116 uint32_t partition_id, dif_flash_ctrl_region_properties_t region_properties,
117 uint32_t *offset) {
118 dif_flash_ctrl_info_region_t info_region = {
119 .bank = bank, .page = page_id, .partition_id = partition_id};
120
121 TRY(dif_flash_ctrl_set_info_region_properties(flash_state, info_region,
122 region_properties));
123 TRY(dif_flash_ctrl_set_info_region_enablement(flash_state, info_region,
125
126 if (offset != NULL) {
127 dif_flash_ctrl_device_info_t device_info = dif_flash_ctrl_get_device_info();
128 *offset = page_id * device_info.bytes_per_page;
129 }
130 return OK_STATUS();
131}
132
133status_t flash_ctrl_testutils_info_region_setup(
134 dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
135 uint32_t partition_id, uint32_t *offset) {
136 dif_flash_ctrl_region_properties_t region_properties = {
137 .ecc_en = kMultiBitBool4True,
138 .high_endurance_en = kMultiBitBool4False,
139 .erase_en = kMultiBitBool4True,
140 .prog_en = kMultiBitBool4True,
141 .rd_en = kMultiBitBool4True,
142 .scramble_en = kMultiBitBool4False};
143 return flash_ctrl_testutils_info_region_setup_properties(
144 flash_state, page_id, bank, partition_id, region_properties, offset);
145}
146
147status_t flash_ctrl_testutils_info_region_scrambled_setup(
148 dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
149 uint32_t partition_id, uint32_t *offset) {
150 dif_flash_ctrl_region_properties_t region_properties = {
151 .ecc_en = kMultiBitBool4True,
152 .high_endurance_en = kMultiBitBool4False,
153 .erase_en = kMultiBitBool4True,
154 .prog_en = kMultiBitBool4True,
155 .rd_en = kMultiBitBool4True,
156 .scramble_en = kMultiBitBool4True};
157 return flash_ctrl_testutils_info_region_setup_properties(
158 flash_state, page_id, bank, partition_id, region_properties, offset);
159}
160
161status_t flash_ctrl_testutils_erase_page(
162 dif_flash_ctrl_state_t *flash_state, uint32_t byte_address,
163 uint32_t partition_id, dif_flash_ctrl_partition_type_t partition_type) {
164 dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address,
166 .partition_type = partition_type,
167 .partition_id = partition_id,
168 .word_count = 0x0};
169 TRY(dif_flash_ctrl_start(flash_state, transaction));
170 return flash_ctrl_testutils_wait_transaction_end(flash_state);
171}
172
173status_t flash_ctrl_testutils_write(
174 dif_flash_ctrl_state_t *flash_state, uint32_t byte_address,
175 uint32_t partition_id, const uint32_t *data,
176 dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count) {
177 // Cannot program partial words.
178 // TODO: #13773 for more details.
179 // When 13773 is supported, programs to non-scrambled or ecc enabled
180 // pages can support byte writes. While programs to scrambled or ecc
181 // enabled pages can support only flash item writes.
182 if (byte_address & (sizeof(uint32_t) - 1)) {
183 LOG_ERROR("Unaligned address 0x%x", byte_address);
184 return INVALID_ARGUMENT();
185 }
186 dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address,
188 .partition_type = partition_type,
189 .partition_id = partition_id,
190 .word_count = 0x0};
191 static_assert(FLASH_CTRL_PARAM_BYTES_PER_WORD >= sizeof(uint32_t),
192 "flash_ctrl_testutils_write() assumes the flash word width is"
193 "greater than or equal to the bus word width.");
194 uint32_t words_written = 0;
195 uint32_t word_address = byte_address / sizeof(uint32_t);
196 const uint32_t prog_window_size =
197 (uint32_t)FLASH_CTRL_PARAM_REG_BUS_PGM_RES_BYTES / sizeof(uint32_t);
198 const uint32_t prog_window_mask = ~(prog_window_size - 1);
199
200 status_t status = OK_STATUS();
201 while (words_written < word_count) {
202 // Writes must not cross programming resolution window boundaries, which
203 // occur at every prog_window_size words.
204 uint32_t window_limit =
205 ((word_address + prog_window_size) & prog_window_mask) - word_address;
206 uint32_t words_remaining = word_count - words_written;
207 uint32_t words_to_write =
208 (words_remaining < window_limit) ? words_remaining : window_limit;
209 transaction.byte_address = word_address * sizeof(uint32_t);
210 transaction.word_count = words_to_write;
211 TRY(dif_flash_ctrl_start(flash_state, transaction));
212 TRY(dif_flash_ctrl_prog_fifo_push(flash_state, words_to_write,
213 data + words_written));
214 status = flash_ctrl_testutils_wait_transaction_end(flash_state);
215 word_address += words_to_write;
216 words_written += words_to_write;
217 }
218
219 return status;
220}
221
222status_t flash_ctrl_testutils_erase_and_write_page(
223 dif_flash_ctrl_state_t *flash_state, uint32_t byte_address,
224 uint32_t partition_id, const uint32_t *data,
225 dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count) {
226 TRY(flash_ctrl_testutils_erase_page(flash_state, byte_address, partition_id,
227 partition_type));
228 TRY(flash_ctrl_testutils_write(flash_state, byte_address, partition_id, data,
229 partition_type, word_count));
230 return OK_STATUS();
231}
232
233status_t flash_ctrl_testutils_read(
234 dif_flash_ctrl_state_t *flash_state, uint32_t byte_address,
235 uint32_t partition_id, uint32_t *data_out,
236 dif_flash_ctrl_partition_type_t partition_type, uint32_t word_count,
237 uint32_t delay_micros) {
238 dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address,
240 .partition_type = partition_type,
241 .partition_id = partition_id,
242 .word_count = word_count};
243
244 // Read Page.
245 TRY(dif_flash_ctrl_start(flash_state, transaction));
246 // Optional delay to allow for read fifo fill testing.
247 busy_spin_micros(delay_micros);
248 TRY(dif_flash_ctrl_read_fifo_pop(flash_state, word_count, data_out));
249 return flash_ctrl_testutils_wait_transaction_end(flash_state);
250}
251
252status_t flash_ctrl_testutils_default_region_access(
253 dif_flash_ctrl_state_t *flash_state, bool rd_en, bool prog_en,
254 bool erase_en, bool scramble_en, bool ecc_en, bool high_endurance_en) {
255 dif_flash_ctrl_region_properties_t default_properties = {
256 .rd_en = rd_en ? kMultiBitBool4True : kMultiBitBool4False,
257 .prog_en = prog_en ? kMultiBitBool4True : kMultiBitBool4False,
258 .erase_en = erase_en ? kMultiBitBool4True : kMultiBitBool4False,
259 .scramble_en = scramble_en ? kMultiBitBool4True : kMultiBitBool4False,
260 .ecc_en = ecc_en ? kMultiBitBool4True : kMultiBitBool4False,
261 .high_endurance_en =
262 high_endurance_en ? kMultiBitBool4True : kMultiBitBool4False};
263
264 TRY(dif_flash_ctrl_set_default_region_properties(flash_state,
265 default_properties));
266 return OK_STATUS();
267}
268
269status_t flash_ctrl_testutils_bank_erase(dif_flash_ctrl_state_t *flash_state,
270 uint32_t bank, bool data_only) {
271 dif_toggle_t bank_erase_enabled;
272 TRY(dif_flash_ctrl_get_bank_erase_enablement(flash_state, bank,
273 &bank_erase_enabled));
274
275 if (bank_erase_enabled == kDifToggleDisabled) {
276 TRY(dif_flash_ctrl_set_bank_erase_enablement(flash_state, bank,
278 }
279
280 dif_flash_ctrl_device_info_t flash_info = dif_flash_ctrl_get_device_info();
281 uint32_t byte_address =
282 bank * flash_info.bytes_per_page * flash_info.data_pages;
283 dif_flash_ctrl_partition_type_t partition_type =
284 data_only ? kDifFlashCtrlPartitionTypeData
285 : kDifFlashCtrlPartitionTypeInfo;
286 dif_flash_ctrl_transaction_t transaction = {.byte_address = byte_address,
288 .partition_type = partition_type,
289 .partition_id = 0,
290 .word_count = 0x0};
291 TRY(dif_flash_ctrl_start(flash_state, transaction));
292 TRY(flash_ctrl_testutils_wait_transaction_end(flash_state));
293
294 TRY(dif_flash_ctrl_set_bank_erase_enablement(flash_state, bank,
295 bank_erase_enabled));
296 return OK_STATUS();
297}
298
299status_t flash_ctrl_testutils_show_faults(
300 const dif_flash_ctrl_state_t *flash_ctrl) {
301 dif_flash_ctrl_faults_t faults = {.memory_properties_error = false};
302 CHECK_DIF_OK(dif_flash_ctrl_get_faults(flash_ctrl, &faults));
303#define LOG_IF_FIELD_SET(_struct, _field) \
304 if (_struct._field != 0) { \
305 LOG_INFO("Flash_ctrl fault status has " #_field); \
306 }
307
308 LOG_IF_FIELD_SET(faults, memory_properties_error);
309 LOG_IF_FIELD_SET(faults, read_error);
310 LOG_IF_FIELD_SET(faults, prog_window_error);
311 LOG_IF_FIELD_SET(faults, prog_type_error);
312 LOG_IF_FIELD_SET(faults, host_gnt_error);
313 LOG_IF_FIELD_SET(faults, register_integrity_error);
314 LOG_IF_FIELD_SET(faults, phy_integrity_error);
315 LOG_IF_FIELD_SET(faults, lifecycle_manager_error);
316 LOG_IF_FIELD_SET(faults, shadow_storage_error);
317#undef LOG_IF_FIELD_SET
318
319 return OK_STATUS();
320}