blob: 391fd8a2204fb02678f783b45bb681c56ebc6e75 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/lib/common.h"
#include "sw/lib/flash_ctrl.h"
#include "sw/lib/gpio.h"
#include "sw/lib/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, iteration;
uint32_t prog_array[FLASH_WORDS_PER_PAGE];
uint32_t rd_array[FLASH_WORDS_PER_PAGE];
uint32_t test_addr;
uint32_t bank1_addr = FLASH_MEM_BASE_ADDR + FLASH_BANK_SZ;
uint32_t bank0_last_page =
FLASH_MEM_BASE_ADDR + (FLASH_PAGES_PER_BANK - 1) * FLASH_PAGE_SZ;
uart_init(UART_BAUD_RATE);
flash_init_block();
// enable all access
flash_cfg_bank_erase(FLASH_BANK_0, /*erase_en=*/true);
flash_cfg_bank_erase(FLASH_BANK_1, /*erase_en=*/true);
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 % 2) ? 0xA5A5A5A5 : 0x5A5A5A5A;
}
// initialize test regions
break_on_error(flash_page_erase(bank1_addr));
break_on_error(flash_page_erase(bank0_last_page));
for (iteration = 0; iteration < 2; iteration++) {
test_addr = iteration ? bank1_addr : bank0_last_page;
break_on_error(flash_write(test_addr, prog_array, ARRAYSIZE(prog_array)));
break_on_error(flash_read(test_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 = {.num = 0,
.base = region_base_page,
.size = region_size,
.rd_en = 1,
.prog_en = 1,
.erase_en = 1};
// 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(&region0);
// 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);
}
}
flash_cfg_bank_erase(FLASH_BANK_0, /*erase_en=*/false);
flash_cfg_bank_erase(FLASH_BANK_1, /*erase_en=*/false);
// cleanly terminate execution
uart_send_str("PASS!\r\n");
__asm__ volatile("wfi;");
}