ddrphy_dly_sel_write(0);
}
-int write_level(void)
+static int write_level_scan(int *delays, int show)
{
int i, j, k;
int one_window_start, one_window_best_start;
int one_window_count, one_window_best_count;
- int delays[SDRAM_PHY_MODULES];
-
unsigned char buf[DFII_PIX_DATA_BYTES];
int ok;
err_ddrphy_wdly = SDRAM_PHY_DELAYS - ddrphy_half_sys8x_taps_read();
- printf("Write leveling:\n");
-
sdrwlon();
cdelay(100);
for(i=0;i<SDRAM_PHY_MODULES;i++) {
- printf("m%d: |", i);
+ if (show)
+ printf("m%d: |", i);
/* rst delay */
write_delay_rst(i);
for(j=0;j<err_ddrphy_wdly;j++) {
int zero_count = 0;
int one_count = 0;
- int show = 1;
+ int show_iter = show;
#if SDRAM_PHY_DELAYS > 32
- show = (j%16 == 0);
+ show_iter = (j%16 == 0) && show;
#endif
for (k=0; k<128; k++) {
ddrphy_wlevel_strobe_write(1);
taps_scan[j] = 1;
else
taps_scan[j] = 0;
- if (show)
+ if (show_iter)
printf("%d", taps_scan[j]);
write_delay_inc(i);
cdelay(10);
}
- printf("|");
+ if (show)
+ printf("|");
/* find longer 1 window and set delay at the 0/1 transition */
one_window_active = 0;
one_window_start = 0;
one_window_count = 0;
one_window_best_start = 0;
- one_window_best_count = 0;
+ one_window_best_count = -1;
delays[i] = -1;
for(j=0;j<err_ddrphy_wdly;j++) {
if (one_window_active) {
}
}
}
- delays[i] = one_window_best_start;
-
- /* configure write delay */
- write_delay_rst(i);
- for(j=0; j<delays[i]; j++)
- write_delay_inc(i);
- printf(" delay: %02d\n", delays[i]);
+ /* succeed only if the start of a 1s window has been found */
+ if (one_window_best_count > 0 && one_window_best_start > 0) {
+ delays[i] = one_window_best_start;
+
+ /* configure write delay */
+ write_delay_rst(i);
+ for(j=0; j<delays[i]; j++)
+ write_delay_inc(i);
+ }
+ if (show)
+ printf(" delay: %02d\n", delays[i]);
}
sdrwloff();
return ok;
}
+static void write_level_cdly_range(unsigned int *best_error, int *best_cdly,
+ int cdly_start, int cdly_stop, int cdly_step)
+{
+ int cdly;
+ int cdly_actual = 0;
+ int delays[SDRAM_PHY_MODULES];
+
+ /* scan through the range */
+ ddrphy_cdly_rst_write(1);
+ for (cdly = cdly_start; cdly < cdly_stop; cdly += cdly_step) {
+ /* increment cdly to current value */
+ while (cdly_actual < cdly) {
+ ddrphy_cdly_inc_write(1);
+ cdelay(10);
+ cdly_actual++;
+ }
+
+ /* write level using this delay */
+ if (write_level_scan(delays, 0)) {
+ /* use the mean of delays for error calulation */
+ int delay_mean = 0;
+ for (int i=0; i < SDRAM_PHY_MODULES; ++i) {
+ delay_mean += delays[i];
+ }
+ delay_mean /= SDRAM_PHY_MODULES;
+
+ /* we want it to be in the middle */
+ int ideal_delay = (SDRAM_PHY_DELAYS - ddrphy_half_sys8x_taps_read()) / 2;
+ int error = ideal_delay - delay_mean;
+ if (error < 0)
+ error *= -1;
+
+ if (error < *best_error) {
+ printf("+");
+ *best_cdly = cdly;
+ *best_error = error;
+ } else {
+ printf("-");
+ }
+ } else {
+ printf(".");
+ }
+ }
+}
+
+int write_level(void)
+{
+ int delays[SDRAM_PHY_MODULES];
+ unsigned int best_error = ~0u;
+ int best_cdly = -1;
+ int cdly_range_start;
+ int cdly_range_end;
+ int cdly_range_step;
+
+ printf("cdly scan: ");
+
+ /* Center write leveling by varying cdly. Searching through all possible
+ * values is slow, but we can use a simple optimization method of iterativly
+ * scanning smaller ranges with decreasing step */
+ cdly_range_start = 0;
+ cdly_range_end = 512;
+ cdly_range_step = 64;
+ while (cdly_range_step > 0) {
+ printf("|");
+ write_level_cdly_range(&best_error, &best_cdly,
+ cdly_range_start, cdly_range_end, cdly_range_step);
+
+ /* small optimization - stop if we have zero error */
+ if (best_error == 0)
+ break;
+
+ /* use best result as the middle of next range */
+ cdly_range_start = best_cdly - cdly_range_step;
+ cdly_range_end = best_cdly + cdly_range_step + 1;
+ if (cdly_range_start < 0)
+ cdly_range_start = 0;
+ if (cdly_range_end > 512)
+ cdly_range_end = 512;
+
+ cdly_range_step /= 4;
+ }
+ printf("| best: %d\n", best_cdly);
+
+ /* if we found any working delay then set it */
+ if (best_cdly >= 0) {
+ ddrphy_cdly_rst_write(1);
+ for (int i = 0; i < best_cdly; ++i) {
+ ddrphy_cdly_inc_write(1);
+ cdelay(10);
+ }
+ }
+
+ /* re-run write leveling the final time */
+ if (!write_level_scan(delays, 1))
+ return 0;
+
+ return best_cdly >= 0;
+}
+
+
#endif /* SDRAM_PHY_WRITE_LEVELING_CAPABLE */
static void read_delay_rst(int module) {
#ifdef CSR_SDRAM_BASE
#if defined(SDRAM_PHY_WRITE_LEVELING_CAPABLE) || defined(SDRAM_PHY_READ_LEVELING_CAPABLE)
-int sdrlevel(void)
+
+static void read_leveling(void)
{
int module;
int bitslip;
int best_score;
int best_bitslip;
- sdrsw();
-
- for(module=0; module<SDRAM_PHY_MODULES; module++) {
-#ifdef SDRAM_PHY_WRITE_LEVELING_CAPABLE
- write_delay_rst(module);
-#endif
- read_delay_rst(module);
- read_bitslip_rst(module);
- }
-
-#ifdef SDRAM_PHY_WRITE_LEVELING_CAPABLE
- if(!write_level())
- return 0;
-#endif
-
-#ifdef SDRAM_PHY_READ_LEVELING_CAPABLE
- printf("Read leveling:\n");
for(module=0; module<SDRAM_PHY_MODULES; module++) {
/* scan possible read windows */
best_score = 0;
read_level(module);
printf("\n");
}
+}
+
+int _write_level_cdly_scan = 1;
+
+int sdrlevel(void)
+{
+ int module;
+ sdrsw();
+
+ for(module=0; module<SDRAM_PHY_MODULES; module++) {
+#ifdef SDRAM_PHY_WRITE_LEVELING_CAPABLE
+ write_delay_rst(module);
+#endif
+ read_delay_rst(module);
+ read_bitslip_rst(module);
+ }
+
+#ifdef SDRAM_PHY_WRITE_LEVELING_CAPABLE
+ printf("Write leveling:\n");
+ if (_write_level_cdly_scan) {
+ if(!write_level())
+ return 0;
+ } else {
+ /* use only the current cdly */
+ int delays[SDRAM_PHY_MODULES];
+ if (!write_level_scan(delays, 1))
+ return 0;
+ }
#endif
+#ifdef SDRAM_PHY_READ_LEVELING_CAPABLE
+ printf("Read leveling:\n");
+ read_leveling();
+#endif
return 1;
}
init_sequence();
#ifdef CSR_DDRPHY_BASE
-#if CSR_DDRPHY_EN_VTC_ADDR
- ddrphy_en_vtc_write(0);
-#endif
#ifdef DDRPHY_CMD_DELAY
ddrphy_cdly(DDRPHY_CMD_DELAY);
#endif
+#if CSR_DDRPHY_EN_VTC_ADDR
+ ddrphy_en_vtc_write(0);
+#endif
#if defined(SDRAM_PHY_WRITE_LEVELING_CAPABLE) || defined(SDRAM_PHY_READ_LEVELING_CAPABLE)
sdrlevel();
#endif
sdrhw();
}
+void sdr_cdly_scan(int enabled)
+{
+ printf("Turning cdly scan %s\n", enabled ? "ON" : "OFF");
+ _write_level_cdly_scan = enabled;
+}
+
#endif
#endif