#!/usr/bin/env python3
from migen import *
from migen.genlib import cdc
from ..test.common import BaseUsbTestCase
import unittest
[docs]class RxClockDataRecovery(Module):
"""RX Clock Data Recovery module.
RxClockDataRecovery synchronizes the USB differential pair with the FPGAs
clocks, de-glitches the differential pair, and recovers the incoming clock
and data.
Clock Domain
------------
usb_48 : 48MHz
Input Ports
-----------
Input ports are passed in via the constructor.
usbp_raw : Signal(1)
Raw USB+ input from the FPGA IOs, no need to synchronize.
usbn_raw : Signal(1)
Raw USB- input from the FPGA IOs, no need to synchronize.
Output Ports
------------
Output ports are data members of the module. All output ports are flopped.
The line_state_dj/dk/se0/se1 outputs are 1-hot encoded.
line_state_valid : Signal(1)
Asserted for one clock when the output line state is ready to be sampled.
line_state_dj : Signal(1)
Represents Full Speed J-state on the incoming USB data pair.
Qualify with line_state_valid.
line_state_dk : Signal(1)
Represents Full Speed K-state on the incoming USB data pair.
Qualify with line_state_valid.
line_state_se0 : Signal(1)
Represents SE0 on the incoming USB data pair.
Qualify with line_state_valid.
line_state_se1 : Signal(1)
Represents SE1 on the incoming USB data pair.
Qualify with line_state_valid.
"""
def __init__(self, usbp_raw, usbn_raw):
if False:
#######################################################################
# Synchronize raw USB signals
#
# We need to synchronize the raw USB signals with the usb_48 clock
# domain. MultiReg implements a multi-stage shift register that takes
# care of this for us. Without MultiReg we would have metastability
# issues.
#
usbp = Signal(reset=1)
usbn = Signal()
self.specials += cdc.MultiReg(usbp_raw, usbp, n=1, reset=1)
self.specials += cdc.MultiReg(usbn_raw, usbn, n=1)
else:
# Leave raw USB signals meta-stable. The synchronizer should clean
# them up.
usbp = usbp_raw
usbn = usbn_raw
#######################################################################
# Line State Recovery State Machine
#
# The receive path doesn't use a differential receiver. Because of
# this there is a chance that one of the differential pairs will appear
# to have changed to the new state while the other is still in the old
# state. The following state machine detects transitions and waits an
# extra sampling clock before decoding the state on the differential
# pair. This transition period # will only ever last for one clock as
# long as there is no noise on the line. If there is enough noise on
# the line then the data may be corrupted and the packet will fail the
# data integrity checks.
#
self.submodules.lsr = lsr = FSM()
dpair = Signal(2)
self.comb += dpair.eq(Cat(usbn, usbp))
# output signals for use by the clock recovery stage
line_state_dt = Signal()
line_state_dj = Signal()
line_state_dk = Signal()
line_state_se0 = Signal()
line_state_se1 = Signal()
# If we are in a transition state, then we can sample the pair and
# move to the next corresponding line state.
lsr.act("DT",
line_state_dt.eq(1),
Case(dpair, {
0b10 : NextState("DJ"),
0b01 : NextState("DK"),
0b00 : NextState("SE0"),
0b11 : NextState("SE1")
})
)
# If we are in a valid line state and the value of the pair changes,
# then we need to move to the transition state.
lsr.act("DJ", line_state_dj.eq(1), If(dpair != 0b10, NextState("DT")))
lsr.act("DK", line_state_dk.eq(1), If(dpair != 0b01, NextState("DT")))
lsr.act("SE0", line_state_se0.eq(1), If(dpair != 0b00, NextState("DT")))
lsr.act("SE1", line_state_se1.eq(1), If(dpair != 0b11, NextState("DT")))
#######################################################################
# Clock and Data Recovery
#
# The DT state from the line state recovery state machine is used to align to
# transmit clock. The line state is sampled in the middle of the bit time.
#
# Example of signal relationships
# -------------------------------
# line_state DT DJ DJ DJ DT DK DK DK DK DK DK DT DJ DJ DJ
# line_state_valid ________----____________----____________----________----____
# bit_phase 0 0 1 2 3 0 1 2 3 0 1 2 0 1 2
#
# We 4x oversample, so make the line_state_phase have
# 4 possible values.
line_state_phase = Signal(2)
self.line_state_valid = Signal()
self.line_state_dj = Signal()
self.line_state_dk = Signal()
self.line_state_se0 = Signal()
self.line_state_se1 = Signal()
self.sync += [
self.line_state_valid.eq(line_state_phase == 1),
If(line_state_dt,
# re-align the phase with the incoming transition
line_state_phase.eq(0),
# make sure we never assert valid on a transition
self.line_state_valid.eq(0),
).Else(
# keep tracking the clock by incrementing the phase
line_state_phase.eq(line_state_phase + 1)
),
# flop all the outputs to help with timing
self.line_state_dj.eq(line_state_dj),
self.line_state_dk.eq(line_state_dk),
self.line_state_se0.eq(line_state_se0),
self.line_state_se1.eq(line_state_se1),
]