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