Created
October 8, 2021 08:58
-
-
Save goran-mahovlic/adf35ce348e856595a8e3e747c78e388 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# This file is part of LiteICLink. | |
# | |
# Copyright (c) 2019-2020 Florent Kermarrec <florent@enjoy-digital.fr> | |
# SPDX-License-Identifier: BSD-2-Clause | |
import sys | |
import argparse | |
from migen import * | |
from migen.genlib.resetsync import AsyncResetSynchronizer | |
from litex.build.generic_platform import * | |
from litex_boards.platforms import radiona_ulx4m | |
from litex.soc.cores.clock import * | |
from litex.soc.integration.soc_core import * | |
from litex.soc.integration.builder import * | |
from litex.soc.cores.code_8b10b import K | |
from liteiclink.serdes.serdes_ecp5 import SerDesECP5PLL, SerDesECP5 | |
# IOs ---------------------------------------------------------------------------------------------- | |
_transceiver_io = [ | |
# PCIe | |
("pcie_tx", 0, | |
Subsignal("p", Pins("W17")), | |
Subsignal("n", Pins("W18")), | |
#Subsignal("p", Pins("W4")), | |
#Subsignal("n", Pins("W5")), | |
), | |
("pcie_rx", 0, | |
Subsignal("p", Pins("Y14")), | |
Subsignal("n", Pins("Y15")), | |
#Subsignal("p", Pins("Y5")), | |
#Subsignal("n", Pins("Y6")), | |
), | |
] | |
# CRG ---------------------------------------------------------------------------------------------- | |
class _CRG(Module): | |
def __init__(self, platform, sys_clk_freq, refclk_from_pll, refclk_freq): | |
self.clock_domains.cd_sys = ClockDomain() | |
self.clock_domains.cd_por = ClockDomain(reset_less=True) | |
self.clock_domains.cd_ref = ClockDomain(reset_less=True) | |
# # # | |
# Clk / Rst | |
clk25 = platform.request("clk25") | |
rst_n = platform.request("rst_n") | |
platform.add_period_constraint(clk25, 1e9/25e6) | |
# Power on reset | |
por_count = Signal(16, reset=2**16-1) | |
por_done = Signal() | |
self.comb += self.cd_por.clk.eq(ClockSignal()) | |
self.comb += por_done.eq(por_count == 0) | |
self.sync.por += If(~por_done, por_count.eq(por_count - 1)) | |
# PLL | |
self.submodules.pll = pll = ECP5PLL() | |
pll.register_clkin(clk25, 25e6) | |
pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False) | |
if not refclk_from_pll: | |
pll.create_clkout(self.cd_ref, refclk_freq) | |
self.specials += AsyncResetSynchronizer(self.cd_sys, ~por_done | ~pll.locked | ~rst_n) | |
# SerDesTestSoC ------------------------------------------------------------------------------------ | |
class SerDesTestSoC(SoCMini): | |
def __init__(self, platform, connector="pcie", linerate=2.5e9): | |
assert connector in ["pcie", "sma"] | |
sys_clk_freq = int(25e6) | |
# SoCMini ---------------------------------------------------------------------------------- | |
SoCMini.__init__(self, platform, sys_clk_freq, | |
ident = "LiteICLink bench on Versa ECP5", | |
ident_version = True, | |
with_uart = True, | |
uart_name = "bridge" | |
) | |
# CRG -------------------------------------------------------------------------------------- | |
refclk_from_pll = { | |
1.25e9: False, # SGMII | |
1.5e9: True, # SATA Gen1 | |
2.5e9: False, # PCIe Gen1 | |
3.0e9: True, # SATA Gen2 | |
5.0e9: True, # PCIe Gen2, USB3. | |
}[linerate] | |
refclk_freq = { | |
1.25e9: 156.25e6, | |
1.5e9: 150e6, | |
2.5e9: 156.25e6, | |
3.0e9: 150e6, | |
5.0e9: 250e6}[linerate] | |
self.submodules.crg = _CRG(platform, sys_clk_freq, refclk_from_pll, refclk_freq) | |
# SerDes RefClk ---------------------------------------------------------------------------- | |
if not refclk_from_pll: | |
refclk = self.crg.cd_ref.clk | |
else: | |
#refclk = self.crg.cd_ref.clk | |
refclk_pads = platform.request("refclk", 1) | |
self.comb += platform.request("refclk_en").eq(1) | |
self.comb += platform.request("refclk_rst_n").eq(1) | |
refclk = Signal() | |
self.specials.extref0 = Instance("EXTREFB", | |
i_REFCLKP = refclk_pads.p, | |
i_REFCLKN = refclk_pads.n, | |
o_REFCLKO = refclk, | |
p_REFCK_PWDNB = "0b1", | |
p_REFCK_RTERM = "0b1", # 100 Ohm | |
) | |
self.extref0.attr.add(("LOC", "EXTREF0")) | |
# SerDes PLL ------------------------------------------------------------------------------- | |
serdes_pll = SerDesECP5PLL(refclk, refclk_freq=refclk_freq, linerate=linerate) | |
self.submodules += serdes_pll | |
print(serdes_pll) | |
# SerDes ----------------------------------------------------------------------------------- | |
tx_pads = platform.request(connector + "_tx") | |
rx_pads = platform.request(connector + "_rx") | |
channel = 1 if connector == "sma" else 0 | |
self.submodules.serdes1 = serdes1 = SerDesECP5(serdes_pll, tx_pads, rx_pads, | |
channel = channel, | |
data_width = 20) | |
serdes1.add_stream_endpoints() | |
serdes1.add_controls() | |
serdes1.add_clock_cycles() | |
self.add_csr("serdes1") | |
platform.add_period_constraint(serdes1.txoutclk, 1e9/serdes1.tx_clk_freq) | |
platform.add_period_constraint(serdes1.rxoutclk, 1e9/serdes1.rx_clk_freq) | |
# Test ------------------------------------------------------------------------------------- | |
counter = Signal(32) | |
self.sync.tx += counter.eq(counter + 1) | |
# K28.5 and slow counter --> TX | |
self.comb += [ | |
serdes1.sink.valid.eq(1), | |
serdes1.sink.ctrl.eq(0b1), | |
serdes1.sink.data[:8].eq(K(28, 5)), | |
serdes1.sink.data[8:].eq(counter[26:]), | |
] | |
# RX (slow counter) --> Leds | |
counter = Signal(8) | |
self.sync.rx += [ | |
serdes1.rx_align.eq(1), | |
serdes1.source.ready.eq(1), | |
# No word aligner, so look for K28.5 and redirect the other byte to the leds | |
If(serdes1.source.data[0:8] == K(28, 5), | |
counter.eq(serdes1.source.data[8:]), | |
).Else( | |
counter.eq(serdes1.source.data[0:]), | |
), | |
platform.request("user_led", 4).eq(~counter[0]), | |
platform.request("user_led", 5).eq(~counter[1]), | |
platform.request("user_led", 6).eq(~counter[2]), | |
platform.request("user_led", 7).eq(~counter[3]), | |
] | |
# Leds ------------------------------------------------------------------------------------- | |
sys_counter = Signal(32) | |
self.sync.sys += sys_counter.eq(sys_counter + 1) | |
self.comb += platform.request("user_led", 0).eq(sys_counter[26]) | |
rx_counter = Signal(32) | |
self.sync.rx += rx_counter.eq(rx_counter + 1) | |
self.comb += platform.request("user_led", 1).eq(rx_counter[26]) | |
tx_counter = Signal(32) | |
self.sync.tx += tx_counter.eq(rx_counter + 1) | |
self.comb += platform.request("user_led", 2).eq(tx_counter[26]) | |
# Analyzer --------------------------------------------------------------------------------- | |
from litescope import LiteScopeAnalyzer | |
self.submodules.analyzer = LiteScopeAnalyzer([ | |
serdes1.init.fsm, | |
serdes1.init.tx_lol, | |
serdes1.init.rx_lol, | |
], depth=512) | |
self.add_csr("analyzer") | |
# Debug ------------------------------------------------------------------------------------ | |
self.platform.add_extension([("debug", 0, Pins("user_led",3), IOStandard("LVCMOS33"))]) | |
self.comb += platform.request("debug").eq(serdes1.init.rx_lol) | |
# Build -------------------------------------------------------------------------------------------- | |
def main(): | |
parser = argparse.ArgumentParser(description="LiteICLink transceiver example on Versa ECP5") | |
parser.add_argument("--build", action="store_true", help="Build bitstream") | |
parser.add_argument("--load", action="store_true", help="Load bitstream (to SRAM)") | |
parser.add_argument("--toolchain", default="trellis", help="FPGA toolchain: trellis (default) or diamond") | |
parser.add_argument("--connector", default="pcie", help="Connector: pcie (default) or sma") | |
parser.add_argument("--linerate", default="2.5e9", help="Linerate (default: 2.5e9)") | |
args = parser.parse_args() | |
platform = radiona_ulx4m.Platform(toolchain=args.toolchain) | |
platform.add_extension(_transceiver_io) | |
soc = SerDesTestSoC(platform, | |
connector = args.connector, | |
linerate = float(args.linerate) | |
) | |
import time | |
time.sleep(1) # Yosys/NextPnr are too fast, add sleep to see LiteX logs :) | |
builder = Builder(soc, csr_csv="csr.csv") | |
builder.build(run=args.build) | |
if args.load: | |
prog = soc.platform.create_programmer() | |
prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".bit")) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment