usb3_pipe/lfps: document and make LFPSChecker/Generator generic.

This commit is contained in:
Florent Kermarrec 2019-12-13 11:50:54 +01:00
parent 34d201e208
commit 1147f23080
3 changed files with 65 additions and 35 deletions

View File

@ -74,7 +74,7 @@ class TestLFPS(unittest.TestCase):
def test_lfps_generator(self):
def lfps_generator(dut):
yield dut.polling.eq(1)
yield dut.generate.eq(1)
for i in range(int(1e4)):
yield
dut.run = False
@ -100,7 +100,7 @@ class TestLFPS(unittest.TestCase):
sys_clk_freq = 100e6
lfps_clk_freq = 25e6
dut = lfps.LFPSGenerator(sys_clk_freq, lfps_clk_freq)
dut = lfps.LFPSGenerator(lfps.PollingLFPS, sys_clk_freq, lfps_clk_freq)
dut.run = True
generators = [
lfps_generator(dut),

View File

@ -36,7 +36,7 @@ class USB3PIPE(Module):
source = self.source
# LFPS -------------------------------------------------------------------------------------
lfps = LFPSUnit(sys_clk_freq=sys_clk_freq, serdes=serdes)
lfps = LFPSUnit(serdes=serdes, sys_clk_freq=sys_clk_freq)
self.submodules.lfps = lfps
# TS----------------------------------------------------------------------------------------

View File

@ -1,6 +1,23 @@
# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
# License: BSD
# LFPS is the first signaling to happen during the initialization of the USB3.0 link.
# LFPS allows partners to exchange Out Of Band (OOB) controls/commands and consists of bursts where
# a "slow" clock is generated (between 10M-50MHz) for a specific duration and with a specific repeat
# period. After the burst, the transceiver is put in electrical idle mode (same electrical level on
# P/N pairs while in nominal mode P/N pairs always have an opposite level):
#
# Transceiver level/mode: _=0, -=1 x=electrical idle
# |-_-_-_-xxxxxxxxxxxxxxxxxxxx|-_-_-_-xxxxxxxxxxxxxxxxxxxx|...
# |<burst> |<burst> |...
# |<-----repeat period------->|<--------period----------->|...
#
# A LFPS pattern is identified by a burst duration and repeat period.
#
# To be able generate and receive LFPS, a transceiver needs to be able put its TX in electrical idle
# and to detect RX electrical idle.
from math import ceil
from migen import *
@ -13,6 +30,7 @@ lfps_clk_freq_min = 1/100e-9
lfps_clk_freq_max = 1/20e-9
class LFPSTiming:
"""LPFS timings with typical, minimum and maximum timing values."""
def __init__(self, t_typ=None, t_min=None, t_max=None):
self.t_typ = t_typ
self.t_min = t_min
@ -22,6 +40,7 @@ class LFPSTiming:
self.range = (t_min, t_max)
class LFPS:
"""LPFS patterns with burst and repeat timings."""
def __init__(self, burst, repeat=None, cycles=None):
self.burst = burst
self.repeat = repeat
@ -30,7 +49,7 @@ class LFPS:
def ns_to_cycles(clk_freq, t):
return ceil(t*clk_freq)
# LFPS Definitions ---------------------------------------------------------------------------------
# LFPS Patterns ------------------------------------------------------------------------------------
PollingLFPSBurst = LFPSTiming(t_typ=1.0e-6, t_min=0.6e-6, t_max=1.4e-6)
PollingLFPSRepeat = LFPSTiming(t_typ=10.0e-6, t_min=6.0e-6, t_max=14.0e-6)
@ -42,9 +61,17 @@ ResetLFPS = LFPS(burst=ResetLFPSBurst)
# LFPS Checker -------------------------------------------------------------------------------------
class LFPSChecker(Module):
def __init__(self, sys_clk_freq):
self.idle = Signal() # i
self.polling = Signal() # o
"""LFPS Checker
Generic LFPS checker.
This module is able to detect a specific LFPS pattern by analyzing the RX electrical idle signal
of the transceiver. It generates the pattern internally and try lock it to the RX electical idle
signal. When locked a detection is reported.
"""
def __init__(self, lfps_pattern, sys_clk_freq):
self.idle = Signal() # i
self.detect = Signal() # o
# # #
@ -52,9 +79,9 @@ class LFPSChecker(Module):
idle = Signal()
self.specials += MultiReg(self.idle, idle)
# Polling LFPS Detection -------------------------------------------------------------------
burst_cycles = ns_to_cycles(sys_clk_freq, PollingLFPS.burst.t_typ)
repeat_cycles = ns_to_cycles(sys_clk_freq, PollingLFPS.repeat.t_typ)
# Polling LFPS Detection ------------------------------------------------------------------
burst_cycles = ns_to_cycles(sys_clk_freq, lfps_pattern.burst.t_typ)
repeat_cycles = ns_to_cycles(sys_clk_freq, lfps_pattern.repeat.t_typ)
self.count = count = Signal(max=max(burst_cycles, repeat_cycles))
self.found = found = Signal()
@ -71,7 +98,7 @@ class LFPSChecker(Module):
NextValue(count, count - 1)
),
If(found & (idle == 0),
self.polling.eq(1),
self.detect.eq(1),
NextValue(found, 0)
),
)
@ -89,8 +116,8 @@ class LFPSChecker(Module):
class LFPSBurstGenerator(Module):
"""LFPS Burst Generator
Generate a LFPS Burst of configurable length on the TX lanes. The LFPS clock is generated by
sending a alternating ones/zeroes data pattern on the parallel interface of the transceiver.
Generate a LFPS burst of configurable length on the TX lane. The LFPS clock is generated by
sending an alternating ones/zeroes data pattern on the parallel interface of the transceiver.
"""
def __init__(self, sys_clk_freq, lfps_clk_freq):
# Control
@ -142,14 +169,13 @@ class LFPSBurstGenerator(Module):
class LFPSGenerator(Module):
"""LFPS Generator
Generate LFPS signals on the TX lanes from simple user controls (User just have to sets LFPS
control signal to 1 to generate a specific LFPS, this module will handle LFPS clock genration,
LFPS Burst length and repeat).
Generate a specific LFPS pattern on the TX lane. This module handles LFPS clock generation, LFPS
burst generation and repetition.
"""
def __init__(self, sys_clk_freq, lfps_clk_freq):
def __init__(self, lfps_pattern, sys_clk_freq, lfps_clk_freq):
# Control
self.polling = Signal() # i
self.count = Signal(16) # o
self.generate = Signal() # i
self.count = Signal(16) # o
# Transceiver
self.tx_idle = Signal() # o
@ -166,11 +192,11 @@ class LFPSGenerator(Module):
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.tx_idle.eq(0),
If(self.polling,
If(self.generate,
self.tx_idle.eq(1),
NextValue(burst_generator.start, 1),
NextValue(burst_generator.length, int(sys_clk_freq*PollingLFPSBurst.t_typ)),
NextValue(burst_repeat_count, int(sys_clk_freq*PollingLFPSRepeat.t_typ)),
NextValue(burst_generator.length, int(sys_clk_freq*lfps_pattern.burst.t_typ)),
NextValue(burst_repeat_count, int(sys_clk_freq*lfps_pattern.repeat.t_typ)),
NextState("RUN")
).Else(
NextValue(self.count, 0)
@ -190,7 +216,11 @@ class LFPSGenerator(Module):
# LFPS Unit ----------------------------------------------------------------------------------------
class LFPSUnit(Module):
def __init__(self, sys_clk_freq, serdes):
"""LFPS Unit
Detect/generate the LFPS patterns required for a USB3.0 link with simple control/status signals.
"""
def __init__(self, serdes, sys_clk_freq, lfps_clk_freq=25e6):
self.rx_polling = Signal() # o
self.tx_idle = Signal() # i
self.tx_polling = Signal() # i
@ -198,22 +228,22 @@ class LFPSUnit(Module):
# # #
# LFPS Checker -----------------------------------------------------------------------------
checker = LFPSChecker(sys_clk_freq=sys_clk_freq)
self.submodules += checker
self.comb += checker.idle.eq(serdes.rx_idle)
self.comb += self.rx_polling.eq(checker.polling)
# LFPS Checkers ----------------------------------------------------------------------------
polling_checker = LFPSChecker(PollingLFPS, sys_clk_freq)
self.submodules += polling_checker
self.comb += polling.checker.idle.eq(serdes.rx_idle)
self.comb += self.rx_polling.eq(polling_checker.detect)
# LFPS Generator ---------------------------------------------------------------------------
generator = LFPSGenerator(sys_clk_freq=sys_clk_freq, lfps_clk_freq=25e6)
self.submodules += generator
# LFPS Generators --------------------------------------------------------------------------
polling_generator = LFPSGenerator(PollingLFPS, sys_clk_freq, lfps_clk_freq)
self.submodules += polling_generator
self.comb += [
If(self.tx_polling,
generator.polling.eq(1),
serdes.tx_idle.eq(generator.tx_idle),
serdes.tx_pattern.eq(generator.tx_pattern)
polling_generator.generate.eq(1),
serdes.tx_idle.eq(polling_generator.tx_idle),
serdes.tx_pattern.eq(polling_generator.tx_pattern)
).Else(
serdes.tx_idle.eq(self.tx_idle)
),
self.tx_count.eq(generator.count)
self.tx_count.eq(polling_generator.count)
]