Merge pull request #174 from enjoy-digital/1000basex_fixes_cleanup

1000basex fixes cleanup.
This commit is contained in:
enjoy-digital 2024-10-15 12:51:58 +02:00 committed by GitHub
commit efcbebe558
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 250 additions and 269 deletions

View File

@ -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,
@ -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(),

View File

@ -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(),

View File

@ -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,

View File

@ -19,244 +19,11 @@ from litex.soc.cores.code_8b10b import K, D, Encoder, Decoder
from liteeth.common import *
# PCS TX -------------------------------------------------------------------------------------------
# PCS Constants / Helpers --------------------------------------------------------------------------
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.encoder = Encoder(lsb_first=lsb_first)
# SGMII Speed Adaptation
self.sgmii_speed = Signal(2)
# # #
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()
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)
)
).Elif(timer_en,
timer.eq(timer - 1)
)
]
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")
).Else(
If(self.tx_valid,
# The first byte sent is replaced by /S/.
self.tx_ready.eq((timer == 0)),
timer_en.eq(1),
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(K(27, 7)),
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_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("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))
),
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)
).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")
)
)
fsm.act("CARRIER_EXTEND_1",
# /R/
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(K(23, 7)),
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")
)
# PCS RX -------------------------------------------------------------------------------------------
class PCSRX(LiteXModule):
def __init__(self, lsb_first=False):
self.rx_en = Signal()
self.rx_data = Signal(8)
self.seen_valid_ci = Signal()
self.seen_config_reg = Signal()
self.config_reg = Signal(16)
self.decoder = Decoder(lsb_first=lsb_first)
# 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))
# Timer for SGMII data rates.
timer = Signal(max=1000)
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)
)
).Elif(timer_en,
timer.eq(timer - 1)
)
]
# Speed adaptation
self.comb += self.sample_en.eq(self.rx_en & (timer == 0))
self.fsm = fsm = FSM()
fsm.act("START",
If(self.decoder.k,
If(self.decoder.d == K(28, 5),
NextState("K28_5")
),
If(self.decoder.d == K(27, 7),
self.rx_en.eq(1),
timer_en.eq(1),
first_preamble_byte.eq(1),
NextState("DATA")
)
)
)
fsm.act("K28_5",
NextState("START"),
If(~self.decoder.k,
If((self.decoder.d == D(21, 5)) | (self.decoder.d == D(2, 2)),
self.seen_valid_ci.eq(1),
NextState("CONFIG_REG_LSB")
),
If((self.decoder.d == D(5, 6)) | (self.decoder.d == D(16, 2)),
# idle
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_en.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",
If(~self.decoder.k,
load_config_reg_msb.eq(1)
),
NextState("START")
)
fsm.act("DATA",
If(self.decoder.k,
NextState("START")
).Else(
self.rx_en.eq(1),
timer_en.eq(1)
)
)
SGMII_1000MBPS_SPEED = 0b10
SGMII_100MBPS_SPEED = 0b01
SGMII_10MBPS_SPEED = 0b00
# PCS Gearbox --------------------------------------------------------------------------------------
@ -287,8 +54,222 @@ class PCSGearbox(LiteXModule):
phase_half.eq(~phase_half),
]
# PCS TX -------------------------------------------------------------------------------------------
class PCSTX(LiteXModule):
def __init__(self, lsb_first=False):
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.
# Signals.
# --------
count = Signal() # Byte counter for config register.
parity = Signal() # Parity for /R/ extension.
ctype = Signal() # Toggles config type.
# SGMII Timer.
# ------------
timer = Signal(max=100)
timer_done = Signal()
timer_enable = Signal()
self.comb += timer_done.eq(timer == 0)
self.sync += [
timer.eq(timer - 1),
If(~timer_enable | timer_done,
Case(self.sgmii_speed, {
SGMII_10MBPS_SPEED : timer.eq(99),
SGMII_100MBPS_SPEED : timer.eq(9),
SGMII_1000MBPS_SPEED : timer.eq(0),
})
)
]
# FSM.
# ----
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,
NextValue(count, 0),
NextState("CONFIG-D")
# Wait for valid Data.
).Else(
If(sink.valid,
self.encoder.d[0].eq(K(27, 7)), # Start-of-packet /S/.
NextState("DATA")
).Else(
NextState("IDLE")
)
)
)
fsm.act("CONFIG-D",
# Send Configuration Word.
Case(ctype, {
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")
),
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",
# Send Idle words and handle disparity.
Case(self.encoder.disparity[0], {
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.
timer_enable.eq(1),
sink.ready.eq(timer_done),
If(sink.valid,
self.encoder.d[0].eq(sink.data),
).Else(
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(K(29, 7)), # End-of-frame /T/.
NextState("CARRIER-EXTEND")
)
)
fsm.act("CARRIER-EXTEND",
# Extend carrier with /R/ symbols.
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(K(23, 7)), # Carrier Extend /R/.
If(parity,
NextState("START")
)
)
self.sync += parity.eq(~parity) # Toggle parity for /R/ extension.
# PCS RX -------------------------------------------------------------------------------------------
class PCSRX(LiteXModule):
def __init__(self, lsb_first=False):
self.rx_en = Signal()
self.rx_data = Signal(8)
self.sample_en = Signal()
self.seen_valid_ci = Signal()
self.seen_config_reg = Signal()
self.config_reg = Signal(16)
self.decoder = Decoder(lsb_first=lsb_first)
# SGMII Speed Adaptation.
self.sgmii_speed = Signal(2)
# # #
# Signals.
# --------
count = Signal() # Byte counter for config register.
# SGMII Timer.
# ------------
timer = Signal(max=100)
timer_enable = Signal()
timer_done = Signal()
self.comb += timer_done.eq(timer == 0)
self.sync += [
timer.eq(timer - 1),
If(~timer_enable | timer_done,
Case(self.sgmii_speed, {
SGMII_10MBPS_SPEED : timer.eq(99),
SGMII_100MBPS_SPEED : timer.eq( 9),
SGMII_1000MBPS_SPEED : timer.eq( 0),
})
)
]
# 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),
NextValue(count, 0),
NextState("CONFIG-D-OR-IDLE")
),
# K-character is Start-of-packet /S/.
If(self.decoder.d == K(27, 7),
timer_enable.eq(1),
self.rx_en.eq(1),
self.rx_data.eq(0x55), # First Preamble Byte.
NextState("DATA")
)
)
)
fsm.act("CONFIG-D-OR-IDLE",
NextState("ERROR"),
If(~self.decoder.k,
# 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")
),
# 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",
NextState("ERROR"),
If(~self.decoder.k,
# 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",
NextState("START"),
If(~self.decoder.k,
# Receive Data.
timer_enable.eq(1),
self.rx_en.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))
@ -301,18 +282,16 @@ class PCS(LiteXModule):
self.link_up = Signal()
self.restart = Signal()
self.align = Signal()
self.lp_abi = BusSynchronizer(16, "eth_rx", "eth_tx")
# # #
# 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),
]
# 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),
@ -321,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()
@ -352,8 +332,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))
@ -363,7 +343,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
@ -391,11 +371,12 @@ 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")
),
If(checker_tick & ~checker_ok,
If((checker_tick & ~checker_ok) | rx_config_reg_ack.o,
self.restart.eq(1),
NextState("AUTONEG_BREAKLINK")
)

View File

@ -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,

View File

@ -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,

View File

@ -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(),