soc/software/liblitedram: add sdram debugging utilities
This commit is contained in:
parent
cd6f98c4c8
commit
e5c1482572
|
@ -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
|
||||
|
||||
|
|
|
@ -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