#!/usr/bin/env python3
from migen import *
from migen.genlib import cdc
import unittest
from .bitstuff import RxBitstuffRemover
from .clock import RxClockDataRecovery
from .detect import RxPacketDetect
from .nrzi import RxNRZIDecoder
from .shifter import RxShifter
from ..utils.packet import b, nrzi
from ..test.common import BaseUsbTestCase
[docs]class RxPipeline(Module):
def __init__(self):
self.reset = Signal()
# 12MHz USB alignment pulse in 48MHz clock domain
self.o_bit_strobe = Signal()
# Reset state is J
self.i_usbp = Signal(reset=1)
self.i_usbn = Signal(reset=0)
self.o_data_strobe = Signal()
self.o_data_payload = Signal(8)
self.o_pkt_start = Signal()
self.o_pkt_in_progress = Signal()
self.o_pkt_end = Signal()
# A reset condition is one where the device is in SE0 for more
# than 2.5 uS, which is ~30 clock cycles as 12 MHz.
self.o_reset = Signal()
reset_counter = Signal(7)
self.comb += self.o_reset.eq(reset_counter[6])
self.sync.usb_12 += [
If(self.i_usbp | self.i_usbn,
reset_counter.eq(0),
).Elif(~reset_counter[6],
reset_counter.eq(reset_counter + 1),
)
]
# 48MHz domain
# Clock recovery
clock_data_recovery = RxClockDataRecovery(self.i_usbp, self.i_usbn)
self.submodules.clock_data_recovery = ClockDomainsRenamer("usb_48")(clock_data_recovery)
self.comb += [
self.o_bit_strobe.eq(clock_data_recovery.line_state_valid),
]
# NRZI decoding
nrzi = RxNRZIDecoder()
self.submodules.nrzi = nrzi = ClockDomainsRenamer("usb_48")(nrzi)
self.comb += [
nrzi.i_valid.eq(self.o_bit_strobe),
nrzi.i_dj.eq(clock_data_recovery.line_state_dj),
nrzi.i_dk.eq(clock_data_recovery.line_state_dk),
nrzi.i_se0.eq(clock_data_recovery.line_state_se0),
]
# The packet detector asserts the reset of the pipeline.
reset = Signal()
detect = RxPacketDetect()
self.submodules.detect = detect = ClockDomainsRenamer("usb_48")(detect)
self.comb += [
detect.reset.eq(self.reset),
detect.i_valid.eq(nrzi.o_valid),
detect.i_se0.eq(nrzi.o_se0),
detect.i_data.eq(nrzi.o_data),
reset.eq(~detect.o_pkt_active),
]
bitstuff = RxBitstuffRemover()
self.submodules.bitstuff = ClockDomainsRenamer("usb_48")(bitstuff)
self.comb += [
bitstuff.reset.eq(~detect.o_pkt_active | self.reset),
bitstuff.i_valid.eq(nrzi.o_valid),
bitstuff.i_data.eq(nrzi.o_data),
]
last_reset = Signal()
self.sync.usb_48 += [
last_reset.eq(reset),
]
# 1bit->8bit (1byte) serial to parallel conversion
shifter = RxShifter(width=8)
self.submodules.shifter = shifter = ClockDomainsRenamer("usb_48")(shifter)
self.comb += [
shifter.reset.eq(last_reset),
shifter.i_data.eq(bitstuff.o_data),
shifter.i_valid.eq(~bitstuff.o_stall & detect.o_pkt_active),
]
# Cross the data from the 48MHz domain to the 12MHz domain
flag_start = Signal()
flag_end = Signal()
flag_valid = Signal()
payloadFifo = genlib.fifo.AsyncFIFO(8, 2)
self.submodules.payloadFifo = payloadFifo = ClockDomainsRenamer({"write":"usb_48", "read":"usb_12"})(payloadFifo)
self.comb += [
payloadFifo.din.eq(shifter.o_data[::-1]),
payloadFifo.we.eq(shifter.o_put),
self.o_data_payload.eq(payloadFifo.dout),
self.o_data_strobe.eq(payloadFifo.readable),
payloadFifo.re.eq(1),
]
flagsFifo = genlib.fifo.AsyncFIFO(2, 2)
self.submodules.flagsFifo = flagsFifo = ClockDomainsRenamer({"write":"usb_48", "read":"usb_12"})(flagsFifo)
self.comb += [
flagsFifo.din[1].eq(detect.o_pkt_start),
flagsFifo.din[0].eq(detect.o_pkt_end),
flagsFifo.we.eq(detect.o_pkt_start | detect.o_pkt_end),
flag_start.eq(flagsFifo.dout[1]),
flag_end.eq(flagsFifo.dout[0]),
flag_valid.eq(flagsFifo.readable),
flagsFifo.re.eq(1),
]
# Packet flag signals (in 12MHz domain)
self.comb += [
self.o_pkt_start.eq(flag_start & flag_valid),
self.o_pkt_end.eq(flag_end & flag_valid),
]
self.sync.usb_12 += [
If(self.o_pkt_start,
self.o_pkt_in_progress.eq(1),
).Elif(self.o_pkt_end,
self.o_pkt_in_progress.eq(0),
),
]