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 */