litex/litex/soc/software/bios/sdram.c
2018-12-17 16:00:44 +01:00

785 lines
16 KiB
C

#include <generated/csr.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef CSR_SDRAM_BASE
#include <generated/sdram_phy.h>
#endif
#include <generated/mem.h>
#include <hw/flags.h>
#include <system.h>
#include "sdram.h"
// FIXME(hack): If we don't have main ram, just target the sram instead.
#ifndef MAIN_RAM_BASE
#define MAIN_RAM_BASE SRAM_BASE
#endif
static void cdelay(int i)
{
while(i > 0) {
#if defined (__lm32__)
__asm__ volatile("nop");
#elif defined (__or1k__)
__asm__ volatile("l.nop");
#elif defined (__picorv32__)
__asm__ volatile("nop");
#elif defined (__vexriscv__)
__asm__ volatile("nop");
#elif defined (__minerva__)
__asm__ volatile("nop");
#else
#error Unsupported architecture
#endif
i--;
}
}
#ifdef CSR_SDRAM_BASE
void sdrsw(void)
{
sdram_dfii_control_write(DFII_CONTROL_CKE|DFII_CONTROL_ODT|DFII_CONTROL_RESET_N);
printf("SDRAM now under software control\n");
}
void sdrhw(void)
{
sdram_dfii_control_write(DFII_CONTROL_SEL);
printf("SDRAM now under hardware control\n");
}
void sdrrow(char *_row)
{
char *c;
unsigned int row;
if(*_row == 0) {
sdram_dfii_pi0_address_write(0x0000);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
cdelay(15);
printf("Precharged\n");
} else {
row = strtoul(_row, &c, 0);
if(*c != 0) {
printf("incorrect row\n");
return;
}
sdram_dfii_pi0_address_write(row);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CS);
cdelay(15);
printf("Activated row %d\n", row);
}
}
void sdrrdbuf(int dq)
{
int i, p;
int first_byte, step;
if(dq < 0) {
first_byte = 0;
step = 1;
} else {
first_byte = DFII_PIX_DATA_SIZE/2 - 1 - dq;
step = DFII_PIX_DATA_SIZE/2;
}
for(p=0;p<DFII_NPHASES;p++)
for(i=first_byte;i<DFII_PIX_DATA_SIZE;i+=step)
printf("%02x", MMPTR(sdram_dfii_pix_rddata_addr[p]+4*i));
printf("\n");
}
void sdrrd(char *startaddr, char *dq)
{
char *c;
unsigned int addr;
int _dq;
if(*startaddr == 0) {
printf("sdrrd <address>\n");
return;
}
addr = strtoul(startaddr, &c, 0);
if(*c != 0) {
printf("incorrect address\n");
return;
}
if(*dq == 0)
_dq = -1;
else {
_dq = strtoul(dq, &c, 0);
if(*c != 0) {
printf("incorrect DQ\n");
return;
}
}
sdram_dfii_pird_address_write(addr);
sdram_dfii_pird_baddress_write(0);
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
sdrrdbuf(_dq);
}
void sdrrderr(char *count)
{
int addr;
char *c;
int _count;
int i, j, p;
unsigned char prev_data[DFII_NPHASES*DFII_PIX_DATA_SIZE];
unsigned char errs[DFII_NPHASES*DFII_PIX_DATA_SIZE];
if(*count == 0) {
printf("sdrrderr <count>\n");
return;
}
_count = strtoul(count, &c, 0);
if(*c != 0) {
printf("incorrect count\n");
return;
}
for(i=0;i<DFII_NPHASES*DFII_PIX_DATA_SIZE;i++)
errs[i] = 0;
for(addr=0;addr<16;addr++) {
sdram_dfii_pird_address_write(addr*8);
sdram_dfii_pird_baddress_write(0);
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++)
prev_data[p*DFII_PIX_DATA_SIZE+i] = MMPTR(sdram_dfii_pix_rddata_addr[p]+4*i);
for(j=0;j<_count;j++) {
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++) {
unsigned char new_data;
new_data = MMPTR(sdram_dfii_pix_rddata_addr[p]+4*i);
errs[p*DFII_PIX_DATA_SIZE+i] |= prev_data[p*DFII_PIX_DATA_SIZE+i] ^ new_data;
prev_data[p*DFII_PIX_DATA_SIZE+i] = new_data;
}
}
}
for(i=0;i<DFII_NPHASES*DFII_PIX_DATA_SIZE;i++)
printf("%02x", errs[i]);
printf("\n");
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++)
printf("%2x", DFII_PIX_DATA_SIZE/2 - 1 - (i % (DFII_PIX_DATA_SIZE/2)));
printf("\n");
}
void sdrwr(char *startaddr)
{
char *c;
unsigned int addr;
int i;
int p;
if(*startaddr == 0) {
printf("sdrrd <address>\n");
return;
}
addr = strtoul(startaddr, &c, 0);
if(*c != 0) {
printf("incorrect address\n");
return;
}
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++)
MMPTR(sdram_dfii_pix_wrdata_addr[p]+4*i) = 0x10*p + i;
sdram_dfii_piwr_address_write(addr);
sdram_dfii_piwr_baddress_write(0);
command_pwr(DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS|DFII_COMMAND_WRDATA);
}
#ifdef CSR_DDRPHY_BASE
#ifdef KUSDDRPHY
#define ERR_DDRPHY_DELAY 512
#else
#define ERR_DDRPHY_DELAY 32
#endif
#define ERR_DDRPHY_BITSLIP 8
#define NBMODULES DFII_PIX_DATA_SIZE/2
#ifdef CSR_DDRPHY_WLEVEL_EN_ADDR
void sdrwlon(void)
{
sdram_dfii_pi0_address_write(DDRX_MR1 | (1 << 7));
sdram_dfii_pi0_baddress_write(1);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
ddrphy_wlevel_en_write(1);
}
void sdrwloff(void)
{
sdram_dfii_pi0_address_write(DDRX_MR1);
sdram_dfii_pi0_baddress_write(1);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
ddrphy_wlevel_en_write(0);
}
int write_level(void)
{
int i, j, k;
int dq_address;
unsigned char dq;
int err_ddrphy_wdly;
unsigned char taps_scan[ERR_DDRPHY_DELAY];
int one_window_active;
int one_window_start;
int delays[NBMODULES];
int ok;
err_ddrphy_wdly = ERR_DDRPHY_DELAY - ddrphy_half_sys8x_taps_read() - 1;
printf("Write leveling:\n");
sdrwlon();
cdelay(100);
for(i=0;i<NBMODULES;i++) {
printf("m%d: |", i);
dq_address = sdram_dfii_pix_rddata_addr[0]+4*(NBMODULES-1-i);
/* reset delay */
ddrphy_dly_sel_write(1 << i);
ddrphy_wdly_dq_rst_write(1);
ddrphy_wdly_dqs_rst_write(1);
#ifdef KUSDDRPHY /* need to init manually on Ultrascale */
for(j=0; j<ddrphy_half_sys8x_taps_read(); j++)
ddrphy_wdly_dqs_inc_write(1);
#endif
/* scan taps */
for(j=0;j<err_ddrphy_wdly;j++) {
int zero_count = 0;
int one_count = 0;
int show = 1;
#ifdef KUSDDRPHY
show = (j%16 == 0);
#endif
for (k=0; k<128; k++) {
ddrphy_wlevel_strobe_write(1);
cdelay(10);
dq = MMPTR(dq_address);
if (dq != 0)
one_count++;
else
zero_count++;
}
if (one_count > zero_count)
taps_scan[j] = 1;
else
taps_scan[j] = 0;
if (show)
printf("%d", taps_scan[j]);
ddrphy_wdly_dq_inc_write(1);
ddrphy_wdly_dqs_inc_write(1);
cdelay(10);
}
printf("|");
/* select last 0/1 transition */
one_window_active = 0;
one_window_start = 0;
delays[i] = -1;
for(j=0;j<err_ddrphy_wdly;j++) {
if (one_window_active) {
if (taps_scan[j] == 0)
one_window_active = 0;
} else {
if (taps_scan[j]) {
one_window_active = 1;
one_window_start = j;
}
}
}
delays[i] = one_window_start;
/* configure delays */
ddrphy_wdly_dq_rst_write(1);
ddrphy_wdly_dqs_rst_write(1);
#ifdef KUSDDRPHY /* need to init manually on Ultrascale */
for(j=0; j<ddrphy_half_sys8x_taps_read(); j++)
ddrphy_wdly_dqs_inc_write(1);
#endif
for(j=0; j<delays[i]; j++) {
ddrphy_wdly_dq_inc_write(1);
ddrphy_wdly_dqs_inc_write(1);
}
printf(" delay: %02d\n", delays[i]);
}
sdrwloff();
ok = 1;
for(i=NBMODULES-1;i>=0;i--) {
if(delays[i] < 0)
ok = 0;
}
return ok;
}
#endif /* CSR_DDRPHY_WLEVEL_EN_ADDR */
static void read_bitslip_inc(char m)
{
ddrphy_dly_sel_write(1 << m);
ddrphy_rdly_dq_bitslip_write(1);
}
static int read_level_scan(int module, int bitslip)
{
unsigned int prv;
unsigned char prs[DFII_NPHASES*DFII_PIX_DATA_SIZE];
int p, i, j;
int score;
/* Generate pseudo-random sequence */
prv = 42;
for(i=0;i<DFII_NPHASES*DFII_PIX_DATA_SIZE;i++) {
prv = 1664525*prv + 1013904223;
prs[i] = prv;
}
/* Activate */
sdram_dfii_pi0_address_write(0);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CS);
cdelay(15);
/* Write test pattern */
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++)
MMPTR(sdram_dfii_pix_wrdata_addr[p]+4*i) = prs[DFII_PIX_DATA_SIZE*p+i];
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);
/* Calibrate each DQ in turn */
sdram_dfii_pird_address_write(0);
sdram_dfii_pird_baddress_write(0);
score = 0;
printf("m%d, b%d: |", module, bitslip);
ddrphy_dly_sel_write(1 << module);
ddrphy_rdly_dq_rst_write(1);
for(j=0; j<ERR_DDRPHY_DELAY;j++) {
int working;
int show = 1;
#ifdef KUSDDRPHY
show = (j%16 == 0);
#endif
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
working = 1;
for(p=0;p<DFII_NPHASES;p++) {
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+(NBMODULES-module-1)])
working = 0;
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1])
working = 0;
}
if (show)
printf("%d", working);
score += working;
ddrphy_rdly_dq_inc_write(1);
}
printf("| ");
/* Precharge */
sdram_dfii_pi0_address_write(0);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
cdelay(15);
return score;
}
static void read_level(int module)
{
unsigned int prv;
unsigned char prs[DFII_NPHASES*DFII_PIX_DATA_SIZE];
int p, i, j;
int working;
int delay, delay_min, delay_max;
printf("delays: ");
/* Generate pseudo-random sequence */
prv = 42;
for(i=0;i<DFII_NPHASES*DFII_PIX_DATA_SIZE;i++) {
prv = 1664525*prv + 1013904223;
prs[i] = prv;
}
/* Activate */
sdram_dfii_pi0_address_write(0);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CS);
cdelay(15);
/* Write test pattern */
for(p=0;p<DFII_NPHASES;p++)
for(i=0;i<DFII_PIX_DATA_SIZE;i++)
MMPTR(sdram_dfii_pix_wrdata_addr[p]+4*i) = prs[DFII_PIX_DATA_SIZE*p+i];
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);
/* Calibrate each DQ in turn */
sdram_dfii_pird_address_write(0);
sdram_dfii_pird_baddress_write(0);
ddrphy_dly_sel_write(1 << module);
delay = 0;
/* Find smallest working delay */
ddrphy_rdly_dq_rst_write(1);
while(1) {
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
working = 1;
for(p=0;p<DFII_NPHASES;p++) {
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+(NBMODULES-module-1)])
working = 0;
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1])
working = 0;
}
if(working)
break;
delay++;
if(delay >= ERR_DDRPHY_DELAY)
break;
ddrphy_rdly_dq_inc_write(1);
}
delay_min = delay;
/* Get a bit further into the working zone */
#ifdef KUSDDRPHY
for(j=0;j<16;j++) {
delay += 1;
ddrphy_rdly_dq_inc_write(1);
}
#else
delay++;
ddrphy_rdly_dq_inc_write(1);
#endif
/* Find largest working delay */
while(1) {
command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
cdelay(15);
working = 1;
for(p=0;p<DFII_NPHASES;p++) {
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+(NBMODULES-module-1)])
working = 0;
if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1])
working = 0;
}
if(!working)
break;
delay++;
if(delay >= ERR_DDRPHY_DELAY)
break;
ddrphy_rdly_dq_inc_write(1);
}
delay_max = delay;
printf("%02d+-%02d", (delay_min+delay_max)/2, (delay_max-delay_min)/2);
/* Set delay to the middle */
ddrphy_rdly_dq_rst_write(1);
for(j=0;j<(delay_min+delay_max)/2;j++)
ddrphy_rdly_dq_inc_write(1);
/* Precharge */
sdram_dfii_pi0_address_write(0);
sdram_dfii_pi0_baddress_write(0);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
cdelay(15);
}
#endif /* CSR_DDRPHY_BASE */
#endif /* CSR_SDRAM_BASE */
static unsigned int seed_to_data_32(unsigned int seed, int random)
{
if (random)
return 1664525*seed + 1013904223;
else
return seed + 1;
}
static unsigned short seed_to_data_16(unsigned short seed, int random)
{
if (random)
return 25173*seed + 13849;
else
return seed + 1;
}
#define ONEZERO 0xAAAAAAAA
#define ZEROONE 0x55555555
#ifndef MEMTEST_BUS_SIZE
#define MEMTEST_BUS_SIZE (512)
#endif
//#define MEMTEST_BUS_DEBUG
static int memtest_bus(void)
{
volatile unsigned int *array = (unsigned int *)MAIN_RAM_BASE;
int i, errors;
unsigned int rdata;
errors = 0;
for(i=0;i<MEMTEST_BUS_SIZE/4;i++) {
array[i] = ONEZERO;
}
flush_cpu_dcache();
#ifdef L2_SIZE
flush_l2_cache();
#endif
for(i=0;i<MEMTEST_BUS_SIZE/4;i++) {
rdata = array[i];
if(rdata != ONEZERO) {
errors++;
#ifdef MEMTEST_BUS_DEBUG
printf("[bus: 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, ONEZERO);
#endif
}
}
for(i=0;i<MEMTEST_BUS_SIZE/4;i++) {
array[i] = ZEROONE;
}
flush_cpu_dcache();
#ifdef L2_SIZE
flush_l2_cache();
#endif
for(i=0;i<MEMTEST_BUS_SIZE/4;i++) {
rdata = array[i];
if(rdata != ZEROONE) {
errors++;
#ifdef MEMTEST_BUS_DEBUG
printf("[bus 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, ZEROONE);
#endif
}
}
return errors;
}
#ifndef MEMTEST_DATA_SIZE
#define MEMTEST_DATA_SIZE (2*1024*1024)
#endif
#define MEMTEST_DATA_RANDOM 1
//#define MEMTEST_DATA_DEBUG
static int memtest_data(void)
{
volatile unsigned int *array = (unsigned int *)MAIN_RAM_BASE;
int i, errors;
unsigned int seed_32;
unsigned int rdata;
errors = 0;
seed_32 = 0;
for(i=0;i<MEMTEST_DATA_SIZE/4;i++) {
seed_32 = seed_to_data_32(seed_32, MEMTEST_DATA_RANDOM);
array[i] = seed_32;
}
seed_32 = 0;
flush_cpu_dcache();
#ifdef L2_SIZE
flush_l2_cache();
#endif
for(i=0;i<MEMTEST_DATA_SIZE/4;i++) {
seed_32 = seed_to_data_32(seed_32, MEMTEST_DATA_RANDOM);
rdata = array[i];
if(rdata != seed_32) {
errors++;
#ifdef MEMTEST_DATA_DEBUG
printf("[data 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, seed_32);
#endif
}
}
return errors;
}
#ifndef MEMTEST_ADDR_SIZE
#define MEMTEST_ADDR_SIZE (32*1024)
#endif
#define MEMTEST_ADDR_RANDOM 0
//#define MEMTEST_ADDR_DEBUG
static int memtest_addr(void)
{
volatile unsigned int *array = (unsigned int *)MAIN_RAM_BASE;
int i, errors;
unsigned short seed_16;
unsigned short rdata;
errors = 0;
seed_16 = 0;
for(i=0;i<MEMTEST_ADDR_SIZE/4;i++) {
seed_16 = seed_to_data_16(seed_16, MEMTEST_ADDR_RANDOM);
array[(unsigned int) seed_16] = i;
}
seed_16 = 0;
flush_cpu_dcache();
#ifdef L2_SIZE
flush_l2_cache();
#endif
for(i=0;i<MEMTEST_ADDR_SIZE/4;i++) {
seed_16 = seed_to_data_16(seed_16, MEMTEST_ADDR_RANDOM);
rdata = array[(unsigned int) seed_16];
if(rdata != i) {
errors++;
#ifdef MEMTEST_ADDR_DEBUG
printf("[addr 0x%0x]: 0x%08x vs 0x%08x\n", i, rdata, i);
#endif
}
}
return errors;
}
int memtest(void)
{
int bus_errors, data_errors, addr_errors;
bus_errors = memtest_bus();
if(bus_errors != 0)
printf("Memtest bus failed: %d/%d errors\n", bus_errors, 2*128);
data_errors = memtest_data();
if(data_errors != 0)
printf("Memtest data failed: %d/%d errors\n", data_errors, MEMTEST_DATA_SIZE/4);
addr_errors = memtest_addr();
if(addr_errors != 0)
printf("Memtest addr failed: %d/%d errors\n", addr_errors, MEMTEST_ADDR_SIZE/4);
if(bus_errors + data_errors + addr_errors != 0)
return 0;
else {
printf("Memtest OK\n");
return 1;
}
}
#ifdef CSR_SDRAM_BASE
#ifdef CSR_DDRPHY_BASE
int sdrlevel(void)
{
int i, j;
int bitslip;
int score;
int best_score;
int best_bitslip;
sdrsw();
for(i=0; i<NBMODULES; i++) {
ddrphy_dly_sel_write(1<<i);
ddrphy_rdly_dq_rst_write(1);
ddrphy_rdly_dq_bitslip_rst_write(1);
}
#ifdef CSR_DDRPHY_WLEVEL_EN_ADDR
if(!write_level())
return 0;
#endif
printf("Read leveling:\n");
for(i=0; i<NBMODULES; i++) {
/* scan possible read windows */
best_score = 0;
best_bitslip = 0;
for(bitslip=0; bitslip<ERR_DDRPHY_BITSLIP; bitslip++) {
/* compute score */
score = read_level_scan(i, bitslip);
read_level(i);
printf("\n");
if (score > best_score) {
best_bitslip = bitslip;
best_score = score;
}
/* exit */
if (bitslip == ERR_DDRPHY_BITSLIP-1)
break;
/* increment bitslip */
read_bitslip_inc(i);
}
/* select best read window */
printf("best: m%d, b%d ", i, best_bitslip);
ddrphy_rdly_dq_bitslip_rst_write(1);
for (j=0; j<best_bitslip; j++)
read_bitslip_inc(i);
/* re-do leveling on best read window*/
read_level(i);
printf("\n");
}
return 1;
}
#endif
int sdrinit(void)
{
printf("Initializing SDRAM...\n");
init_sequence();
#ifdef CSR_DDRPHY_BASE
#if CSR_DDRPHY_EN_VTC_ADDR
ddrphy_en_vtc_write(0);
#endif
sdrlevel();
#if CSR_DDRPHY_EN_VTC_ADDR
ddrphy_en_vtc_write(1);
#endif
#endif
sdrhw();
if(!memtest()) {
return 0;
}
return 1;
}
#endif