diff options
Diffstat (limited to 'avr/cmd_mem.c')
-rw-r--r-- | avr/cmd_mem.c | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/avr/cmd_mem.c b/avr/cmd_mem.c new file mode 100644 index 0000000..500b973 --- /dev/null +++ b/avr/cmd_mem.c @@ -0,0 +1,868 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Memory Functions + * + * Copied from FADS ROM, Dan Malek (dmalek@jlc.net) + */ + +#include "common.h" +#include <stdlib.h> +#include <ctype.h> + +#include "command.h" +#include "cli_readline.h" +#include "print-utils.h" +#include "con-utils.h" +#include "z80-if.h" +//#include "debug.h" + + +#ifndef CONFIG_SYS_MEMTEST_SCRATCH +#define CONFIG_SYS_MEMTEST_SCRATCH 0 +#endif + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static uint32_t dp_last_addr; +static uint32_t dp_last_length = 0x100; +static uint32_t mm_last_addr; + +static uint32_t base_address = 0; + +/*--------------------------------------------------------------------------*/ + +int z180_dump_mem(uint32_t startaddr, uint32_t len, const char *title) +{ + uint8_t buf[16]; + uint8_t llen = 16; + uint8_t pre = startaddr % 16; + uint32_t addr = startaddr & ~0x0f; + len += pre; + uint8_t i; + + if (title && *title) + printf_P(PSTR("%s\n"),title); + + while (len) { + if (len < 16) + llen = len; + + z80_bus_cmd(Request); + for (i = pre; i < llen; i++) + buf[i] = z80_read(addr + i); + z80_bus_cmd(Release); + + printf_P(PSTR("%.5lx:"), addr); +#if 0 + print_blanks(3 * pre); + + /* Print hex values */ + for (i = pre; i < llen; i++) + printf_P(PSTR(" %.2x"), buf[i]); +#else + for (i = 0; i < llen; i++) { + if ((i % 8) == 0) + putchar(' '); + if (i < pre) + printf_P(PSTR(".. ")); + else + printf_P(PSTR("%.2x "), buf[i]); + } +#endif + /* fill line with whitespace for nice ASCII print */ +#if 1 + print_blanks(3 * (16u - i) + (16u-i)/8 + 1 + pre); +#else + +#endif + /* Print data in ASCII characters */ + for (i = pre; i < llen; i++) + printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.'); + putchar('\n'); + + pre = 0; + addr += 16; + len -= llen; + + if (ctrlc()) + return -1; + } + return 0; +} + + +/*--------------------------------------------------------------------------*/ + +/* Memory Display + * + * Syntax: + * md {addr} {len} + */ +command_ret_t do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint32_t addr, length; + + (void) cmdtp; + +#if 0 + printf_P(PSTR("flag: %d, argc: %d"), flag, argc); + for (int i = 0; i < argc; i++) { + printf_P(PSTR(", argv[%d]: %s"), i, argv[i] ? argv[i] : "<NULL>"); + } + putchar('\n'); +#endif + + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = dp_last_addr; + length = dp_last_length; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* Address is specified since argc > 1 */ + addr = strtoul(argv[1], NULL, 16); + addr += base_address; + + /* If another parameter, it is the length to display. */ + if (argc > 2) + length = strtoul(argv[2], NULL, 16); + } + + /* Print the lines. */ + z180_dump_mem(addr, length, NULL); + + dp_last_addr = addr + length; + dp_last_length = length; + return CMD_RET_SUCCESS; +} + +/* Modify memory. + * + * Syntax: + * mm {addr} + * nm {addr} + */ +static command_ret_t +mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) +{ + uint32_t addr; + uint8_t data; + int nbytes; + + (void) cmdtp; + + if (argc != 2) + return CMD_RET_USAGE; + + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = mm_last_addr; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command specified. + */ + + /* Address is specified since argc > 1 + */ + addr = strtoul(argv[1], NULL, 16); + addr += base_address; + } + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + z80_bus_cmd(Request); + data = z80_read(addr); + printf_P(PSTR("%05lx: %02x"), addr, data); + z80_bus_cmd(Release); + + nbytes = cli_readline(PSTR(" ? ")); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -1 : 1; + nbytes = 1; + } + else { + char *endp; + data = strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { + z80_bus_cmd(Request); + z80_write(addr, data); + z80_bus_cmd(Release); + if (incrflag) + addr++; + } + } + } while (nbytes); + + mm_last_addr = addr; + return CMD_RET_SUCCESS; +} + + +command_ret_t do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 1, flag, argc, argv); +} +command_ret_t do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return mod_mem (cmdtp, 0, flag, argc, argv); +} + +command_ret_t do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint8_t writeval; + uint32_t addr, count; + + (void) cmdtp; + (void) flag; + + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + /* Address is specified since argc > 1 + */ + addr = strtoul(argv[1], NULL, 16); + addr += base_address; + + /* Get the value to write. + */ + writeval = (uint8_t) strtoul(argv[2], NULL, 16); + + /* Count ? */ + if (argc == 4) { + count = strtoul(argv[3], NULL, 16); + } else { + count = 1; + } + + z80_bus_cmd(Request); + while (count-- > 0) { + z80_write(addr, writeval); + ++addr; + } + z80_bus_cmd(Release); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_MX_CYCLIC +command_ret_t do_mem_mdc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + uint32_t count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_md (NULL, 0, 3, argv); + + /* delay for <count> ms... */ +/* TODO: use timer */ + for (i=0; i<count; i++) + udelay (1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + my_puts_P(PSTR("Abort\n")); + return CMD_RET_SUCCESS; + } + } + + return CMD_RET_SUCCESS; +} + +command_ret_t do_mem_mwc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + uint32_t count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_mw (NULL, 0, 3, argv); + + /* delay for <count> ms... */ +/* TODO: use timer */ + for (i=0; i<count; i++) + udelay (1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + my_puts_P(PSTR("Abort\n")); + return CMD_RET_SUCCESS; + } + } + + return CMD_RET_SUCCESS; +} +#endif /* CONFIG_MX_CYCLIC */ + +command_ret_t do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint32_t addr1, addr2, count, ngood; + command_ret_t rcode = CMD_RET_SUCCESS; + uint8_t byte1, byte2; + + (void) cmdtp; + (void) flag; + + if (argc != 4) + return CMD_RET_USAGE; + + + addr1 = strtoul(argv[1], NULL, 16); + addr1 += base_address; + addr2 = strtoul(argv[2], NULL, 16); + addr2 += base_address; + count = strtoul(argv[3], NULL, 16); + + for (ngood = 0; ngood < count; ++ngood) { + z80_bus_cmd(Request); + byte1 = z80_read(addr1); + byte2 = z80_read(addr2); + z80_bus_cmd(Release); + if (byte1 != byte2) { + printf_P(PSTR("byte at 0x%05lx (%#02x) != " + "byte at 0x%05lx (%#02x)\n"), + addr1, byte1, addr2, byte2); + rcode = CMD_RET_FAILURE; + break; + } + addr1++; + addr2++; + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + my_puts_P(PSTR("Abort\n")); + return CMD_RET_SUCCESS; + } + } + + printf_P(PSTR("Total of %ld byte(s) (0x%lx) were the same\n"), ngood, ngood); + return rcode; +} + +command_ret_t do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint32_t src, dest, count; + int_fast8_t step; + + (void) cmdtp; + (void) flag; + + if (argc != 4) + return CMD_RET_USAGE; + + src = strtoul(argv[1], NULL, 16); + src += base_address; + dest = strtoul(argv[2], NULL, 16); + dest += base_address; + count = strtoul(argv[3], NULL, 16); + + if (count == 0) { + my_puts_P(PSTR("Zero length?\n")); + return CMD_RET_FAILURE; + } + + if (dest > src) { + src += count - 1; + dest += count - 1; + step = -1; + } else + step = 1; + + while (count-- > 0) { + uint8_t data; + z80_bus_cmd(Request); + data = z80_read(src); + z80_write(dest, data); + z80_bus_cmd(Release); + src += step; + dest += step; + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + my_puts_P(PSTR("Abort\n")); + return CMD_RET_SUCCESS; + } + } + return CMD_RET_SUCCESS; +} + +command_ret_t do_mem_base(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + (void) cmdtp; + (void) flag; + + if (argc > 1) { + /* Set new base address. */ + base_address = strtoul(argv[1], NULL, 16); + } + /* Print the current base address. */ + printf_P(PSTR("Base Address: 0x%05lx\n"), base_address); + return CMD_RET_SUCCESS; +} + +command_ret_t do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + uint32_t addr, length; + + (void) cmdtp; + (void) flag; + + if (argc < 3) + return CMD_RET_USAGE; + + /* Address is always specified. */ + addr = strtoul(argv[1], NULL, 16); + + /* Length is the number of bytes. */ + length = strtoul(argv[2], NULL, 16); + + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { + z80_bus_cmd(Request); + for (;;) + z80_read(addr); + z80_bus_cmd(Release); + } + + z80_bus_cmd(Request); + for (;;) { + uint32_t i = length; + uint32_t p = addr; + while (i-- > 0) + z80_read(p++); + } + z80_bus_cmd(Release); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_LOOPW +command_ret_t do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint32_t addr, length; + uint8_t data; + + (void) cmdtp; + (void) flag; + + if (argc < 4) + return CMD_RET_USAGE; + + /* Address is always specified. */ + addr = strtoul(argv[1], NULL, 16); + + /* Length is the number of bytes. */ + length = strtoul(argv[2], NULL, 16); + + data = strtoul(argv[3], NULL, 16); + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { + z80_bus_cmd(Request); + for (;;) + z80_write(addr, data); + } + + for (;;) { + uint32_t i = length; + uint32_t p = addr; + while (i-- > 0) + z80_write(p++, data); + } +} +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +static uint32_t mem_test_alt(vu_long *buf, uint32_t start_addr, uint32_t end_addr, + vu_long *dummy) +{ + vu_long *addr; + uint32_t errs = 0; + uint32_t val, readback; + int j; + vu_long offset; + vu_long test_offset; + vu_long pattern; + vu_long temp; + vu_long anti_pattern; + vu_long num_words; + static const FLASH uint32_t bitpattern[] = { + 0x00000001, /* single bit */ + 0x00000003, /* two adjacent bits */ + 0x00000007, /* three adjacent bits */ + 0x0000000F, /* four adjacent bits */ + 0x00000005, /* two non-adjacent bits */ + 0x00000015, /* three non-adjacent bits */ + 0x00000055, /* four non-adjacent bits */ + 0xaaaaaaaa, /* alternating 1/0 */ + }; + + num_words = (end_addr - start_addr) / sizeof(vu_long); + + /* + * Data line test: write a pattern to the first + * location, write the 1's complement to a 'parking' + * address (changes the state of the data bus so a + * floating bus doesn't give a false OK), and then + * read the value back. Note that we read it back + * into a variable because the next time we read it, + * it might be right (been there, tough to explain to + * the quality guys why it prints a failure when the + * "is" and "should be" are obviously the same in the + * error message). + * + * Rather than exhaustively testing, we test some + * patterns by shifting '1' bits through a field of + * '0's and '0' bits through a field of '1's (i.e. + * pattern and ~pattern). + */ + addr = buf; + for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) { + val = bitpattern[j]; + for (; val != 0; val <<= 1) { + *addr = val; + *dummy = ~val; /* clear the test data off the bus */ + readback = *addr; + if (readback != val) { + printf_P(PSTR("FAILURE (data line): " + "expected %05lx, actual %05lx\n"), + val, readback); + errs++; + if (ctrlc()) + return -1; + } + *addr = ~val; + *dummy = val; + readback = *addr; + if (readback != ~val) { + printf_P(PSTR("FAILURE (data line): " + "Is %05lx, should be %05lx\n"), + readback, ~val); + errs++; + if (ctrlc()) + return -1; + } + } + } + + /* + * Based on code whose Original Author and Copyright + * information follows: Copyright (c) 1998 by Michael + * Barr. This software is placed into the public + * domain and may be used for any purpose. However, + * this notice must not be changed or removed and no + * warranty is either expressed or implied by its + * publication or distribution. + */ + + /* + * Address line test + + * Description: 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: 0 if the test succeeds, 1 if the test fails. + */ + pattern = (vu_long) 0xaaaaaaaa; + anti_pattern = (vu_long) 0x55555555; + + debug("%s:%d: length = 0x%.5lx\n", __func__, __LINE__, num_words); + /* + * Write the default pattern at each of the + * power-of-two offsets. + */ + for (offset = 1; offset < num_words; offset <<= 1) + addr[offset] = pattern; + + /* + * Check for address bits stuck high. + */ + test_offset = 0; + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if (temp != pattern) { + printf_P(PSTR("\nFAILURE: Address bit stuck high @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + + /* + * Check for addr bits stuck low or shorted. + */ + for (test_offset = 1; test_offset < num_words; test_offset <<= 1) { + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if ((temp != pattern) && (offset != test_offset)) { + printf_P(PSTR("\nFAILURE: Address bit stuck low or" + " shorted @ 0x%.5lx: expected 0x%.5lx," + " actual 0x%.5lx\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + } + + /* + * Description: 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: 0 if the test succeeds, 1 if the test fails. + */ + num_words++; + + /* + * Fill memory with a known pattern. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + addr[offset] = pattern; + } + + /* + * Check each location and invert it for the second pass. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + temp = addr[offset]; + if (temp != pattern) { + printf_P(PSTR("\nFAILURE (read/write) @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx)\n"), + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + + anti_pattern = ~pattern; + addr[offset] = anti_pattern; + } + + /* + * Check each location for the inverted pattern and zero it. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + anti_pattern = ~pattern; + temp = addr[offset]; + if (temp != anti_pattern) { + printf_P(PSTR("\nFAILURE (read/write): @ 0x%.5lx:" + " expected 0x%.5lx, actual 0x%.5lx)\n"), + start_addr + offset*sizeof(vu_long), + anti_pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + addr[offset] = 0; + } + + return 0; +} + +static uint32_t mem_test_quick(vu_long *buf, uint32_t start_addr, uint32_t end_addr, + vu_long pattern, int iteration) +{ + vu_long *end; + vu_long *addr; + uint32_t errs = 0; + uint32_t incr, length; + uint32_t val, readback; + + /* Alternate the pattern */ + incr = 1; + if (iteration & 1) { + incr = -incr; + /* + * Flip the pattern each time to make lots of zeros and + * then, the next time, lots of ones. We decrement + * the "negative" patterns and increment the "positive" + * patterns to preserve this feature. + */ + if (pattern & 0x80000000) + pattern = -pattern; /* complement & increment */ + else + pattern = ~pattern; + } + length = (end_addr - start_addr) / sizeof(uint32_t); + end = buf + length; + printf_P(PSTR("\rPattern %08lX Writing..." + "%12s" + "\b\b\b\b\b\b\b\b\b\b"), + pattern, ""); + + for (addr = buf, val = pattern; addr < end; addr++) { + *addr = val; + val += incr; + } + + my_puts_P(PSTR("Reading...")); + + for (addr = buf, val = pattern; addr < end; addr++) { + readback = *addr; + if (readback != val) { + uint32_t offset = addr - buf; + + printf_P(PSTR("\nMem error @ 0x%08X: " + "found %08lX, expected %08lX\n"), + (unsigned int)(uintptr_t)(start_addr + offset*sizeof(vu_long)), + readback, val); + errs++; + if (ctrlc()) + return -1; + } + val += incr; + } + + return 0; +} + +/* + * Perform a memory test. A more complete alternative test can be + * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until + * interrupted by ctrl-c or by a failure of one of the sub-tests. + */ +command_ret_t do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + uint32_t start, end; + vu_long *buf, *dummy; + int iteration_limit; +/* TODO: command_ret_t */ + int ret; + uint32_t errs = 0; /* number of errors, or -1 if interrupted */ + uint32_t pattern; + int iteration; +#if defined(CONFIG_SYS_ALT_MEMTEST) + const int alt_test = 1; +#else + const int alt_test = 0; +#endif + + if (argc > 1) + start = strtoul(argv[1], NULL, 16); + else + start = CONFIG_SYS_MEMTEST_START; + + if (argc > 2) + end = strtoul(argv[2], NULL, 16); + else + end = CONFIG_SYS_MEMTEST_END; + + if (argc > 3) + pattern = (uint32_t)strtoul(argv[3], NULL, 16); + else + pattern = 0; + + if (argc > 4) + iteration_limit = (uint32_t)strtoul(argv[4], NULL, 16); + else + iteration_limit = 0; + + printf_P(PSTR("Testing %08x ... %08x:\n"), (unsigned int)start, (unsigned int)end); + debug("%s:%d: start %#05lx end %#05lx\n", __func__, __LINE__, + start, end); + +/* TODO: */ +// buf = map_sysmem(start, end - start); +// dummy = map_sysmem(CONFIG_SYS_MEMTEST_SCRATCH, sizeof(vu_long)); + for (iteration = 0; + !iteration_limit || iteration < iteration_limit; + iteration++) { + if (ctrlc()) { + errs = -1UL; + break; + } + + printf_P(PSTR("Iteration: %6d\r"), iteration + 1); + debug("\n"); + if (alt_test) { + errs = mem_test_alt(buf, start, end, dummy); + } else { + errs = mem_test_quick(buf, start, end, pattern, + iteration); + } + if (errs == -1UL) + break; + } + + if (errs == -1UL) { + /* Memory test was aborted - write a newline to finish off */ + putc('\n'); + ret = 1; + } else { + printf_P(PSTR("Tested %d iteration(s) with %lu errors.\n"), + iteration, errs); + ret = errs != 0; + } + + return ret; /* not reached */ +} +#endif /* CONFIG_CMD_MEMTEST */ |