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 
23 status_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 
32 status_t flash_ctrl_testutils_wait_transaction_end(
33  dif_flash_ctrl_state_t *flash_state) {
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) {
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,
57  codes.shadow_register_error);
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 
63 status_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) {
79  *offset = base_page_index * device_info.bytes_per_page;
80  }
81  return OK_STATUS();
82 }
83 
84 status_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 
99 status_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 
114 status_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) {
128  *offset = page_id * device_info.bytes_per_page;
129  }
130  return OK_STATUS();
131 }
132 
133 status_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 
147 status_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 
161 status_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 
173 status_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 
222 status_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 
233 status_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,
239  .op = kDifFlashCtrlOpRead,
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 
252 status_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 
265  default_properties));
266  return OK_STATUS();
267 }
268 
269 status_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 
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 
299 status_t flash_ctrl_testutils_show_faults(
300  const dif_flash_ctrl_state_t *flash_ctrl) {
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 }