mirror of
https://github.com/enjoy-digital/usb3_pipe.git
synced 2025-01-04 10:18:41 +08:00
253 lines
9.5 KiB
Python
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)
|
|
]
|