bios/sdram: improve read leveling (artix7 read-leveling is now done automatically...
[litex.git] / litex / soc / software / bios / sdram.c
index e83483dbe3d0635f6f23c8ee767eaeaa11c7d53c..4575156973fd28fd55330260a229682625b93520 100644 (file)
@@ -18,7 +18,9 @@ static void cdelay(int i)
                __asm__ volatile("nop");
 #elif defined (__or1k__)
                __asm__ volatile("l.nop");
-#elif defined (__riscv__)
+#elif defined (__picorv32__)
+               __asm__ volatile("nop");
+#elif defined (__vexriscv__)
                __asm__ volatile("nop");
 #else
 #error Unsupported architecture
@@ -220,13 +222,46 @@ void sdrwloff(void)
        ddrphy_wlevel_en_write(0);
 }
 
+static void write_level_scan(void)
+{
+       int i, j;
+       int dq_address;
+       unsigned char dq;
+
+       printf("Write leveling scan:\n");
+
+       sdrwlon();
+       cdelay(100);
+       for(i=0;i<DFII_PIX_DATA_SIZE/2;i++) {
+               printf("m%d: ", i);
+           dq_address = sdram_dfii_pix_rddata_addr[0]+4*(DFII_PIX_DATA_SIZE/2-1-i);
+               ddrphy_dly_sel_write(1 << i);
+               ddrphy_wdly_dq_rst_write(1);
+               ddrphy_wdly_dqs_rst_write(1);
+               for(j=0;j<ERR_DDRPHY_DELAY - ddrphy_half_sys8x_taps_read();j++) {
+                       ddrphy_wlevel_strobe_write(1);
+                       cdelay(10);
+                       dq = MMPTR(dq_address);
+                       printf("%d", dq != 0);
+                       ddrphy_wdly_dq_inc_write(1);
+                       ddrphy_wdly_dqs_inc_write(1);
+                       cdelay(10);
+               }
+               printf("\n");
+       }
+       sdrwloff();
+}
+
 static int write_level(int *delay, int *high_skew)
 {
        int i;
        int dq_address;
        unsigned char dq;
+       int err_ddrphy_wdly;
        int ok;
 
+       err_ddrphy_wdly = ERR_DDRPHY_DELAY - ddrphy_half_sys8x_taps_read();
+
        printf("Write leveling: ");
 
        sdrwlon();
@@ -236,6 +271,11 @@ static int write_level(int *delay, int *high_skew)
                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 */
+               int j;
+               for(j=0; j<ddrphy_wdly_dqs_taps_read(); j++)
+                       ddrphy_wdly_dqs_inc_write(1);
+#endif
 
                delay[i] = 0;
 
@@ -247,10 +287,11 @@ static int write_level(int *delay, int *high_skew)
                         * Assume this DQ group has between 1 and 2 bit times of skew.
                         * Bring DQS into the CK=0 zone before continuing leveling.
                         */
+#ifndef DDRPHY_HIGH_SKEW_DISABLE
                        high_skew[i] = 1;
                        while(dq != 0) {
                                delay[i]++;
-                               if(delay[i] >= ERR_DDRPHY_DELAY)
+                               if(delay[i] >= err_ddrphy_wdly)
                                        break;
                                ddrphy_wdly_dq_inc_write(1);
                                ddrphy_wdly_dqs_inc_write(1);
@@ -258,12 +299,15 @@ static int write_level(int *delay, int *high_skew)
                                cdelay(10);
                                dq = MMPTR(dq_address);
                         }
+#else
+                       high_skew[i] = 0;
+#endif
                } else
                        high_skew[i] = 0;
 
                while(dq == 0) {
                        delay[i]++;
-                       if(delay[i] >= ERR_DDRPHY_DELAY)
+                       if(delay[i] >= err_ddrphy_wdly)
                                break;
                        ddrphy_wdly_dq_inc_write(1);
                        ddrphy_wdly_dqs_inc_write(1);
@@ -278,7 +322,7 @@ static int write_level(int *delay, int *high_skew)
        ok = 1;
        for(i=DFII_PIX_DATA_SIZE/2-1;i>=0;i--) {
                printf("%2d%c ", delay[i], high_skew[i] ? '*' : ' ');
-               if(delay[i] >= ERR_DDRPHY_DELAY)
+               if(delay[i] >= err_ddrphy_wdly)
                        ok = 0;
        }
 
@@ -292,6 +336,19 @@ static int write_level(int *delay, int *high_skew)
 
 #endif /* CSR_DDRPHY_WLEVEL_EN_ADDR */
 
+static void read_bitslip_inc(char m)
+{
+               ddrphy_dly_sel_write(1 << m);
+#ifdef KUSDDRPHY
+               ddrphy_rdly_dq_bitslip_write(1);
+#else
+               /* 7-series SERDES in DDR mode needs 3 pulses for 1 bitslip */
+               ddrphy_rdly_dq_bitslip_write(1);
+               ddrphy_rdly_dq_bitslip_write(1);
+               ddrphy_rdly_dq_bitslip_write(1);
+#endif
+}
+
 static void read_bitslip(int *delay, int *high_skew)
 {
        int bitslip_thr;
@@ -308,21 +365,87 @@ static void read_bitslip(int *delay, int *high_skew)
        printf("Read bitslip: ");
        for(i=DFII_PIX_DATA_SIZE/2-1;i>=0;i--)
                if(delay[i] > bitslip_thr) {
-                       ddrphy_dly_sel_write(1 << i);
-#ifdef KUSDDRPHY
-                       ddrphy_rdly_dq_bitslip_write(1);
-#else
-                       /* 7-series SERDES in DDR mode needs 3 pulses for 1 bitslip */
-                       ddrphy_rdly_dq_bitslip_write(1);
-                       ddrphy_rdly_dq_bitslip_write(1);
-                       ddrphy_rdly_dq_bitslip_write(1);
-#endif
+                       read_bitslip_inc(i);
                        printf("%d ", i);
                }
        printf("\n");
 }
 
-static void read_delays(void)
+static int read_level_scan(int silent)
+{
+       unsigned int prv;
+       unsigned char prs[DFII_NPHASES*DFII_PIX_DATA_SIZE];
+       int p, i, j;
+       int working;
+       int optimal;
+
+       if (!silent)
+               printf("Read delays scan:\n");
+
+       /* 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);
+       working = 0;
+       optimal = 1;
+       for(i=DFII_PIX_DATA_SIZE/2-1;i>=0;i--) {
+               if (!silent)
+                       printf("m%d: ", (DFII_PIX_DATA_SIZE/2-i-1));
+               ddrphy_dly_sel_write(1 << (DFII_PIX_DATA_SIZE/2-i-1));
+               ddrphy_rdly_dq_rst_write(1);
+               for(j=0; j<ERR_DDRPHY_DELAY;j++) {
+                       int working_delay;
+                       command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA);
+                       cdelay(15);
+                       working_delay = 1;
+                       for(p=0;p<DFII_NPHASES;p++) {
+                               if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*i) != prs[DFII_PIX_DATA_SIZE*p+i])
+                                       working_delay = 0;
+                               if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(i+DFII_PIX_DATA_SIZE/2)) != prs[DFII_PIX_DATA_SIZE*p+i+DFII_PIX_DATA_SIZE/2])
+                                       working_delay = 0;
+                       }
+                       working |= working_delay;
+                       if ((j == 0) || (j == (ERR_DDRPHY_DELAY-1)))
+                               /* to have an optimal scan, first tap and last tap should not be working */
+                               optimal &= (working_delay == 0);
+                       if (!silent)
+                               printf("%d", working_delay);
+                       ddrphy_rdly_dq_inc_write(1);
+               }
+               if (!silent)
+                       printf("\n");
+       }
+
+       /* 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 working & optimal;
+}
+
+static void read_level(void)
 {
        unsigned int prv;
        unsigned char prs[DFII_NPHASES*DFII_PIX_DATA_SIZE];
@@ -383,7 +506,7 @@ static void read_delays(void)
 
                /* Get a bit further into the working zone */
 #ifdef KUSDDRPHY
-               for(j=0;j<8;j++) {
+               for(j=0;j<16;j++) {
                        delay += 1;
                        ddrphy_rdly_dq_inc_write(1);
                }
@@ -582,7 +705,7 @@ int memtest(void)
                printf("Memtest bus failed: %d/%d errors\n", bus_errors, 2*128);
 
        data_errors = memtest_data();
-       if(bus_errors != 0)
+       if(data_errors != 0)
                printf("Memtest data failed: %d/%d errors\n", data_errors, MEMTEST_DATA_SIZE/4);
 
        addr_errors = memtest_addr();
@@ -602,19 +725,38 @@ int sdrlevel(void)
 {
        int delay[DFII_PIX_DATA_SIZE/2];
        int high_skew[DFII_PIX_DATA_SIZE/2];
+       int i, j;
+
+       for(i=0; i<DFII_PIX_DATA_SIZE/2; i++) {
+               ddrphy_dly_sel_write(1<<i);
+               ddrphy_rdly_dq_rst_write(1);
+               ddrphy_rdly_dq_bitslip_rst_write(1);
+       }
 
 #ifndef CSR_DDRPHY_WLEVEL_EN_ADDR
-       int i;
        for(i=0; i<DFII_PIX_DATA_SIZE/2; i++) {
                delay[i] = 0;
                high_skew[i] = 0;
        }
 #else
+       write_level_scan();
        if(!write_level(delay, high_skew))
                return 0;
 #endif
-       read_bitslip(delay, high_skew);
-       read_delays();
+       /* check for optimal read leveling window */
+       for(i=0; i<8; i++) {
+               if (read_level_scan(1)) {
+                       break;
+               } else {
+                       /* else increment bitslip and re-scan */
+                       for(j=0; j<DFII_PIX_DATA_SIZE/2; j++)
+                               read_bitslip_inc(j);
+               }
+       }
+       /* show bitslip and scan */
+       printf("Read bitslip: %d\n", i);
+    read_level_scan(0);
+       read_level();
 
        return 1;
 }
@@ -626,7 +768,13 @@ int sdrinit(void)
 
        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
        sdram_dfii_control_write(DFII_CONTROL_SEL);
        if(!memtest())