Merge pull request #917 from antmicro/jboc/sdram-dbg
Add utilities for debugging DRAM problems
This commit is contained in:
commit
4886623df6
|
@ -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);
|
define_command(sdram_spd, sdram_spd_handler, "Read SDRAM SPD EEPROM", LITEDRAM_CMDS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SDRAM_DEBUG
|
||||||
|
define_command(sdram_debug, sdram_debug, "Run SDRAM debug tests", LITEDRAM_CMDS);
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
include ../include/generated/variables.mak
|
include ../include/generated/variables.mak
|
||||||
include $(SOC_DIRECTORY)/software/common.mak
|
include $(SOC_DIRECTORY)/software/common.mak
|
||||||
|
|
||||||
OBJECTS = sdram.o bist.o
|
OBJECTS = sdram.o bist.o sdram_dbg.o
|
||||||
|
|
||||||
all: liblitedram.a
|
all: liblitedram.a
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <system.h>
|
#include <system.h>
|
||||||
|
|
||||||
#include <liblitedram/sdram.h>
|
#include <liblitedram/sdram.h>
|
||||||
|
#include <liblitedram/sdram_dbg.h>
|
||||||
|
|
||||||
//#define SDRAM_TEST_DISABLE
|
//#define SDRAM_TEST_DISABLE
|
||||||
//#define SDRAM_WRITE_LEVELING_CMD_DELAY_DEBUG
|
//#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 */
|
/* Write pseudo-random sequence */
|
||||||
for(p=0;p<SDRAM_PHY_PHASES;p++)
|
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_address_write(0);
|
||||||
sdram_dfii_piwr_baddress_write(0);
|
sdram_dfii_piwr_baddress_write(0);
|
||||||
command_pwr(DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS|DFII_COMMAND_WRDATA);
|
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++) {
|
for(p=0;p<SDRAM_PHY_PHASES;p++) {
|
||||||
/* Read back test pattern */
|
/* 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' */
|
/* Verify bytes matching current 'module' */
|
||||||
if (prs[p][ SDRAM_PHY_MODULES-1-module] != tst[ SDRAM_PHY_MODULES-1-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])
|
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++) {
|
for (k=0; k<loops; k++) {
|
||||||
ddrphy_wlevel_strobe_write(1);
|
ddrphy_wlevel_strobe_write(1);
|
||||||
cdelay(100);
|
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)
|
if (buf[SDRAM_PHY_MODULES-1-i] != 0)
|
||||||
one_count++;
|
one_count++;
|
||||||
else
|
else
|
||||||
|
@ -1205,4 +1206,144 @@ int sdram_init(void)
|
||||||
return 1;
|
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
|
#endif
|
||||||
|
|
|
@ -48,4 +48,9 @@ int sdram_leveling(void);
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
int sdram_init(void);
|
int sdram_init(void);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Debugging */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
void sdram_debug(void);
|
||||||
|
|
||||||
#endif /* __SDRAM_H */
|
#endif /* __SDRAM_H */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
Loading…
Reference in New Issue