Merge pull request #917 from antmicro/jboc/sdram-dbg

Add utilities for debugging DRAM problems
This commit is contained in:
enjoy-digital 2021-05-18 10:31:33 +02:00 committed by GitHub
commit 4886623df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 292 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -23,6 +23,7 @@
#include <system.h>
#include <liblitedram/sdram.h>
#include <liblitedram/sdram_dbg.h>
//#define SDRAM_TEST_DISABLE
//#define SDRAM_WRITE_LEVELING_CMD_DELAY_DEBUG
@ -290,7 +291,7 @@ static int sdram_write_read_check_test_pattern(int module, unsigned int seed) {
/* Write pseudo-random sequence */
for(p=0;p<SDRAM_PHY_PHASES;p++)
csr_wr_buf_uint8(sdram_dfii_pix_wrdata_addr[p], prs[p], DFII_PIX_DATA_BYTES);
csr_wr_buf_uint8(sdram_dfii_pix_wrdata_addr(p), prs[p], DFII_PIX_DATA_BYTES);
sdram_dfii_piwr_address_write(0);
sdram_dfii_piwr_baddress_write(0);
command_pwr(DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS|DFII_COMMAND_WRDATA);
@ -311,7 +312,7 @@ static int sdram_write_read_check_test_pattern(int module, unsigned int seed) {
for(p=0;p<SDRAM_PHY_PHASES;p++) {
/* Read back test pattern */
csr_rd_buf_uint8(sdram_dfii_pix_rddata_addr[p], tst, DFII_PIX_DATA_BYTES);
csr_rd_buf_uint8(sdram_dfii_pix_rddata_addr(p), tst, DFII_PIX_DATA_BYTES);
/* Verify bytes matching current 'module' */
if (prs[p][ SDRAM_PHY_MODULES-1-module] != tst[ SDRAM_PHY_MODULES-1-module] ||
prs[p][2*SDRAM_PHY_MODULES-1-module] != tst[2*SDRAM_PHY_MODULES-1-module])
@ -583,7 +584,7 @@ static int sdram_write_leveling_scan(int *delays, int loops, int show)
for (k=0; k<loops; k++) {
ddrphy_wlevel_strobe_write(1);
cdelay(100);
csr_rd_buf_uint8(sdram_dfii_pix_rddata_addr[0], buf, DFII_PIX_DATA_BYTES);
csr_rd_buf_uint8(sdram_dfii_pix_rddata_addr(0), buf, DFII_PIX_DATA_BYTES);
if (buf[SDRAM_PHY_MODULES-1-i] != 0)
one_count++;
else
@ -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

View File

@ -48,4 +48,9 @@ int sdram_leveling(void);
/*-----------------------------------------------------------------------*/
int sdram_init(void);
/*-----------------------------------------------------------------------*/
/* Debugging */
/*-----------------------------------------------------------------------*/
void sdram_debug(void);
#endif /* __SDRAM_H */

View File

@ -0,0 +1,87 @@
#include <liblitedram/sdram_dbg.h>
#include <string.h>
#include <stdio.h>
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;
}

View File

@ -0,0 +1,51 @@
#ifndef __SDRAM_DBG_H
#define __SDRAM_DBG_H
#include <generated/sdram_phy.h>
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 */