diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 4c14d98b6..d07cf750e 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -216,16 +216,6 @@ define_command(sdrwloff, sdrwloff, "Disable write leveling", LITEDRAM_CMDS); define_command(sdrlevel, sdrlevel, "Perform read/write leveling", LITEDRAM_CMDS); #endif -/** - * Command "memtest" - * - * Run a memory test - * - */ -#ifdef CSR_SDRAM_BASE -define_command(memtest, memtest, "Run a memory test", LITEDRAM_CMDS); -#endif - /** * Command "spdread" * diff --git a/litex/soc/software/bios/cmds/cmd_mem.c b/litex/soc/software/bios/cmds/cmd_mem.c index 01c607e1f..ad1638169 100644 --- a/litex/soc/software/bios/cmds/cmd_mem.c +++ b/litex/soc/software/bios/cmds/cmd_mem.c @@ -2,6 +2,7 @@ #include #include +#include #include @@ -137,3 +138,175 @@ static void mc(int nb_params, char **params) } define_command(mc, mc, "Copy address space", MEM_CMDS); + +/** + * Command "memtest" + * + * Run a memory test + * + */ +static void memtest_handler(int nb_params, char **params) +{ + char *c; + unsigned int *addr; + unsigned long maxsize = ~0uL; + + if (nb_params < 1) { + printf("memtest []"); + return; + } + + addr = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + + if (nb_params >= 2) { + maxsize = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect max size"); + return; + } + + } + + memtest(addr, maxsize); +} +define_command(memtest, memtest_handler, "Run a memory test", MEM_CMDS); + +/** + * Command "memspeed" + * + * Run a memory speed test + * + */ +static void memspeed_handler(int nb_params, char **params) +{ + char *c; + unsigned int *addr; + unsigned long size; + bool read_only = false; + + if (nb_params < 1) { + printf("memspeed []"); + return; + } + + addr = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + + size = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect size"); + return; + } + + if (nb_params >= 3) { + read_only = (bool) strtoul(params[2], &c, 0); + if (*c != 0) { + printf("Incorrect readonly value"); + return; + } + } + + memspeed(addr, size, read_only); +} +define_command(memspeed, memspeed_handler, "Run a memory speed test", MEM_CMDS); + +#ifdef CSR_DEBUG_PRINTER +/** + * Command "csrprint" + * + * Print CSR values + * + */ +static void csrprint(int nb_params, char **params) +{ + print_csrs(); +} +define_command(csrprint, csrprint, "Print CSR values", MEM_CMDS); +#endif + + +#ifdef CSR_WB_SOFTCONTROL_BASE +static void wbr(int nb_params, char **params) +{ + char *c; + unsigned int *addr; + unsigned int length; + unsigned int i; + + if (nb_params < 1) { + printf("mr
[length]"); + return; + } + addr = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + if (nb_params == 1) { + length = 4; + } else { + length = strtoul(params[1], &c, 0); + if(*c != 0) { + printf("\nIncorrect length"); + return; + } + } + + for (i = 0; i < length; ++i) { + wb_softcontrol_adr_write((unsigned long)(addr + i)); + wb_softcontrol_read_write(1); + printf("0x%08x: 0x%08x\n", (unsigned long)(addr + i), wb_softcontrol_data_read()); + } +} +define_command(wbr, wbr, "Read using softcontrol wishbone controller", MEM_CMDS); + +static void wbw(int nb_params, char **params) +{ + char *c; + unsigned int *addr; + unsigned int value; + unsigned int count; + unsigned int i; + + if (nb_params < 2) { + printf("mw
[count]"); + return; + } + + addr = (unsigned int *)strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + + value = strtoul(params[1], &c, 0); + if(*c != 0) { + printf("Incorrect value"); + return; + } + + if (nb_params == 2) { + count = 1; + } else { + count = strtoul(params[2], &c, 0); + if(*c != 0) { + printf("Incorrect count"); + return; + } + } + + wb_softcontrol_data_write(value); + for (i = 0; i < count; i++) { + wb_softcontrol_adr_write((unsigned long)(addr + i)); + wb_softcontrol_write_write(1); + } +} +define_command(wbw, wbw, "Write using softcontrol wishbone controller", MEM_CMDS); +#endif diff --git a/litex/soc/software/liblitedram/lfsr.h b/litex/soc/software/include/base/lfsr.h similarity index 100% rename from litex/soc/software/liblitedram/lfsr.h rename to litex/soc/software/include/base/lfsr.h diff --git a/litex/soc/software/include/base/memtest.h b/litex/soc/software/include/base/memtest.h new file mode 100644 index 000000000..111960830 --- /dev/null +++ b/litex/soc/software/include/base/memtest.h @@ -0,0 +1,13 @@ +#ifndef __MEMTEST_H +#define __MEMTEST_H + +#include + +int memtest(unsigned int *addr, unsigned long maxsize); +void memspeed(unsigned int *addr, unsigned long size, bool read_only); + +int memtest_addr(unsigned int *addr, unsigned long size, int random); +int memtest_data(unsigned int *addr, unsigned long size, int random); +int memtest_bus(unsigned int *addr, unsigned long size); + +#endif /* __MEMTEST_H */ diff --git a/litex/soc/software/libbase/Makefile b/litex/soc/software/libbase/Makefile index 1094ac573..9aebad8e1 100755 --- a/litex/soc/software/libbase/Makefile +++ b/litex/soc/software/libbase/Makefile @@ -17,7 +17,8 @@ OBJECTS = exception.o \ strcasecmp.o \ i2c.o \ div64.o \ - progress.o + progress.o \ + memtest.o all: crt0.o libbase.a libbase-nofloat.a diff --git a/litex/soc/software/libbase/memtest.c b/litex/soc/software/libbase/memtest.c new file mode 100644 index 000000000..d3193dab5 --- /dev/null +++ b/litex/soc/software/libbase/memtest.c @@ -0,0 +1,228 @@ +#include "memtest.h" + +#include +#include + +#include +#include +#include + +// #define MEMTEST_BUS_DEBUG +// #define MEMTEST_DATA_DEBUG +// #define MEMTEST_ADDR_DEBUG + +#ifndef MEMTEST_BUS_SIZE +#define MEMTEST_BUS_SIZE (512) +#endif + +#ifndef MEMTEST_DATA_SIZE +#define MEMTEST_DATA_SIZE (2*1024*1024) +#endif +#define MEMTEST_DATA_RANDOM 1 + +#ifndef MEMTEST_ADDR_SIZE +#define MEMTEST_ADDR_SIZE (32*1024) +#endif +#define MEMTEST_ADDR_RANDOM 0 + +#define ONEZERO 0xAAAAAAAA +#define ZEROONE 0x55555555 + +static unsigned int seed_to_data_32(unsigned int seed, int random) +{ + return random ? lfsr(32, seed) : seed + 1; +} + +static unsigned short seed_to_data_16(unsigned short seed, int random) +{ + return random ? lfsr(16, seed) : seed + 1; +} + +int memtest_bus(unsigned int *addr, unsigned long size) +{ + volatile unsigned int *array = addr; + int i, errors; + unsigned int rdata; + + errors = 0; + + for(i = 0; i < size/4;i++) { + array[i] = ONEZERO; + } + flush_cpu_dcache(); +#ifdef CONFIG_L2_SIZE + flush_l2_cache(); +#endif + for(i = 0; i < size/4; i++) { + rdata = array[i]; + if(rdata != ONEZERO) { + errors++; +#ifdef MEMTEST_BUS_DEBUG + printf("[bus: 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, ONEZERO); +#endif + } + } + + for(i = 0; i < size/4; i++) { + array[i] = ZEROONE; + } + flush_cpu_dcache(); +#ifdef CONFIG_L2_SIZE + flush_l2_cache(); +#endif + for(i = 0; i < size/4; i++) { + rdata = array[i]; + if(rdata != ZEROONE) { + errors++; +#ifdef MEMTEST_BUS_DEBUG + printf("[bus 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, ZEROONE); +#endif + } + } + + return errors; +} + +int memtest_data(unsigned int *addr, unsigned long size, int random) +{ + volatile unsigned int *array = addr; + int i, errors; + unsigned int seed_32; + unsigned int rdata; + + errors = 0; + seed_32 = 1; + + for(i = 0; i < size/4; i++) { + seed_32 = seed_to_data_32(seed_32, random); + array[i] = seed_32; + } + + seed_32 = 1; + flush_cpu_dcache(); +#ifdef CONFIG_L2_SIZE + flush_l2_cache(); +#endif + for(i = 0; i < size/4; i++) { + seed_32 = seed_to_data_32(seed_32, random); + rdata = array[i]; + if(rdata != seed_32) { + errors++; +#ifdef MEMTEST_DATA_DEBUG + printf("[data 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, seed_32); +#endif + } + } + + return errors; +} + +int memtest_addr(unsigned int *addr, unsigned long size, int random) +{ + volatile unsigned int *array = addr; + int i, errors; + unsigned short seed_16; + unsigned short rdata; + + errors = 0; + seed_16 = 1; + + for(i = 0; i < size/4; i++) { + seed_16 = seed_to_data_16(seed_16, random); + array[(unsigned int) seed_16] = i; + } + + seed_16 = 1; + flush_cpu_dcache(); +#ifdef CONFIG_L2_SIZE + flush_l2_cache(); +#endif + for(i = 0; i < size/4; i++) { + seed_16 = seed_to_data_16(seed_16, random); + rdata = array[(unsigned int) seed_16]; + if(rdata != i) { + errors++; +#ifdef MEMTEST_ADDR_DEBUG + printf("[addr 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, i); +#endif + } + } + + return errors; +} + +void memspeed(unsigned int *addr, unsigned long size, bool read_only) +{ + volatile unsigned int *array = addr; + int i; + unsigned int start, end; + unsigned long write_speed = 0; + unsigned long read_speed; + __attribute__((unused)) unsigned long data; + const unsigned int sz = sizeof(unsigned long); + + /* init timer */ + timer0_en_write(0); + timer0_reload_write(0); + timer0_load_write(0xffffffff); + timer0_en_write(1); + + /* write speed */ + if (!read_only) { + timer0_update_value_write(1); + start = timer0_value_read(); + for(i = 0; i < size/sz; i++) { + array[i] = i; + } + timer0_update_value_write(1); + end = timer0_value_read(); + write_speed = (8*size*(CONFIG_CLOCK_FREQUENCY/1000000))/(start - end); + } + + /* flush CPU and L2 caches */ + flush_cpu_dcache(); +#ifdef CONFIG_L2_SIZE + flush_l2_cache(); +#endif + + /* read speed */ + timer0_en_write(1); + timer0_update_value_write(1); + start = timer0_value_read(); + for(i = 0; i < size/sz; i++) { + data = array[i]; + } + timer0_update_value_write(1); + end = timer0_value_read(); + read_speed = (8*size*(CONFIG_CLOCK_FREQUENCY/1000000))/(start - end); + + printf("Memspeed Writes: %ldMbps Reads: %ldMbps\n", write_speed, read_speed); +} + +int memtest(unsigned int *addr, unsigned long maxsize) +{ + int bus_errors, data_errors, addr_errors; + unsigned long bus_size = MEMTEST_BUS_SIZE < maxsize ? MEMTEST_BUS_SIZE : maxsize; + unsigned long data_size = MEMTEST_DATA_SIZE < maxsize ? MEMTEST_DATA_SIZE : maxsize; + unsigned long addr_size = MEMTEST_ADDR_SIZE < maxsize ? MEMTEST_ADDR_SIZE : maxsize; + + bus_errors = memtest_bus(addr, bus_size); + if(bus_errors != 0) + printf("Memtest bus failed: %d/%d errors\n", bus_errors, bus_size/4); + + data_errors = memtest_data(addr, data_size, MEMTEST_DATA_RANDOM); + if(data_errors != 0) + printf("Memtest data failed: %d/%d errors\n", data_errors, data_size/4); + + addr_errors = memtest_addr(addr, addr_size, MEMTEST_ADDR_RANDOM); + if(addr_errors != 0) + printf("Memtest addr failed: %d/%d errors\n", addr_errors, addr_size/4); + + if(bus_errors + data_errors + addr_errors != 0) + return 0; + else { + printf("Memtest OK\n"); + memspeed(addr, data_size, false); + return 1; + } +} diff --git a/litex/soc/software/liblitedram/sdram.c b/litex/soc/software/liblitedram/sdram.c index 3d35d1b40..64ed5f1e1 100644 --- a/litex/soc/software/liblitedram/sdram.c +++ b/litex/soc/software/liblitedram/sdram.c @@ -12,6 +12,8 @@ #include #include +#include +#include #ifdef CSR_SDRAM_BASE #include @@ -20,12 +22,14 @@ #include #include "sdram.h" -#include "lfsr.h" // FIXME(hack): If we don't have main ram, just target the sram instead. #ifndef MAIN_RAM_BASE #define MAIN_RAM_BASE SRAM_BASE #endif +#ifndef MAIN_RAM_SIZE +#define MAIN_RAM_SIZE SRAM_SIZE +#endif __attribute__((unused)) static void cdelay(int i) { @@ -725,227 +729,8 @@ static void read_level(int module) #endif /* CSR_SDRAM_BASE */ -static unsigned int seed_to_data_32(unsigned int seed, int random) -{ - if (random) - return lfsr(32, seed); - else - return seed + 1; -} -static unsigned short seed_to_data_16(unsigned short seed, int random) -{ - if (random) - return lfsr(16, seed); - else - return seed + 1; -} -#define ONEZERO 0xAAAAAAAA -#define ZEROONE 0x55555555 - -#ifndef MEMTEST_BUS_SIZE -#define MEMTEST_BUS_SIZE (512) -#endif - -//#define MEMTEST_BUS_DEBUG - -static int memtest_bus(void) -{ - volatile unsigned int *array = (unsigned int *)MAIN_RAM_BASE; - int i, errors; - unsigned int rdata; - - errors = 0; - - for(i=0;i