soc/software/liblitedram: add sdram debugging utilities

This commit is contained in:
Jędrzej Boczar 2021-05-17 14:18:18 +02:00
parent cd6f98c4c8
commit e5c1482572
3 changed files with 139 additions and 1 deletions

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

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