From ae71bf283064dfef19eeebccd3f92a84a8ae53ae Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sun, 26 Apr 2015 14:13:09 +0200 Subject: [PATCH] liteeth: fix and improve 10/100/1000Mbps speed auto detection --- misoclib/com/liteeth/phy/gmii_mii.py | 51 +++++++++++++++++++++------ software/bios/main.c | 11 ++++-- software/include/net/microudp.h | 2 +- software/libnet/microudp.c | 52 ++++++++++++++++++++-------- 4 files changed, 87 insertions(+), 29 deletions(-) diff --git a/misoclib/com/liteeth/phy/gmii_mii.py b/misoclib/com/liteeth/phy/gmii_mii.py index c147f56fb..b2b9b8d22 100644 --- a/misoclib/com/liteeth/phy/gmii_mii.py +++ b/misoclib/com/liteeth/phy/gmii_mii.py @@ -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 diff --git a/software/bios/main.c b/software/bios/main.c index 28f79dfc8..e6b5a8181 100644 --- a/software/bios/main.c +++ b/software/bios/main.c @@ -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"); } diff --git a/software/include/net/microudp.h b/software/include/net/microudp.h index 15c2a47dc..36ac52882 100644 --- a/software/include/net/microudp.h +++ b/software/include/net/microudp.h @@ -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 */ diff --git a/software/libnet/microudp.c b/software/libnet/microudp.c index fce73819b..1bb56f247 100644 --- a/software/libnet/microudp.c +++ b/software/libnet/microudp.c @@ -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