[sw/silicon_creator] Add program/erase to flash_ctrl
Adds program and erase functionality to the mask rom flash_ctrl driver.
Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl.c b/sw/device/silicon_creator/lib/drivers/flash_ctrl.c
index 11d1b0f..839a040 100644
--- a/sw/device/silicon_creator/lib/drivers/flash_ctrl.c
+++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl.c
@@ -23,39 +23,10 @@
return !bitfield_bit32_read(bitfield, FLASH_CTRL_CTRL_REGWEN_EN_BIT);
}
-void flash_ctrl_init(void) {
- // Initialize the flash controller.
- abs_mmio_write32(kBase + FLASH_CTRL_INIT_REG_OFFSET,
- bitfield_bit32_write(0u, FLASH_CTRL_INIT_VAL_BIT, true));
-}
-
-rom_error_t flash_ctrl_status_get(flash_ctrl_status_t *status) {
- if (status == NULL) {
- return kErrorFlashCtrlInvalidArgument;
- }
-
- // Read flash operation status.
- uint32_t op_status = abs_mmio_read32(kBase + FLASH_CTRL_OP_STATUS_REG_OFFSET);
- // Read flash controller status.
- uint32_t fc_status = abs_mmio_read32(kBase + FLASH_CTRL_STATUS_REG_OFFSET);
-
- // Extract operation status bits.
- status->done = bitfield_bit32_read(op_status, FLASH_CTRL_OP_STATUS_DONE_BIT);
- status->err = bitfield_bit32_read(op_status, FLASH_CTRL_OP_STATUS_ERR_BIT);
-
- // Extract flash controller status bits.
- status->rd_full =
- bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_RD_FULL_BIT);
- status->rd_empty =
- bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_RD_EMPTY_BIT);
- status->init_wip =
- bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_INIT_WIP_BIT);
-
- return kErrorOk;
-}
-
-rom_error_t flash_ctrl_read_start(uint32_t addr, uint32_t word_count,
- flash_ctrl_partition_t region) {
+static rom_error_t transaction_start(uint32_t addr, uint32_t word_count,
+ flash_ctrl_partition_t region,
+ flash_ctrl_erase_type_t erase_type,
+ uint32_t op) {
if (is_busy()) {
return kErrorFlashCtrlBusy;
}
@@ -64,8 +35,8 @@
abs_mmio_write32(kBase + FLASH_CTRL_ADDR_REG_OFFSET, addr);
// Set the operation of the transaction: read, program, or erase.
- uint32_t control_reg_val = bitfield_field32_write(
- 0, FLASH_CTRL_CONTROL_OP_FIELD, FLASH_CTRL_CONTROL_OP_VALUE_READ);
+ uint32_t control_reg_val =
+ bitfield_field32_write(0, FLASH_CTRL_CONTROL_OP_FIELD, op);
// Ensure special enum values match register definitions.
static_assert(kFlashCtrlRegionData == 0u,
@@ -81,13 +52,20 @@
kFlashCtrlRegionInfo2 == (1u << FLASH_CTRL_CONTROL_PARTITION_SEL_BIT |
2u << FLASH_CTRL_CONTROL_INFO_SEL_OFFSET),
"Incorrect enum value for kFlashCtrlRegionInfo2");
+ static_assert(kFlashCtrlErasePage == 0u,
+ "Incorrect enum value for kFlashCtrlErasePage");
+ static_assert(kFlashCtrlEraseBank == 1u << FLASH_CTRL_CONTROL_ERASE_SEL_BIT,
+ "Incorrect enum value for kFlashCtrlEraseBank");
// Set the partition.
control_reg_val |= (uint32_t)region;
- // Set the number of words.
+ // Set the erase type.
+ control_reg_val |= (uint32_t)erase_type;
+
+ // Set the number of words as `word_count - 1` as noted in #3353.
control_reg_val = bitfield_field32_write(
- control_reg_val, FLASH_CTRL_CONTROL_NUM_FIELD, word_count);
+ control_reg_val, FLASH_CTRL_CONTROL_NUM_FIELD, word_count - 1);
// Start the transaction.
control_reg_val =
@@ -99,16 +77,115 @@
return kErrorOk;
}
-size_t flash_ctrl_fifo_read(size_t word_count, uint32_t *data_out) {
+static void fifo_read(size_t word_count, uint32_t *data_out) {
// Keep reading until no words remain. For large reads this may create back
// pressure.
- size_t words_read = 0;
- for (; words_read < word_count; ++words_read) {
+ for (size_t words_read = 0; words_read < word_count; ++words_read) {
data_out[words_read] =
abs_mmio_read32(kBase + FLASH_CTRL_RD_FIFO_REG_OFFSET);
}
+}
- return words_read;
+static void fifo_push(size_t word_count, const uint32_t *data) {
+ // Keep writing until no words remain. For large writes this may create back
+ // pressure.
+ for (size_t words_programmed = 0; words_programmed < word_count;
+ ++words_programmed) {
+ abs_mmio_write32(kBase + FLASH_CTRL_PROG_FIFO_REG_OFFSET,
+ data[words_programmed]);
+ }
+}
+
+static rom_error_t check_errors(void) {
+ uint32_t op_status = abs_mmio_read32(kBase + FLASH_CTRL_OP_STATUS_REG_OFFSET);
+ if (bitfield_bit32_read(op_status, FLASH_CTRL_OP_STATUS_ERR_BIT)) {
+ return kErrorFlashCtrlInternal;
+ }
+ return kErrorOk;
+}
+
+static rom_error_t wait_for_done(void) {
+ uint32_t op_status;
+ do {
+ op_status = abs_mmio_read32(kBase + FLASH_CTRL_OP_STATUS_REG_OFFSET);
+ } while (!bitfield_bit32_read(op_status, FLASH_CTRL_OP_STATUS_DONE_BIT));
+
+ rom_error_t res = check_errors();
+
+ // Clear OP_STATUS.
+ abs_mmio_write32(kBase + FLASH_CTRL_OP_STATUS_REG_OFFSET, 0u);
+
+ return res;
+}
+
+void flash_ctrl_init(void) {
+ // Initialize the flash controller.
+ abs_mmio_write32(kBase + FLASH_CTRL_INIT_REG_OFFSET,
+ bitfield_bit32_write(0u, FLASH_CTRL_INIT_VAL_BIT, true));
+}
+
+void flash_ctrl_status_get(flash_ctrl_status_t *status) {
+ // Read flash controller status.
+ uint32_t fc_status = abs_mmio_read32(kBase + FLASH_CTRL_STATUS_REG_OFFSET);
+
+ // Extract flash controller status bits.
+ status->rd_full =
+ bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_RD_FULL_BIT);
+ status->rd_empty =
+ bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_RD_EMPTY_BIT);
+ status->prog_full =
+ bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_PROG_FULL_BIT);
+ status->prog_empty =
+ bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_PROG_EMPTY_BIT);
+ status->init_wip =
+ bitfield_bit32_read(fc_status, FLASH_CTRL_STATUS_INIT_WIP_BIT);
+}
+
+rom_error_t flash_ctrl_read(uint32_t addr, uint32_t word_count,
+ flash_ctrl_partition_t region, uint32_t *data) {
+ // Start the read transaction, the value of `erase_type` doesn't matter.
+ RETURN_IF_ERROR(transaction_start(addr, word_count, region,
+ kFlashCtrlErasePage,
+ FLASH_CTRL_CONTROL_OP_VALUE_READ));
+ fifo_read(word_count, data);
+ return wait_for_done();
+}
+
+rom_error_t flash_ctrl_prog(uint32_t addr, uint32_t word_count,
+ flash_ctrl_partition_t region,
+ const uint32_t *data) {
+ uint32_t window_offset = addr % FLASH_CTRL_PARAM_REG_BUS_PGM_RES_BYTES;
+
+ while (word_count > 0) {
+ // Program operations can't cross window boundaries.
+ uint32_t max_bytes = FLASH_CTRL_PARAM_REG_BUS_PGM_RES_BYTES - window_offset;
+ uint32_t write_size = max_bytes / sizeof(uint32_t);
+ write_size = word_count < write_size ? word_count : write_size;
+ window_offset = 0;
+
+ // Start the program transaction, the value of `erase_type` doesn't matter.
+ RETURN_IF_ERROR(transaction_start(addr, write_size, region,
+ kFlashCtrlErasePage,
+ FLASH_CTRL_CONTROL_OP_VALUE_PROG));
+
+ fifo_push(write_size, data);
+ RETURN_IF_ERROR(wait_for_done());
+
+ addr += write_size * sizeof(uint32_t);
+ data += write_size;
+ word_count -= write_size;
+ }
+
+ return kErrorOk;
+}
+
+rom_error_t flash_ctrl_erase(uint32_t addr, flash_ctrl_partition_t region,
+ flash_ctrl_erase_type_t type) {
+ // Start the erase transaction, the value of `word_count` doesn't matter.
+ RETURN_IF_ERROR(transaction_start(addr, 1u, region, type,
+ FLASH_CTRL_CONTROL_OP_VALUE_ERASE));
+
+ return wait_for_done();
}
void flash_ctrl_exec_set(flash_ctrl_exec_t enable) {
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl.h b/sw/device/silicon_creator/lib/drivers/flash_ctrl.h
index dfce36b..1ad9ac1 100644
--- a/sw/device/silicon_creator/lib/drivers/flash_ctrl.h
+++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl.h
@@ -33,17 +33,17 @@
*/
bool rd_empty;
/**
+ * Flash program FIFO full, software must consume data.
+ */
+ bool prog_full;
+ /**
+ * Flash program FIFO empty.
+ */
+ bool prog_empty;
+ /**
* Flash controller undergoing init.
*/
bool init_wip;
- /**
- * Flash operation done.
- */
- bool done;
- /**
- * Flash operation error.
- */
- bool err;
} flash_ctrl_status_t;
/**
@@ -54,10 +54,8 @@
*
* @param flash_ctrl flash controller device to check the status bits for.
* @param[out] status_out The current status of the flash controller.
- * @return `kErrorFlashCtrlInvalidArgument` if `status` is NULL, `kErrorOk`
- * otherwise.
*/
-rom_error_t flash_ctrl_status_get(flash_ctrl_status_t *status);
+void flash_ctrl_status_get(flash_ctrl_status_t *status);
/**
* Region selection enumeration. Represents both the partition and the info
@@ -83,34 +81,85 @@
} flash_ctrl_partition_t;
/**
- * Start a read transaction.
+ * Perform a read transaction.
*
* The flash controller will truncate to the closest, lower word aligned
* address. For example, if 0x13 is supplied, the controller will perform a read
* at address 0x10.
*
+ * On success, `data` is populated with the read data.
+ *
+ * For operations that fail with `kErrorFlashCtrlInternal`, `err` is set to the
+ * internal error mask for flash_ctrl, which can be checked against the
+ * `kFlashCtrlErr*` bits. The internal error state is cleared after each call.
+ *
* @param addr The address to read from.
* @param word_count The number of bus words the flash operation should read.
* @param region The region to read from.
+ * @param[out] data The buffer to store the read data.
+ * @param[out] err The internal error state of flash_ctrl.
* @return `kErrorFlashCtrlBusy` if the flash controller is already processing a
- * transaction, `kErrorOk` otherwise.
+ * transaction, `kErrorFlashCtrlInternal` if the operations fails, `kErrorOk`
+ * otherwise.
*/
-rom_error_t flash_ctrl_read_start(uint32_t addr, uint32_t word_count,
- flash_ctrl_partition_t region);
+rom_error_t flash_ctrl_read(uint32_t addr, uint32_t word_count,
+ flash_ctrl_partition_t region, uint32_t *data);
+
+typedef enum flash_ctrl_erase_type {
+ /**
+ * Erase a page.
+ */
+ kFlashCtrlErasePage = 0x0000,
+ /**
+ * Erase a bank.
+ */
+ kFlashCtrlEraseBank = 0x0080,
+} flash_ctrl_erase_type_t;
/**
- * Read data from the read FIFO.
+ * Perform a program transaction.
*
- * Attempts to read `word_count` words from the read FIFO.
+ * The flash controller will truncate to the closest, lower word aligned
+ * address. For example, if 0x13 is supplied, the controller will start writing
+ * at address 0x10.
*
- * It is up to the caller to call `flash_ctrl_status_get()` to ensure the
- * flash controller completed this transaction successfully.
+ * For operations that fail with `kErrorFlashCtrlInternal`, `err` is set to the
+ * internal error mask for flash_ctrl, which can be checked against the
+ * `kFlashCtrlErr*` bits. The internal error state is cleared after each call.
*
- * @param word_count The number of words to read.
- * @param data_out The region in memory to store the data read off the FIFO.
- * @return The number of words read from the FIFO.
+ * @param addr The address to write to.
+ * @param word_count The number of bus words the flash operation should program.
+ * @param region The region to program.
+ * @param data The buffer containing the data to program to flash.
+ * @param[out] err The internal error state of flash_ctrl.
+ * @return `kErrorFlashCtrlBusy` if the flash controller is already processing a
+ * transaction, `kErrorFlashCtrlInternal` if the operations fails, `kErrorOk`
+ * otherwise.
*/
-size_t flash_ctrl_fifo_read(size_t word_count, uint32_t *data_out);
+rom_error_t flash_ctrl_prog(uint32_t addr, uint32_t word_count,
+ flash_ctrl_partition_t region,
+ const uint32_t *data);
+
+/**
+ * Invoke a blocking erase transaction.
+ *
+ * The flash controller will truncate to the closest page boundary for page
+ * erase operations, and to the nearest bank aligned boundary for bank erase
+ * operations.
+ *
+ * For operations that fail with `kErrorFlashCtrlInternal`, `err` is set to the
+ * internal error mask for flash_ctrl, which can be checked against the
+ * `kFlashCtrlErr*` bits. The internal error state is cleared after each call.
+ *
+ * @param addr The address that falls within the bank or page being deleted.
+ * @param region The region that contains the bank or page being deleted.
+ * @param[out] err The internal error state of flash_ctrl.
+ * @return `kErrorFlashCtrlBusy` if the flash controller is already processing a
+ * transaction, `kErrorFlashCtrlInternal` if the operations fails, `kErrorOk`
+ * otherwise.
+ */
+rom_error_t flash_ctrl_erase(uint32_t addr, flash_ctrl_partition_t region,
+ flash_ctrl_erase_type_t type);
typedef enum flash_ctrl_exec {
kFlashCtrlExecDisable = kMultiBitBool4False,
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
index b7d1169..1e0e187 100644
--- a/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
+++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
@@ -15,7 +15,6 @@
namespace flash_ctrl_unittest {
namespace {
-using ::testing::ElementsAre;
class FlashCtrlTest : public mask_rom_test::MaskRomTest {
protected:
@@ -34,16 +33,7 @@
class StatusCheckTest : public FlashCtrlTest {};
-TEST_F(StatusCheckTest, InvalidArgument) {
- EXPECT_EQ(flash_ctrl_status_get(NULL), kErrorFlashCtrlInvalidArgument);
-}
-
TEST_F(StatusCheckTest, DefaultStatus) {
- EXPECT_ABS_READ32(base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
- {
- {FLASH_CTRL_OP_STATUS_DONE_BIT, false},
- {FLASH_CTRL_OP_STATUS_ERR_BIT, false},
- });
EXPECT_ABS_READ32(base_ + FLASH_CTRL_STATUS_REG_OFFSET,
{
{FLASH_CTRL_STATUS_RD_FULL_BIT, false},
@@ -52,21 +42,14 @@
});
flash_ctrl_status_t status;
- EXPECT_EQ(flash_ctrl_status_get(&status), kErrorOk);
+ flash_ctrl_status_get(&status);
- EXPECT_EQ(status.done, false);
- EXPECT_EQ(status.err, false);
EXPECT_EQ(status.init_wip, false);
EXPECT_EQ(status.rd_empty, false);
EXPECT_EQ(status.rd_full, false);
}
TEST_F(StatusCheckTest, AllSetStatus) {
- EXPECT_ABS_READ32(base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
- {
- {FLASH_CTRL_OP_STATUS_DONE_BIT, true},
- {FLASH_CTRL_OP_STATUS_ERR_BIT, true},
- });
EXPECT_ABS_READ32(base_ + FLASH_CTRL_STATUS_REG_OFFSET,
{
{FLASH_CTRL_STATUS_RD_FULL_BIT, true},
@@ -75,74 +58,160 @@
});
flash_ctrl_status_t status;
- EXPECT_EQ(flash_ctrl_status_get(&status), kErrorOk);
+ flash_ctrl_status_get(&status);
- EXPECT_EQ(status.done, true);
- EXPECT_EQ(status.err, true);
EXPECT_EQ(status.init_wip, true);
EXPECT_EQ(status.rd_empty, true);
EXPECT_EQ(status.rd_full, true);
}
-class ReadStartTest : public FlashCtrlTest {
+class TransferTest : public FlashCtrlTest {
protected:
+ const std::vector<uint32_t> words_ = {0x12345678, 0x90ABCDEF, 0x0F1E2D3C,
+ 0x4B5A6978};
void ExpectCheckBusy(bool busy) {
EXPECT_ABS_READ32(base_ + FLASH_CTRL_CTRL_REGWEN_REG_OFFSET,
{{FLASH_CTRL_CTRL_REGWEN_EN_BIT, !busy}});
}
- void ExpectReadStart(uint8_t part_sel, uint8_t info_sel) {
- EXPECT_ABS_WRITE32(base_ + FLASH_CTRL_ADDR_REG_OFFSET, 0x01234567);
- EXPECT_ABS_WRITE32(
- base_ + FLASH_CTRL_CONTROL_REG_OFFSET,
- {
- {FLASH_CTRL_CONTROL_OP_OFFSET, FLASH_CTRL_CONTROL_OP_VALUE_READ},
- {FLASH_CTRL_CONTROL_PARTITION_SEL_BIT, part_sel},
- {FLASH_CTRL_CONTROL_INFO_SEL_OFFSET, info_sel},
- {FLASH_CTRL_CONTROL_NUM_OFFSET, 42},
- {FLASH_CTRL_CONTROL_START_BIT, 1},
- });
+
+ void ExpectWaitForDone(bool done, bool error) {
+ EXPECT_ABS_READ32(base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
+ {{FLASH_CTRL_OP_STATUS_DONE_BIT, done}});
+ if (done) {
+ EXPECT_ABS_READ32(base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
+ {{FLASH_CTRL_OP_STATUS_ERR_BIT, error}});
+ EXPECT_ABS_WRITE32(base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET, 0u);
+ }
}
-};
-TEST_F(ReadStartTest, Busy) {
- ExpectCheckBusy(true);
- EXPECT_EQ(flash_ctrl_read_start(0, 0, kFlashCtrlRegionData),
- kErrorFlashCtrlBusy);
-}
+ void ExpectTransferStart(uint8_t part_sel, uint8_t info_sel,
+ uint8_t erase_sel, uint32_t op, uint32_t addr,
+ uint32_t word_count) {
+ ExpectCheckBusy(false);
+ EXPECT_ABS_WRITE32(base_ + FLASH_CTRL_ADDR_REG_OFFSET, addr);
+ EXPECT_ABS_WRITE32(base_ + FLASH_CTRL_CONTROL_REG_OFFSET,
+ {
+ {FLASH_CTRL_CONTROL_OP_OFFSET, op},
+ {FLASH_CTRL_CONTROL_PARTITION_SEL_BIT, part_sel},
+ {FLASH_CTRL_CONTROL_INFO_SEL_OFFSET, info_sel},
+ {FLASH_CTRL_CONTROL_ERASE_SEL_BIT, erase_sel},
+ {FLASH_CTRL_CONTROL_NUM_OFFSET, word_count - 1},
+ {FLASH_CTRL_CONTROL_START_BIT, 1},
+ });
+ }
-TEST_F(ReadStartTest, ReadData) {
- ExpectCheckBusy(false);
- ExpectReadStart(0, 0);
- EXPECT_EQ(flash_ctrl_read_start(0x01234567, 42, kFlashCtrlRegionData),
- kErrorOk);
-}
-
-TEST_F(ReadStartTest, ReadInfo) {
- ExpectCheckBusy(false);
- ExpectReadStart(1, 2);
- EXPECT_EQ(flash_ctrl_read_start(0x01234567, 42, kFlashCtrlRegionInfo2),
- kErrorOk);
-}
-
-class ReadTest : public FlashCtrlTest {
- protected:
- const std::vector<uint32_t> words_ = {0x12345678, 0x90ABCDEF, 0x0F1E2D3C,
- 0x4B5A6978};
void ExpectReadData(const std::vector<uint32_t> &data) {
for (auto val : data) {
EXPECT_ABS_READ32(base_ + FLASH_CTRL_RD_FIFO_REG_OFFSET, val);
}
}
+
+ void ExpectProgData(const std::vector<uint32_t> &data) {
+ for (auto val : data) {
+ EXPECT_ABS_WRITE32(base_ + FLASH_CTRL_PROG_FIFO_REG_OFFSET, val);
+ }
+ }
};
-TEST_F(ReadTest, ReadAll) {
+TEST_F(TransferTest, ReadBusy) {
+ ExpectCheckBusy(true);
+ EXPECT_EQ(flash_ctrl_read(0, 0, kFlashCtrlRegionData, NULL),
+ kErrorFlashCtrlBusy);
+}
+
+TEST_F(TransferTest, ProgBusy) {
+ ExpectCheckBusy(true);
+ EXPECT_EQ(flash_ctrl_prog(0, 4, kFlashCtrlRegionData, NULL),
+ kErrorFlashCtrlBusy);
+}
+
+TEST_F(TransferTest, EraseBusy) {
+ ExpectCheckBusy(true);
+ EXPECT_EQ(flash_ctrl_erase(0, kFlashCtrlRegionData, kFlashCtrlErasePage),
+ kErrorFlashCtrlBusy);
+}
+
+TEST_F(TransferTest, ReadDataOk) {
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_READ, 0x01234567,
+ words_.size());
ExpectReadData(words_);
+ ExpectWaitForDone(true, false);
std::vector<uint32_t> words_out(words_.size());
- EXPECT_EQ(flash_ctrl_fifo_read(words_.size(), &words_out.front()),
- words_.size());
+ EXPECT_EQ(flash_ctrl_read(0x01234567, words_.size(), kFlashCtrlRegionData,
+ &words_out.front()),
+ kErrorOk);
EXPECT_EQ(words_out, words_);
}
+TEST_F(TransferTest, ProgDataOk) {
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_PROG, 0x01234567,
+ words_.size());
+ ExpectProgData(words_);
+ ExpectWaitForDone(true, false);
+ EXPECT_EQ(flash_ctrl_prog(0x01234567, words_.size(), kFlashCtrlRegionData,
+ &words_.front()),
+ kErrorOk);
+}
+
+TEST_F(TransferTest, EraseDataPageOk) {
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_ERASE, 0x01234567,
+ 1);
+ ExpectWaitForDone(true, false);
+ EXPECT_EQ(
+ flash_ctrl_erase(0x01234567, kFlashCtrlRegionData, kFlashCtrlErasePage),
+ kErrorOk);
+}
+
+TEST_F(TransferTest, ProgAcrossWindows) {
+ static const uint32_t kWinSize = FLASH_CTRL_PARAM_REG_BUS_PGM_RES_BYTES;
+ static const uint32_t kManyWordsSize = 2 * kWinSize / sizeof(uint32_t);
+
+ std::array<uint32_t, kManyWordsSize> many_words;
+ for (uint32_t i = 0; i < many_words.size(); ++i) {
+ many_words[i] = i;
+ }
+
+ auto iter = many_words.begin();
+ size_t half_step = kWinSize / sizeof(uint32_t) / 2;
+
+ // Program address range [0x20, 0x40)
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_PROG, kWinSize / 2,
+ half_step);
+ ExpectProgData(std::vector<uint32_t>(iter, iter + half_step));
+ ExpectWaitForDone(true, false);
+ iter += half_step;
+
+ // Program address range [0x40, 0x80)
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_PROG, kWinSize,
+ 2 * half_step);
+ ExpectProgData(std::vector<uint32_t>(iter, iter + 2 * half_step));
+ ExpectWaitForDone(true, false);
+ iter += 2 * half_step;
+
+ // Programm address range [0x80, 0xA0)
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_PROG, 2 * kWinSize,
+ half_step);
+ ExpectProgData(std::vector<uint32_t>(iter, iter + half_step));
+ ExpectWaitForDone(true, false);
+
+ EXPECT_EQ(iter + half_step, many_words.end());
+
+ EXPECT_EQ(flash_ctrl_prog(kWinSize / 2, many_words.size(),
+ kFlashCtrlRegionData, &many_words.front()),
+ kErrorOk);
+}
+
+TEST_F(TransferTest, TransferInternalError) {
+ ExpectTransferStart(0, 0, 0, FLASH_CTRL_CONTROL_OP_VALUE_READ, 0x01234567,
+ words_.size());
+ ExpectReadData(words_);
+ ExpectWaitForDone(true, true);
+ std::vector<uint32_t> words_out(words_.size());
+ EXPECT_EQ(flash_ctrl_read(0x01234567, words_.size(), kFlashCtrlRegionData,
+ &words_out.front()),
+ kErrorFlashCtrlInternal);
+}
+
class ExecTest : public FlashCtrlTest {};
TEST_F(ExecTest, Enable) {
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h
index bc881a0..81d9d32 100644
--- a/sw/device/silicon_creator/lib/error.h
+++ b/sw/device/silicon_creator/lib/error.h
@@ -97,6 +97,7 @@
X(kErrorOtbnUnavailable, ERROR_(4, kModuleOtbn, kFailedPrecondition)), \
X(kErrorFlashCtrlInvalidArgument, ERROR_(1, kModuleFlashCtrl, kInvalidArgument)), \
X(kErrorFlashCtrlBusy, ERROR_(2, kModuleFlashCtrl, kUnavailable)), \
+ X(kErrorFlashCtrlInternal, ERROR_(3, kModuleFlashCtrl, kInternal)), \
X(kErrorSecMmioRegFileSize, ERROR_(0, kModuleSecMmio, kResourceExhausted)), \
X(kErrorSecMmioReadFault, ERROR_(1, kModuleSecMmio, kInternal)), \
X(kErrorSecMmioWriteFault, ERROR_(2, kModuleSecMmio, kInternal)), \