From 41c8b50ba531308ae3651e3c9706ecced02128fb Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 09:26:34 +0200 Subject: [PATCH 1/9] phy/pcs_1000basex: Cleanup sgmii timer reload. --- liteeth/phy/pcs_1000basex.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index d750d95..6aff0ab 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -49,13 +49,11 @@ class PCSTX(LiteXModule): timer_en = Signal() self.sync += [ If(~timer_en | (timer == 0), - If(self.sgmii_speed == 0b00, - timer.eq(99) - ).Elif(self.sgmii_speed == 0b01, - timer.eq(9) - ).Elif(self.sgmii_speed == 0b10, - timer.eq(0) - ) + Case(self.sgmii_speed, { + 0b00: timer.eq(99), + 0b01: timer.eq( 9), + 0b10: timer.eq( 0), + }) ).Elif(timer_en, timer.eq(timer - 1) ) @@ -185,13 +183,11 @@ class PCSRX(LiteXModule): timer_en = Signal() self.sync += [ If(~timer_en | (timer == 0), - If(self.sgmii_speed == 0b00, - timer.eq(99) - ).Elif(self.sgmii_speed == 0b01, - timer.eq(9) - ).Elif(self.sgmii_speed == 0b10, - timer.eq(0) - ) + Case(self.sgmii_speed, { + 0b00: timer.eq(99), + 0b01: timer.eq( 9), + 0b10: timer.eq( 0), + }) ).Elif(timer_en, timer.eq(timer - 1) ) From 7e602c406dd659f5a60a244a31e072181ed886c4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 09:28:09 +0200 Subject: [PATCH 2/9] phy/pcs_1000basex: Replace self.lp_abi.o[0] with is_sgmii to ease understanding. --- liteeth/phy/pcs_1000basex.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index 6aff0ab..2a17532 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -348,8 +348,8 @@ class PCS(LiteXModule): # Detect that link is down: # - 1000BASE-X : linkup can be inferred by non-empty reg. # - SGMII : linkup is indicated with bit 15. - linkdown.eq((self.lp_abi.o[0] & ~self.lp_abi.o[15]) | (self.lp_abi.o == 0)), - self.tx.sgmii_speed.eq(Mux(self.lp_abi.o[0], + linkdown.eq((is_sgmii & ~self.lp_abi.o[15]) | (self.lp_abi.o == 0)), + self.tx.sgmii_speed.eq(Mux(is_sgmii, self.lp_abi.o[10:12], 0b10)), self.rx.sgmii_speed.eq(Mux(self.lp_abi.i[0], self.lp_abi.i[10:12], 0b10)) @@ -359,7 +359,7 @@ class PCS(LiteXModule): self.tx.config_reg.eq(Mux(tx_config_empty, 0, (is_sgmii) | # SGMII: SGMII in-use (~is_sgmii << 5) | # 1000BASE-X: Full-duplex - (Mux(self.lp_abi.o[0], # SGMII: Speed + (Mux(is_sgmii, # SGMII: Speed self.lp_abi.o[10:12], 0) << 10) | (is_sgmii << 12) | # SGMII: Full-duplex (autoneg_ack << 14) | # SGMII/1000BASE-X: Acknowledge Bit From e5746c8a8125d18eede8c39c4b45560c45a46666 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 09:47:46 +0200 Subject: [PATCH 3/9] phy/pcs_1000basex: Add missing RX Align during AUTONEG_WAIT_ABI state and enable/connect it on all PHYs. --- liteeth/phy/a7_1000basex.py | 6 +++--- liteeth/phy/k7_1000basex.py | 6 +++--- liteeth/phy/ku_1000basex.py | 6 +++--- liteeth/phy/pcs_1000basex.py | 3 +++ liteeth/phy/usp_gth_1000basex.py | 6 +++--- liteeth/phy/usp_gty_1000basex.py | 6 +++--- liteeth/phy/v7_1000basex.py | 6 +++--- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/liteeth/phy/a7_1000basex.py b/liteeth/phy/a7_1000basex.py index 14d8471..995a388 100644 --- a/liteeth/phy/a7_1000basex.py +++ b/liteeth/phy/a7_1000basex.py @@ -505,9 +505,9 @@ class A7_1000BASEX(LiteXModule): o_RXBYTEISALIGNED = Open(), o_RXBYTEREALIGN = Open(), o_RXCOMMADET = Open(), - i_RXCOMMADETEN = 0, - i_RXMCOMMAALIGNEN = 0, - i_RXPCOMMAALIGNEN = 0, + i_RXCOMMADETEN = 0b1, + i_RXMCOMMAALIGNEN = pcs.align, + i_RXPCOMMAALIGNEN = pcs.align, i_RXSLIDE = 0, # Receive Ports - RX Channel Bonding Ports o_RXCHANBONDSEQ = Open(), diff --git a/liteeth/phy/k7_1000basex.py b/liteeth/phy/k7_1000basex.py index d9e9526..a872c73 100644 --- a/liteeth/phy/k7_1000basex.py +++ b/liteeth/phy/k7_1000basex.py @@ -484,9 +484,9 @@ class K7_1000BASEX(LiteXModule): o_RXBYTEISALIGNED = Open(), o_RXBYTEREALIGN = Open(), o_RXCOMMADET = Open(), - i_RXCOMMADETEN = 1, - i_RXMCOMMAALIGNEN = 1, - i_RXPCOMMAALIGNEN = 1, + i_RXCOMMADETEN = 0b1, + i_RXMCOMMAALIGNEN = pcs.align, + i_RXPCOMMAALIGNEN = pcs.align, # Receive Ports - RX Channel Bonding Ports o_RXCHANBONDSEQ = Open(), diff --git a/liteeth/phy/ku_1000basex.py b/liteeth/phy/ku_1000basex.py index 2552fab..4177a12 100644 --- a/liteeth/phy/ku_1000basex.py +++ b/liteeth/phy/ku_1000basex.py @@ -536,7 +536,7 @@ class KU_1000BASEX(LiteXModule): i_RXCHBONDLEVEL = 0b000, i_RXCHBONDMASTER = 0b0, i_RXCHBONDSLAVE = 0b0, - i_RXCOMMADETEN = 0b0, + i_RXCOMMADETEN = 0b1, i_RXDFEAGCCTRL = 0b01, i_RXDFEAGCHOLD = 0b0, i_RXDFEAGCOVRDEN = 0b0, @@ -593,7 +593,7 @@ class KU_1000BASEX(LiteXModule): i_RXLPMLFKLOVRDEN = 0b0, i_RXLPMOSHOLD = 0b0, i_RXLPMOSOVRDEN = 0b0, - i_RXMCOMMAALIGNEN = 0b0, + i_RXMCOMMAALIGNEN = pcs.align, i_RXMONITORSEL = 0b00, i_RXOOBRESET = 0b0, i_RXOSCALRESET = 0b0, @@ -606,7 +606,7 @@ class KU_1000BASEX(LiteXModule): i_RXOSINTTESTOVRDEN = 0b0, i_RXOSOVRDEN = 0b0, i_RXOUTCLKSEL = 0b101, - i_RXPCOMMAALIGNEN = 0b0, + i_RXPCOMMAALIGNEN = pcs.align, i_RXPCSRESET = 0b0, i_RXPD = 0b00, i_RXPHALIGNEN = 0b0, diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index 2a17532..8892a5e 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -297,6 +297,8 @@ class PCS(LiteXModule): self.link_up = Signal() self.restart = Signal() + self.align = Signal() + self.lp_abi = BusSynchronizer(16, "eth_rx", "eth_tx") @@ -387,6 +389,7 @@ class PCS(LiteXModule): ) # ABILITY_DETECT fsm.act("AUTONEG_WAIT_ABI", + self.align.eq(1), self.tx.config_valid.eq(1), If(rx_config_reg_abi.o, NextState("AUTONEG_WAIT_ACK") diff --git a/liteeth/phy/usp_gth_1000basex.py b/liteeth/phy/usp_gth_1000basex.py index 04f253f..09fe8bf 100644 --- a/liteeth/phy/usp_gth_1000basex.py +++ b/liteeth/phy/usp_gth_1000basex.py @@ -612,7 +612,7 @@ class USP_GTH_1000BASEX(LiteXModule): i_RXCHBONDLEVEL = 0b000, i_RXCHBONDMASTER = 0b0, i_RXCHBONDSLAVE = 0b0, - i_RXCOMMADETEN = 0b0, + i_RXCOMMADETEN = 0b1, i_RXDFEAGCCTRL = 0b01, i_RXDFEAGCHOLD = 0b0, i_RXDFEAGCOVRDEN = 0b0, @@ -668,14 +668,14 @@ class USP_GTH_1000BASEX(LiteXModule): i_RXLPMLFKLOVRDEN = 0b0, i_RXLPMOSHOLD = 0b0, i_RXLPMOSOVRDEN = 0b0, - i_RXMCOMMAALIGNEN = 0b0, + i_RXMCOMMAALIGNEN = pcs.align, i_RXMONITORSEL = 0b00, i_RXOOBRESET = 0b0, i_RXOSCALRESET = 0b0, i_RXOSHOLD = 0b0, i_RXOSOVRDEN = 0b0, i_RXOUTCLKSEL = 0b101, - i_RXPCOMMAALIGNEN = 0b0, + i_RXPCOMMAALIGNEN = pcs.align, i_RXPCSRESET = 0b0, i_RXPD = 0b00, i_RXPHALIGNEN = 0b0, diff --git a/liteeth/phy/usp_gty_1000basex.py b/liteeth/phy/usp_gty_1000basex.py index 5e1f3c8..4125fae 100644 --- a/liteeth/phy/usp_gty_1000basex.py +++ b/liteeth/phy/usp_gty_1000basex.py @@ -641,7 +641,7 @@ class USP_GTY_1000BASEX(LiteXModule): i_RXCHBONDSLAVE = 0b0, i_RXCKCALRESET = 0b0, i_RXCKCALSTART = 0b0, - i_RXCOMMADETEN = 0b0, + i_RXCOMMADETEN = 0b1, i_RXDFEAGCHOLD = 0b0, i_RXDFEAGCOVRDEN = 0b0, i_RXDFECFOKFCNUM = 0b0, @@ -704,14 +704,14 @@ class USP_GTY_1000BASEX(LiteXModule): i_RXLPMLFKLOVRDEN = 0b0, i_RXLPMOSHOLD = 0b0, i_RXLPMOSOVRDEN = 0b0, - i_RXMCOMMAALIGNEN = 0b0, + i_RXMCOMMAALIGNEN = pcs.align, i_RXMONITORSEL = 0b00, i_RXOOBRESET = 0b0, i_RXOSCALRESET = 0b0, i_RXOSHOLD = 0b0, i_RXOSOVRDEN = 0b0, i_RXOUTCLKSEL = 0b101, - i_RXPCOMMAALIGNEN = 0b0, + i_RXPCOMMAALIGNEN = pcs.align, i_RXPCSRESET = 0b0, i_RXPD = 0b00, i_RXPHALIGN = 0b0, diff --git a/liteeth/phy/v7_1000basex.py b/liteeth/phy/v7_1000basex.py index 7b899d6..d5bbdf4 100644 --- a/liteeth/phy/v7_1000basex.py +++ b/liteeth/phy/v7_1000basex.py @@ -479,9 +479,9 @@ class V7_1000BASEX(LiteXModule): o_RXBYTEISALIGNED = Open(), o_RXBYTEREALIGN = Open(), o_RXCOMMADET = Open(), - i_RXCOMMADETEN = 1, - i_RXMCOMMAALIGNEN = 1, - i_RXPCOMMAALIGNEN = 1, + i_RXCOMMADETEN = 0b1, + i_RXMCOMMAALIGNEN = pcs.align, + i_RXPCOMMAALIGNEN = pcs.align, # Receive Ports - RX Channel Bonding Ports o_RXCHANBONDSEQ = Open(), From 04fc8882855426111c8aee6a569fed412fd48153 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 09:52:15 +0200 Subject: [PATCH 4/9] liteeth/phy/pcs_1000basex: Avoid deadlock situation in AUTONEG_WAIT_ABI if receiving ACKNOWLEDGE instead of ABILITY. --- liteeth/phy/pcs_1000basex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index 8892a5e..8aeff39 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -394,7 +394,7 @@ class PCS(LiteXModule): If(rx_config_reg_abi.o, NextState("AUTONEG_WAIT_ACK") ), - If(checker_tick & ~checker_ok, + If((checker_tick & ~checker_ok) | rx_config_reg_ack.o, self.restart.eq(1), NextState("AUTONEG_BREAKLINK") ) From 8a3e0a23aaf2a34b1a7ab52e13802d1c813a54ff Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 10:06:45 +0200 Subject: [PATCH 5/9] phy/a7_1000basex: Use ALIGN_COMMA_WORD/RXCDR_CFG from Xilinx wizard. --- liteeth/phy/a7_1000basex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/liteeth/phy/a7_1000basex.py b/liteeth/phy/a7_1000basex.py index 995a388..2c31087 100644 --- a/liteeth/phy/a7_1000basex.py +++ b/liteeth/phy/a7_1000basex.py @@ -90,7 +90,7 @@ class A7_1000BASEX(LiteXModule): # RX Byte and Word Alignment Attributes p_ALIGN_COMMA_DOUBLE = "FALSE", p_ALIGN_COMMA_ENABLE = 0b1111111111, - p_ALIGN_COMMA_WORD = 1, + p_ALIGN_COMMA_WORD = 2, p_ALIGN_MCOMMA_DET = "TRUE", p_ALIGN_MCOMMA_VALUE = 0b1010000011, p_ALIGN_PCOMMA_DET = "TRUE", @@ -212,7 +212,7 @@ class A7_1000BASEX(LiteXModule): # CDR Attributes p_RXCDR_CFG = { - 1.25e9 : 0x0001107FE086021101010, + 1.25e9 : 0x0000107FE106001041010, 3.125e9 : 0x0000107FE206001041010, }[self.linerate], p_RXCDR_FR_RESET_ON_EIDLE = 0b0, From 313e7a985cc5091d0879036afb30a1c7bf58a1ed Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 10:19:53 +0200 Subject: [PATCH 6/9] phy/pcs_1000basex: Simplify/Cleanup PCSTX. --- liteeth/phy/pcs_1000basex.py | 182 +++++++++++++++++------------------ 1 file changed, 86 insertions(+), 96 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index 8aeff39..e9d4cf5 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -19,128 +19,120 @@ from litex.soc.cores.code_8b10b import K, D, Encoder, Decoder from liteeth.common import * +# Constants / Helpers ------------------------------------------------------------------------------ + +SGMII_1000MBPS_SPEED = 0b10 +SGMII_100MBPS_SPEED = 0b01 +SGMII_10MBPS_SPEED = 0b00 + +CTYPE_C1 = 0b0 +CTYPE_C2 = 0b1 + +ITYPE_I1 = 0b0 +ITYPË_I2 = 0b1 + # PCS TX ------------------------------------------------------------------------------------------- class PCSTX(LiteXModule): def __init__(self, lsb_first=False): self.config_valid = Signal() - self.config_reg = Signal(16) - self.tx_valid = Signal() - self.tx_ready = Signal() - self.tx_data = Signal(8) + self.config_reg = Signal(16) # Config register (16-bit). + self.sgmii_speed = Signal(2) # SGMII speed. + self.sink = sink = stream.Endpoint([("data", 8)]) # Data input. - self.encoder = Encoder(lsb_first=lsb_first) + self.encoder = Encoder(lsb_first=lsb_first) # 8b/10b encoder. - # SGMII Speed Adaptation - self.sgmii_speed = Signal(2) + # Signals. + # -------- + count = Signal() # Byte counter for config register. + parity = Signal() # Parity for /R/ extension. + ctype = Signal() # Toggles config type. - # # # - - parity = Signal() - c_type = Signal() - self.sync += parity.eq(~parity) - - config_reg_buffer = Signal(16) - load_config_reg_buffer = Signal() - self.sync += If(load_config_reg_buffer, config_reg_buffer.eq(self.config_reg)) - - # Timer for SGMII data rates. - timer = Signal(max=1000) - timer_en = Signal() + # SGMII timer. + # ------------ + timer = Signal(max=100) + timer_done = Signal() + timer_enable = Signal() + self.comb += timer_done.eq(timer == 0) self.sync += [ - If(~timer_en | (timer == 0), + timer.eq(timer - 1), + If(~timer_enable | timer_done, Case(self.sgmii_speed, { - 0b00: timer.eq(99), - 0b01: timer.eq( 9), - 0b10: timer.eq( 0), + SGMII_10MBPS_SPEED : timer.eq(99), + SGMII_100MBPS_SPEED : timer.eq(9), + SGMII_1000MBPS_SPEED : timer.eq(0), }) - ).Elif(timer_en, - timer.eq(timer - 1) ) ] + # FSM. + # ---- self.fsm = fsm = FSM() fsm.act("START", If(self.config_valid, - self.tx_ready.eq(1), # Discard TX data if we are in config_reg phase. - load_config_reg_buffer.eq(1), self.encoder.k[0].eq(1), self.encoder.d[0].eq(K(28, 5)), - NextState("CONFIG_D") + NextValue(count, 0), + NextState("CONFIG-D") ).Else( - If(self.tx_valid, - # The first byte sent is replaced by /S/. - self.tx_ready.eq((timer == 0)), - timer_en.eq(1), + If(sink.valid, self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(27, 7)), + self.encoder.d[0].eq(K(27, 7)), # Start-of-packet /S/. NextState("DATA") ).Else( - self.tx_ready.eq(1), # Discard TX data. self.encoder.k[0].eq(1), self.encoder.d[0].eq(K(28, 5)), NextState("IDLE") ) ) ) - fsm.act("CONFIG_D", - If(c_type, - self.encoder.d[0].eq(D(2, 2)) - ).Else( - self.encoder.d[0].eq(D(21, 5)) - ), - NextValue(c_type, ~c_type), - NextState("CONFIG_REG_LSB") + fsm.act("CONFIG-D", + # Send Configuration Word. + Case(ctype, { + CTYPE_C1 : self.encoder.d[0].eq(D(21, 5)), # C1. + CTYPE_C2 : self.encoder.d[0].eq(D( 2, 2)), # C2. + }), + NextValue(ctype, ~ctype), + NextState("CONFIG-REG") ), - fsm.act("CONFIG_REG_LSB", - self.encoder.d[0].eq(config_reg_buffer[:8]), - NextState("CONFIG_REG_MSB") - ) - fsm.act("CONFIG_REG_MSB", - self.encoder.d[0].eq(config_reg_buffer[8:]), - NextState("START") + fsm.act("CONFIG-REG", + # Send Configuration Register. + NextValue(count, count + 1), + Case(count, { + 0 : self.encoder.d[0].eq(self.config_reg[:8]), # LSB. + 1 : self.encoder.d[0].eq(self.config_reg[8:]), # MSB. + }), + If(count == (2 - 1), NextState("START")) ) fsm.act("IDLE", - # Due to latency in the encoder, we read here the disparity - # just before the K28.5 was sent. K28.5 flips the disparity. - If(self.encoder.disparity[0], - # Correcting /I1/ (D5.6 preserves the disparity). - self.encoder.d[0].eq(D(5, 6)) - ).Else( - # Preserving /I2/ (D16.2 flips the disparity). - self.encoder.d[0].eq(D(16, 2)) - ), + # Send Idle characters and handle disparity. + Case(self.encoder.disparity[0], { + ITYPE_I1 : self.encoder.d[0].eq(D(5, 6)), # /I1/ preserves disparity. + ITYPË_I2 : self.encoder.d[0].eq(D(16, 2)), # /I2/ flips disparity. + }), NextState("START") ) fsm.act("DATA", - If(self.tx_valid, - self.tx_ready.eq((timer == 0)), - timer_en.eq(1), - self.encoder.d[0].eq(self.tx_data) + # Send Data frame. + timer_enable.eq(1), + sink.ready.eq(timer_done), + If(sink.valid, + self.encoder.d[0].eq(sink.data), ).Else( - self.tx_ready.eq(1), - # /T/ self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(29, 7)), - NextState("CARRIER_EXTEND_1") + self.encoder.d[0].eq(K(29, 7)), # End-of-frame /T/. + NextState("CARRIER-EXTEND") ) ) - fsm.act("CARRIER_EXTEND_1", - # /R/ + fsm.act("CARRIER-EXTEND", + # Extend carrier with /R/ symbols. self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(23, 7)), + self.encoder.d[0].eq(K(23, 7)), # Carrier Extend /R/. If(parity, NextState("START") - ).Else( - NextState("CARRIER_EXTEND_2") ) ) - fsm.act("CARRIER_EXTEND_2", - # /R/ - self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(23, 7)), - NextState("START") - ) + self.sync += parity.eq(~parity) # Toggle parity for /R/ extension. # PCS RX ------------------------------------------------------------------------------------------- @@ -178,23 +170,25 @@ class PCSRX(LiteXModule): first_preamble_byte = Signal() self.comb += self.rx_data.eq(Mux(first_preamble_byte, 0x55, self.decoder.d)) - # Timer for SGMII data rates. - timer = Signal(max=1000) - timer_en = Signal() + # SGMII Timer. + # ------------ + timer = Signal(max=100) + timer_enable = Signal() + timer_done = Signal() + self.comb += timer_done.eq(timer == 0) self.sync += [ - If(~timer_en | (timer == 0), + timer.eq(timer - 1), + If(~timer_enable | timer_done, Case(self.sgmii_speed, { - 0b00: timer.eq(99), - 0b01: timer.eq( 9), - 0b10: timer.eq( 0), + SGMII_10MBPS_SPEED : timer.eq(99), + SGMII_100MBPS_SPEED : timer.eq( 9), + SGMII_1000MBPS_SPEED : timer.eq( 0), }) - ).Elif(timer_en, - timer.eq(timer - 1) ) ] # Speed adaptation - self.comb += self.sample_en.eq(self.rx_en & (timer == 0)) + self.comb += self.sample_en.eq(self.rx_en & timer_done) self.fsm = fsm = FSM() fsm.act("START", @@ -204,7 +198,7 @@ class PCSRX(LiteXModule): ), If(self.decoder.d == K(27, 7), self.rx_en.eq(1), - timer_en.eq(1), + timer_enable.eq(1), first_preamble_byte.eq(1), NextState("DATA") ) @@ -228,7 +222,7 @@ class PCSRX(LiteXModule): If(self.decoder.k, If(self.decoder.d == K(27, 7), self.rx_en.eq(1), - timer_en.eq(1), + timer_enable.eq(1), first_preamble_byte.eq(1), NextState("DATA") ).Else( @@ -250,7 +244,7 @@ class PCSRX(LiteXModule): NextState("START") ).Else( self.rx_en.eq(1), - timer_en.eq(1) + timer_enable.eq(1) ) ) @@ -305,11 +299,7 @@ class PCS(LiteXModule): # # # # Endpoint interface. - self.comb += [ - self.tx.tx_valid.eq(self.sink.valid), - self.sink.ready.eq(self.tx.tx_ready), - self.tx.tx_data.eq(self.sink.data), - ] + self.comb += self.sink.connect(self.tx.sink, omit={"last_be", "error"}) rx_en_d = Signal() self.sync.eth_rx += [ From 19e1d1944420fc783cb5e41fa0c5e26883add385 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 11:40:13 +0200 Subject: [PATCH 7/9] phy/pcs_1000basex: Move PCS Gearbox. --- liteeth/phy/pcs_1000basex.py | 60 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index e9d4cf5..ca71216 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -19,7 +19,7 @@ from litex.soc.cores.code_8b10b import K, D, Encoder, Decoder from liteeth.common import * -# Constants / Helpers ------------------------------------------------------------------------------ +# PCS Constants / Helpers -------------------------------------------------------------------------- SGMII_1000MBPS_SPEED = 0b10 SGMII_100MBPS_SPEED = 0b01 @@ -31,6 +31,35 @@ CTYPE_C2 = 0b1 ITYPE_I1 = 0b0 ITYPË_I2 = 0b1 +# PCS Gearbox -------------------------------------------------------------------------------------- + +class PCSGearbox(LiteXModule): + def __init__(self): + self.tx_data = Signal(10) + self.tx_data_half = Signal(20) + self.rx_data_half = Signal(20) + self.rx_data = Signal(10) + + # # # + + # TX + buf = Signal(20) + self.sync.eth_tx += buf.eq(Cat(buf[10:], self.tx_data)) + self.sync.eth_tx_half += self.tx_data_half.eq(buf) + + # RX + phase_half = Signal() + phase_half_rereg = Signal() + self.sync.eth_rx_half += phase_half_rereg.eq(phase_half) + self.sync.eth_rx += [ + If(phase_half == phase_half_rereg, + self.rx_data.eq(self.rx_data_half[10:]) + ).Else( + self.rx_data.eq(self.rx_data_half[:10]) + ), + phase_half.eq(~phase_half), + ] + # PCS TX ------------------------------------------------------------------------------------------- class PCSTX(LiteXModule): @@ -248,35 +277,6 @@ class PCSRX(LiteXModule): ) ) -# PCS Gearbox -------------------------------------------------------------------------------------- - -class PCSGearbox(LiteXModule): - def __init__(self): - self.tx_data = Signal(10) - self.tx_data_half = Signal(20) - self.rx_data_half = Signal(20) - self.rx_data = Signal(10) - - # # # - - # TX - buf = Signal(20) - self.sync.eth_tx += buf.eq(Cat(buf[10:], self.tx_data)) - self.sync.eth_tx_half += self.tx_data_half.eq(buf) - - # RX - phase_half = Signal() - phase_half_rereg = Signal() - self.sync.eth_rx_half += phase_half_rereg.eq(phase_half) - self.sync.eth_rx += [ - If(phase_half == phase_half_rereg, - self.rx_data.eq(self.rx_data_half[10:]) - ).Else( - self.rx_data.eq(self.rx_data_half[:10]) - ), - phase_half.eq(~phase_half), - ] - # PCS ---------------------------------------------------------------------------------------------- class PCS(LiteXModule): From faf426f54a55964020f8bbf9def072e4aeb6b7b4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 11:46:39 +0200 Subject: [PATCH 8/9] phy/pcs_1000basex: Other cosmetic cleanup on PCSTX. --- liteeth/phy/pcs_1000basex.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index ca71216..fc4f7ef 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -64,20 +64,20 @@ class PCSGearbox(LiteXModule): class PCSTX(LiteXModule): def __init__(self, lsb_first=False): - self.config_valid = Signal() - self.config_reg = Signal(16) # Config register (16-bit). - self.sgmii_speed = Signal(2) # SGMII speed. + self.config_valid = Signal() # Config valid. + self.config_reg = Signal(16) # Config register (16-bit). + self.sgmii_speed = Signal(2) # SGMII speed. self.sink = sink = stream.Endpoint([("data", 8)]) # Data input. - self.encoder = Encoder(lsb_first=lsb_first) # 8b/10b encoder. + self.encoder = Encoder(lsb_first=lsb_first) # 8b/10b Encoder. # Signals. # -------- - count = Signal() # Byte counter for config register. - parity = Signal() # Parity for /R/ extension. - ctype = Signal() # Toggles config type. + count = Signal() # Byte counter for config register. + parity = Signal() # Parity for /R/ extension. + ctype = Signal() # Toggles config type. - # SGMII timer. + # SGMII Timer. # ------------ timer = Signal(max=100) timer_done = Signal() @@ -98,19 +98,18 @@ class PCSTX(LiteXModule): # ---- self.fsm = fsm = FSM() fsm.act("START", + self.encoder.k[0].eq(1), + self.encoder.d[0].eq(K(28, 5)), + # Wait for valid Config. If(self.config_valid, - self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(28, 5)), NextValue(count, 0), NextState("CONFIG-D") + # Wait for valid Data. ).Else( If(sink.valid, - self.encoder.k[0].eq(1), self.encoder.d[0].eq(K(27, 7)), # Start-of-packet /S/. NextState("DATA") ).Else( - self.encoder.k[0].eq(1), - self.encoder.d[0].eq(K(28, 5)), NextState("IDLE") ) ) From 16998377f63bfcd79bdd228abbc1fb8e8873ef41 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 Oct 2024 11:54:31 +0200 Subject: [PATCH 9/9] phy/pcs_1000basex: Simplify/Cleanup PCSRX. --- liteeth/phy/pcs_1000basex.py | 127 +++++++++++++++++------------------ 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/liteeth/phy/pcs_1000basex.py b/liteeth/phy/pcs_1000basex.py index fc4f7ef..7c369d0 100644 --- a/liteeth/phy/pcs_1000basex.py +++ b/liteeth/phy/pcs_1000basex.py @@ -25,12 +25,6 @@ SGMII_1000MBPS_SPEED = 0b10 SGMII_100MBPS_SPEED = 0b01 SGMII_10MBPS_SPEED = 0b00 -CTYPE_C1 = 0b0 -CTYPE_C2 = 0b1 - -ITYPE_I1 = 0b0 -ITYPË_I2 = 0b1 - # PCS Gearbox -------------------------------------------------------------------------------------- class PCSGearbox(LiteXModule): @@ -117,8 +111,8 @@ class PCSTX(LiteXModule): fsm.act("CONFIG-D", # Send Configuration Word. Case(ctype, { - CTYPE_C1 : self.encoder.d[0].eq(D(21, 5)), # C1. - CTYPE_C2 : self.encoder.d[0].eq(D( 2, 2)), # C2. + 0b0 : self.encoder.d[0].eq(D(21, 5)), # /C1/. + 0b1 : self.encoder.d[0].eq(D( 2, 2)), # /C2/. }), NextValue(ctype, ~ctype), NextState("CONFIG-REG") @@ -133,15 +127,15 @@ class PCSTX(LiteXModule): If(count == (2 - 1), NextState("START")) ) fsm.act("IDLE", - # Send Idle characters and handle disparity. + # Send Idle words and handle disparity. Case(self.encoder.disparity[0], { - ITYPE_I1 : self.encoder.d[0].eq(D(5, 6)), # /I1/ preserves disparity. - ITYPË_I2 : self.encoder.d[0].eq(D(16, 2)), # /I2/ flips disparity. + 0b0 : self.encoder.d[0].eq(D(5, 6)), # /I1/ (Preserves disparity). + 0b1 : self.encoder.d[0].eq(D(16, 2)), # /I2/ (Flips disparity). }), NextState("START") ) fsm.act("DATA", - # Send Data frame. + # Send Data. timer_enable.eq(1), sink.ready.eq(timer_done), If(sink.valid, @@ -166,8 +160,9 @@ class PCSTX(LiteXModule): class PCSRX(LiteXModule): def __init__(self, lsb_first=False): - self.rx_en = Signal() - self.rx_data = Signal(8) + self.rx_en = Signal() + self.rx_data = Signal(8) + self.sample_en = Signal() self.seen_valid_ci = Signal() self.seen_config_reg = Signal() @@ -177,26 +172,12 @@ class PCSRX(LiteXModule): # SGMII Speed Adaptation. self.sgmii_speed = Signal(2) - self.sample_en = Signal() # # # - config_reg_lsb = Signal(8) - load_config_reg_lsb = Signal() - load_config_reg_msb = Signal() - self.sync += [ - self.seen_config_reg.eq(0), - If(load_config_reg_lsb, - config_reg_lsb.eq(self.decoder.d) - ), - If(load_config_reg_msb, - self.config_reg.eq(Cat(config_reg_lsb, self.decoder.d)), - self.seen_config_reg.eq(1) - ) - ] - - first_preamble_byte = Signal() - self.comb += self.rx_data.eq(Mux(first_preamble_byte, 0x55, self.decoder.d)) + # Signals. + # -------- + count = Signal() # Byte counter for config register. # SGMII Timer. # ------------ @@ -218,66 +199,77 @@ class PCSRX(LiteXModule): # Speed adaptation self.comb += self.sample_en.eq(self.rx_en & timer_done) + # FSM. + # ---- self.fsm = fsm = FSM() fsm.act("START", + # Wait for a K-character. If(self.decoder.k, + # K-character is Config or Idle K28.5. If(self.decoder.d == K(28, 5), - NextState("K28_5") + NextValue(count, 0), + NextState("CONFIG-D-OR-IDLE") ), + # K-character is Start-of-packet /S/. If(self.decoder.d == K(27, 7), - self.rx_en.eq(1), timer_enable.eq(1), - first_preamble_byte.eq(1), + self.rx_en.eq(1), + self.rx_data.eq(0x55), # First Preamble Byte. NextState("DATA") ) ) ) - fsm.act("K28_5", - NextState("START"), + fsm.act("CONFIG-D-OR-IDLE", + NextState("ERROR"), If(~self.decoder.k, - If((self.decoder.d == D(21, 5)) | (self.decoder.d == D(2, 2)), + # Check for Configuration Word. + If((self.decoder.d == D(21, 5)) | # /C1/. + (self.decoder.d == D( 2, 2)), # /C2/. self.seen_valid_ci.eq(1), - NextState("CONFIG_REG_LSB") + NextState("CONFIG-REG") ), - If((self.decoder.d == D(5, 6)) | (self.decoder.d == D(16, 2)), - # idle + # Check for Idle Word. + If((self.decoder.d == D( 5, 6)) | # /I1/. + (self.decoder.d == D(16, 2)), # /I2/. self.seen_valid_ci.eq(1), NextState("START") - ), - ) - ) - fsm.act("CONFIG_REG_LSB", - If(self.decoder.k, - If(self.decoder.d == K(27, 7), - self.rx_en.eq(1), - timer_enable.eq(1), - first_preamble_byte.eq(1), - NextState("DATA") - ).Else( - NextState("START") # error ) - ).Else( - load_config_reg_lsb.eq(1), - NextState("CONFIG_REG_MSB") ) ) - fsm.act("CONFIG_REG_MSB", + fsm.act("CONFIG-REG", + NextState("ERROR"), If(~self.decoder.k, - load_config_reg_msb.eq(1) - ), - NextState("START") + # Receive for Configuration Register. + NextState("CONFIG-REG"), + NextValue(count, count + 1), + Case(count, { + 0b0 : NextValue(self.config_reg[:8], self.decoder.d), # LSB. + 0b1 : NextValue(self.config_reg[8:], self.decoder.d), # MSB. + }), + If(count == (2 - 1), + self.seen_config_reg.eq(1), + NextState("START") + ) + ) ) fsm.act("DATA", - If(self.decoder.k, - NextState("START") - ).Else( + NextState("START"), + If(~self.decoder.k, + # Receive Data. + timer_enable.eq(1), self.rx_en.eq(1), - timer_enable.eq(1) + self.rx_data.eq(self.decoder.d), + NextState("DATA") ) ) + fsm.act("ERROR", + NextState("START") + ) # PCS ---------------------------------------------------------------------------------------------- +# FIXME: Needs similar cleanup than PCSTX/RX. + class PCS(LiteXModule): def __init__(self, lsb_first=False, check_period=6e-3, more_ack_time=10e-3): self.tx = ClockDomainsRenamer("eth_tx")(PCSTX(lsb_first=lsb_first)) @@ -292,14 +284,14 @@ class PCS(LiteXModule): self.restart = Signal() self.align = Signal() - self.lp_abi = BusSynchronizer(16, "eth_rx", "eth_tx") # # # - - # Endpoint interface. + + # Sink -> TX. self.comb += self.sink.connect(self.tx.sink, omit={"last_be", "error"}) + # RX -> Source. rx_en_d = Signal() self.sync.eth_rx += [ rx_en_d.eq(self.rx.rx_en), @@ -308,11 +300,12 @@ class PCS(LiteXModule): ] self.comb += self.source.last.eq(~self.rx.rx_en & rx_en_d) - # Main module. + # Seen Valid Synchronizer. seen_valid_ci = PulseSynchronizer("eth_rx", "eth_tx") self.submodules += seen_valid_ci self.comb += seen_valid_ci.i.eq(self.rx.seen_valid_ci) + # Checker. checker_max_val = ceil(check_period*125e6) checker_counter = Signal(max=checker_max_val+1) checker_tick = Signal()