/**************************************************************************** Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED. This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT be copied by any method or incorporated into another program without the express written consent of Aerospace C.Power. This Information or any portion thereof remains the property of Aerospace C.Power. The Information contained herein is believed to be accurate and Aerospace C.Power assumes no responsibility or liability for its use in any way and conveys no license or title under any patent or copyright and makes no representation or warranty that this Information is free from patent or copyright infringement. ****************************************************************************/ #include "hw_reg_api.h" #include "chip_reg_base.h" #include "apb_cache_reg.h" #include "ahb.h" #include "smc.h" #include "sram.h" #include "sfc.h" #include "sfc_rf.h" #include "flash.h" #include "gp_timer.h" #include "uart.h" #include "iot_system.h" #include "dbg_io.h" #include "iot_clock.h" #include "iot_io_api.h" #include "iot_errno_api.h" #include "dtest_printf.h" #define DTEST_CACHE_RAM_CASE_RAM_FULL_RW (1 << 0) #define DTEST_CACHE_RAM_CASE_CACHE_READ_EM (1 << 1) #define DTEST_CACHE_RAM_CASE_CACHE_AS_RAM (1 << 2) #define DTEST_CACHE_RAM_CASE_CACHE_DIRTY_MISS (1 << 3) #define DTEST_CACHE_RAM_CASE_CACHE_LINE (1 << 4) #define DTEST_CACHE_RAM_CASE_CACHE_ADDR_PROTECT (1 << 5) #define DTEST_CACHE_RAM_CASE_SFC_SMC_IO_SHARE (1 << 6) #define DTEST_CACHE_RAM_CASE_SFC_SMC_IO_REMAP (1 << 7) #define DTEST_CACHE_RAM_RAM_SIZE 0x70000 //448kb #define TEST_MEMORY_LENGTH(m) (m##_ENDADDR - m##_BASEADDR + 1) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) typedef struct test_mem_block { uint32_t start; uint32_t length; } test_mem_block_t; test_mem_block_t test_mem_table[] = { {ICACHE0_SMC_RAM_BASEADDR, TEST_MEMORY_LENGTH(ICACHE0_SMC_RAM)}, {ICACHE1_SMC_RAM_BASEADDR, TEST_MEMORY_LENGTH(ICACHE1_SMC_RAM)}, {ICACHE2_SMC_RAM_BASEADDR, TEST_MEMORY_LENGTH(ICACHE2_SMC_RAM)}, {DCACHE0_SMC_RAM_BASEADDR, TEST_MEMORY_LENGTH(DCACHE0_SMC_RAM)}, {DCACHE1_SMC_RAM_BASEADDR, TEST_MEMORY_LENGTH(DCACHE1_SMC_RAM)}, }; /** * @brief: Test the data bus wiring in a memory region by * performing a walking 1's test at a fixed address * within that region. The address (and hence the * memory region) is selected by the caller. * * @return: 0 if the test succeeds. * A non-zero result is the first pattern that failed. */ static bool_t test_mem_data_bus(uint32_t addr) { /* Perform a walking 1's test at the given address */ for (uint8_t pattern = 1; pattern != 0; pattern <<= 1) { /* Write the test pattern */ *(volatile uint8_t *)addr = pattern; /* Read it back (immediately is okay for this test) */ if (*(volatile uint8_t *)addr != pattern) { dprintf("[MEM_CHECK] data bus check fail, addr:0x%08x\n", addr); return false; } } return true; } /** * @brief: Test the address bus wiring in a memory region by * performing a walking 1's test on the relevant bits * of the address and checking for aliasing. This test * will find single-bit address failures such as stuck * -high, stuck-low, and shorted pins. The base address * and size of the region are selected by the caller. * * Notes: For best results, the selected base address should * have enough LSB 0's to guarantee single address bit * changes. For example, to test a 64-Kbyte region, * select a base address on a 64-Kbyte boundary. Also, * select the region size as a power-of-two--if at all * possible. * * Returns: NULL if the test succeeds. * A non-zero result is the first address at which an * aliasing problem was uncovered. By examining the * contents of memory, it may be possible to gather * additional information about the problem. */ static bool_t test_mem_addr_bus(uint32_t addr, uint32_t length) { uint32_t addr_mask = (length / sizeof(uint32_t) - 1); uint32_t test_offset; uint32_t pattern = 0xAA; uint32_t antipattern = 0x55; /* Write the default pattern at each of the power-of-two offsets */ for (uint32_t offset = 1; (offset & addr_mask) != 0; offset <<= 1) { *(volatile uint8_t *)(addr + offset) = pattern; } /* Check for address bits stuck high. */ test_offset = 0; *(volatile uint8_t *)(addr + test_offset) = antipattern; for (uint32_t offset = 1; (offset & addr_mask) != 0; offset <<= 1) { if (*(volatile uint8_t *)(addr + offset) != pattern) { dprintf("[MEM_CHECK] addr bus check fail, addr:0x%08x\n", addr + offset); return false; } } *(volatile uint8_t *)(addr + test_offset) = pattern; /* Check for address bits stuck low or shorted */ for (test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1) { *(volatile uint8_t *)(addr + test_offset) = antipattern; if (*(volatile uint8_t *)addr != pattern) { dprintf("[MEM_CHECK] addr bus check fail, addr:0x%08x\n", addr + test_offset); return false; } for (uint32_t offset = 1; (offset & addr_mask) != 0; offset <<= 1) { if ((*(volatile uint8_t *)(addr + offset) != pattern) && (offset != test_offset)) { dprintf("[MEM_CHECK] addr bus check fail, addr:0x%08x\n", addr + test_offset); return false; } } *(volatile uint8_t *)(addr + test_offset) = pattern; } return true; } /** * @brief: Test the integrity of a physical memory device by * performing an increment/decrement test over the * entire region. In the process every storage bit * in the device is tested as a zero and a one. The * base address and the size of the region are * selected by the caller. * * Returns: NULL if the test succeeds. Also, in that case, the * entire memory region will be filled with zeros. * * A non-zero result is the first address at which an * incorrect value was read back. By examining the * contents of memory, it may be possible to gather * additional information about the problem. */ static bool_t test_mem_device(uint32_t addr, uint32_t length) { uint32_t offset; uint32_t word_len = length / sizeof(uint32_t); uint32_t pattern; uint32_t antipattern; uint32_t *base_addr = (uint32_t *)addr; /* Fill memory with a known pattern */ for (pattern = 1, offset = 0; offset < word_len; pattern++, offset++) { base_addr[offset] = pattern; } /* Check each location and invert it for the second pass */ for (pattern = 1, offset = 0; offset < word_len; pattern++, offset++) { if (base_addr[offset] != pattern) { dprintf("[MEM_CHECK] device write check fail, addr:0x%08x\n", addr + offset); dprintf("[MEM_CHECK]\t write 0x%08x, read:0x%08x\n", pattern, base_addr[offset]); return false; } antipattern = ~pattern; base_addr[offset] = antipattern; } /* Check each location for the inverted pattern and zero it. */ for (pattern = 1, offset = 0; offset < word_len; pattern++, offset++) { antipattern = ~pattern; if (base_addr[offset] != antipattern) { dprintf("[MEM_CHECK] device invert check fail, addr:0x%08x\n", addr + offset); return false; } } return true; } static uint32_t dtest_cache_ram_cache_line_test() { uint32_t i; dcase_start("cache data bus line and addr bus line\n"); for (i = 0; i < ARRAY_SIZE(test_mem_table); i++) { if (!test_mem_data_bus(test_mem_table[i].start) || !test_mem_addr_bus(test_mem_table[i].start, test_mem_table[i].length) || !test_mem_device(test_mem_table[i].start, test_mem_table[i].length)) { goto fail; } } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; } /* there is a space reserved for ROM at the beginning of ram, * and ram is divided into flash, IRAM and DRAM in link script */ static uint32_t dtest_cache_ram_ram_full_rw_test() { uint32_t i, j; uint32_t len[3], addr[3]; uint8_t data_test = 0x55, data; dcase_start("ram full read/write test(space outside code)\n"); addr[0] = AHB_RAM0_BASEADDR; len[0] = (uint32_t)&_start - AHB_RAM0_BASEADDR; addr[1] = (uint32_t)&_flash_end; len[1] = (uint32_t)&_data - (uint32_t)&_flash_end; addr[2] = (uint32_t)&_iram_end; len[2] = DTEST_CACHE_RAM_RAM_SIZE - ((uint32_t)&_iram_end - AHB_RAM0_BASEADDR); dprintf("read/write space range:\n"); for (i = 0; i < 3; i++) { iot_printf("\tindex:%d, addr:0x%08x, len:0x%x\n", i, addr[i], len[i]); } for (i = 0; i < 3; i++) { for (j = addr[i]; j < len[i]; j++) { *(uint8_t *)j = data_test; } for (j = addr[i]; j < len[i]; j++) { data = *(uint8_t *)j; if (data != data_test) { dprintf("ram read data error, addr:0x%08x, rdata:0x%x, wdata:0x%x\n", j, data, data_test); goto fail; } } } data_test = 0xaa; for (i = 0; i < 3; i++) { for (j = addr[i]; j < len[i]; j++) { *(uint8_t *)j = data_test; } for (j = addr[i]; j < len[i]; j++) { data = *(uint8_t *)j; if (data != data_test) { dprintf("ram read data error, addr:0x%08x, rdata:0x%x, wdata:0x%x\n", j, data, data_test); goto fail; } } } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; } static uint32_t dtest_cache_ram_cache_rw_ext_mem_test() { uint8_t i; uint32_t addr_test = 0x00; uint32_t data_test = 0x11223344, data; dcase_start("cache read/write extern memory(flash/psram) test\n"); dprintf("write extern memory, test addr:0x%08x, test data:0x%08x\n", addr_test, data_test); *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test) = data_test; cache_flush(0, addr_test + ICACHE0_SMC_RAM_BASEADDR, 32); g_sfc_ctrl->erase_sector(addr_test, MOD_SW_MODE_DIS); g_sfc_ctrl->write((uint8_t*)&data_test, addr_test, sizeof(data_test), MOD_SFC_PROG_STAND, MOD_SW_MODE_DIS); dprintf("read extern memory\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { // cache_clear(i); data = *(uint32_t *)(test_mem_table[i].start + addr_test); if (data != data_test) { dprintf("cache read psram error, rdata:0x%08x, wdata:0x%08x\n", data, data_test); goto fail; } } for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { // cache_clear(i); data = *(uint32_t *)(test_mem_table[i].start + 0x4000000 + addr_test); if (data != data_test) { dprintf("cache read flash error, rdata:0x%08x, wdata:0x%08x\n", data, data_test); goto fail; } } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; } static uint32_t dtest_cache_ram_cache_as_ram_test() { uint32_t i, err_cnt = 0; uint32_t offset_test = 0x00; uint32_t data_test = 0x11223344, data; dcase_start("cache is configured for internal ram use\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { cache_disable(i); cache_enable(i); cache_set_buffer_mode(i, AHB_CACHE_MODE_BUFFER); cache_space_dis(i); iot_delay_us(10); *(uint32_t *)(test_mem_table[i].start + offset_test) = data_test; data = *(uint32_t *)(test_mem_table[i].start + offset_test); if (data != data_test) { dprintf("cache as ram failed, test offset:0x%08x, rdata:0x%08x, wdata:0x%08x\n", offset_test, data, data_test); err_cnt++; } } dprintf("restore cache as cache function\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { cache_disable(i); cache_enable(i); cache_set_buffer_mode(i, AHB_CACHE_MODE_CACHE); cache_space_ena(i); } iot_delay_us(10); data_test = 0xaabbccdd; offset_test = 0x100000; *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + offset_test) = data_test; cache_flush(0, offset_test, 32); cache_clear(0); data = *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + offset_test); if (data != data_test) { dprintf("restore cache failed, test offset:0x%08x, rdata:0x%08x, wdata:0x%08x\n", offset_test, data, data_test); err_cnt++; } if (err_cnt) { dcase_failed(); return ERR_FAIL; } else { dcase_success(); return ERR_OK; } } static uint32_t dtest_cache_ram_cache_dirty_miss_test() { uint32_t addr_test = 0x100000; volatile uint32_t data_test = 0x12345678, data_old1, data_old2, data_new; dcase_start("cache(sfc) dirty(clear/invalidata) and miss test\n"); dprintf("cache invalidata operation test\n"); g_sfc_ctrl->erase_sector(addr_test, MOD_SW_MODE_DIS); cache_clear(0); data_old1 = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); g_sfc_ctrl->write((uint8_t*)&data_test, addr_test, sizeof(data_test), MOD_SFC_PROG_STAND, MOD_SW_MODE_DIS); data_old2 = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); cache_invalidate(0, addr_test + ICACHE0_SFC_RAM_BASEADDR, 32); data_new = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); dprintf("invalidata test addr:0x%08x, test data:0x%08x, data_old1:0x%08x, " "data_old2:0x%08x, data_new:0x%08x\n", addr_test, data_test, data_old1, data_old2, data_new); if (data_old1 != 0xffffffff) { dprintf("sfc erase error\n"); goto fail; } if (data_old1 != data_old2) { dprintf("cache dirty data error\n"); goto fail; } if (data_new != data_test) { dprintf("cache incalidata operation failed\n"); goto fail; } dprintf("cache clear operation test\n"); g_sfc_ctrl->erase_sector(addr_test, MOD_SW_MODE_DIS); cache_clear(0); data_old1 = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); g_sfc_ctrl->write((uint8_t*)&data_test, addr_test, sizeof(data_test), MOD_SFC_PROG_STAND, MOD_SW_MODE_DIS); data_old2 = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); cache_clear(0); data_new = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); dprintf("clear test addr:0x%08x, test data:0x%08x, data_old1:0x%08x, " "data_old2:0x%08x, data_new:0x%08x\n", addr_test, data_test, data_old1, data_old2, data_new); if (data_old1 != 0xffffffff) { dprintf("sfc erase error\n"); goto fail; } if (data_old1 != data_old2) { dprintf("cache dirty data error\n"); goto fail; } if (data_new != data_test) { dprintf("cache clear operation failed\n"); goto fail; } dprintf("cache miss and auto load test\n"); g_sfc_ctrl->erase_sector(addr_test, MOD_SW_MODE_DIS); cache_clear(0); data_old1 = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); g_sfc_ctrl->write((uint8_t*)&data_test, addr_test, sizeof(data_test), MOD_SFC_PROG_STAND, MOD_SW_MODE_DIS); /* icache0 has 32KB space, continuous reading of 32KB data will fill the cache */ for (uint32_t i = 1; i <= 0x8000; i++) { data_old2 = *(uint8_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test + i); } data_new = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); dprintf("auto load test addr:0x%08x, test data:0x%08x, data_old1:0x%08x, " "data_old2:0x%08x, data_new:0x%08x\n", addr_test, data_test, data_old1, data_old2, data_new); if (data_old1 != 0xffffffff) { dprintf("sfc erase error\n"); goto fail; } if (data_new != data_test) { dprintf("cache miss and auto load operation failed\n"); goto fail; } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; } //extern void cache_monitor_enable(uint8_t cache_id, uint8_t en); //extern void cache_monitor_clear(uint8_t cache_id); //extern uint8_t cache_monitor_state(uint8_t cache_id); //extern void cache_monitor_set_acc_addr(uint8_t cache_id, // uint32_t addr_s, uint32_t addr_e); //extern void cache_monitor_set_mon_addr(uint8_t cache_id, // uint32_t addr_s, uint32_t addr_e); static uint32_t dtest_cache_ram_cache_addr_protect_test() { #if 0 uint8_t i, state; uint32_t addr_acc_s = 0x00000000, addr_acc_e = 0x00008000; uint32_t addr_mon_s = 0x00001000, addr_mon_e = 0x00002000; dcase_start("cache address protection\n"); dprintf("enable all cache addr monitor function\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { cache_monitor_enable(i, 1); cache_monitor_clear(i); cache_monitor_set_acc_addr(i, addr_acc_s, addr_acc_e); cache_monitor_set_mon_addr(i, addr_mon_s, addr_mon_e); } dprintf("using cache to access protected addresses\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { *(uint32_t *)(test_mem_table[i].start + addr_mon_s + 32) = 0x11223344; cache_flush(i, addr_mon_s + 32, 32); } dprintf("check cross-border access status\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { state = cache_monitor_state(i); if (!state) { dprintf("cache:%d address out of bounds access not detected\n", i); goto fail; } } dprintf("disable all cache addr monitor function\n"); for (i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { cache_monitor_clear(i); cache_monitor_enable(i, 0); } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; #endif return ERR_OK; } static uint32_t dtest_cache_ram_sfc_smc_io_share_test() { uint32_t temp; uint32_t addr_test = 0x100000; uint32_t data, data_flash = 0x11223344, data_psram = 0xaabbccdd; dcase_start("sfc and smc io share\n"); dprintf("init flash/psram data\n"); g_sfc_ctrl->erase_sector(addr_test, MOD_SW_MODE_DIS); g_sfc_ctrl->write((uint8_t*)&data_flash, addr_test, sizeof(data_flash), MOD_SFC_PROG_STAND, MOD_SW_MODE_DIS); cache_clear(0); data = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); if (data != data_flash) { dprintf("flash write/read failed\n"); goto fail; } *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test) = data_psram; cache_flush(0, addr_test, 32); cache_clear(0); data = *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test); if (data != data_psram) { dprintf("psram write/read failed\n"); goto fail; } dprintf("enable io share, need init flash again\n"); temp = SFC_READ_REG(CFG_SFC_DBG_ADDR); REG_FIELD_SET(SFC_SMC_IO_SHARE, temp, 1); REG_FIELD_SET(SFC_SMC_ARB_SHARE, temp, 1); SFC_WRITE_REG(CFG_SFC_DBG_ADDR, temp); iot_delay_us(1000); uint8_t id[8] = {0}; sfc_qspi_get_id_mult(id, MOD_SFC_SERIAL); dprintf("Manufacturer ID(serial):0x%x, device id:0x%x\n",id[0], id[1]); flash_init(1); sfc_set_endian_mode(1); dprintf("read flash/psram data again\n"); cache_clear(0); data = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); if (data != data_flash) { dprintf("flash data read failed\n"); goto fail; } cache_clear(0); data = *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test); if (data != data_psram) { dprintf("psram data read failed\n"); goto fail; } dprintf("disable io share\n"); temp = SFC_READ_REG(CFG_SFC_DBG_ADDR); REG_FIELD_SET(SFC_SMC_IO_SHARE, temp, 0); REG_FIELD_SET(SFC_SMC_ARB_SHARE, temp, 0); SFC_WRITE_REG(CFG_SFC_DBG_ADDR, temp); iot_delay_us(1000); dprintf("read flash/psram data again\n"); cache_clear(0); data = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); if (data != data_flash) { dprintf("flash data read failed\n"); goto fail; } cache_clear(0); data = *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test); if (data != data_psram) { dprintf("psram data read failed\n"); goto fail; } dcase_success(); return ERR_OK; fail: dcase_failed(); return ERR_FAIL; } static uint32_t dtest_cache_ram_sfc_smc_io_remap_test() { volatile uint32_t data; /* QPI mode requires 4 address lines. Staggering 1 helps to observe the timing */ uint32_t addr_test = 0x8421; uint32_t cnt = 1000; (void)data; /* It is necessary to manually change the register * and check whether the timing is correct on the logic analyzer */ while (cnt--) { cache_clear(0); data = *(uint32_t *)(ICACHE0_SMC_RAM_BASEADDR + addr_test); dprintf("get psram data:0x%08x\n"); cache_clear(0); data = *(uint32_t *)(ICACHE0_SFC_RAM_BASEADDR + addr_test); dprintf("get flash data:0x%08x\n"); } return ERR_OK; } static void dtest_cache_ram_main() { uint32_t case_group = 0, error_cnt = 0; dbg_uart_init(); dconfig(); dstart(); dversion(); if (dtest_get_case_group(&case_group) < 0) { case_group = 0xffffffff; } dprintf("get case group:0x%08X\n", case_group); dprintf("init gptimer0 for delay\n"); gp_timer_init(); gp_timer_set(0, 0xffffffff, 0); gp_timer_start(0); dprintf("reset emc\n"); ahb_emc_disable(); ahb_emc_enable(); ahb_emc_reset(); dprintf("initialize psram\n"); sram_qspi_init(); sram_qspi_enter(); hal_smc_qspi_quad_cfg(0); dprintf("init sfc and flash\n"); flash_init(1); sfc_set_endian_mode(1); dprintf("initialize all of the cache\n"); for (uint8_t i = AHB_CACHE_I0; i < AHB_CACHE_MAX; i++) { cache_disable(i); cache_enable(i); cache_fill_valid_space(i); } if (case_group & DTEST_CACHE_RAM_CASE_RAM_FULL_RW) { if (dtest_cache_ram_ram_full_rw_test()) { error_cnt++; } } if (case_group & DTEST_CACHE_RAM_CASE_CACHE_READ_EM) { if (dtest_cache_ram_cache_rw_ext_mem_test()) { error_cnt++; } } if (case_group & DTEST_CACHE_RAM_CASE_CACHE_AS_RAM) { if (dtest_cache_ram_cache_as_ram_test()) { error_cnt++; } } if (case_group & DTEST_CACHE_RAM_CASE_CACHE_DIRTY_MISS) { if (dtest_cache_ram_cache_dirty_miss_test()) { error_cnt++; } } if (case_group & DTEST_CACHE_RAM_CASE_CACHE_LINE) { if (dtest_cache_ram_cache_line_test()) { error_cnt++; } } /* kl3 hardware is not support cache address protect function */ if (0) { if (dtest_cache_ram_cache_addr_protect_test()) { error_cnt++; } } /* KL3 does not adopt IO share scheme, and image does not support IO share */ if (0) { if (dtest_cache_ram_sfc_smc_io_share_test()) { error_cnt++; } } /* Manual test */ if (0) { if (dtest_cache_ram_sfc_smc_io_remap_test()) { error_cnt++; } } if (error_cnt) { dprintf("cache_ram dtest failed\n"); } else { dprintf("cache_ram dtest succeed\n"); } dend(); } int main(void) { dtest_cache_ram_main(); return 0; }