253 lines
9.5 KiB
Python

#
# This file is part of USB3-PIPE project.
#
# Copyright (c) 2019-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
# 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------->|<-----repeat 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 *
from migen.genlib.cdc import MultiReg
from migen.genlib.misc import WaitTimer
# Constants/Helpers --------------------------------------------------------------------------------
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
self.t_max = t_max
assert t_min is not None
assert t_max is not None
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
self.cycles = None
def ns_to_cycles(clk_freq, t):
return ceil(t*clk_freq)
# 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)
PollingLFPS = LFPS(burst=PollingLFPSBurst, repeat=PollingLFPSRepeat)
ResetLFPSBurst = LFPSTiming(t_typ=100.0e-3, t_min=80.0e-3, t_max=120.0e-3)
ResetLFPS = LFPS(burst=ResetLFPSBurst)
# LFPS Checker -------------------------------------------------------------------------------------
class LFPSChecker(Module):
"""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
# # #
# Idle Resynchronization -------------------------------------------------------------------
idle = Signal()
self.specials += MultiReg(self.idle, idle)
# 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()
self.submodules.fsm = fsm = FSM(reset_state="TBURST")
fsm.act("TBURST",
If(count == 0,
If(idle == 0,
NextValue(count, burst_cycles - 1),
).Else(
NextValue(count, repeat_cycles - 2*burst_cycles - 1),
NextState("TREPEAT")
)
).Else(
NextValue(count, count - 1)
),
If(found & (idle == 0),
self.detect.eq(1),
NextValue(found, 0)
),
)
fsm.act("TREPEAT",
NextValue(count, count - 1),
If((count == 0) | (idle == 0),
NextValue(found, (count == 0)),
NextValue(count, burst_cycles - 1),
NextState("TBURST")
)
)
# LFPS Generator -----------------------------------------------------------------------------------
class LFPSBurstGenerator(Module):
"""LFPS Burst Generator
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
self.start = Signal() # i
self.done = Signal() # o
self.length = Signal(32) # i
# Transceiver
self.tx_idle = Signal(reset=1) # o
self.tx_pattern = Signal(20) # o
# # #
# Assertions -------------------------------------------------------------------------------
# The LFPS Burst has a minimum and maximum allowed period.
assert lfps_clk_freq >= lfps_clk_freq_min
assert lfps_clk_freq <= lfps_clk_freq_max
# LFPS Burst Clock generation --------------------------------------------------------------
clk = Signal()
clk_timer = WaitTimer(ceil(sys_clk_freq/(2*lfps_clk_freq)) - 1)
clk_timer = ResetInserter()(clk_timer)
self.submodules += clk_timer
self.comb += clk_timer.wait.eq(~clk_timer.done)
self.sync += If(clk_timer.done, clk.eq(~clk))
# LFPS Burst generation --------------------------------------------------------------------
count = Signal.like(self.length)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.done.eq(1),
clk_timer.reset.eq(1),
NextValue(count, self.length),
If(self.start,
NextState("BURST")
)
)
fsm.act("BURST",
self.tx_idle.eq(0),
self.tx_pattern.eq(Replicate(clk, 20)),
NextValue(count, count - 1),
If(count == 0,
NextState("IDLE")
)
)
# LFPS Generator -----------------------------------------------------------------------------------
class LFPSGenerator(Module):
"""LFPS Generator
Generate a specific LFPS pattern on the TX lane. This module handles LFPS clock generation, LFPS
burst generation and repetition.
"""
def __init__(self, lfps_pattern, sys_clk_freq, lfps_clk_freq):
# Control
self.generate = Signal() # i
self.count = Signal(16) # o
# Transceiver
self.tx_idle = Signal() # o
self.tx_pattern = Signal(20) # o
# # #
# Burst Generator ------------------------------------------------------------------------
burst_generator = LFPSBurstGenerator(sys_clk_freq=sys_clk_freq, lfps_clk_freq=lfps_clk_freq)
self.submodules += burst_generator
# Burst Generation -------------------------------------------------------------------------
burst_repeat_count = Signal(32)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.tx_idle.eq(0),
If(self.generate,
self.tx_idle.eq(1),
NextValue(burst_generator.start, 1),
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)
)
)
fsm.act("RUN",
NextValue(burst_generator.start, 0),
self.tx_idle.eq(burst_generator.tx_idle),
self.tx_pattern.eq(burst_generator.tx_pattern),
NextValue(burst_repeat_count, burst_repeat_count - 1),
If(burst_repeat_count == 0,
NextState("IDLE"),
NextValue(self.count, self.count + 1)
),
)
# LFPS Unit ----------------------------------------------------------------------------------------
class LFPSUnit(Module):
"""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
self.tx_count = Signal(16) # o
# # #
# 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 Generators --------------------------------------------------------------------------
polling_generator = LFPSGenerator(PollingLFPS, sys_clk_freq, lfps_clk_freq)
self.submodules += polling_generator
self.comb += [
If(self.tx_polling,
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(polling_generator.count)
]