blob: 6624509825ba9cca1edc439b80bc13396c45967a [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 "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(FLASH_CTRL0_BASE_ADDR)) = 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;");
}