From cd6f98c4c887c4981ed00075b9920c27be45e505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Mon, 17 May 2021 14:11:10 +0200 Subject: [PATCH 1/3] soc/software/liblitedram: use updated sdram_dfii_pix_X_addr interface --- litex/soc/software/liblitedram/sdram.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litex/soc/software/liblitedram/sdram.c b/litex/soc/software/liblitedram/sdram.c index 804c1bd53..72fbc9f34 100644 --- a/litex/soc/software/liblitedram/sdram.c +++ b/litex/soc/software/liblitedram/sdram.c @@ -290,7 +290,7 @@ static int sdram_write_read_check_test_pattern(int module, unsigned int seed) { /* Write pseudo-random sequence */ for(p=0;p Date: Mon, 17 May 2021 14:18:18 +0200 Subject: [PATCH 2/3] soc/software/liblitedram: add sdram debugging utilities --- litex/soc/software/liblitedram/Makefile | 2 +- litex/soc/software/liblitedram/sdram_dbg.c | 87 ++++++++++++++++++++++ litex/soc/software/liblitedram/sdram_dbg.h | 51 +++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 litex/soc/software/liblitedram/sdram_dbg.c create mode 100644 litex/soc/software/liblitedram/sdram_dbg.h diff --git a/litex/soc/software/liblitedram/Makefile b/litex/soc/software/liblitedram/Makefile index 56f4a5445..cfa022f8d 100644 --- a/litex/soc/software/liblitedram/Makefile +++ b/litex/soc/software/liblitedram/Makefile @@ -1,7 +1,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS = sdram.o bist.o +OBJECTS = sdram.o bist.o sdram_dbg.o all: liblitedram.a diff --git a/litex/soc/software/liblitedram/sdram_dbg.c b/litex/soc/software/liblitedram/sdram_dbg.c new file mode 100644 index 000000000..c77afcd91 --- /dev/null +++ b/litex/soc/software/liblitedram/sdram_dbg.c @@ -0,0 +1,87 @@ +#include + +#include +#include + +void error_stats_init(struct error_stats *stats) { + memset(stats, 0, sizeof(struct error_stats)); +} + +// TODO: Make the computations more generic +#if SDRAM_PHY_DATABITS * SDRAM_PHY_XDR > 32 +#error "At most 32 databits SDR or 16 databits DDR supported" +#endif +void error_stats_update(struct error_stats *stats, struct memory_error err) { + unsigned int phase = (err.addr % (SDRAM_PHY_PHASES*4)) / 4; + unsigned int errors = err.data ^ err.ref; + for (int edge = 0; edge < SDRAM_PHY_XDR; ++edge) { + for (int bit = 0; bit < SDRAM_PHY_DATABITS; ++bit) { + unsigned int bitval = 1 << (SDRAM_PHY_DATABITS*edge + bit); + if ((errors & bitval) != 0) { + stats->phase[phase].edge[edge].dq[bit]++; + } + } + } +} + +void error_stats_print(struct error_stats *stats) { + printf(" DQ:"); + for (int bit = 0; bit < 16; ++bit) { + printf(" %5d", bit); + } + printf("\n"); + for (int phase = 0; phase < 8; ++phase) { + for (int edge = 0; edge < 2; ++edge) { + unsigned int beat = 2*phase + edge; + printf(" beat[%2d]:", beat); + for (int bit = 0; bit < 16; ++bit) { + unsigned int dq_errors = stats->phase[phase].edge[edge].dq[bit]; + printf(" %5d", dq_errors); + } + printf("\n"); + } + } +} + +void readback_init(struct readback *readback) { + readback->len = 0; +} + +int readback_find(struct readback *readback, unsigned int addr) { + int left = 0; + int right = readback->len - 1; + while (left <= right) { + int mid = (left + right) / 2; + if (readback->errors[mid].addr == addr) { + return mid; + } else if (readback->errors[mid].addr < addr) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; +} + +int readback_add(struct readback *readback, unsigned int max_len, struct memory_error error) { + if (readback->len >= max_len) + return 0; + readback->errors[readback->len++] = error; + return 1; +} + +int readback_compare(struct readback *readback, struct readback *other, int verbose) { + int missing = 0; + for (unsigned int i = 0; i < readback->len ; ++i) { + struct memory_error *err = &readback->errors[i]; + int at = readback_find(other, err->addr); + if (at < 0) { + if (verbose) { + printf(" Missing @0x%08x: 0x%08x vs 0x%08x\n", + err->addr, err->data, err->ref); + } + missing++; + } + } + return missing; +} diff --git a/litex/soc/software/liblitedram/sdram_dbg.h b/litex/soc/software/liblitedram/sdram_dbg.h new file mode 100644 index 000000000..470ff26e6 --- /dev/null +++ b/litex/soc/software/liblitedram/sdram_dbg.h @@ -0,0 +1,51 @@ +#ifndef __SDRAM_DBG_H +#define __SDRAM_DBG_H + +#include + +struct memory_error { + unsigned int addr; + unsigned int data; + unsigned int ref; +}; + +/* Provides error statictics per phase/edge/dq */ +struct error_stats { + struct { + struct { + unsigned int dq[SDRAM_PHY_DATABITS]; + } edge[SDRAM_PHY_XDR]; + } phase[SDRAM_PHY_PHASES]; +}; + +void error_stats_init(struct error_stats *stats); +void error_stats_update(struct error_stats *stats, struct memory_error error); +void error_stats_print(struct error_stats *stats); + +/* Allows to store memory error information to compare several readbacks from memory. + * + * To achieve sensible results we need to store a lot of data, and we cannot use DRAM + * for that purpose (because we are debugging DRAM issues). This structure should be + * mapped to some memory region available in the SoC, ideally the SoC has some other + * memory that can be used, e.g. HyperRAM. + * + * This structure uses flexible array, so user must ensure number of errors fits into + * memory and must pass maximum size to readback_add when adding new entry. + */ +struct readback { + unsigned int len; + struct memory_error errors[]; +}; + +#define READBACK_SIZE(n) (sizeof(struct readback) + (n) * sizeof(struct memory_error)) + +void readback_init(struct readback *readback); +// Uses binary search to find given address and return its index or -1 if not found. +// The addresses int the whole readback array must be non-decreasing. +int readback_find(struct readback *readback, unsigned int addr); +// Add new entry if there is space (depending on max_len). Returns 1 if added new entry. +int readback_add(struct readback *readback, unsigned int max_len, struct memory_error error); +// Print errors that occured in `readback` that didn't occure in `other`. Returns number of errors. +int readback_compare(struct readback *readback, struct readback *other, int verbose); + +#endif /* __SDRAM_DBG_H */ From 1f2b8621fec8cee607ebf8c88e35c75c23cd5f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Mon, 17 May 2021 14:38:35 +0200 Subject: [PATCH 3/3] soc/software/liblitedram: add sdram_debug command --- litex/soc/software/bios/cmds/cmd_litedram.c | 4 + litex/soc/software/liblitedram/sdram.c | 141 ++++++++++++++++++++ litex/soc/software/liblitedram/sdram.h | 5 + 3 files changed, 150 insertions(+) diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 71a931321..e8c713f7f 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -409,3 +409,7 @@ static void sdram_spd_handler(int nb_params, char **params) } define_command(sdram_spd, sdram_spd_handler, "Read SDRAM SPD EEPROM", LITEDRAM_CMDS); #endif + +#ifdef SDRAM_DEBUG +define_command(sdram_debug, sdram_debug, "Run SDRAM debug tests", LITEDRAM_CMDS); +#endif diff --git a/litex/soc/software/liblitedram/sdram.c b/litex/soc/software/liblitedram/sdram.c index 72fbc9f34..884de3192 100644 --- a/litex/soc/software/liblitedram/sdram.c +++ b/litex/soc/software/liblitedram/sdram.c @@ -23,6 +23,7 @@ #include #include +#include //#define SDRAM_TEST_DISABLE //#define SDRAM_WRITE_LEVELING_CMD_DELAY_DEBUG @@ -1205,4 +1206,144 @@ int sdram_init(void) return 1; } +/*-----------------------------------------------------------------------*/ +/* Debugging */ +/*-----------------------------------------------------------------------*/ + +#ifdef SDRAM_DEBUG + +#define SDRAM_DEBUG_STATS_NUM_RUNS 10 +#define SDRAM_DEBUG_STATS_MEMTEST_SIZE MEMTEST_DATA_SIZE + +#ifdef SDRAM_DEBUG_READBACK_MEM_ADDR +#ifndef SDRAM_DEBUG_READBACK_MEM_SIZE +#error "Provide readback memory size via SDRAM_DEBUG_READBACK_MEM_SIZE" +#endif +#define SDRAM_DEBUG_READBACK_VERBOSE 1 + +#define SDRAM_DEBUG_READBACK_COUNT 3 +#define SDRAM_DEBUG_READBACK_MEMTEST_SIZE MEMTEST_DATA_SIZE + +#define _SINGLE_READBACK (SDRAM_DEBUG_READBACK_MEM_SIZE/SDRAM_DEBUG_READBACK_COUNT) +#define _READBACK_ERRORS_SIZE (_SINGLE_READBACK - sizeof(struct readback)) +#define SDRAM_DEBUG_READBACK_LEN (_READBACK_ERRORS_SIZE / sizeof(struct memory_error)) +#endif + +static int sdram_debug_error_stats_on_error( + unsigned int addr, unsigned int rdata, unsigned int refdata, void *arg) +{ + struct error_stats *stats = (struct error_stats *) arg; + struct memory_error error = { + .addr = addr, + .data = rdata, + .ref = refdata, + }; + error_stats_update(stats, error); + return 0; +} + +static void sdram_debug_error_stats(void) { + printf("Running initial memtest to fill memory ...\n"); + memtest_data((unsigned int *) MAIN_RAM_BASE, SDRAM_DEBUG_STATS_MEMTEST_SIZE, 1, NULL); + + struct error_stats stats; + error_stats_init(&stats); + + struct memtest_config config = { + .show_progress = 0, + .read_only = 1, + .on_error = sdram_debug_error_stats_on_error, + .arg = &stats, + }; + + printf("Running read-only memtests ... \n"); + for (int i = 0; i < SDRAM_DEBUG_STATS_NUM_RUNS; ++i) { + printf("Running read-only memtest %3d/%3d ... \r", i + 1, SDRAM_DEBUG_STATS_NUM_RUNS); + memtest_data((unsigned int *) MAIN_RAM_BASE, SDRAM_DEBUG_STATS_MEMTEST_SIZE, 1, &config); + } + + printf("\n"); + error_stats_print(&stats); +} + +#ifdef SDRAM_DEBUG_READBACK_MEM_ADDR +static int sdram_debug_readback_on_error( + unsigned int addr, unsigned int rdata, unsigned int refdata, void *arg) +{ + struct readback *readback = (struct readback *) arg; + struct memory_error error = { + .addr = addr, + .data = rdata, + .ref = refdata, + }; + // run only as long as we have space for new entries + return readback_add(readback, SDRAM_DEBUG_READBACK_LEN, error) != 1; +} + +static void sdram_debug_readback(void) +{ + printf("Using storage @0x%08x with size 0x%08x for %d readbacks.\n", + SDRAM_DEBUG_READBACK_MEM_ADDR, SDRAM_DEBUG_READBACK_MEM_SIZE, SDRAM_DEBUG_READBACK_COUNT); + + printf("Running initial memtest to fill memory ...\n"); + memtest_data((unsigned int *) MAIN_RAM_BASE, SDRAM_DEBUG_READBACK_MEMTEST_SIZE, 1, NULL); + + for (int i = 0; i < SDRAM_DEBUG_READBACK_COUNT; ++i) { + struct readback *readback = (struct readback *) + (SDRAM_DEBUG_READBACK_MEM_ADDR + i * READBACK_SIZE(SDRAM_DEBUG_READBACK_LEN)); + readback_init(readback); + + struct memtest_config config = { + .show_progress = 0, + .read_only = 1, + .on_error = sdram_debug_readback_on_error, + .arg = readback, + }; + + printf("Running readback %3d/%3d ... \r", i + 1, SDRAM_DEBUG_READBACK_COUNT); + memtest_data((unsigned int *) MAIN_RAM_BASE, SDRAM_DEBUG_READBACK_MEMTEST_SIZE, 1, &config); + } + printf("\n"); + + + // Iterate over all combinations + for (int i = 0; i < SDRAM_DEBUG_READBACK_COUNT; ++i) { + struct readback *first = (struct readback *) + (SDRAM_DEBUG_READBACK_MEM_ADDR + i * READBACK_SIZE(SDRAM_DEBUG_READBACK_LEN)); + + for (int j = i + 1; j < SDRAM_DEBUG_READBACK_COUNT; ++j) { + int nums[] = {i, j}; + struct readback *readbacks[] = { + (struct readback *) (SDRAM_DEBUG_READBACK_MEM_ADDR + i * READBACK_SIZE(SDRAM_DEBUG_READBACK_LEN)), + (struct readback *) (SDRAM_DEBUG_READBACK_MEM_ADDR + j * READBACK_SIZE(SDRAM_DEBUG_READBACK_LEN)), + }; + + // Compare i vs j and j vs i + for (int k = 0; k < 2; ++k) { + printf("Comparing readbacks %d vs %d:\n", nums[k], nums[1 - k]); + int missing = readback_compare(readbacks[k], readbacks[1 - k], SDRAM_DEBUG_READBACK_VERBOSE); + if (missing == 0) + printf(" OK\n"); + else + printf(" N missing = %d\n", missing); + } + } + } +} +#endif + +void sdram_debug(void) +{ +#if defined(SDRAM_DEBUG_STATS_NUM_RUNS) && SDRAM_DEBUG_STATS_NUM_RUNS > 0 + printf("\nError stats:\n"); + sdram_debug_error_stats(); +#endif + +#ifdef SDRAM_DEBUG_READBACK_MEM_ADDR + printf("\nReadback:\n"); + sdram_debug_readback(); +#endif +} +#endif + #endif diff --git a/litex/soc/software/liblitedram/sdram.h b/litex/soc/software/liblitedram/sdram.h index 417d890fb..d8df2b2c1 100644 --- a/litex/soc/software/liblitedram/sdram.h +++ b/litex/soc/software/liblitedram/sdram.h @@ -48,4 +48,9 @@ int sdram_leveling(void); /*-----------------------------------------------------------------------*/ int sdram_init(void); +/*-----------------------------------------------------------------------*/ +/* Debugging */ +/*-----------------------------------------------------------------------*/ +void sdram_debug(void); + #endif /* __SDRAM_H */