liteeth: fix and improve 10/100/1000Mbps speed auto detection
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 26 Apr 2015 12:13:09 +0000 (14:13 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 26 Apr 2015 12:54:53 +0000 (14:54 +0200)
misoclib/com/liteeth/phy/gmii_mii.py
software/bios/main.c
software/include/net/microudp.h
software/libnet/microudp.c

index c147f56fbcc525f29d6033bff4e03379c130263b..b2b9b8d222da8720f6a579241096db9c24c390fe 100644 (file)
@@ -1,6 +1,6 @@
 from migen.genlib.io import DDROutput
 from migen.flow.plumbing import Multiplexer, Demultiplexer
-from migen.genlib.cdc import MultiReg
+from migen.genlib.cdc import PulseSynchronizer
 
 from misoclib.com.liteeth.common import *
 from misoclib.com.liteeth.generic import *
@@ -82,30 +82,61 @@ class LiteEthPHYGMIIMIIRX(Module):
         ]
 
 
-class LiteEthGMIIMIIClockCounter(Module, AutoCSR):
+class LiteEthGMIIMIIModeDetection(Module, AutoCSR):
     def __init__(self):
         self._reset = CSRStorage()
-        self._value = CSRStatus(32)
+        self._counter = CSRStatus(32)
+        self._mode = CSRStorage()
+        self.mode = Signal()
 
         # # #
 
-        counter = RenameClockDomains(Counter(32), "eth_rx")
+        # Note:
+        # For now mode detection is done with gateware and software.
+        # We will probably do it in gateware in the future
+        # (we will need to pass clk_freq parameter to PHY)
+
+        # Principle:
+        #  sys_clk >= 125MHz
+        #  eth_rx <= 125Mhz
+        # We generate a pulse in eth_rx clock domain that increments
+        # a counter in sys_clk domain.
+
+        # Generate a pulse every 4 clock cycles (eth_rx clock domain)
+        eth_pulse = Signal()
+        eth_counter = Signal(2)
+        self.sync.eth_rx += eth_counter.eq(eth_counter + 1)
+        self.comb += eth_pulse.eq(eth_counter == 0)
+
+        # Synchronize pulse (sys clock domain)
+        sys_pulse = Signal()
+        eth_ps = PulseSynchronizer("eth_rx", "sys")
+        self.comb += [
+            eth_ps.i.eq(eth_pulse),
+            sys_pulse.eq(eth_ps.o)
+        ]
+        self.submodules += eth_ps
+
+        # Count pulses (sys clock domain)
+        counter = Counter(32)
         self.submodules += counter
         self.comb += [
-            counter.reset.eq(self._reset.storage),  # slow, don't need CDC
-            counter.ce.eq(1),
+            counter.reset.eq(self._reset.storage),
+            counter.ce.eq(sys_pulse)
         ]
-        self.specials += MultiReg(counter.value, self._value.status)
+        self.comb += self._counter.status.eq(counter.value)
+
+        # Output mode
+        self.comb += self.mode.eq(self._mode.storage)
 
 
 class LiteEthPHYGMIIMII(Module, AutoCSR):
     def __init__(self, clock_pads, pads, with_hw_init_reset=True):
         self.dw = 8
-        self._mode = CSRStorage()
-        mode = self._mode.storage
         # Note: we can use GMII CRG since it also handles tx clock pad used for MII
+        self.submodules.mode_detection = LiteEthGMIIMIIModeDetection()
+        mode = self.mode_detection.mode
         self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset, mode == modes["MII"])
-        self.submodules.clock_counter = LiteEthGMIIMIIClockCounter()
         self.submodules.tx = RenameClockDomains(LiteEthPHYGMIIMIITX(pads, mode), "eth_tx")
         self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIMIIRX(pads, mode), "eth_rx")
         self.sink, self.source = self.tx.sink, self.rx.source
index 28f79dfc8e58cb3f762b63d6e27c26ab3fa5a91d..e6b5a8181cf78558ab1602cc0412de46e2ae2a2f 100644 (file)
@@ -497,16 +497,21 @@ static int test_user_abort(void)
 
 static void boot_sequence(void)
 {
+       int eth_ok;
+
        if(test_user_abort()) {
 #ifdef FLASH_BOOT_ADDRESS
                flashboot();
 #endif
                serialboot();
-#ifdef CSR_ETHPHY_MODE_ADDR
-               ethmode();
+#ifdef CSR_ETHPHY_MODE_DETECTION_MODE_ADDR
+               eth_ok = eth_mode_detection();
+#else
+               eth_ok = 1;
 #endif
 #ifdef CSR_ETHMAC_BASE
-               netboot();
+               if (eth_ok)
+                       netboot();
 #endif
                printf("No boot medium found\n");
        }
index 15c2a47dc4ef828af5a051e14f1d0323456b16c2..36ac5288209cd95c2e1cd65859806f5f0194614d 100644 (file)
@@ -15,6 +15,6 @@ void microudp_set_callback(udp_callback callback);
 void microudp_service(void);
 
 void ethreset(void);
-void ethmode(void);
+int eth_mode_detection(void);
 
 #endif /* __MICROUDP_H */
index fce73819b788a14d2a27b1119e2abe4df03fcc4b..1bb56f24725e19f45155f3e14d9beef7ff121c5e 100644 (file)
@@ -444,27 +444,49 @@ void ethreset(void)
        busy_wait(2);
 }
 
-#ifdef CSR_ETHPHY_MODE_ADDR
-void ethmode(void)
+#ifdef CSR_ETHPHY_MODE_DETECTION_MODE_ADDR
+static int eth_test_frequency(unsigned int freq, unsigned int target, unsigned int margin)
 {
-       ethphy_clock_counter_reset_write(1);
+       if (freq < (target - margin))
+               return 0;
+       else if (freq > (target + margin))
+               return 0;
+       else
+               return 1;
+}
+
+int eth_mode_detection(void)
+{
+       unsigned int frequency;
+
+       ethphy_mode_detection_reset_write(1);
        busy_wait(1);
-       ethphy_clock_counter_reset_write(0);
+       ethphy_mode_detection_reset_write(0);
        busy_wait(1);
+       frequency = ethphy_mode_detection_counter_read()*4*10;
+       ethphy_mode_detection_reset_write(1);
 
        printf("Ethernet phy mode: ");
-       /* if freq > 120 MHz, use GMII (5MHz margin)*/
-       if(ethphy_clock_counter_value_read() > 120000000/10) {
-               ethphy_mode_write(0);
-               printf("GMII");
-       /* else use MII */
-       } else {
-               ethphy_mode_write(1);
-               printf("MII");
+       /* 10Mbps */
+       if(eth_test_frequency(frequency, 2500000, 1000000)) {
+               ethphy_mode_detection_mode_write(1);
+               printf("10Mbps (MII)\n");
+               return 1;
+       /* 100Mbps */
+       } else if(eth_test_frequency(frequency, 25000000, 1000000)) {
+               ethphy_mode_detection_mode_write(1);
+               printf("100Mbps (MII)\n");
+               return 1;
+       /* 1Gbps */
+       } else if(eth_test_frequency(frequency, 125000000, 1000000)) {
+               ethphy_mode_detection_mode_write(0);
+               printf("1Gbps (GMII)\n");
+               return 1;
+       /* Failed */
+        } else {
+               printf("Failed to detect link speed\n");
+               return 0;
        }
-       printf("\n");
-
-       ethphy_clock_counter_reset_write(1);
 }
 #endif