[sw/lib] Merge flash_ctrl from boot_rom into lib
This is the first commit in a series of changes to move the boot_rom
device interface function modules into the lib folder.
* Updated sw/lib/flash_ctrl.c to follow API naming conventions from
boot_rom.
* Added Doxygen doc strings.
* Updated enums to use DEFINE_FORMAT.
* Renamed sw/tests/hello_flash/hello_flash.c as
sw/tests/flash_ctrl/flash_test.c.
* Updated flash_test.c to use new flash_ctrl API.
diff --git a/sw/lib/common.h b/sw/lib/common.h
index 7908d6e..f7f2840 100644
--- a/sw/lib/common.h
+++ b/sw/lib/common.h
@@ -30,6 +30,8 @@
#define SETBIT(val, bit) (val | 1 << bit)
#define CLRBIT(val, bit) (val & ~(1 << bit))
+#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
+
/* Hamming weight */
#define BITLENGTH_1(X) ((X) - (((X) >> 1) & 0x55555555))
#define BITLENGTH_2(X) (((X)&0x33333333) + (((X) >> 2) & 0x33333333))
diff --git a/sw/lib/flash_ctrl.c b/sw/lib/flash_ctrl.c
index 0add8c4..163c772 100644
--- a/sw/lib/flash_ctrl.c
+++ b/sw/lib/flash_ctrl.c
@@ -1,164 +1,153 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
-
#include "flash_ctrl.h"
-#include <stdint.h>
-
#include "common.h"
+#include "flash_ctrl_regs.h"
-static uint32_t get_clr_err(void) {
- uint32_t err_status;
+#define FLASH_CTRL0_BASE_ADDR 0x40030000
- // extract error status
- err_status =
- REG32(FLASH_CTRL_INTR_STATE(0)) & (0x1 << FLASH_CTRL_INTR_STATE_OP_ERROR);
+typedef enum flash_op {
+ FLASH_READ = 0,
+ FLASH_PROG = 1,
+ FLASH_ERASE = 2
+} flash_op_t;
- // clear error if set
- REG32(FLASH_CTRL_INTR_STATE(0)) = err_status;
+typedef enum erase_type {
+ FLASH_PAGE_ERASE = 0,
+ FLASH_BANK_ERASE = 1
+} erase_type_t;
- // return status
- return err_status;
+/* Wait for flash command to complete and set ACK in controller */
+static inline void wait_done_and_ack(void) {
+ while ((REG32(FLASH_CTRL_OP_STATUS(0)) & (1 << FLASH_CTRL_OP_STATUS_DONE)) ==
+ 0) {
+ }
+ REG32(FLASH_CTRL_OP_STATUS(0)) = 0;
}
-// flash initialization done
-void wait_flash_init(void) {
+void flash_init_block(void) {
while ((REG32(FLASH_CTRL_STATUS(0)) & (1 << FLASH_CTRL_STATUS_INIT_WIP)) >
0) {
}
}
-// wait for flash done and ack
-void wait_done_and_ack(void) {
- while ((REG32(FLASH_CTRL_OP_STATUS(0)) & (1 << FLASH_CTRL_OP_STATUS_DONE)) ==
- 0)
- ;
-
- REG32(FLASH_CTRL_OP_STATUS(0)) = 0;
+/* Return status error and clear internal status register */
+static int get_clr_err(void) {
+ uint32_t err_status =
+ REG32(FLASH_CTRL_INTR_STATE(0)) & (0x1 << FLASH_CTRL_INTR_STATE_OP_ERROR);
+ REG32(FLASH_CTRL_INTR_STATE(0)) = err_status;
+ return err_status;
}
-// setup flash prog
-void setup_flash_prog(uint32_t addr, uint32_t num) {
- uint32_t val;
-
- val = FlashProg << FLASH_CTRL_CONTROL_OP_OFFSET |
- (num - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
- 0x1 << FLASH_CTRL_CONTROL_START;
-
- REG32(FLASH_CTRL_ADDR(0)) = addr;
-
- REG32(FLASH_CTRL_CONTROL(0)) = val;
-}
-
-// program data
-uint32_t prog_flash(uint32_t addr, uint32_t num, uint32_t *data) {
- uint32_t i = 0;
-
- // setup flash programming
- setup_flash_prog(addr, num);
-
- // beginning filling up the fifo
- for (i = 0; i < num; i++) {
- REG32(FLASH_CTRL_PROG_FIFO(0)) = *data;
- data++;
+int flash_check_empty(void) {
+ uint32_t mask = -1u;
+ uint32_t *p = (uint32_t *)FLASH_MEM_BASE_ADDR;
+ // TODO: Update range to cover entire flash. Limited now to one bank while
+ // we debu initialization.
+ for (; p < (uint32_t *)(FLASH_MEM_BASE_ADDR + FLASH_BANK_SZ);) {
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ mask &= *p++;
+ if (mask != -1u) {
+ return 0;
+ }
}
+ return 1;
+}
- // wait for operation finish
+int flash_bank_erase(bank_index_t idx) {
+ REG32(FLASH_CTRL_MP_BANK_CFG(0)) =
+ 0x1 << ((idx == FLASH_BANK_0) ? FLASH_CTRL_MP_BANK_CFG_ERASE_EN0
+ : FLASH_CTRL_MP_BANK_CFG_ERASE_EN1);
+
+ // TODO: Add timeout conditions and add error codes.
+ REG32(FLASH_CTRL_ADDR(0)) = (idx == FLASH_BANK_0)
+ ? FLASH_MEM_BASE_ADDR
+ : (FLASH_MEM_BASE_ADDR + FLASH_BANK_SZ);
+ REG32(FLASH_CTRL_CONTROL(0)) =
+ (FLASH_ERASE << FLASH_CTRL_CONTROL_OP_OFFSET |
+ FLASH_BANK_ERASE << FLASH_CTRL_CONTROL_ERASE_SEL |
+ 0x1 << FLASH_CTRL_CONTROL_START);
wait_done_and_ack();
- // return error status
+ REG32(FLASH_CTRL_MP_BANK_CFG(0)) =
+ 0x0 << ((idx == FLASH_BANK_0) ? FLASH_CTRL_MP_BANK_CFG_ERASE_EN0
+ : FLASH_CTRL_MP_BANK_CFG_ERASE_EN1);
return get_clr_err();
}
-// read data
-uint32_t read_flash(uint32_t addr, uint32_t num, uint32_t *data) {
- uint32_t val;
- uint32_t i = 0;
-
- // kick off flash operation
- val = FlashRead << FLASH_CTRL_CONTROL_OP_OFFSET |
- (num - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
- 0x1 << FLASH_CTRL_CONTROL_START;
-
+int flash_page_erase(uint32_t addr) {
REG32(FLASH_CTRL_ADDR(0)) = addr;
+ REG32(FLASH_CTRL_CONTROL(0)) = FLASH_ERASE << FLASH_CTRL_CONTROL_OP_OFFSET |
+ FLASH_PAGE_ERASE
+ << FLASH_CTRL_CONTROL_ERASE_SEL |
+ 0x1 << FLASH_CTRL_CONTROL_START;
+ wait_done_and_ack();
+ return get_clr_err();
+}
- REG32(FLASH_CTRL_CONTROL(0)) = val;
+static int flash_write_internal(uint32_t addr, const uint32_t *data,
+ uint32_t size) {
+ // TODO: Do we need to select bank as part of the write?
+ // TODO: Update with address alignment requirements.
+ REG32(FLASH_CTRL_ADDR(0)) = addr;
+ REG32(FLASH_CTRL_CONTROL(0)) = (FLASH_PROG << FLASH_CTRL_CONTROL_OP_OFFSET |
+ (size - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
+ 0x1 << FLASH_CTRL_CONTROL_START);
+ for (int i = 0; i < size; ++i) {
+ REG32(FLASH_CTRL_PROG_FIFO(0)) = data[i];
+ }
+ wait_done_and_ack();
+ return get_clr_err();
+}
- while (i < num) {
- // if not empty, read a word
+int flash_write(uint32_t addr, const uint32_t *data, uint32_t size) {
+ // TODO: Breakdown into FIFO chunks if needed.
+ return flash_write_internal(addr, data, size);
+}
+
+int flash_read(uint32_t addr, uint32_t size, uint32_t *data) {
+ REG32(FLASH_CTRL_ADDR(0)) = addr;
+ REG32(FLASH_CTRL_CONTROL(0)) = FLASH_READ << FLASH_CTRL_CONTROL_OP_OFFSET |
+ (size - 1) << FLASH_CTRL_CONTROL_NUM_OFFSET |
+ 0x1 << FLASH_CTRL_CONTROL_START;
+ for (uint32_t i = 0; i < size;) {
if (((REG32(FLASH_CTRL_STATUS(0)) >> FLASH_CTRL_STATUS_RD_EMPTY) & 0x1) ==
0) {
*data++ = REG32(FLASH_CTRL_RD_FIFO(0));
i++;
}
}
-
- // wait for operation finish
wait_done_and_ack();
-
- // return error status
return get_clr_err();
}
-// page erase flash
-// wrap down to closest down to page boundary
-uint32_t page_erase(uint32_t addr) {
- uint32_t val;
- uint32_t data[ERASE_CHECK_WORDS];
- uint32_t verify_rounds;
- uint32_t error;
-
- error = 0;
- verify_rounds = WORDS_PER_PAGE / ERASE_CHECK_WORDS;
-
- // kick off flash operation
- val = FlashErase << FLASH_CTRL_CONTROL_OP_OFFSET |
- PageErase << FLASH_CTRL_CONTROL_ERASE_SEL |
- 0x1 << FLASH_CTRL_CONTROL_START;
-
- REG32(FLASH_CTRL_ADDR(0)) = addr;
-
- REG32(FLASH_CTRL_CONTROL(0)) = val;
-
- // wait for operation finish
- wait_done_and_ack();
-
- error += get_clr_err();
-
- // verify erase
- for (uint32_t i = 0; i < verify_rounds; i++) {
- error += read_flash(addr + i * ERASE_CHECK_WORDS * BYTES_PER_WORD,
- ERASE_CHECK_WORDS, data);
-
- for (uint32_t j = 0; j < ERASE_CHECK_WORDS; j++) {
- if (data[i] != 0xFFFFFFFF) {
- REG32(FLASH_CTRL_SCRATCH(0)) = data[i];
-
- // re-init array
- data[i] = 0;
- error++;
- }
- }
- }
-
- // return error status
- return error;
-}
-
-void flash_default_region(uint32_t rd_en, uint32_t prog_en, uint32_t erase_en) {
+void flash_default_region_access(bool rd_en, bool prog_en, bool erase_en) {
REG32(FLASH_CTRL_DEFAULT_REGION(0)) =
rd_en << FLASH_CTRL_DEFAULT_REGION_RD_EN |
prog_en << FLASH_CTRL_DEFAULT_REGION_PROG_EN |
erase_en << FLASH_CTRL_DEFAULT_REGION_ERASE_EN;
}
-void flash_cfg_region(mp_region_t region_cfg) {
- REG32(FLASH_CTRL_MP_REGION_CFG0(0) + region_cfg.num * 4) =
- region_cfg.base << FLASH_CTRL_MP_REGION_CFG0_BASE0_OFFSET |
- region_cfg.size << FLASH_CTRL_MP_REGION_CFG0_SIZE0_OFFSET |
- region_cfg.rd_en << FLASH_CTRL_MP_REGION_CFG0_RD_EN0 |
- region_cfg.prog_en << FLASH_CTRL_MP_REGION_CFG0_PROG_EN0 |
- region_cfg.erase_en << FLASH_CTRL_MP_REGION_CFG0_ERASE_EN0 |
+void flash_cfg_region(const mp_region_t *region_cfg) {
+ REG32(FLASH_CTRL_MP_REGION_CFG0(0) + region_cfg->num * 4) =
+ region_cfg->base << FLASH_CTRL_MP_REGION_CFG0_BASE0_OFFSET |
+ region_cfg->size << FLASH_CTRL_MP_REGION_CFG0_SIZE0_OFFSET |
+ region_cfg->rd_en << FLASH_CTRL_MP_REGION_CFG0_RD_EN0 |
+ region_cfg->prog_en << FLASH_CTRL_MP_REGION_CFG0_PROG_EN0 |
+ region_cfg->erase_en << FLASH_CTRL_MP_REGION_CFG0_ERASE_EN0 |
0x1 << FLASH_CTRL_MP_REGION_CFG0_EN0;
}
+
+void flash_write_scratch_reg(uint32_t value) {
+ REG32(FLASH_CTRL_SCRATCH(0)) = value;
+}
+
+uint32_t flash_read_scratch_reg(void) { return REG32(FLASH_CTRL_SCRATCH(0)); }
diff --git a/sw/lib/flash_ctrl.h b/sw/lib/flash_ctrl.h
index 21e90a1..43eb18f 100644
--- a/sw/lib/flash_ctrl.h
+++ b/sw/lib/flash_ctrl.h
@@ -2,42 +2,94 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
-#ifndef _FLASH_H_
-#define _FLASH_H_
+#ifndef _F_FLASH_CTRL_H__
+#define _F_FLASH_CTRL_H__
+#include <stdbool.h>
#include <stdint.h>
-#include "flash_ctrl_regs.h"
+/**
+ * Flash bank IDs
+ */
+typedef enum bank_index { FLASH_BANK_0 = 0, FLASH_BANK_1 = 1 } bank_index_t;
-#define FLASH_CTRL0_BASE_ADDR 0x40030000
-#define WORDS_PER_PAGE 256
-#define ERASE_CHECK_WORDS 16
-#define BYTES_PER_WORD 4
-
-typedef enum flash_op {
- FlashRead = 0,
- FlashProg = 1,
- FlashErase = 2
-} flash_op_t;
-
-typedef enum erase_type { PageErase = 0, BankErase = 1 } erase_type_t;
-
+/**
+ * Memory protection configuration options.
+ */
typedef struct mp_region {
- uint32_t num; // which region to program
+ /** Which region to program. */
+ uint32_t num;
+ /** Region offset. */
uint32_t base;
+ /** Region config size. */
uint32_t size;
+ /** Read enable flag. */
uint32_t rd_en;
+ /** Program enable flag. */
uint32_t prog_en;
+ /** Erase enable flag. */
uint32_t erase_en;
} mp_region_t;
-void wait_flash_init(void);
-void wait_done_and_ack(void);
-void setup_flash_prog(uint32_t addr, uint32_t num);
-uint32_t prog_flash(uint32_t addr, uint32_t num, uint32_t *data);
-uint32_t read_flash(uint32_t addr, uint32_t num, uint32_t *data);
-uint32_t page_erase(uint32_t addr);
-void flash_default_region(uint32_t rd_en, uint32_t prog_en, uint32_t erase_en);
-void flash_cfg_region(mp_region_t region_cfg);
+/**
+ * Block until flash is initialized.
+ */
+void flash_init_block(void);
-#endif
+/**
+ * Returns 1 if flash is empty, otherwise 0.
+ */
+int flash_check_empty(void);
+
+/*
+ * Erase flash bank |bank_idx|. Blocks until erase is complete.
+ *
+ * @param idx Flash bank index.
+ * @return Non zero on failure.
+ */
+int flash_bank_erase(bank_index_t idx);
+int flash_page_erase(uint32_t addr);
+
+/**
+ * Write |data| at |addr| offset with |size| in 4B words
+ *
+ * @param addr Flash address 32bit aligned.
+ * @param data Data to write.
+ * @param size Number of 4B words to write from |data| buffer.
+ * @return Non zero on failure.
+ */
+int flash_write(uint32_t addr, const uint32_t *data, uint32_t size);
+
+/**
+ * Read |size| 4B words and write result to |data|.
+ *
+ * @param addr Read start address.
+ * @param size Number of 4B words to read.
+ * @param data Output buffer.
+ * @return Non zero on failure.
+ */
+int flash_read(uint32_t addr, uint32_t size, uint32_t *data);
+
+/**
+ * Set flash controller default permissions.
+ *
+ * @param rd_end Read enable.
+ * @param prog_en Write enable.
+ * @param erase_en Erase enable.
+ */
+void flash_default_region_access(bool rd_en, bool prog_en, bool erase_en);
+
+/**
+ * Configure memory protection region.
+ *
+ * @param region_cfg Region configuration.
+ */
+void flash_cfg_region(const mp_region_t *region_cfg);
+
+/** Write value to flash scratch register */
+void flash_write_scratch_reg(uint32_t value);
+
+/** Read scratch register */
+uint32_t flash_read_scratch_reg(void);
+
+#endif // _F_FLASH_CTRL_H__
diff --git a/sw/tests/hello_flash/Makefile b/sw/tests/flash_ctrl/Makefile
similarity index 87%
rename from sw/tests/hello_flash/Makefile
rename to sw/tests/flash_ctrl/Makefile
index 4f4c6c1..6f595dd 100644
--- a/sw/tests/hello_flash/Makefile
+++ b/sw/tests/flash_ctrl/Makefile
@@ -4,8 +4,8 @@
#
# Generate a baremetal application for the microcontroller
-NAME = hello_flash
-SRCS = hello_flash.c
+NAME = flash_test
+SRCS = flash_test.c
PROGRAM_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
include ${PROGRAM_DIR}/../../exts/common/options.mk
diff --git a/sw/tests/flash_ctrl/flash_test.c b/sw/tests/flash_ctrl/flash_test.c
new file mode 100644
index 0000000..c527795
--- /dev/null
+++ b/sw/tests/flash_ctrl/flash_test.c
@@ -0,0 +1,153 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "common.h"
+#include "flash_ctrl.h"
+#include "gpio.h"
+#include "uart.h"
+
+/**
+ * Delay loop executing within 8 cycles on ibex
+ */
+static void delay_loop_ibex(unsigned long loops) {
+ int out; /* only to notify compiler of modifications to |loops| */
+ asm volatile(
+ "1: nop \n" // 1 cycle
+ " nop \n" // 1 cycle
+ " nop \n" // 1 cycle
+ " nop \n" // 1 cycle
+ " addi %1, %1, -1 \n" // 1 cycle
+ " bnez %1, 1b \n" // 3 cycles
+ : "=&r"(out)
+ : "0"(loops));
+}
+
+static int usleep_ibex(unsigned long usec) {
+ unsigned long usec_cycles;
+ usec_cycles = CLK_FIXED_FREQ_HZ * usec / 1000 / 1000 / 8;
+
+ delay_loop_ibex(usec_cycles);
+ return 0;
+}
+
+static int usleep(unsigned long usec) { return usleep_ibex(usec); }
+
+static void break_on_error(uint32_t error) {
+ if (error) {
+ // inifinitely fetch instructions, will flag an assertion error
+ uart_send_str("FAIL!\r\n");
+ while (1) {
+ usleep(100);
+ }
+ }
+}
+
+/* Returns 1 if |a| and |b| are equal. */
+int check_arr_eq(const uint32_t *a, const uint32_t *b, uint32_t len) {
+ for (int i = 0; i < len; ++i) {
+ if (a[i] != b[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ uint32_t i;
+ uint32_t prog_array[FLASH_WORDS_PER_PAGE];
+ uint32_t rd_array[FLASH_WORDS_PER_PAGE];
+ uint32_t bank1_addr = FLASH_MEM_BASE_ADDR + FLASH_BANK_SZ;
+
+ uart_init(UART_BAUD_RATE);
+ flash_init_block();
+
+ // enable all access
+ flash_default_region_access(1, 1, 1);
+ break_on_error(flash_page_erase(bank1_addr));
+ flash_write_scratch_reg(0xFACEDEAD);
+ // read flash back via host to ensure everything is cleared
+ for (i = 0; i < FLASH_WORDS_PER_PAGE; i++) {
+ if (REG32(bank1_addr + i * 4) != 0xFFFFFFFF) {
+ flash_write_scratch_reg(0xDEADBEEF);
+ break_on_error(1);
+ }
+ }
+
+ // do 4K programming
+ // employ the live programming method where overall payload >> flash fifo size
+ for (i = 0; i < ARRAYSIZE(prog_array); i++) {
+ prog_array[i] = i + (i % 2) ? 0xA5A5A5A5 : 0x5A5A5A5A;
+ }
+ break_on_error(flash_write(bank1_addr, prog_array, ARRAYSIZE(prog_array)));
+ break_on_error(flash_read(bank1_addr, ARRAYSIZE(rd_array), rd_array));
+ break_on_error(!check_arr_eq(rd_array, prog_array, ARRAYSIZE(rd_array)));
+
+ /////////////////////////////////////////////////////////////
+ // Begin flash memory protection testing
+ /////////////////////////////////////////////////////////////
+ uint32_t region_base_page = FLASH_PAGES_PER_BANK;
+ uint32_t region_size = 1;
+ uint32_t good_addr_start =
+ FLASH_MEM_BASE_ADDR + region_base_page * FLASH_PAGE_SZ;
+ uint32_t good_addr_end = good_addr_start + region_size * FLASH_PAGE_SZ - 1;
+ uint32_t bad_addr_start =
+ good_addr_end + 1; // this is always aligned to a page
+ uint32_t good_words = 3;
+ uint32_t bad_words = 3;
+ uint32_t chk_addr = bad_addr_start - (FLASH_WORD_SZ * good_words);
+
+ mp_region_t region0 = {
+ 0, // region 0
+ region_base_page, // page 1 of bank1
+ region_size, // size of 1 page
+ 1, // allow read
+ 1, // allow program
+ 1 // allow erase
+ };
+
+ // initialize good and bad regions.
+ break_on_error(flash_page_erase(good_addr_start));
+ break_on_error(flash_page_erase(bad_addr_start));
+
+ // turn off default region all access
+ flash_default_region_access(0, 0, 0);
+ flash_cfg_region(®ion0);
+
+ // expect write to fail.
+ for (uint32_t i = 0; i < good_words + bad_words; i++) {
+ prog_array[i] = 0xA5A5A5A5 + i;
+ }
+ break_on_error(!flash_write(chk_addr, prog_array, good_words + bad_words));
+
+ // the good words should match
+ for (uint32_t i = 0; i < good_words; i++) {
+ if (REG32(chk_addr + i * 4) != prog_array[i]) {
+ break_on_error(1);
+ }
+ }
+
+ // the bad word contents should not have gone through
+ for (uint32_t i = good_words; i < good_words + bad_words; i++) {
+ if (REG32(chk_addr + i * 4) != 0xFFFFFFFF) {
+ break_on_error(1);
+ }
+ }
+
+ // attempt to erase bad page, should error
+ break_on_error(!flash_page_erase(bad_addr_start));
+
+ // erase the good page
+ break_on_error(flash_page_erase(good_addr_start));
+
+ // double check erase results
+ for (uint32_t i = 0; i < FLASH_WORDS_PER_PAGE; i++) {
+ if (REG32(good_addr_start + i * 4) != 0xFFFFFFFF) {
+ break_on_error(1);
+ }
+ }
+
+ // cleanly terminate execution
+ uart_send_str("PASS!\r\n");
+ __asm__ volatile("wfi;");
+}
diff --git a/sw/tests/hello_flash/hello_flash.c b/sw/tests/hello_flash/hello_flash.c
deleted file mode 100644
index db38731..0000000
--- a/sw/tests/hello_flash/hello_flash.c
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-#include "common.h"
-#include "flash_ctrl.h"
-#include "gpio.h"
-#include "uart.h"
-
-#define DATA_MAX 32
-
-/**
- * Delay loop executing within 8 cycles on ibex
- */
-static void delay_loop_ibex(unsigned long loops) {
- int out; /* only to notify compiler of modifications to |loops| */
- asm volatile(
- "1: nop \n" // 1 cycle
- " nop \n" // 1 cycle
- " nop \n" // 1 cycle
- " nop \n" // 1 cycle
- " addi %1, %1, -1 \n" // 1 cycle
- " bnez %1, 1b \n" // 3 cycles
- : "=&r"(out)
- : "0"(loops));
-}
-
-static int usleep_ibex(unsigned long usec) {
- unsigned long usec_cycles;
- usec_cycles = CLK_FIXED_FREQ_HZ * usec / 1000 / 1000 / 8;
-
- delay_loop_ibex(usec_cycles);
- return 0;
-}
-
-static int usleep(unsigned long usec) { return usleep_ibex(usec); }
-
-static void break_on_error(uint32_t error) {
- if (error) {
- // inifinitely fetch instructions, will flag an assertion error
- uart_send_str("Test Failed!\r\n");
- while (1) {
- usleep_ibex(100);
- }
- }
-
- // otherwise do nothing and continue
-}
-
-#define MK_PRINT(c) (((c < 32) || (c > 126)) ? '_' : c)
-
-int main(int argc, char **argv) {
- uart_init(UART_BAUD_RATE);
-
- // Stupid flash testing
- uint32_t num_words;
- uint32_t i;
- uint32_t prog_array[DATA_MAX];
- uint32_t rd_array[DATA_MAX];
- uint32_t max_size;
- uint32_t start_addr;
- uint32_t data_pat;
- uint32_t flash_bank1_loc = FLASH_MEM_BASE_ADDR + FLASH_BANK_SZ;
-
- // wait for flash to finish "initializing"
- wait_flash_init();
-
- // enable all access
- flash_default_region(1, 1, 1);
-
- // program flash
- num_words = 32;
- for (i = 0; i < num_words; i++) {
- prog_array[i] = i;
- }
-
- break_on_error(prog_flash(flash_bank1_loc, num_words, prog_array));
-
- // read flash back
- num_words = 32;
- break_on_error(read_flash(flash_bank1_loc, num_words, rd_array));
-
- for (i = 0; i < num_words; i++) {
- if (prog_array[i] != rd_array[i]) {
- REG32(FLASH_CTRL_SCRATCH(0)) = rd_array[i];
- break_on_error(1);
- }
- }
-
- // erase flash and verify
- // The controller auto rounds down to the closest page for page erase
- break_on_error(page_erase(flash_bank1_loc + FLASH_PAGE_SZ - 1));
-
- REG32(FLASH_CTRL_SCRATCH(0)) = 0xFACEDEAD;
-
- // read flash back via host to ensure everything is cleared
- for (i = 0; i < 256; i++) {
- if (REG32(flash_bank1_loc + i * 4) != 0xFFFFFFFF) {
- REG32(FLASH_CTRL_SCRATCH(0)) = 0xDEADBEEF;
- break_on_error(1);
- }
- }
-
- // do 4K programming
- // employ the live programming method where overall payload >> flash fifo size
- max_size = 1024;
- start_addr = flash_bank1_loc + 0x8c68;
- setup_flash_prog(start_addr, max_size);
-
- for (i = 0; i < max_size; i++) {
- data_pat = (i % 2) ? 0xA5A5A5A5 : 0x5A5A5A5A;
- REG32(FLASH_CTRL_PROG_FIFO(0)) = data_pat + i;
- }
-
- // wait for operation finish
- wait_done_and_ack();
-
- // read back 4K programming
- for (i = 0; i < max_size; i++) {
- data_pat = (i % 2) ? 0xA5A5A5A5 : 0x5A5A5A5A;
-
- if (REG32(start_addr + i * 4) != data_pat + i) {
- REG32(FLASH_CTRL_SCRATCH(0)) = i << 16 | 0xDEAD;
- break_on_error(1);
- }
- }
-
- /////////////////////////////////////////////////////////////
- // Begin flash memory protection testing
- /////////////////////////////////////////////////////////////
-
- // turn off default region all access
- flash_default_region(0, 0, 0);
-
- uint32_t region_base_page = FLASH_PAGES_PER_BANK;
- uint32_t region_size = 1;
- uint32_t good_addr_start =
- FLASH_MEM_BASE_ADDR + region_base_page * FLASH_PAGE_SZ;
- uint32_t good_addr_end = good_addr_start + region_size * FLASH_PAGE_SZ - 1;
- uint32_t bad_addr_start =
- good_addr_end + 1; // this is always aligned to a page
- uint32_t good_words = 3;
- uint32_t bad_words = 3;
- uint32_t chk_addr = bad_addr_start - (FLASH_WORD_SZ * good_words);
-
- mp_region_t region0 = {
- 0, // region 0
- region_base_page, // page 1 of bank1
- region_size, // size of 1 page
- 1, // allow read
- 1, // allow program
- 1 // allow erase
- };
- flash_cfg_region(region0);
-
- for (uint32_t i = 0; i < good_words + bad_words; i++) {
- prog_array[i] = 0xA5A5A5A5 + i;
- }
-
- break_on_error(!prog_flash(chk_addr, good_words + bad_words, prog_array));
-
- // the good words should match
- for (uint32_t i = 0; i < good_words; i++) {
- if (REG32(chk_addr + i * 4) != prog_array[i]) {
- break_on_error(1);
- }
- }
-
- // the bad word contents should not have gone through
- for (uint32_t i = good_words; i < good_words + bad_words; i++) {
- if (REG32(chk_addr + i * 4) != 0xFFFFFFFF) {
- break_on_error(1);
- }
- }
-
- // attempt to erase bad page, should error
- break_on_error(!page_erase(bad_addr_start));
-
- // erase the good page
- break_on_error(page_erase(good_addr_start));
-
- // double check erase results
- for (uint32_t i = 0; i < FLASH_WORDS_PER_PAGE; i++) {
- if (REG32(good_addr_start + i * 4) != 0xFFFFFFFF) {
- break_on_error(1);
- }
- }
-
- // cleanly terminiate execution
- uart_send_str("Test Passed!\r\n");
- __asm__ volatile("wfi;");
-}