From b9f771e939753104f38ec15921eea14893ecf84b Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 7 Oct 2019 12:49:11 +0200 Subject: [PATCH] pocs: add pcie_screamer --- pocs/pcie_screamer/load.py | 5 + pocs/pcie_screamer/pcie_screamer.py | 321 +++++++++++++++++++++++++ pocs/pcie_screamer/test_rx_analyzer.py | 56 +++++ 3 files changed, 382 insertions(+) create mode 100755 pocs/pcie_screamer/load.py create mode 100755 pocs/pcie_screamer/pcie_screamer.py create mode 100755 pocs/pcie_screamer/test_rx_analyzer.py diff --git a/pocs/pcie_screamer/load.py b/pocs/pcie_screamer/load.py new file mode 100755 index 0000000..7f276c3 --- /dev/null +++ b/pocs/pcie_screamer/load.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from litex.build.xilinx import VivadoProgrammer + +prog = VivadoProgrammer() +prog.load_bitstream("build/gateware/top.bit") diff --git a/pocs/pcie_screamer/pcie_screamer.py b/pocs/pcie_screamer/pcie_screamer.py new file mode 100755 index 0000000..6437230 --- /dev/null +++ b/pocs/pcie_screamer/pcie_screamer.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 + +from migen import * +from migen.genlib.misc import WaitTimer +from migen.genlib.cdc import PulseSynchronizer, MultiReg + +from litex.build.generic_platform import * +from litex.build.xilinx import XilinxPlatform + +from litex.soc.cores.clock import * +from litex.soc.interconnect.csr import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.cores.uart import UARTWishboneBridge + +from litex.boards.platforms import nexys_video + +from litescope import LiteScopeAnalyzer + +from usb3_pipe.common import TSEQ, TS1, TS2 +from usb3_pipe.scrambler import Scrambler +from usb3_pipe.gtp_7series import GTPQuadPLL, GTP +from usb3_pipe.lfps import LFPSReceiver, LFPSTransmitter +from usb3_pipe.ordered_set import OrderedSetReceiver, OrderedSetTransmitter + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + ("clk100", 0, Pins("R4"), IOStandard("LVCMOS33")), + + ("user_led", 0, Pins("AB1"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("AB8"), IOStandard("LVCMOS33")), + + ("user_btn", 0, Pins("AA1"), IOStandard("LVCMOS33")), + ("user_btn", 1, Pins("AB6"), IOStandard("LVCMOS33")), + + ("serial", 0, + Subsignal("tx", Pins("T1")), + Subsignal("rx", Pins("U1")), + IOStandard("LVCMOS33"), + ), + + ("pcie_tx", 0, + Subsignal("p", Pins("B6")), + Subsignal("n", Pins("A6")), + ), + + ("pcie_rx", 0, + Subsignal("p", Pins("B10")), + Subsignal("n", Pins("A10")), + ), +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(XilinxPlatform): + def __init__(self): + XilinxPlatform.__init__(self, "xc7a35t-fgg484-2", _io, toolchain="vivado") + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq): + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_oob = ClockDomain() + self.clock_domains.cd_clk125 = ClockDomain() + + # # # + + clk100 = platform.request("clk100") + + self.cd_sys.clk.attr.add("keep") + self.cd_clk125.clk.attr.add("keep") + + self.submodules.pll = pll = S7PLL(speedgrade=-2) + pll.register_clkin(clk100, 100e6) + pll.create_clkout(self.cd_sys, sys_clk_freq) + pll.create_clkout(self.cd_oob, sys_clk_freq/8) + pll.create_clkout(self.cd_clk125, 125e6) + +# USB3SoC ------------------------------------------------------------------------------------------ + +class USB3SoC(SoCMini): + def __init__(self, platform, + with_lfps_analyzer=False, + with_rx_analyzer=True, + with_tx_analyzer=True, + with_fsm_analyzer=True): + + sys_clk_freq = int(100e6) + SoCMini.__init__(self, platform, sys_clk_freq, ident="USB3SoC", ident_version=True) + + # CRG -------------------------------------------------------------------------------------- + self.submodules.crg = _CRG(platform, sys_clk_freq) + + # Serial bridge ---------------------------------------------------------------------------- + self.submodules.serial_bridge = UARTWishboneBridge(platform.request("serial"), sys_clk_freq) + self.add_wb_master(self.serial_bridge.wishbone) + + # Transceiver ------------------------------------------------------------------------------ + # qpll + qpll = GTPQuadPLL(ClockSignal("clk125"), 125e6, 5e9) + print(qpll) + self.submodules += qpll + platform.add_platform_command("set_property SEVERITY {{Warning}} [get_drc_checks REQP-49]") + + # gtp + tx_pads = platform.request("pcie_tx") + rx_pads = platform.request("pcie_rx") + self.submodules.gtp = gtp = GTP(qpll, tx_pads, rx_pads, sys_clk_freq, + data_width=20, + clock_aligner=False, + tx_buffer_enable=True, + rx_buffer_enable=True) + gtp.add_stream_endpoints() + gtp.add_controls() + self.add_csr("gtp") + gtp._tx_enable.storage.reset = 1 # Enabled by default + gtp._rx_enable.storage.reset = 1 # Enabled by default + gtp.cd_tx.clk.attr.add("keep") + gtp.cd_rx.clk.attr.add("keep") + platform.add_period_constraint(gtp.cd_tx.clk, 1e9/gtp.tx_clk_freq) + platform.add_period_constraint(gtp.cd_rx.clk, 1e9/gtp.rx_clk_freq) + self.platform.add_false_path_constraints( + self.crg.cd_sys.clk, + gtp.cd_tx.clk, + gtp.cd_rx.clk) + + # Override GTP parameters/signals for LFPS ------------------------------------------------- + txelecidle = Signal() + rxelecidle = Signal() + gtp.gtp_params.update( + p_PCS_RSVD_ATTR = 0x000000000100, # bit 8 enable OOB + #p_RXOOB_CLK_CFG = "FABRIC", + #i_SIGVALIDCLK = ClockSignal("oob"), + p_RXOOB_CLK_CFG = "PMA", + i_RXOOBRESET = 0b0, + p_RXOOB_CFG = 0b0000110, + i_RXELECIDLEMODE = 0b00, + o_RXELECIDLE = rxelecidle, + i_TXELECIDLE = txelecidle) + + # LFPS Polling Receive --------------------------------------------------------------------- + lfps_receiver = LFPSReceiver(sys_clk_freq=sys_clk_freq) + self.submodules += lfps_receiver + self.comb += lfps_receiver.idle.eq(rxelecidle) + + # LFPS Polling Transmit -------------------------------------------------------------------- + lfps_transmitter = LFPSTransmitter(sys_clk_freq=sys_clk_freq, lfps_clk_freq=25e6) + self.submodules += lfps_transmitter + self.comb += [ + If(lfps_transmitter.polling, + txelecidle.eq(lfps_transmitter.tx_idle), + gtp.tx_produce_pattern.eq(~lfps_transmitter.tx_idle), + gtp.tx_pattern.eq(lfps_transmitter.tx_pattern) + ).Else( + txelecidle.eq(0), + gtp.tx_produce_pattern.eq(0) + + ) + ] + + # TSEQ Receiver ---------------------------------------------------------------------------- + tseq_receiver = OrderedSetReceiver(ordered_set=TSEQ, n_ordered_sets=1024, data_width=16) + tseq_receiver = ClockDomainsRenamer("rx")(tseq_receiver) + self.submodules += tseq_receiver + + # TS1 Receiver ----------------------------------------------------------------------------- + ts1_receiver = OrderedSetReceiver(ordered_set=TS1, n_ordered_sets=16, data_width=16) + ts1_receiver = ClockDomainsRenamer("rx")(ts1_receiver) + self.submodules += ts1_receiver + + # TS2 Receiver ----------------------------------------------------------------------------- + ts2_receiver = OrderedSetReceiver(ordered_set=TS2, n_ordered_sets=1024, data_width=16) + ts2_receiver = ClockDomainsRenamer("rx")(ts2_receiver) + self.submodules += ts2_receiver + + # TS2 Transmitter -------------------------------------------------------------------------- + ts2_transmitter = OrderedSetTransmitter(ordered_set=TS2, n_ordered_sets=1024, data_width=16) + ts2_transmitter = ClockDomainsRenamer("tx")(ts2_transmitter) + self.submodules += ts2_transmitter + + # Scrambler -------------------------------------------------------------------------------- + scrambler = Scrambler() + scrambler = ClockDomainsRenamer("tx")(scrambler) + self.submodules += scrambler + + # Hacky Startup FSM (just to experiment on hardware) --------------------------------------- + tseq_det_sync = PulseSynchronizer("rx", "tx") + ts1_det_sync = PulseSynchronizer("rx", "tx") + ts2_det_sync = PulseSynchronizer("rx", "tx") + self.submodules += tseq_det_sync, ts1_det_sync, ts2_det_sync + self.comb += [ + tseq_det_sync.i.eq(tseq_receiver.detected), + ts1_det_sync.i.eq(ts1_receiver.detected), + ts2_det_sync.i.eq(ts2_receiver.detected), + ] + + fsm = FSM(reset_state="POLLING-LFPS") + fsm = ClockDomainsRenamer("tx")(fsm) + fsm = ResetInserter()(fsm) + self.submodules += fsm + self.comb += fsm.reset.eq(lfps_receiver.polling) + fsm.act("POLLING-LFPS", + scrambler.reset.eq(1), + gtp.rx_align.eq(1), + lfps_transmitter.polling.eq(1), + NextValue(ts2_transmitter.send, 0), + NextState("WAIT-TSEQ"), + ) + fsm.act("WAIT-TSEQ", + gtp.rx_align.eq(1), + lfps_transmitter.polling.eq(1), + gtp.source.connect(tseq_receiver.sink), + If(tseq_det_sync.o, + NextState("SEND-POLLING-LFPS-WAIT-TS1") + ) + ) + fsm.act("SEND-POLLING-LFPS-WAIT-TS1", + gtp.rx_align.eq(0), + gtp.source.connect(ts1_receiver.sink), + If(ts1_det_sync.o, + NextValue(ts2_transmitter.send, 1), + NextState("SEND-TS2-WAIT-TS2") + ) + ) + ts2_det = Signal() + fsm.act("SEND-TS2-WAIT-TS2", + gtp.rx_align.eq(0), + gtp.source.connect(ts2_receiver.sink), + ts2_transmitter.source.connect(gtp.sink), + NextValue(ts2_det, ts2_det | ts2_det_sync.o), + NextValue(ts2_transmitter.send, 0), + If(ts2_transmitter.done, + If(ts2_det, + NextState("READY") + ).Else( + NextValue(ts2_transmitter.send, 1) + ) + ) + ) + fsm.act("READY", + gtp.rx_align.eq(0), + scrambler.sink.valid.eq(1), + scrambler.source.connect(gtp.sink), + ) + + # Leds ------------------------------------------------------------------------------------- + self.comb += platform.request("user_led", 0).eq(gtp.tx_ready & gtp.rx_ready) + self.comb += platform.request("user_led", 1).eq(rxelecidle) + #polling_timer = WaitTimer(int(sys_clk_freq*1e-1)) + #self.submodules += polling_timer + #self.comb += [ + # polling_timer.wait.eq(~lfps_receiver.polling), + # platform.request("user_led", 1).eq(~polling_timer.done) + #] + + # LFPS Analyzer ---------------------------------------------------------------------------- + if with_lfps_analyzer: + analyzer_signals = [ + rxelecidle, + txelecidle, + + lfps_receiver.polling, + lfps_receiver.count, + lfps_receiver.found, + lfps_receiver.fsm, + ] + self.submodules.lfps_analyzer = LiteScopeAnalyzer(analyzer_signals, 32768, clock_domain="sys", csr_csv="lfps_analyzer.csv") + self.add_csr("lfps_analyzer") + + # RX Analyzer ------------------------------------------------------------------------------ + if with_rx_analyzer: + analyzer_signals = [ + fsm, + gtp.source, + tseq_receiver.detected, + ts1_receiver.detected, + ts1_receiver.reset, + ts1_receiver.loopback, + ts1_receiver.scrambling, + ts2_receiver.detected, + ts2_receiver.reset, + ts2_receiver.loopback, + ts2_receiver.scrambling + ] + self.submodules.rx_analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, clock_domain="rx", csr_csv="rx_analyzer.csv") + self.add_csr("rx_analyzer") + + # TX Analyzer ------------------------------------------------------------------------------ + if with_tx_analyzer: + analyzer_signals = [ + fsm, + gtp.sink, + ts2_transmitter.send, + ts2_transmitter.done, + ] + self.submodules.tx_analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, clock_domain="tx", csr_csv="tx_analyzer.csv") + self.add_csr("tx_analyzer") + + # FSM Analyzer ----------------------------------------------------------------------------- + if with_fsm_analyzer: + analyzer_signals = [ + fsm, + tseq_det_sync.o, + ts1_det_sync.o, + ts2_det_sync.o, + ] + self.submodules.fsm_analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, clock_domain="sys", csr_csv="fsm_analyzer.csv") + self.add_csr("fsm_analyzer") + +# Build -------------------------------------------------------------------------------------------- + +def main(): + platform = Platform() + soc = USB3SoC(platform) + builder = Builder(soc, output_dir="build", csr_csv="csr.csv") + vns = builder.build() + +if __name__ == "__main__": + main() diff --git a/pocs/pcie_screamer/test_rx_analyzer.py b/pocs/pcie_screamer/test_rx_analyzer.py new file mode 100755 index 0000000..c4a9654 --- /dev/null +++ b/pocs/pcie_screamer/test_rx_analyzer.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import sys +import time + +from litex import RemoteClient +from litescope import LiteScopeAnalyzerDriver + +from usb3_pipe.common import TSEQ, TS1, TS2 + +wb = RemoteClient() +wb.open() + +# # # + +TSEQ_FIRST_WORD = int.from_bytes(TSEQ.to_bytes()[0:2], byteorder="little") +TS1_FIRST_WORD = int.from_bytes(TS1.to_bytes()[0:2], byteorder="little") + +# FPGA ID ------------------------------------------------------------------------------------------ +fpga_id = "" +for i in range(256): + c = chr(wb.read(wb.bases.identifier_mem + 4*i) & 0xff) + fpga_id += c + if c == "\0": + break +print("FPGA: " + fpga_id) + +# Enable Capture ----------------------------------------------------------------------------------- +wb.regs.gtp_tx_enable.write(1) +while (wb.regs.gtp_tx_ready.read() == 0): + pass +wb.regs.gtp_rx_enable.write(1) +while (wb.regs.gtp_rx_ready.read() == 0): + pass + +# Analyzer dump ------------------------------------------------------------------------------------ +analyzer = LiteScopeAnalyzerDriver(wb.regs, "rx_analyzer", debug=True) +analyzer.configure_subsampler(1) +#analyzer.configure_trigger(cond={ +# "gtp0_source_payload_ctrl": 0b01, +# "gtp0_source_payload_data": TSEQ_FIRST_WORD}) +analyzer.configure_trigger(cond={ + "gtp0_source_payload_ctrl": 0b11, + "gtp0_source_payload_data": TS1_FIRST_WORD}) +#analyzer.configure_trigger(cond={"soc_tseq_receiver_detected": 1}) +#analyzer.configure_trigger(cond={"soc_ts1_receiver_detected": 1}) +#analyzer.configure_trigger(cond={"soc_ts2_receiver_detected": 1}) +#analyzer.configure_trigger(cond={}) +analyzer.run(offset=32, length=256) +analyzer.wait_done() +analyzer.upload() +analyzer.save("analyzer.vcd") + +# # # + +wb.close()